Skip to content

Commit faf6ab5

Browse files
committed
implement Encode/Decode for Transaction
1 parent b7b5886 commit faf6ab5

5 files changed

Lines changed: 203 additions & 29 deletions

File tree

src/lib.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,12 @@ pub use crate::transaction::{
9898
AssetIssuance, AssetIssuanceDecoder, AssetIssuanceDecoderError, AssetIssuanceEncoder,
9999
EcdsaSighashType, OutPoint, PeginData, PeginDataDecoder, PeginDataEncoder, PeginWitness,
100100
PeginWitnessDecoder, PeginWitnessDecoderError, PeginWitnessEncoder, PegoutData, Sequence,
101-
SequenceDecoder, SequenceDecoderError, SequenceEncoder, Transaction, TxIn, TxInDecoder,
102-
TxInDecoderError, TxInEncoder, TxInWitness, TxInWitnessDecoder, TxInWitnessDecoderError,
103-
TxInWitnessEncoder, TxOut, TxOutDecoder, TxOutDecoderError, TxOutEncoder, TxOutWitness,
104-
TxOutWitnessDecoder, TxOutWitnessDecoderError, TxOutWitnessEncoder, Witness, WitnessDecoder,
105-
WitnessDecoderError, WitnessEncoder,
101+
SequenceDecoder, SequenceDecoderError, SequenceEncoder, Transaction, TransactionDecoder,
102+
TransactionDecoderError, TransactionEncoder, TxIn, TxInDecoder, TxInDecoderError, TxInEncoder,
103+
TxInWitness, TxInWitnessDecoder, TxInWitnessDecoderError, TxInWitnessEncoder, TxOut,
104+
TxOutDecoder, TxOutDecoderError, TxOutEncoder, TxOutWitness, TxOutWitnessDecoder,
105+
TxOutWitnessDecoderError, TxOutWitnessEncoder, Witness, WitnessDecoder, WitnessDecoderError,
106+
WitnessEncoder,
106107
};
107108

108109
// Encode a compact size to a slice without allocating

src/locktime.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use std::cmp::{PartialOrd, Ordering};
2222
use std::convert::TryFrom;
2323
use std::str::FromStr;
2424
use std::io::{Read, Write};
25+
use crate::encoding;
2526
use crate::error::ParseIntError;
2627
use crate::parse;
2728

@@ -274,6 +275,36 @@ impl fmt::Display for LockTime {
274275
}
275276
}
276277

278+
encoding::encoder_newtype_exact! {
279+
/// Encoder for the [`LockTime`] type.
280+
pub struct LockTimeEncoder<'e>(encoding::ArrayEncoder<4>);
281+
}
282+
283+
impl encoding::Encode for LockTime {
284+
type Encoder<'e> = LockTimeEncoder<'e>;
285+
286+
fn encoder(&self) -> Self::Encoder<'_> {
287+
LockTimeEncoder::new(encoding::ArrayEncoder::without_length_prefix(self.to_consensus_u32().to_le_bytes()))
288+
}
289+
}
290+
291+
decoder_newtype! {
292+
/// Decoder for the [`LockTime`] type.
293+
#[derive(Default)]
294+
pub struct LockTimeDecoder(encoding::ArrayDecoder<4>);
295+
296+
/// Decoder error for the [`LockTime`] type.
297+
#[derive(Clone, PartialEq, Eq, Debug)]
298+
pub struct LockTimeDecoderError(encoding::UnexpectedEofError);
299+
const ERROR_DISPLAY = "failed to decode locktime";
300+
301+
impl Decode for LockTime {
302+
fn convert_inner(bytes) -> Result<_, UnexpectedEofError> {
303+
Ok(LockTime::from_consensus(u32::from_le_bytes(bytes)))
304+
}
305+
}
306+
}
307+
277308
impl Encodable for LockTime {
278309
#[inline]
279310
fn consensus_encode<W: Write>(&self, w: W) -> Result<usize, encode::Error> {

src/transaction/decoders.rs

Lines changed: 124 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
use core::fmt;
99

1010
use super::{
11-
AssetIssuance, OutPoint, Script, Sequence, TxIn, TxInWitness, TxOut, TxOutWitness, Txid,
11+
AssetIssuance, OutPoint, Script, Sequence, Transaction, TxIn, TxInWitness, TxOut, TxOutWitness,
12+
Txid,
1213
};
1314
use crate::confidential::{RangeProofDecoder, RangeProofDecoderError};
1415
use crate::encoding::{
1516
ArrayDecoder, Decode, Decoder, Decoder2, Decoder2Error, Decoder3, Decoder4, Decoder4Error,
16-
DecoderStatus, UnexpectedEofError,
17+
DecoderStatus, UnexpectedEofError, VecDecoder,
1718
};
19+
use crate::locktime::{LockTime, LockTimeDecoder};
1820
use crate::{PeginWitnessDecoder, PeginWitnessDecoderError, WitnessDecoder, WitnessDecoderError};
1921

2022
/// Decoder for the [`OutPoint`] type.
@@ -130,9 +132,6 @@ impl std::error::Error for AssetIssuanceDecoderError {
130132
}
131133
}
132134

133-
fn vout_is_pegin(vout: u32) -> bool { vout != 0xffff_ffff && vout & (1 << 30) != 0 }
134-
fn vout_has_issuance(vout: u32) -> bool { vout != 0xffff_ffff && vout & (1 << 31) != 0 }
135-
136135
decoder_state_machine! {
137136
/// Decoder for the [`TxIn`] type.
138137
pub struct TxInDecoder(enum TxInDecoderInner {
@@ -146,17 +145,25 @@ decoder_state_machine! {
146145
>,
147146
=> transition_initial(output, ...) -> Result {
148147
let (mut outpoint, script_sig, sequence) = output;
149-
if vout_has_issuance(outpoint.vout) {
148+
let (is_pegin, has_issuance) = if outpoint.vout == 0xffff_ffff {
149+
(false, false)
150+
} else {
151+
let vout = outpoint.vout;
152+
outpoint.vout &= !((1 << 30) | (1 << 31));
153+
(vout & (1 << 30) != 0, vout & (1 << 31) != 0)
154+
};
155+
if has_issuance {
150156
Ok(TxInDecoderInner::AssetIssuance {
151157
decoder: AssetIssuanceDecoder::default(),
152-
outpoint, script_sig, sequence,
158+
outpoint, script_sig, sequence, is_pegin,
153159
})
154160
} else {
155-
let orig_vout = outpoint.vout;
156-
outpoint.vout &= !((1 << 30) | (1 << 31));
161+
if outpoint.vout != 0xffff_ffff {
162+
outpoint.vout &= !((1 << 30) | (1 << 31));
163+
}
157164
Ok(TxInDecoderInner::Done(TxIn {
158165
previous_output: outpoint,
159-
is_pegin: vout_is_pegin(orig_vout),
166+
is_pegin,
160167
script_sig,
161168
sequence,
162169
asset_issuance: AssetIssuance::null(),
@@ -169,18 +176,16 @@ decoder_state_machine! {
169176
decoder: AssetIssuanceDecoder,
170177
outpoint: OutPoint,
171178
script_sig: Script,
172-
sequence: Sequence
179+
sequence: Sequence,
180+
is_pegin: bool
173181
=> transition_asset_issuance(asset_issuance, ...) -> Result {
174182
if asset_issuance.is_null() {
175183
return Err(TxInDecoderErrorInner::SuperfluousIssuance);
176184
}
177185

178-
let orig_vout = outpoint.vout;
179-
let mut outpoint = outpoint;
180-
outpoint.vout &= !((1 << 30) | (1 << 31));
181186
Ok(TxInDecoderInner::Done(TxIn {
182187
previous_output: outpoint,
183-
is_pegin: vout_is_pegin(orig_vout),
188+
is_pegin,
184189
script_sig,
185190
sequence,
186191
asset_issuance,
@@ -292,7 +297,6 @@ struct TxInWitnessesDecoder {
292297
}
293298

294299
impl TxInWitnessesDecoder {
295-
#[allow(dead_code)] // will be used in the Transaction Encode/Decode commit
296300
fn new(txins: Vec<TxIn>) -> Self { Self { txins, index: 0, decoder: None } }
297301
}
298302

@@ -369,7 +373,6 @@ struct TxOutWitnessesDecoder {
369373
}
370374

371375
impl TxOutWitnessesDecoder {
372-
#[allow(dead_code)] // will be used in the Transaction Encode/Decode commit
373376
fn new(txouts: Vec<TxOut>) -> Self { Self { txouts, index: 0, decoder: None } }
374377
}
375378

@@ -437,3 +440,107 @@ decoder_newtype! {
437440
}
438441
}
439442
}
443+
444+
decoder_state_machine! {
445+
/// Decoder for the [`Transaction`] type.
446+
pub struct TransactionDecoder(enum TransactionDecoderInner {
447+
Done(Transaction),
448+
Errored,
449+
DecodingNonWitness {
450+
decoder: Decoder4<
451+
ArrayDecoder<4>,
452+
ArrayDecoder<1>,
453+
Decoder2<
454+
VecDecoder<TxIn>,
455+
VecDecoder<TxOut>,
456+
>,
457+
LockTimeDecoder,
458+
>,
459+
=> transition_non_witness(output, ...) -> Result {
460+
let (version, wit_flag, (input, output), lock_time) = output;
461+
match wit_flag {
462+
[0] => Ok(TransactionDecoderInner::Done(Transaction {
463+
version: u32::from_le_bytes(version),
464+
lock_time,
465+
input,
466+
output,
467+
})),
468+
[1] => Ok(TransactionDecoderInner::DecodingWitnesses {
469+
decoder: Decoder2::new(
470+
TxInWitnessesDecoder::new(input),
471+
TxOutWitnessesDecoder::new(output),
472+
),
473+
version: u32::from_le_bytes(version),
474+
lock_time,
475+
}),
476+
[x] => Err(TransactionDecoderErrorInner::InvalidWitnessFlag(x)),
477+
}
478+
}
479+
},
480+
DecodingWitnesses {
481+
decoder: Decoder2<
482+
TxInWitnessesDecoder,
483+
TxOutWitnessesDecoder,
484+
>,
485+
version: u32,
486+
lock_time: LockTime
487+
=> transition_witnesses(output, ...) -> Result {
488+
let (input, output) = output;
489+
if input.iter().all(|input| input.witness.is_empty()) &&
490+
output.iter().all(|output| output.witness.is_empty()) {
491+
Err(TransactionDecoderErrorInner::NoWitnesses)
492+
} else {
493+
Ok(TransactionDecoderInner::Done(Transaction {
494+
version,
495+
lock_time,
496+
input,
497+
output,
498+
}))
499+
}
500+
}
501+
},
502+
});
503+
504+
/// Decoder error for the [`Transaction`] type.
505+
#[derive(Clone, PartialEq, Eq, Debug)]
506+
pub struct TransactionDecoderError(enum TransactionDecoderErrorInner {
507+
[macro-inserted decoder variants]
508+
InvalidWitnessFlag(u8),
509+
NoWitnesses,
510+
});
511+
}
512+
513+
impl Default for TransactionDecoder {
514+
fn default() -> Self {
515+
Self(TransactionDecoderInner::DecodingNonWitness { decoder: Decoder4::default() })
516+
}
517+
}
518+
519+
impl fmt::Display for TransactionDecoderError {
520+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
521+
use TransactionDecoderErrorInner as Inner;
522+
523+
match self.0 {
524+
Inner::DecodingNonWitness(..) =>
525+
f.write_str("failed to decode non-witness part of transaction"),
526+
Inner::DecodingWitnesses(..) => f.write_str("failed to decode transaction witnesses"),
527+
Inner::InvalidWitnessFlag(flag) => {
528+
write!(f, "invalid witness flag {flag} (must be 0 or 1)")
529+
}
530+
Inner::NoWitnesses => f.write_str("witness flag set but all witnesses were empty"),
531+
}
532+
}
533+
}
534+
535+
impl std::error::Error for TransactionDecoderError {
536+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
537+
use TransactionDecoderErrorInner as Inner;
538+
539+
match self.0 {
540+
Inner::DecodingNonWitness(ref e) => Some(e),
541+
Inner::DecodingWitnesses(ref e) => Some(e),
542+
Inner::InvalidWitnessFlag(_) => None,
543+
Inner::NoWitnesses => None,
544+
}
545+
}
546+
}

src/transaction/encoders.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
//! These are encapsulated because there are many of them, but in the end we
66
//! only expose the top-level ones outside of this module.
77
8-
use super::{AssetIssuance, Sequence, TxIn, TxInWitness, TxOut, TxOutWitness};
8+
use super::{AssetIssuance, Sequence, Transaction, TxIn, TxInWitness, TxOut, TxOutWitness};
99
use crate::confidential::RangeProofEncoder;
1010
use crate::encoding::{
11-
encoder_newtype_exact, ArrayEncoder, ArrayRefEncoder, Encode, Encoder, Encoder2, Encoder4,
12-
EncoderStatus,
11+
encoder_newtype, encoder_newtype_exact, ArrayEncoder, ArrayRefEncoder, Encode, Encoder,
12+
Encoder2, Encoder4, Encoder6, EncoderStatus, PrefixedSliceEncoder,
1313
};
14+
use crate::locktime::LockTimeEncoder;
1415
use crate::{PeginWitnessEncoder, WitnessEncoder};
1516

1617
// While we define an [`OutPointEncoder`] struct, we don't actually implement `Encode` or `Decode`
@@ -115,7 +116,6 @@ struct TxInWitnessesEncoder<'e> {
115116
}
116117

117118
impl<'e> TxInWitnessesEncoder<'e> {
118-
#[allow(dead_code)] // will be used in the Transaction Encode/Decode commit
119119
fn new(txins: &'e [TxIn]) -> Self {
120120
Self { txins, cur_enc: txins.first().map(|txin| txin.witness.encoder()) }
121121
}
@@ -202,7 +202,6 @@ struct TxOutWitnessesEncoder<'e> {
202202
}
203203

204204
impl<'e> TxOutWitnessesEncoder<'e> {
205-
#[allow(dead_code)] // will be used in the Transaction Encode/Decode commit
206205
fn new(txouts: &'e [TxOut]) -> Self {
207206
Self { txouts, cur_enc: txouts.first().map(|txout| txout.witness.encoder()) }
208207
}
@@ -260,3 +259,39 @@ impl Encode for TxOut {
260259
))
261260
}
262261
}
262+
263+
encoder_newtype! {
264+
/// Encoder for the [`Transaction`] type.
265+
pub struct TransactionEncoder<'e>(Encoder6<
266+
ArrayEncoder<4>,
267+
ArrayEncoder<1>,
268+
PrefixedSliceEncoder<'e, TxIn>,
269+
PrefixedSliceEncoder<'e, TxOut>,
270+
LockTimeEncoder<'e>,
271+
Option<Encoder2<
272+
TxInWitnessesEncoder<'e>,
273+
TxOutWitnessesEncoder<'e>,
274+
>>
275+
>);
276+
}
277+
278+
impl Encode for Transaction {
279+
type Encoder<'e> = TransactionEncoder<'e>;
280+
281+
fn encoder(&self) -> Self::Encoder<'_> {
282+
let witness_flag = self.has_witness();
283+
TransactionEncoder::new(Encoder6::new(
284+
ArrayEncoder::without_length_prefix(self.version.to_le_bytes()),
285+
ArrayEncoder::without_length_prefix([u8::from(witness_flag)]),
286+
PrefixedSliceEncoder::new(&self.input),
287+
PrefixedSliceEncoder::new(&self.output),
288+
self.lock_time.encoder(),
289+
witness_flag.then(|| {
290+
Encoder2::new(
291+
TxInWitnessesEncoder::new(&self.input),
292+
TxOutWitnessesEncoder::new(&self.output),
293+
)
294+
}),
295+
))
296+
}
297+
}

src/transaction/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ use secp256k1_zkp::{
3939
Tweak, ZERO_TWEAK,
4040
};
4141

42-
pub use self::decoders::{AssetIssuanceDecoder, AssetIssuanceDecoderError, SequenceDecoder, SequenceDecoderError, TxInDecoder, TxInDecoderError, TxInWitnessDecoder, TxInWitnessDecoderError, TxOutDecoder, TxOutDecoderError, TxOutWitnessDecoder, TxOutWitnessDecoderError};
43-
pub use self::encoders::{AssetIssuanceEncoder, SequenceEncoder, TxInEncoder, TxInWitnessEncoder, TxOutEncoder, TxOutWitnessEncoder};
42+
pub use self::decoders::{AssetIssuanceDecoder, AssetIssuanceDecoderError, SequenceDecoder, SequenceDecoderError, TransactionDecoder, TransactionDecoderError, TxInDecoder, TxInDecoderError, TxInWitnessDecoder, TxInWitnessDecoderError, TxOutDecoder, TxOutDecoderError, TxOutWitnessDecoder, TxOutWitnessDecoderError};
43+
pub use self::encoders::{AssetIssuanceEncoder, SequenceEncoder, TransactionEncoder, TxInEncoder, TxInWitnessEncoder, TxOutEncoder, TxOutWitnessEncoder};
4444
pub use self::pegin_witness::{
4545
PeginData, PeginDataDecoder, PeginDataEncoder,
4646
PeginWitness, PeginWitnessDecoder, PeginWitnessDecoderError, PeginWitnessEncoder};

0 commit comments

Comments
 (0)