Skip to content

Commit aacd081

Browse files
committed
confidential: implement Encode/Decode for confidential commitments
This commit introduces the `decoder_state_machine` macro which generates (a lot of) the boilerplate needed to implement the encoding::Decoder trait. This includes internal enums, wrapper types to hide the internal enums, the state machine accounting, impls of std::error::Error, etc etc. I got Claude 4 to write a detailed doccomment, from which I deleted a bunch of fluff. But all the code in the macro was hand-written based on my implementing this decoder state machine a dozen times across the codebase. (My original implementations are thrown away and not included in this PR.)
1 parent 6baad08 commit aacd081

5 files changed

Lines changed: 663 additions & 5 deletions

File tree

src/confidential/asset.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@ use secp256k1_zkp::{self, Generator, Secp256k1, Signing, Tweak, ZERO_TWEAK};
1010
#[cfg(feature = "serde")]
1111
use serde::{Deserialize, Deserializer, Serialize, Serializer};
1212

13+
use super::CommitmentEncoder;
1314
use crate::encode::{self, Decodable, Encodable};
15+
use crate::encoding;
1416
use crate::issuance::AssetId;
1517

1618
type ExplicitInner = AssetId;
1719
type ConfInner = Generator;
1820

1921
const EXPLICIT_LEN: usize = 32;
2022
const CONFIDENTIAL_LEN: usize = 33;
23+
const CONFIDENTIAL_LEN_LESS_PREFIX: usize = CONFIDENTIAL_LEN - 1;
2124
const CONF_PREFIX_1: u8 = 0x0a;
2225
const CONF_PREFIX_2: u8 = 0x0b;
2326

@@ -344,3 +347,112 @@ impl<'de> Deserialize<'de> for BlindingFactor {
344347
}
345348
}
346349
}
350+
351+
encoding::encoder_newtype_exact! {
352+
/// Encoder for the [`Asset`] type.
353+
#[derive(Clone, Debug)]
354+
pub struct Encoder<'e>(CommitmentEncoder<'e>);
355+
}
356+
357+
impl encoding::Encode for Asset {
358+
type Encoder<'e> = Encoder<'e>;
359+
360+
fn encoder(&self) -> Self::Encoder<'_> {
361+
Encoder::new(match *self {
362+
Self::Null => CommitmentEncoder::Null(0),
363+
Self::Explicit(ref id) => CommitmentEncoder::Explicit32(Some(1), id.as_byte_array()),
364+
Self::Confidential(ref gen) => CommitmentEncoder::Explicit33(gen.serialize()),
365+
})
366+
}
367+
}
368+
369+
decoder_state_machine! {
370+
/// A decoder for the [`Asset`] type.
371+
pub struct Decoder(enum DecoderInner {
372+
Done(Asset),
373+
Errored,
374+
DecodePrefix {
375+
decoder: encoding::ArrayDecoder<1>,
376+
=> transition_decode_prefix(prefix, ...) -> Result {
377+
match prefix {
378+
[0] => Ok(DecoderInner::Done(Asset::Null)),
379+
[1] => {
380+
Ok(DecoderInner::DecodeExplicit { decoder: encoding::ArrayDecoder::default() })
381+
},
382+
[prefix @ (CONF_PREFIX_1 | CONF_PREFIX_2)] => {
383+
Ok(DecoderInner::DecodeConfidential { decoder: encoding::ArrayDecoder::default(), prefix })
384+
},
385+
[prefix] => Err(DecoderErrorInner::InvalidConfidentialPrefix { prefix })
386+
}
387+
}
388+
},
389+
DecodeExplicit {
390+
decoder: encoding::ArrayDecoder<EXPLICIT_LEN>
391+
=> transition_decode_explicit(bytes, ...) -> Result {
392+
Ok(DecoderInner::Done(Asset::Explicit(AssetId::from_byte_array(bytes))))
393+
}
394+
},
395+
DecodeConfidential {
396+
decoder: encoding::ArrayDecoder<CONFIDENTIAL_LEN_LESS_PREFIX>,
397+
prefix: u8
398+
=> transition_decode_confidential(x_coord, ...) -> Result {
399+
let mut bytes = [0; CONFIDENTIAL_LEN];
400+
bytes[0] = prefix;
401+
bytes[1..].copy_from_slice(&x_coord);
402+
let gen = ConfInner::from_slice(&bytes)
403+
.map_err(DecoderErrorInner::InvalidCommitment)?;
404+
Ok(DecoderInner::Done(Asset::Confidential(gen)))
405+
}
406+
},
407+
});
408+
409+
/// A decoder error for the [`Asset`] type.
410+
#[derive(Clone, PartialEq, Eq, Debug)]
411+
pub struct DecoderError(enum DecoderErrorInner {
412+
[macro-inserted decoder variants]
413+
/// Confidential prefix was not one of the two allowable values.
414+
InvalidConfidentialPrefix {
415+
prefix: u8,
416+
},
417+
/// Malformed confidential commitment.
418+
InvalidCommitment(secp256k1_zkp::Error),
419+
});
420+
}
421+
422+
impl fmt::Display for DecoderError {
423+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
424+
use DecoderErrorInner as Inner;
425+
match self.0 {
426+
Inner::DecodePrefix(_) => f.write_str("failed to decode prefix"),
427+
Inner::DecodeExplicit(_) => f.write_str("failed to decode explicit value"),
428+
Inner::DecodeConfidential(_) => f.write_str("failed to decode confidential value"),
429+
Inner::InvalidConfidentialPrefix { prefix, .. } => {
430+
write!(
431+
f,
432+
"confidential prefix 0x{:02x} was not one of 0, 1, 0x{:02x} or 0x{:02x}",
433+
prefix, CONF_PREFIX_1, CONF_PREFIX_2,
434+
)
435+
}
436+
Inner::InvalidCommitment(_) => f.write_str("failed to parse confidential commitment"),
437+
}
438+
}
439+
}
440+
441+
impl std::error::Error for DecoderError {
442+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
443+
use DecoderErrorInner as Inner;
444+
match self.0 {
445+
Inner::DecodePrefix(ref e) => Some(e),
446+
Inner::DecodeExplicit(ref e) => Some(e),
447+
Inner::DecodeConfidential(ref e) => Some(e),
448+
Inner::InvalidConfidentialPrefix { .. } => None,
449+
Inner::InvalidCommitment(ref e) => Some(e),
450+
}
451+
}
452+
}
453+
454+
impl Default for Decoder {
455+
fn default() -> Self {
456+
Self(DecoderInner::DecodePrefix { decoder: encoding::ArrayDecoder::default() })
457+
}
458+
}

src/confidential/mod.rs

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,68 @@ mod range_proof;
2525
mod surjection_proof;
2626
mod value;
2727

28-
use core::fmt;
28+
use core::{fmt, slice};
2929

3030
use secp256k1_zkp;
3131

32-
pub use self::asset::{Asset, BlindingFactor as AssetBlindingFactor};
33-
pub use self::nonce::Nonce;
32+
pub use self::asset::{
33+
Asset, BlindingFactor as AssetBlindingFactor, Decoder as AssetDecoder,
34+
DecoderError as AssetDecoderError, Encoder as AssetEncoder,
35+
};
36+
pub use self::nonce::{
37+
Decoder as NonceDecoder, DecoderError as NonceDecoderError, Encoder as NonceEncoder, Nonce,
38+
};
3439
pub use self::range_proof::RangeProof;
3540
pub use self::surjection_proof::SurjectionProof;
36-
pub use self::value::{BlindingFactor as ValueBlindingFactor, Value};
37-
use crate::encode;
41+
pub use self::value::{
42+
BlindingFactor as ValueBlindingFactor, Decoder as ValueDecoder,
43+
DecoderError as ValueDecoderError, Encoder as ValueEncoder, Value,
44+
};
3845
use crate::issuance::AssetId;
46+
use crate::{encode, encoding};
47+
48+
#[derive(Clone, Debug)]
49+
enum CommitmentEncoder<'e> {
50+
Null(u8),
51+
Explicit8(Option<u8>, [u8; 8]),
52+
Explicit32(Option<u8>, &'e [u8; 32]),
53+
Explicit33([u8; 33]),
54+
}
55+
56+
impl encoding::Encoder for CommitmentEncoder<'_> {
57+
fn current_chunk(&self) -> &[u8] {
58+
match *self {
59+
Self::Null(ref prefix) => slice::from_ref(prefix),
60+
Self::Explicit8(ref prefix, ref arr) => prefix.as_ref().map_or(arr, slice::from_ref),
61+
Self::Explicit32(ref prefix, arr) => prefix.as_ref().map_or(arr, slice::from_ref),
62+
Self::Explicit33(ref arr) => arr,
63+
}
64+
}
65+
66+
fn advance(&mut self) -> encoding::EncoderStatus {
67+
match *self {
68+
Self::Explicit8(ref mut prefix @ Some(_), _)
69+
| Self::Explicit32(ref mut prefix @ Some(_), _) => {
70+
*prefix = None;
71+
encoding::EncoderStatus::HasMore
72+
}
73+
_ => encoding::EncoderStatus::Finished,
74+
}
75+
}
76+
}
77+
78+
impl encoding::ExactSizeEncoder for CommitmentEncoder<'_> {
79+
fn len(&self) -> usize {
80+
match *self {
81+
Self::Null(_) => 1,
82+
Self::Explicit8(Some(_), _) => 9,
83+
Self::Explicit8(None, _) => 8,
84+
Self::Explicit32(Some(_), _) => 33,
85+
Self::Explicit32(None, _) => 32,
86+
Self::Explicit33(_) => 33,
87+
}
88+
}
89+
}
3990

4091
/// Error decoding hexadecimal string into tweak-like value.
4192
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -96,6 +147,7 @@ mod tests {
96147

97148
use super::*;
98149
use crate::encode::Encodable as _;
150+
use crate::encoding;
99151

100152
const VALUE_EXPLICIT: [u8; 9] = [1, 0, 0, 0, 0, 0, 0, 3, 232];
101153

@@ -158,6 +210,9 @@ mod tests {
158210
assert_eq!(v.consensus_encode(&mut x).unwrap(), v.encoded_length());
159211
assert_eq!(x.len(), v.encoded_length());
160212
assert_eq!(x, *enc);
213+
214+
assert_eq!(encoding::encode_to_vec(v), *enc);
215+
assert_eq!(encoding::decode_from_slice(enc), Ok(*v));
161216
}
162217

163218
let nonce_encodings = [
@@ -177,6 +232,9 @@ mod tests {
177232
assert_eq!(v.consensus_encode(&mut x).unwrap(), v.encoded_length());
178233
assert_eq!(x.len(), v.encoded_length());
179234
assert_eq!(x, *enc);
235+
236+
assert_eq!(encoding::encode_to_vec(v), *enc);
237+
assert_eq!(encoding::decode_from_slice(enc), Ok(*v));
180238
}
181239

182240
let asset_encodings = [
@@ -196,6 +254,9 @@ mod tests {
196254
assert_eq!(v.consensus_encode(&mut x).unwrap(), v.encoded_length());
197255
assert_eq!(x.len(), v.encoded_length());
198256
assert_eq!(x, *enc);
257+
258+
assert_eq!(encoding::encode_to_vec(v), *enc);
259+
assert_eq!(encoding::decode_from_slice(enc), Ok(*v));
199260
}
200261
}
201262

@@ -207,20 +268,23 @@ mod tests {
207268
assert_eq!(x, Value::from_commitment(&commitment[..]).unwrap());
208269
commitment[0] = 42;
209270
assert!(Value::from_commitment(&commitment[..]).is_err());
271+
assert_eq!(encoding::encode_to_vec(&x), VALUE_COMMITMENT1);
210272

211273
let x = Asset::from_commitment(&ASSET_COMMITMENT1).unwrap();
212274
let commitment = x.commitment().unwrap();
213275
let mut commitment = commitment.serialize();
214276
assert_eq!(x, Asset::from_commitment(&commitment[..]).unwrap());
215277
commitment[0] = 42;
216278
assert!(Asset::from_commitment(&commitment[..]).is_err());
279+
assert_eq!(encoding::encode_to_vec(&x), ASSET_COMMITMENT1);
217280

218281
let x = Nonce::from_commitment(&NONCE_COMMITMENT1).unwrap();
219282
let commitment = x.commitment().unwrap();
220283
let mut commitment = commitment.serialize();
221284
assert_eq!(x, Nonce::from_commitment(&commitment[..]).unwrap());
222285
commitment[0] = 42;
223286
assert!(Nonce::from_commitment(&commitment[..]).is_err());
287+
assert_eq!(encoding::encode_to_vec(&x), NONCE_COMMITMENT1);
224288
}
225289

226290
#[cfg(feature = "serde")]

src/confidential/nonce.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@ use secp256k1_zkp::{self, PublicKey, Secp256k1, SecretKey, Signing};
1010
#[cfg(feature = "serde")]
1111
use serde::{Deserialize, Deserializer, Serialize, Serializer};
1212

13+
use super::CommitmentEncoder;
1314
use crate::encode::{self, Decodable, Encodable};
15+
use crate::encoding;
1416
use crate::hashes::sha256d;
1517

1618
type ExplicitInner = [u8; 32];
1719
type ConfInner = PublicKey;
1820

1921
const EXPLICIT_LEN: usize = 32;
2022
const CONFIDENTIAL_LEN: usize = 33;
23+
const CONFIDENTIAL_LEN_LESS_PREFIX: usize = CONFIDENTIAL_LEN - 1;
2124
const CONF_PREFIX_1: u8 = 0x02;
2225
const CONF_PREFIX_2: u8 = 0x03;
2326

@@ -247,3 +250,112 @@ impl<'de> Deserialize<'de> for Nonce {
247250
d.deserialize_seq(CommitVisitor)
248251
}
249252
}
253+
254+
encoding::encoder_newtype_exact! {
255+
/// Encoder for the [`Nonce`] type.
256+
#[derive(Clone, Debug)]
257+
pub struct Encoder<'e>(CommitmentEncoder<'e>);
258+
}
259+
260+
impl encoding::Encode for Nonce {
261+
type Encoder<'e> = Encoder<'e>;
262+
263+
fn encoder(&self) -> Self::Encoder<'_> {
264+
Encoder::new(match *self {
265+
Self::Null => CommitmentEncoder::Null(0),
266+
Self::Explicit(ref id) => CommitmentEncoder::Explicit32(Some(1), id),
267+
Self::Confidential(ref gen) => CommitmentEncoder::Explicit33(gen.serialize()),
268+
})
269+
}
270+
}
271+
272+
decoder_state_machine! {
273+
/// A decoder for the [`Nonce`] type.
274+
pub struct Decoder(enum DecoderInner {
275+
Done(Nonce),
276+
Errored,
277+
DecodePrefix {
278+
decoder: encoding::ArrayDecoder<1>,
279+
=> transition_decode_prefix(prefix, ...) -> Result {
280+
match prefix {
281+
[0] => Ok(DecoderInner::Done(Nonce::Null)),
282+
[1] => {
283+
Ok(DecoderInner::DecodeExplicit { decoder: encoding::ArrayDecoder::default() })
284+
},
285+
[prefix @ (CONF_PREFIX_1 | CONF_PREFIX_2)] => {
286+
Ok(DecoderInner::DecodeConfidential { decoder: encoding::ArrayDecoder::default(), prefix })
287+
},
288+
[prefix] => Err(DecoderErrorInner::InvalidConfidentialPrefix { prefix })
289+
}
290+
}
291+
},
292+
DecodeExplicit {
293+
decoder: encoding::ArrayDecoder<EXPLICIT_LEN>
294+
=> transition_decode_explicit(bytes, ...) -> Result {
295+
Ok(DecoderInner::Done(Nonce::Explicit(bytes)))
296+
}
297+
},
298+
DecodeConfidential {
299+
decoder: encoding::ArrayDecoder<CONFIDENTIAL_LEN_LESS_PREFIX>,
300+
prefix: u8
301+
=> transition_decode_confidential(x_coord, ...) -> Result {
302+
let mut bytes = [0; CONFIDENTIAL_LEN];
303+
bytes[0] = prefix;
304+
bytes[1..].copy_from_slice(&x_coord);
305+
let gen = ConfInner::from_slice(&bytes)
306+
.map_err(DecoderErrorInner::InvalidCommitment)?;
307+
Ok(DecoderInner::Done(Nonce::Confidential(gen)))
308+
}
309+
},
310+
});
311+
312+
/// A decoder error for the [`Nonce`] type.
313+
#[derive(Clone, PartialEq, Eq, Debug)]
314+
pub struct DecoderError(enum DecoderErrorInner {
315+
[macro-inserted decoder variants]
316+
/// Confidential prefix was not one of the two allowable values.
317+
InvalidConfidentialPrefix {
318+
prefix: u8,
319+
},
320+
/// Malformed confidential commitment.
321+
InvalidCommitment(bitcoin::secp256k1::Error),
322+
});
323+
}
324+
325+
impl fmt::Display for DecoderError {
326+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
327+
use DecoderErrorInner as Inner;
328+
match self.0 {
329+
Inner::DecodePrefix(_) => f.write_str("failed to decode prefix"),
330+
Inner::DecodeExplicit(_) => f.write_str("failed to decode explicit value"),
331+
Inner::DecodeConfidential(_) => f.write_str("failed to decode confidential value"),
332+
Inner::InvalidConfidentialPrefix { prefix, .. } => {
333+
write!(
334+
f,
335+
"confidential prefix 0x{:02x} was not one of 0, 1, 0x{:02x} or 0x{:02x}",
336+
prefix, CONF_PREFIX_1, CONF_PREFIX_2,
337+
)
338+
}
339+
Inner::InvalidCommitment(_) => f.write_str("failed to parse confidential commitment"),
340+
}
341+
}
342+
}
343+
344+
impl std::error::Error for DecoderError {
345+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
346+
use DecoderErrorInner as Inner;
347+
match self.0 {
348+
Inner::DecodePrefix(ref e) => Some(e),
349+
Inner::DecodeExplicit(ref e) => Some(e),
350+
Inner::DecodeConfidential(ref e) => Some(e),
351+
Inner::InvalidConfidentialPrefix { .. } => None,
352+
Inner::InvalidCommitment(ref e) => Some(e),
353+
}
354+
}
355+
}
356+
357+
impl Default for Decoder {
358+
fn default() -> Self {
359+
Self(DecoderInner::DecodePrefix { decoder: encoding::ArrayDecoder::default() })
360+
}
361+
}

0 commit comments

Comments
 (0)