Skip to content

Commit 28a3d6d

Browse files
committed
implement Encode/Decode for TxOut
1 parent a61cbab commit 28a3d6d

7 files changed

Lines changed: 248 additions & 7 deletions

File tree

rustfmt.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ ignore = [
22
"/",
33
"!/src/lib.rs",
44
"!/src/confidential/*.rs",
5+
"!/src/transaction/decoders.rs",
6+
"!/src/transaction/encoders.rs",
57
"!/src/transaction/pegin_witness.rs"
68
]
79
hard_tabs = false

src/internal_macros.rs

Lines changed: 136 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,29 @@ macro_rules! decoder_state_machine {
794794
///
795795
/// # Syntax
796796
///
797+
/// There are two invocation patterns:
798+
///
799+
/// ## Direct Error Wrapping
800+
/// ```ignore
801+
/// decoder_newtype! {
802+
/// /// Documentation for the decoder struct
803+
/// pub struct DecoderName(InnerDecoderType);
804+
///
805+
/// /// Documentation for the error struct
806+
/// pub struct ErrorName(InnerErrorName);
807+
/// const ERROR_DISPLAY = "output of the Display::fmt function";
808+
///
809+
/// impl Decode for TargetType {
810+
/// fn convert_inner(output_var) -> Result<_, InnerErrorName> {
811+
/// // Conversion logic -- returns `InnerErrorName` for errors!
812+
/// }
813+
/// }
814+
/// }
815+
/// ```
816+
///
817+
/// In this case `InnerErrorName` must be an already-existing error type.
818+
///
819+
/// ## Error Enum Wrapping
797820
/// ```ignore
798821
/// decoder_newtype! {
799822
/// /// Documentation for the decoder struct
@@ -811,12 +834,14 @@ macro_rules! decoder_state_machine {
811834
///
812835
/// impl Decode for TargetType {
813836
/// fn convert_inner(output_var) -> Result<_, ErrorName> {
814-
/// // Conversion logic
837+
/// // Conversion logic -- returns `ErrorName` for errors!
815838
/// }
816839
/// }
817840
/// }
818841
/// ```
819842
///
843+
/// In this case `InnerErrorName` will be generated as a private enum by the macro.
844+
///
820845
/// # Generated Code
821846
///
822847
/// The macro generates:
@@ -826,8 +851,10 @@ macro_rules! decoder_state_machine {
826851
/// - `Decoder` trait implementation with `push_bytes`, `end`, and `read_limit` methods
827852
/// - `Decode` trait implementation for the target type
828853
///
829-
/// The macro does **not** generate a `fmt::Display` or `std::error::Error` impl
830-
/// for `ErrorName`, so the user must implement these outside of the macro.
854+
/// **When using the enum pattern, the macro does not generate a `fmt::Display` or
855+
/// `std::error::Error` impl for `ErrorName`. The user must implement these outside
856+
/// of the macro.** When using the non-enum pattern, both `Display` and `Error` are
857+
/// generated by the macro.
831858
///
832859
/// # Parameters
833860
///
@@ -843,9 +870,15 @@ macro_rules! decoder_state_machine {
843870
///
844871
/// The conversion function:
845872
/// - Takes the inner decoder's output as its parameter
846-
/// - Returns `Result<TargetType, ErrorName>`
873+
/// - Returns `Result<TargetType, ErrorName>` for enum pattern, `Result<TargetType, InnerErrorName>`
874+
/// for direct-wrapping pattern.
847875
/// - Is called automatically when the inner decoder completes
848-
/// - Can return custom errors defined in the error enum
876+
/// - Can return custom errors defined in the error enum (enum pattern only)
877+
///
878+
/// # Pattern Selection
879+
///
880+
/// - Use **direct error wrapping** when conversion cannot fail or only needs to propagate the inner decoder's errors
881+
/// - Use **error enum wrapping** when conversion can fail with custom error types
849882
///
850883
/// # Error Handling
851884
///
@@ -856,6 +889,37 @@ macro_rules! decoder_state_machine {
856889
///
857890
/// # Example Usage
858891
///
892+
/// ## Direct Error Wrapping
893+
/// ```ignore
894+
/// decoder_newtype! {
895+
/// /// Decoder for the [`TxOut`] type.
896+
/// #[derive(Default)]
897+
/// pub struct TxOutDecoder(Decoder4< crate::confidential::AssetDecoder,
898+
/// crate::confidential::ValueDecoder,
899+
/// crate::confidential::NonceDecoder,
900+
/// crate::script::ScriptDecoder,
901+
/// >);
902+
///
903+
/// /// Decoder error for the [`TxOut`] type.
904+
/// #[derive(Clone, PartialEq, Eq, Debug)]
905+
/// pub struct TxOutDecoderError(Decoder4Error<
906+
/// crate::confidential::AssetDecoderError,
907+
/// crate::confidential::ValueDecoderError,
908+
/// crate::confidential::NonceDecoderError,
909+
/// crate::script::ScriptDecoderError,
910+
/// >);
911+
/// const ERROR_DISPLAY = "error decoding transaction output witness";
912+
///
913+
/// impl Decode for TxOut {
914+
/// fn convert_inner(output) -> Result<_, TxOutDecoderErrorInner> {
915+
/// let (asset, value, nonce, script_pubkey) = output;
916+
/// Ok(TxOut { asset, value, nonce, script_pubkey, witness: TxOutWitness::empty() })
917+
/// }
918+
/// }
919+
/// }
920+
/// ```
921+
///
922+
/// ## Error Enum Wrapping
859923
/// ```ignore
860924
/// decoder_newtype! {
861925
/// /// Decoder for range proofs
@@ -877,6 +941,73 @@ macro_rules! decoder_state_machine {
877941
/// }
878942
/// ```
879943
macro_rules! decoder_newtype {
944+
// With directly wrapped inner error
945+
(
946+
$(#[$($struct_attr:tt)*])*
947+
pub struct $outer_ty:ident($inner_ty:ty);
948+
949+
$(#[$($error_struct_attr:tt)*])*
950+
pub struct $error_ty:ident($inner_ty_error:ty);
951+
952+
const ERROR_DISPLAY = $error_display:expr;
953+
954+
impl Decode for $target_ty:ty {
955+
fn convert_inner($output:ident) -> Result<_, $error_inner1:ty> {
956+
$($output_fn_inner:tt)*
957+
}
958+
}
959+
) => {
960+
$(#[$($struct_attr)*])*
961+
pub struct $outer_ty($inner_ty);
962+
963+
$(#[$($error_struct_attr)*])*
964+
pub struct $error_ty($inner_ty_error);
965+
966+
impl core::fmt::Display for $error_ty {
967+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
968+
f.write_str($error_display)
969+
}
970+
}
971+
972+
impl std::error::Error for $error_ty {
973+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
974+
Some(&self.0)
975+
}
976+
}
977+
978+
impl $crate::encoding::Decoder for $outer_ty {
979+
type Output = $target_ty;
980+
type Error = $error_ty;
981+
982+
fn push_bytes(
983+
&mut self,
984+
bytes: &mut &[u8],
985+
) -> Result<$crate::encoding::DecoderStatus, Self::Error> {
986+
self.0
987+
.push_bytes(bytes)
988+
.map_err($error_ty)
989+
}
990+
991+
fn end(self) -> Result<Self::Output, Self::Error> {
992+
let $output = self.0
993+
.end()
994+
.map_err($error_ty)?;
995+
let converted = {
996+
$($output_fn_inner)*
997+
};
998+
converted.map_err($error_ty)
999+
}
1000+
1001+
fn read_limit(&self) -> usize {
1002+
self.0.read_limit()
1003+
}
1004+
}
1005+
1006+
impl $crate::encoding::Decode for $target_ty{
1007+
type Decoder = $outer_ty;
1008+
}
1009+
};
1010+
// With inner error enum
8801011
(
8811012
$(#[$($struct_attr:tt)*])*
8821013
pub struct $outer_ty:ident($inner_ty:ty);

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ pub use crate::sighash::SchnorrSighashType;
9797
pub use crate::transaction::{
9898
AssetIssuance, EcdsaSighashType, OutPoint, PeginData, PeginDataDecoder, PeginDataEncoder,
9999
PeginWitness, PeginWitnessDecoder, PeginWitnessDecoderError, PeginWitnessEncoder, PegoutData,
100-
Sequence, Transaction, TxIn, TxInWitness, TxOut, TxOutWitness, Witness, WitnessDecoder,
101-
WitnessDecoderError, WitnessEncoder,
100+
Sequence, Transaction, TxIn, TxInWitness, TxOut, TxOutDecoder, TxOutDecoderError, TxOutEncoder,
101+
TxOutWitness, Witness, WitnessDecoder, WitnessDecoderError, WitnessEncoder,
102102
};
103103

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

src/script.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use secp256k1_zkp::{Verification, Secp256k1};
3131
#[cfg(feature = "serde")] use serde;
3232

3333
use crate::encode::{self, Decodable, Encodable};
34+
use crate::encoding;
3435
use crate::{opcodes, ScriptHash, WScriptHash, PubkeyHash, WPubkeyHash};
3536

3637
use bitcoin::PublicKey;
@@ -943,6 +944,36 @@ impl Decodable for Script {
943944
}
944945
}
945946

947+
encoding::encoder_newtype_exact! {
948+
/// Encoder for a [`Script`].
949+
#[derive(Clone, Debug)]
950+
pub struct ScriptEncoder<'e>(encoding::PrefixedBytesEncoder<'e>);
951+
}
952+
953+
impl encoding::Encode for Script {
954+
type Encoder<'e> = ScriptEncoder<'e>;
955+
fn encoder(&self) -> Self::Encoder<'_> {
956+
ScriptEncoder::new(encoding::PrefixedBytesEncoder::new(self.as_bytes()))
957+
}
958+
}
959+
960+
decoder_newtype! {
961+
/// Decoder for a [`Script`].
962+
#[derive(Default)]
963+
pub struct ScriptDecoder(encoding::ByteVecDecoder);
964+
965+
/// Decoder error for the [`Script`] type.
966+
#[derive(Clone, PartialEq, Eq, Debug)]
967+
pub struct ScriptDecoderError(encoding::ByteVecDecoderError);
968+
const ERROR_DISPLAY = "failed to decode script";
969+
970+
impl Decode for Script {
971+
fn convert_inner(bytes) -> Result<_, _> {
972+
Ok(Script::from(bytes))
973+
}
974+
}
975+
}
976+
946977
#[cfg(test)]
947978
mod test {
948979
use bitcoin::PublicKey;

src/transaction/decoders.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! Transaction Decoders
4+
//!
5+
//! These are encapsulated because there are many of them, but in the end we
6+
//! only expose the top-level ones outside of this module.
7+
8+
use core::fmt;
9+
10+
use super::{TxOut, TxOutWitness};
11+
use crate::encoding::{Decoder4, Decoder4Error};
12+
13+
decoder_newtype! {
14+
/// Decoder for the [`TxOut`] type.
15+
#[derive(Default)]
16+
pub struct TxOutDecoder(Decoder4<
17+
crate::confidential::AssetDecoder,
18+
crate::confidential::ValueDecoder,
19+
crate::confidential::NonceDecoder,
20+
crate::script::ScriptDecoder,
21+
>);
22+
23+
24+
/// Decoder error for the [`TxOut`] type.
25+
#[derive(Clone, PartialEq, Eq, Debug)]
26+
pub struct TxOutDecoderError(Decoder4Error<
27+
crate::confidential::AssetDecoderError,
28+
crate::confidential::ValueDecoderError,
29+
crate::confidential::NonceDecoderError,
30+
crate::script::ScriptDecoderError,
31+
>);
32+
const ERROR_DISPLAY = "error decoding transaction output witness";
33+
34+
impl Decode for TxOut {
35+
fn convert_inner(output) -> Result<_, TxOutDecoderErrorInner> {
36+
let (asset, value, nonce, script_pubkey) = output;
37+
Ok(TxOut { asset, value, nonce, script_pubkey, witness: TxOutWitness::empty() })
38+
}
39+
}
40+
}

src/transaction/encoders.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! Transaction Encoders
4+
//!
5+
//! These are encapsulated because there are many of them, but in the end we
6+
//! only expose the top-level ones outside of this module.
7+
8+
use super::TxOut;
9+
use crate::encoding::{encoder_newtype_exact, Encode, Encoder4};
10+
11+
encoder_newtype_exact! {
12+
/// Encoder for the [`TxOut`] type.
13+
#[derive(Clone, Debug)]
14+
pub struct TxOutEncoder<'e>(Encoder4<
15+
crate::confidential::AssetEncoder<'e>,
16+
crate::confidential::ValueEncoder<'e>,
17+
crate::confidential::NonceEncoder<'e>,
18+
crate::script::ScriptEncoder<'e>,
19+
>);
20+
}
21+
22+
impl Encode for TxOut {
23+
type Encoder<'e> = TxOutEncoder<'e>;
24+
25+
fn encoder(&self) -> Self::Encoder<'_> {
26+
TxOutEncoder::new(Encoder4::new(
27+
self.asset.encoder(),
28+
self.value.encoder(),
29+
self.nonce.encoder(),
30+
self.script_pubkey.encoder(),
31+
))
32+
}
33+
}

src/transaction/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
//! # Transactions
1616
//!
1717
18+
mod decoders;
19+
mod encoders;
1820
mod pegin_witness;
1921
mod witness;
2022

@@ -37,6 +39,8 @@ use secp256k1_zkp::{
3739
Tweak, ZERO_TWEAK,
3840
};
3941

42+
pub use self::decoders::{TxOutDecoder, TxOutDecoderError};
43+
pub use self::encoders::TxOutEncoder;
4044
pub use self::pegin_witness::{
4145
PeginData, PeginDataDecoder, PeginDataEncoder,
4246
PeginWitness, PeginWitnessDecoder, PeginWitnessDecoderError, PeginWitnessEncoder};

0 commit comments

Comments
 (0)