Skip to content

Commit c7e16cd

Browse files
committed
confidential: implement Encode and Decode for rangeproof/surjectionproof
Introduces the decoder_newtype! macro. As with the other macro, this one was hand-written based on multiple examples of hand-written code, but I let Claude write the doccomment (which I made some minor touchups to). There is a similarly-named macro in rust-bitcoin `primitives` but this is *not* that macro. A later PR will elaborate this macro, but this is all we need for now.
1 parent aacd081 commit c7e16cd

4 files changed

Lines changed: 328 additions & 4 deletions

File tree

src/confidential/mod.rs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,14 @@ pub use self::asset::{
3636
pub use self::nonce::{
3737
Decoder as NonceDecoder, DecoderError as NonceDecoderError, Encoder as NonceEncoder, Nonce,
3838
};
39-
pub use self::range_proof::RangeProof;
40-
pub use self::surjection_proof::SurjectionProof;
39+
pub use self::range_proof::{
40+
Decoder as RangeProofDecoder, DecoderError as RangeProofDecoderError,
41+
Encoder as RangeProofEncoder, RangeProof,
42+
};
43+
pub use self::surjection_proof::{
44+
Decoder as SurjectionProofDecoder, DecoderError as SurjectionProofDecoderError,
45+
Encoder as SurjectionProofEncoder, SurjectionProof,
46+
};
4147
pub use self::value::{
4248
BlindingFactor as ValueBlindingFactor, Decoder as ValueDecoder,
4349
DecoderError as ValueDecoderError, Encoder as ValueEncoder, Value,
@@ -88,6 +94,52 @@ impl encoding::ExactSizeEncoder for CommitmentEncoder<'_> {
8894
}
8995
}
9096

97+
/// Because the rust-secp256k1-zkp proof types have no `as_bytes()` method, we need
98+
/// to serialize them to a byte vector before encoding them.
99+
///
100+
/// This encoder accomplishes that -- this situation never happens in rust-bitcoin
101+
/// so there is no "owned bytes encoder" shipped with bitcoin-consensus-encoding.
102+
#[derive(Clone, Debug)]
103+
struct PrefixedByteVecEncoder {
104+
prefix_encoder: Option<encoding::CompactSizeEncoder>,
105+
data: Vec<u8>,
106+
}
107+
108+
impl PrefixedByteVecEncoder {
109+
pub fn new(data: Vec<u8>) -> Self {
110+
Self { prefix_encoder: Some(encoding::CompactSizeEncoder::new(data.len())), data }
111+
}
112+
}
113+
114+
impl encoding::Encoder for PrefixedByteVecEncoder {
115+
fn current_chunk(&self) -> &[u8] {
116+
if let Some(ref enc) = self.prefix_encoder {
117+
return enc.current_chunk();
118+
}
119+
&self.data
120+
}
121+
122+
fn advance(&mut self) -> encoding::EncoderStatus {
123+
if let Some(ref mut enc) = self.prefix_encoder {
124+
if enc.advance().has_finished() {
125+
self.prefix_encoder = None;
126+
if self.data.is_empty() {
127+
return encoding::EncoderStatus::Finished;
128+
}
129+
}
130+
encoding::EncoderStatus::HasMore
131+
} else {
132+
encoding::EncoderStatus::Finished
133+
}
134+
}
135+
}
136+
137+
impl encoding::ExactSizeEncoder for PrefixedByteVecEncoder {
138+
fn len(&self) -> usize {
139+
self.prefix_encoder.as_ref().map_or(0, encoding::CompactSizeEncoder::len) + self.data.len()
140+
}
141+
}
142+
91143
/// Error decoding hexadecimal string into tweak-like value.
92144
#[derive(Debug, Clone, PartialEq, Eq)]
93145
pub enum TweakHexDecodeError {
@@ -191,6 +243,15 @@ mod tests {
191243
1, 1, 1, 1,
192244
];
193245

246+
#[test]
247+
fn prefixed_byte_encoder() {
248+
assert_eq!(encoding::drain_to_vec(&mut PrefixedByteVecEncoder::new(vec![])), [0]);
249+
assert_eq!(
250+
encoding::drain_to_vec(&mut PrefixedByteVecEncoder::new(vec![1, 2, 3])),
251+
[3, 1, 2, 3]
252+
);
253+
}
254+
194255
#[test]
195256
fn encode_length() {
196257
let val_encodings = [

src/confidential/range_proof.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! Range Proofs
44
55
use core::convert::TryInto;
6+
use core::fmt;
67
use std::io;
78

89
use secp256k1_zkp::rand::{CryptoRng, RngCore};
@@ -11,7 +12,7 @@ use secp256k1_zkp::{self, Generator, PedersenCommitment, Secp256k1, SecretKey, S
1112
use serde::{Deserializer, Serializer};
1213

1314
use crate::confidential::ValueBlindingFactor;
14-
use crate::encode;
15+
use crate::{encode, encoding};
1516

1617
/// A range proof, which represents a proof that a confidential value lies within
1718
/// some range (typically `[0, 2^64)`).
@@ -190,3 +191,56 @@ impl<'de> serde::Deserialize<'de> for RangeProof {
190191
.map(|inner| Self { inner: inner.map(Box::new) })
191192
}
192193
}
194+
195+
encoding::encoder_newtype_exact! {
196+
/// Encoder for the [`RangeProof`] type.
197+
#[derive(Clone, Debug)]
198+
pub struct Encoder<'e>(super::PrefixedByteVecEncoder);
199+
}
200+
201+
impl encoding::Encode for RangeProof {
202+
type Encoder<'e> = Encoder<'e>;
203+
204+
fn encoder(&self) -> Self::Encoder<'_> {
205+
Encoder::new(super::PrefixedByteVecEncoder::new(self.to_vec()))
206+
}
207+
}
208+
209+
decoder_newtype! {
210+
/// Decoder for the [`RangeProof`] type.
211+
#[derive(Default)]
212+
pub struct Decoder(encoding::ByteVecDecoder);
213+
214+
/// Decoder error for the [`RangeProof`] type.
215+
#[derive(Clone, PartialEq, Eq, Debug)]
216+
pub struct DecoderError(enum DecoderErrorInner {
217+
Decode(encoding::ByteVecDecoderError),
218+
RangeProof(secp256k1_zkp::Error),
219+
});
220+
221+
impl Decode for RangeProof {
222+
fn convert_inner(v) -> Result<_, DecoderErrorInner> {
223+
Self::Output::from_slice(&v).map_err(DecoderErrorInner::RangeProof)
224+
}
225+
}
226+
}
227+
228+
impl fmt::Display for DecoderError {
229+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230+
use DecoderErrorInner as Inner;
231+
match self.0 {
232+
Inner::Decode(..) => f.write_str("error decoding byte vector"),
233+
Inner::RangeProof(..) => f.write_str("error decoding range proof"),
234+
}
235+
}
236+
}
237+
238+
impl std::error::Error for DecoderError {
239+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
240+
use DecoderErrorInner as Inner;
241+
match self.0 {
242+
Inner::Decode(ref e) => Some(e),
243+
Inner::RangeProof(ref e) => Some(e),
244+
}
245+
}
246+
}

src/confidential/surjection_proof.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
//! Surjection Proofs
44
5+
use core::fmt;
56
use std::io;
67

78
use secp256k1_zkp::rand::{CryptoRng, RngCore};
@@ -10,7 +11,7 @@ use secp256k1_zkp::{self, Generator, Secp256k1, Signing, Tweak, ZERO_TWEAK};
1011
use serde::{Deserializer, Serializer};
1112

1213
use crate::confidential::{AssetBlindingFactor, AssetId};
13-
use crate::encode;
14+
use crate::{encode, encoding};
1415

1516
/// A surjection proof, proving that an asset commitment commits to the same asset ID
1617
/// as a commitment from a given set.
@@ -143,3 +144,56 @@ impl<'de> serde::Deserialize<'de> for SurjectionProof {
143144
.map(|inner| Self { inner: inner.map(Box::new) })
144145
}
145146
}
147+
148+
encoding::encoder_newtype_exact! {
149+
/// Encoder for the [`SurjectionProof`] type.
150+
#[derive(Clone, Debug)]
151+
pub struct Encoder<'e>(super::PrefixedByteVecEncoder);
152+
}
153+
154+
impl encoding::Encode for SurjectionProof {
155+
type Encoder<'e> = Encoder<'e>;
156+
157+
fn encoder(&self) -> Self::Encoder<'_> {
158+
Encoder::new(super::PrefixedByteVecEncoder::new(self.to_vec()))
159+
}
160+
}
161+
162+
decoder_newtype! {
163+
/// Decoder for the [`SurjectionProof`] type.
164+
#[derive(Default)]
165+
pub struct Decoder(encoding::ByteVecDecoder);
166+
167+
/// Decoder error for the [`SurjectionProof`] type.
168+
#[derive(Clone, PartialEq, Eq, Debug)]
169+
pub struct DecoderError(enum DecoderErrorInner {
170+
Decode(encoding::ByteVecDecoderError),
171+
SurjectionProof(secp256k1_zkp::Error),
172+
});
173+
174+
impl Decode for SurjectionProof {
175+
fn convert_inner(v) -> Result<_, DecoderErrorInner> {
176+
Self::Output::from_slice(&v).map_err(DecoderErrorInner::SurjectionProof)
177+
}
178+
}
179+
}
180+
181+
impl fmt::Display for DecoderError {
182+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183+
use DecoderErrorInner as Inner;
184+
match self.0 {
185+
Inner::Decode(..) => f.write_str("error decoding byte vector"),
186+
Inner::SurjectionProof(..) => f.write_str("error decoding surjection proof"),
187+
}
188+
}
189+
}
190+
191+
impl std::error::Error for DecoderError {
192+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
193+
use DecoderErrorInner as Inner;
194+
match self.0 {
195+
Inner::Decode(ref e) => Some(e),
196+
Inner::SurjectionProof(ref e) => Some(e),
197+
}
198+
}
199+
}

src/internal_macros.rs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,3 +785,158 @@ macro_rules! decoder_state_machine {
785785
}
786786
};
787787
}
788+
789+
/// Generates a simple decoder wrapper that converts output from an inner decoder.
790+
///
791+
/// This macro creates a decoder that wraps an existing decoder and applies a conversion
792+
/// function to transform the inner decoder's output into the target type. It's simpler
793+
/// than `decoder_state_machine!` as it only wraps a single decoder without state transitions.
794+
///
795+
/// # Syntax
796+
///
797+
/// ```ignore
798+
/// decoder_newtype! {
799+
/// /// Documentation for the decoder struct
800+
/// pub struct DecoderName(InnerDecoderType);
801+
///
802+
/// /// Documentation for the error struct
803+
/// pub struct ErrorName(enum InnerErrorName {
804+
/// // The first variant must be called Decode and hold the inner decoder type.
805+
/// Decode(InnerDecoderErrorType),
806+
/// // All other variants are free-form.
807+
/// CustomError1(ErrorType1),
808+
/// CustomError2 { field: ErrorType2 },
809+
/// // ... custom error variants
810+
/// });
811+
///
812+
/// impl Decode for TargetType {
813+
/// fn convert_inner(output_var) -> Result<_, ErrorName> {
814+
/// // Conversion logic
815+
/// }
816+
/// }
817+
/// }
818+
/// ```
819+
///
820+
/// # Generated Code
821+
///
822+
/// The macro generates:
823+
/// - A public decoder struct wrapping the inner decoder
824+
/// - A public error struct wrapping a private error enum
825+
/// - A private error enum with `Decode` variant and custom variants
826+
/// - `Decoder` trait implementation with `push_bytes`, `end`, and `read_limit` methods
827+
/// - `Decode` trait implementation for the target type
828+
///
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.
831+
///
832+
/// # Parameters
833+
///
834+
/// - `DecoderName` - The wrapper decoder struct name
835+
/// - `InnerDecoderType` - The existing decoder type to wrap
836+
/// - `ErrorName` - The error struct name
837+
/// - `InnerErrorName` - The private error enum name
838+
/// - `InnerDecoderErrorType` - The error type from the inner decoder
839+
/// - `TargetType` - The final output type after conversion
840+
/// - `convert_inner` - Function that converts inner output to target type
841+
///
842+
/// # Conversion Function
843+
///
844+
/// The conversion function:
845+
/// - Takes the inner decoder's output as its parameter
846+
/// - Returns `Result<TargetType, ErrorName>`
847+
/// - Is called automatically when the inner decoder completes
848+
/// - Can return custom errors defined in the error enum
849+
///
850+
/// # Error Handling
851+
///
852+
/// The macro automatically:
853+
/// - Creates a `Decode` variant containing the inner decoder's error
854+
/// - Maps inner decoder errors to the `Decode` variant
855+
/// - Allows custom error variants for conversion failures
856+
///
857+
/// # Example Usage
858+
///
859+
/// ```ignore
860+
/// decoder_newtype! {
861+
/// /// Decoder for range proofs
862+
/// #[derive(Default)]
863+
/// pub struct Decoder(encoding::ByteVecDecoder);
864+
///
865+
/// /// Decoder error for range proofs
866+
/// #[derive(Clone, PartialEq, Eq, Debug)]
867+
/// pub struct DecoderError(enum DecoderErrorInner {
868+
/// Decode(encoding::ByteVecDecoderError),
869+
/// RangeProof(secp256k1_zkp::Error),
870+
/// });
871+
///
872+
/// impl Decode for RangeProof {
873+
/// fn convert_inner(v) -> Result<_, DecoderError> {
874+
/// RangeProof::from_slice(&v).map_err(DecoderError::RangeProof)
875+
/// }
876+
/// }
877+
/// }
878+
/// ```
879+
macro_rules! decoder_newtype {
880+
(
881+
$(#[$($struct_attr:tt)*])*
882+
pub struct $outer_ty:ident($inner_ty:ty);
883+
884+
$(#[$($error_struct_attr:tt)*])*
885+
pub struct $error_ty:ident(enum $inner_error_ty:ident {
886+
Decode($inner_ty_error:ty),
887+
$($extra_variants:tt)*
888+
});
889+
890+
impl Decode for $target_ty:ty {
891+
fn convert_inner($output:ident) -> Result<_, $error_inner1:ty> {
892+
$($output_fn_inner:tt)*
893+
}
894+
}
895+
) => {
896+
$(#[$($struct_attr)*])*
897+
pub struct $outer_ty($inner_ty);
898+
899+
$(#[$($error_struct_attr)*])*
900+
pub struct $error_ty($inner_error_ty);
901+
902+
$(#[$($error_struct_attr)*])*
903+
enum $inner_error_ty {
904+
Decode($inner_ty_error),
905+
$($extra_variants)*
906+
}
907+
908+
impl $crate::encoding::Decoder for $outer_ty {
909+
type Output = $target_ty;
910+
type Error = $error_ty;
911+
912+
fn push_bytes(
913+
&mut self,
914+
bytes: &mut &[u8],
915+
) -> Result<$crate::encoding::DecoderStatus, Self::Error> {
916+
self.0
917+
.push_bytes(bytes)
918+
.map_err($inner_error_ty::Decode)
919+
.map_err($error_ty)
920+
}
921+
922+
fn end(self) -> Result<Self::Output, Self::Error> {
923+
let $output = self.0
924+
.end()
925+
.map_err($inner_error_ty::Decode)
926+
.map_err($error_ty)?;
927+
let converted = {
928+
$($output_fn_inner)*
929+
};
930+
converted.map_err($error_ty)
931+
}
932+
933+
fn read_limit(&self) -> usize {
934+
self.0.read_limit()
935+
}
936+
}
937+
938+
impl $crate::encoding::Decode for $target_ty{
939+
type Decoder = $outer_ty;
940+
}
941+
};
942+
}

0 commit comments

Comments
 (0)