Skip to content

Commit 02ab70d

Browse files
committed
implement Encode/Decode for TxOutWitness
1 parent 28a3d6d commit 02ab70d

4 files changed

Lines changed: 149 additions & 6 deletions

File tree

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ pub use crate::transaction::{
9898
AssetIssuance, EcdsaSighashType, OutPoint, PeginData, PeginDataDecoder, PeginDataEncoder,
9999
PeginWitness, PeginWitnessDecoder, PeginWitnessDecoderError, PeginWitnessEncoder, PegoutData,
100100
Sequence, Transaction, TxIn, TxInWitness, TxOut, TxOutDecoder, TxOutDecoderError, TxOutEncoder,
101-
TxOutWitness, Witness, WitnessDecoder, WitnessDecoderError, WitnessEncoder,
101+
TxOutWitness, TxOutWitnessDecoder, TxOutWitnessDecoderError, TxOutWitnessEncoder, Witness,
102+
WitnessDecoder, WitnessDecoderError, WitnessEncoder,
102103
};
103104

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

src/transaction/decoders.rs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,86 @@
88
use core::fmt;
99

1010
use super::{TxOut, TxOutWitness};
11-
use crate::encoding::{Decoder4, Decoder4Error};
11+
use crate::encoding::{
12+
Decode, Decoder, Decoder2, Decoder2Error, Decoder4, Decoder4Error, DecoderStatus,
13+
};
14+
15+
decoder_newtype! {
16+
/// Decoder for the [`TxOutWitness`] type.
17+
#[derive(Default)]
18+
pub struct TxOutWitnessDecoder(Decoder2<
19+
crate::confidential::SurjectionProofDecoder,
20+
crate::confidential::RangeProofDecoder,
21+
>);
22+
23+
/// Decoder error for the [`TxOutWitness`] type.
24+
#[derive(Clone, PartialEq, Eq, Debug)]
25+
pub struct TxOutWitnessDecoderError(Decoder2Error<
26+
crate::confidential::SurjectionProofDecoderError,
27+
crate::confidential::RangeProofDecoderError,
28+
>);
29+
const ERROR_DISPLAY = "error decoding transaction output witness";
30+
31+
impl Decode for TxOutWitness {
32+
fn convert_inner(output) -> Result<_, TxOutWitnessDecoderErrorInner> {
33+
let (surjection_proof, rangeproof) = output;
34+
Ok(TxOutWitness {surjection_proof, rangeproof })
35+
}
36+
}
37+
}
38+
39+
/// An decoder for the witnesses in a sequence of [`TxOut`]s.
40+
///
41+
/// Comsumes a vec of [`TxOut`]s on construction and then yields that
42+
/// same vector, with the witness fields overwritten.
43+
#[derive(Default)]
44+
struct TxOutWitnessesDecoder {
45+
txouts: Vec<TxOut>,
46+
index: usize,
47+
// Invariant: if this is Some then
48+
decoder: Option<TxOutWitnessDecoder>,
49+
}
50+
51+
impl TxOutWitnessesDecoder {
52+
#[allow(dead_code)] // will be used in the Transaction Encode/Decode commit
53+
fn new(txouts: Vec<TxOut>) -> Self { Self { txouts, index: 0, decoder: None } }
54+
}
55+
56+
impl Decoder for TxOutWitnessesDecoder {
57+
type Output = Vec<TxOut>;
58+
type Error = TxOutWitnessDecoderError;
59+
60+
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<DecoderStatus, Self::Error> {
61+
loop {
62+
let Some(next_txout) = self.txouts.get_mut(self.index) else {
63+
return Ok(DecoderStatus::Ready);
64+
};
65+
66+
let mut decoder = self.decoder.take().unwrap_or_else(TxOutWitness::decoder);
67+
if decoder.push_bytes(bytes)?.needs_more() {
68+
self.decoder = Some(decoder);
69+
return Ok(DecoderStatus::NeedsMore);
70+
}
71+
next_txout.witness = decoder.end()?;
72+
self.index += 1;
73+
}
74+
}
75+
76+
fn end(mut self) -> Result<Self::Output, Self::Error> {
77+
loop {
78+
let Some(last_txout) = self.txouts.get_mut(self.index) else {
79+
return Ok(self.txouts);
80+
};
81+
82+
last_txout.witness = self.decoder.take().unwrap_or_else(TxOutWitness::decoder).end()?;
83+
self.index += 1;
84+
}
85+
}
86+
87+
fn read_limit(&self) -> usize {
88+
self.decoder.as_ref().map_or(0, TxOutWitnessDecoder::read_limit)
89+
}
90+
}
1291

1392
decoder_newtype! {
1493
/// Decoder for the [`TxOut`] type.

src/transaction/encoders.rs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,71 @@
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::TxOut;
9-
use crate::encoding::{encoder_newtype_exact, Encode, Encoder4};
8+
use super::{TxOut, TxOutWitness};
9+
use crate::encoding::{encoder_newtype_exact, Encode, Encoder, Encoder2, Encoder4, EncoderStatus};
10+
11+
encoder_newtype_exact! {
12+
/// Encoder for the [`TxOutWitness`] type.
13+
#[derive(Clone, Debug)]
14+
pub struct TxOutWitnessEncoder<'e>(Encoder2<
15+
crate::confidential::SurjectionProofEncoder<'e>,
16+
crate::confidential::RangeProofEncoder<'e>,
17+
>);
18+
}
19+
20+
impl Encode for TxOutWitness {
21+
type Encoder<'e> = TxOutWitnessEncoder<'e>;
22+
23+
fn encoder(&self) -> Self::Encoder<'_> {
24+
TxOutWitnessEncoder::new(Encoder2::new(
25+
self.surjection_proof.encoder(),
26+
self.rangeproof.encoder(),
27+
))
28+
}
29+
}
30+
31+
/// An encoder for the witnesses in a sequence of [`TxOut`]s.
32+
#[derive(Clone, Debug)]
33+
struct TxOutWitnessesEncoder<'e> {
34+
txouts: &'e [TxOut],
35+
cur_enc: Option<TxOutWitnessEncoder<'e>>,
36+
}
37+
38+
impl<'e> TxOutWitnessesEncoder<'e> {
39+
#[allow(dead_code)] // will be used in the Transaction Encode/Decode commit
40+
fn new(txouts: &'e [TxOut]) -> Self {
41+
Self { txouts, cur_enc: txouts.first().map(|txout| txout.witness.encoder()) }
42+
}
43+
}
44+
45+
impl Encoder for TxOutWitnessesEncoder<'_> {
46+
fn current_chunk(&self) -> &[u8] {
47+
self.cur_enc.as_ref().map(Encoder::current_chunk).unwrap_or_default()
48+
}
49+
50+
fn advance(&mut self) -> EncoderStatus {
51+
let Some(cur) = self.cur_enc.as_mut() else {
52+
return EncoderStatus::Finished;
53+
};
54+
55+
loop {
56+
if cur.advance().has_more() {
57+
return EncoderStatus::HasMore;
58+
}
59+
// self.inputs guaranteed to be non-empty if cur_enc is non-None.
60+
self.txouts = &self.txouts[1..];
61+
if let Some(txout) = self.txouts.first() {
62+
*cur = txout.witness.encoder();
63+
if !cur.current_chunk().is_empty() {
64+
return EncoderStatus::HasMore;
65+
}
66+
} else {
67+
self.cur_enc = None;
68+
return EncoderStatus::Finished;
69+
}
70+
}
71+
}
72+
}
1073

1174
encoder_newtype_exact! {
1275
/// Encoder for the [`TxOut`] type.

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::{TxOutDecoder, TxOutDecoderError};
43-
pub use self::encoders::TxOutEncoder;
42+
pub use self::decoders::{TxOutDecoder, TxOutDecoderError, TxOutWitnessDecoder, TxOutWitnessDecoderError};
43+
pub use self::encoders::{TxOutEncoder, TxOutWitnessEncoder};
4444
pub use self::pegin_witness::{
4545
PeginData, PeginDataDecoder, PeginDataEncoder,
4646
PeginWitness, PeginWitnessDecoder, PeginWitnessDecoderError, PeginWitnessEncoder};

0 commit comments

Comments
 (0)