Skip to content

Commit 15762c8

Browse files
authored
Merge pull request #47 from stevenroose/confidential-prefix
Confidential improvements
2 parents 84c7ee0 + 818ce2d commit 15762c8

File tree

3 files changed

+147
-70
lines changed

3 files changed

+147
-70
lines changed

src/confidential.rs

Lines changed: 141 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -30,32 +30,80 @@ use issuance::AssetId;
3030
// Helper macro to implement various things for the various confidential
3131
// commitment types
3232
macro_rules! impl_confidential_commitment {
33-
($name:ident, $prefixA:expr, $prefixB:expr) => (
34-
impl_confidential_commitment!($name, $prefixA, $prefixB, |x|x);
33+
($name:ident, $inner:ty, $prefixA:expr, $prefixB:expr) => (
34+
impl_confidential_commitment!($name, $inner, $prefixA, $prefixB, |x|x);
3535
);
36-
($name:ident, $prefixA:expr, $prefixB:expr, $explicit_fn:expr) => (
37-
impl Default for $name {
38-
fn default() -> Self {
39-
$name::Null
36+
($name:ident, $inner:ty, $prefixA:expr, $prefixB:expr, $explicit_fn:expr) => (
37+
impl $name {
38+
/// Create from commitment.
39+
pub fn from_commitment(bytes: &[u8]) -> Result<$name, encode::Error> {
40+
if bytes.len() != 33 {
41+
return Err(encode::Error::ParseFailed("commitments must be 33 bytes long"));
42+
}
43+
let prefix = bytes[0];
44+
if prefix != $prefixA && prefix != $prefixB {
45+
return Err(encode::Error::InvalidConfidentialPrefix(prefix));
46+
}
47+
let mut c = [0; 32];
48+
c.copy_from_slice(&bytes[1..]);
49+
Ok($name::Confidential(prefix, c))
4050
}
41-
}
4251

43-
impl fmt::Display for $name {
44-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52+
/// Check if the object is null.
53+
pub fn is_null(&self) -> bool {
4554
match *self {
46-
$name::Null => f.write_str("null"),
47-
$name::Explicit(n) => write!(f, "{}", n),
48-
$name::Confidential(prefix, bytes) => {
49-
write!(f, "{:02x}", prefix)?;
50-
for b in bytes.iter() {
51-
write!(f, "{:02x}", b)?;
52-
}
53-
Ok(())
55+
$name::Null => true,
56+
_ => false,
57+
}
58+
}
59+
60+
/// Check if the object is explicit.
61+
pub fn is_explicit(&self) -> bool {
62+
match *self {
63+
$name::Explicit(_) => true,
64+
_ => false,
65+
}
66+
}
67+
68+
/// Check if the object is confidential.
69+
pub fn is_confidential(&self) -> bool {
70+
match *self {
71+
// Impossible to create an object with invalid prefix.
72+
$name::Explicit(_) => true,
73+
_ => false,
74+
}
75+
}
76+
77+
/// Returns the explicit inner value.
78+
/// Returns [None] if [is_explicit] returns false.
79+
pub fn explicit(&self) -> Option<$inner> {
80+
match *self {
81+
$name::Explicit(i) => Some(i),
82+
_ => None,
83+
}
84+
}
85+
86+
/// Returns the confidential commitment in case of a confidential value.
87+
/// Returns [None] if [is_confidential] returns false.
88+
pub fn commitment(&self) -> Option<[u8; 33]> {
89+
match *self {
90+
$name::Confidential(p, c) => {
91+
let mut res = [0; 33];
92+
res[0] = p;
93+
res[1..].copy_from_slice(&c[..]);
94+
Some(res)
5495
}
96+
_ => None,
5597
}
5698
}
5799
}
58100

101+
impl Default for $name {
102+
fn default() -> Self {
103+
$name::Null
104+
}
105+
}
106+
59107
impl Encodable for $name {
60108
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, encode::Error> {
61109
match *self {
@@ -82,10 +130,11 @@ macro_rules! impl_confidential_commitment {
82130
let explicit = $explicit_fn(Decodable::consensus_decode(&mut d)?);
83131
Ok($name::Explicit(explicit))
84132
}
85-
x => {
133+
p if p == $prefixA || p == $prefixB => {
86134
let commitment = <[u8; 32]>::consensus_decode(&mut d)?;
87-
Ok($name::Confidential(x, commitment))
135+
Ok($name::Confidential(p, commitment))
88136
}
137+
p => return Err(encode::Error::InvalidConfidentialPrefix(p)),
89138
}
90139
}
91140
}
@@ -143,12 +192,15 @@ macro_rules! impl_confidential_commitment {
143192
None => Err(A::Error::custom("missing commitment")),
144193
}
145194
}
146-
x => {
195+
p if p == $prefixA || p == $prefixB => {
147196
match access.next_element()? {
148-
Some(y) => Ok($name::Confidential(x, y)),
197+
Some(y) => Ok($name::Confidential(p, y)),
149198
None => Err(A::Error::custom("missing commitment")),
150199
}
151200
}
201+
p => return Err(A::Error::custom(format!(
202+
"invalid commitment, invalid prefix: 0x{:02x}", p
203+
))),
152204
}
153205
}
154206
}
@@ -172,7 +224,7 @@ pub enum Value {
172224
/// Value is committed
173225
Confidential(u8, [u8; 32]),
174226
}
175-
impl_confidential_commitment!(Value, 0x08, 0x09, u64::swap_bytes);
227+
impl_confidential_commitment!(Value, u64, 0x08, 0x09, u64::swap_bytes);
176228

177229
impl Value {
178230
/// Serialized length, in bytes
@@ -183,21 +235,20 @@ impl Value {
183235
Value::Confidential(..) => 33,
184236
}
185237
}
238+
}
186239

187-
/// Check if the value is explicit.
188-
pub fn is_explicit(&self) -> bool {
189-
match *self {
190-
Value::Explicit(_) => true,
191-
_ => false,
192-
}
193-
}
194-
195-
/// Returns the explicit value.
196-
/// Returns [None] if [is_explicit] returns false.
197-
pub fn explicit(&self) -> Option<u64> {
240+
impl fmt::Display for Value {
241+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
198242
match *self {
199-
Value::Explicit(v) => Some(v),
200-
_ => None,
243+
Value::Null => f.write_str("null"),
244+
Value::Explicit(n) => write!(f, "{}", n),
245+
Value::Confidential(prefix, bytes) => {
246+
write!(f, "{:02x}", prefix)?;
247+
for b in bytes.iter() {
248+
write!(f, "{:02x}", b)?;
249+
}
250+
Ok(())
251+
}
201252
}
202253
}
203254
}
@@ -212,7 +263,7 @@ pub enum Asset {
212263
/// Asset is committed
213264
Confidential(u8, [u8; 32]),
214265
}
215-
impl_confidential_commitment!(Asset, 0x0a, 0x0b);
266+
impl_confidential_commitment!(Asset, AssetId, 0x0a, 0x0b);
216267

217268
impl Asset {
218269
/// Serialized length, in bytes
@@ -223,26 +274,24 @@ impl Asset {
223274
Asset::Confidential(..) => 33,
224275
}
225276
}
277+
}
226278

227-
/// Check if the asset is explicit.
228-
pub fn is_explicit(&self) -> bool {
229-
match *self {
230-
Asset::Explicit(_) => true,
231-
_ => false,
232-
}
233-
}
234-
235-
/// Returns the explicit asset.
236-
/// Returns [None] if [is_explicit] returns false.
237-
pub fn explicit(&self) -> Option<AssetId> {
279+
impl fmt::Display for Asset {
280+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
238281
match *self {
239-
Asset::Explicit(v) => Some(v),
240-
_ => None,
282+
Asset::Null => f.write_str("null"),
283+
Asset::Explicit(n) => write!(f, "{}", n),
284+
Asset::Confidential(prefix, bytes) => {
285+
write!(f, "{:02x}", prefix)?;
286+
for b in bytes.iter() {
287+
write!(f, "{:02x}", b)?;
288+
}
289+
Ok(())
290+
}
241291
}
242292
}
243293
}
244294

245-
246295
/// A CT commitment to an output nonce (i.e. a public key)
247296
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
248297
pub enum Nonce {
@@ -251,11 +300,11 @@ pub enum Nonce {
251300
/// There should be no such thing as an "explicit nonce", but Elements will deserialize
252301
/// such a thing (and insists that its size be 32 bytes). So we stick a 32-byte type here
253302
/// that implements all the traits we need.
254-
Explicit(sha256d::Hash),
303+
Explicit([u8; 32]),
255304
/// Nonce is committed
256305
Confidential(u8, [u8; 32]),
257306
}
258-
impl_confidential_commitment!(Nonce, 0x02, 0x03);
307+
impl_confidential_commitment!(Nonce, [u8; 32], 0x02, 0x03);
259308

260309
impl Nonce {
261310
/// Serialized length, in bytes
@@ -266,28 +315,32 @@ impl Nonce {
266315
Nonce::Confidential(..) => 33,
267316
}
268317
}
318+
}
269319

270-
/// Check if the nonce is explicit.
271-
pub fn is_explicit(&self) -> bool {
272-
match *self {
273-
Nonce::Explicit(_) => true,
274-
_ => false,
275-
}
276-
}
277-
278-
/// Returns the explicit nonce.
279-
/// Returns [None] if [is_explicit] returns false.
280-
pub fn explicit(&self) -> Option<sha256d::Hash> {
320+
impl fmt::Display for Nonce {
321+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
281322
match *self {
282-
Nonce::Explicit(v) => Some(v),
283-
_ => None,
323+
Nonce::Null => f.write_str("null"),
324+
Nonce::Explicit(n) => {
325+
for b in n.iter() {
326+
write!(f, "{:02x}", b)?;
327+
}
328+
Ok(())
329+
},
330+
Nonce::Confidential(prefix, bytes) => {
331+
write!(f, "{:02x}", prefix)?;
332+
for b in bytes.iter() {
333+
write!(f, "{:02x}", b)?;
334+
}
335+
Ok(())
336+
}
284337
}
285338
}
286339
}
287340

288341
#[cfg(test)]
289342
mod tests {
290-
use bitcoin::hashes::{Hash, sha256};
343+
use bitcoin::hashes::sha256;
291344
use super::*;
292345

293346
#[test]
@@ -305,7 +358,7 @@ mod tests {
305358

306359
let nonces = [
307360
Nonce::Null,
308-
Nonce::Explicit(sha256d::Hash::from_inner([0; 32])),
361+
Nonce::Explicit([0; 32]),
309362
Nonce::Confidential(0x02, [1; 32]),
310363
];
311364
for v in &nonces[..] {
@@ -325,5 +378,26 @@ mod tests {
325378
assert_eq!(x.len(), v.encoded_length());
326379
}
327380
}
381+
382+
#[test]
383+
fn commitments() {
384+
let x = Value::Confidential(0x08, [1; 32]);
385+
let mut commitment = x.commitment().unwrap();
386+
assert_eq!(x, Value::from_commitment(&commitment[..]).unwrap());
387+
commitment[0] = 42;
388+
assert!(Value::from_commitment(&commitment[..]).is_err());
389+
390+
let x = Asset::Confidential(0x0a, [1; 32]);
391+
let mut commitment = x.commitment().unwrap();
392+
assert_eq!(x, Asset::from_commitment(&commitment[..]).unwrap());
393+
commitment[0] = 42;
394+
assert!(Asset::from_commitment(&commitment[..]).is_err());
395+
396+
let x = Nonce::Confidential(0x02, [1; 32]);
397+
let mut commitment = x.commitment().unwrap();
398+
assert_eq!(x, Nonce::from_commitment(&commitment[..]).unwrap());
399+
commitment[0] = 42;
400+
assert!(Nonce::from_commitment(&commitment[..]).is_err());
401+
}
328402
}
329403

src/encode.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ pub enum Error {
3939
},
4040
/// Parsing error
4141
ParseFailed(&'static str),
42+
/// Invalid prefix for the confidential type.
43+
InvalidConfidentialPrefix(u8),
4244
}
4345

4446
impl fmt::Display for Error {
@@ -50,6 +52,7 @@ impl fmt::Display for Error {
5052
max: ref m,
5153
} => write!(f, "oversized vector allocation: requested {}, maximum {}", r, m),
5254
Error::ParseFailed(ref e) => write!(f, "parse failed: {}", e),
55+
Error::InvalidConfidentialPrefix(p) => write!(f, "invalid confidential prefix: 0x{:02x}", p),
5356
}
5457
}
5558
}

src/transaction.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,7 +1562,7 @@ mod tests {
15621562
fn txout_null_data() {
15631563
// Output with high opcodes should not be considered nulldata
15641564
let output: TxOut = hex_deserialize!("\
1565-
c3319c0000000000d3d3d3d3d3d3d3d3d3d3d3d3fdfdfd0101010101010101010\
1565+
0a319c0000000000d3d3d3d3d3d3d3d3d3d3d3d3fdfdfd0101010101010101010\
15661566
101010101010101010101010101012e010101010101010101fdfdfdfdfdfdfdfd\
15671567
fdfdfdfdfdfdfdfdfdfdfdfd006a209f6a6a6a6a6a6a806a6afdfdfdfd17fdfdf\
15681568
dfdfdfdfdfdfdfdddfdfdfdfdfdfdfdfdfddedededededededededededededede\
@@ -1580,7 +1580,7 @@ mod tests {
15801580

15811581
// Output with pushes that are e.g. OP_1 are nulldata but not pegouts
15821582
let output: TxOut = hex_deserialize!("\
1583-
c32d3634393536d9a2d0aaba3823f442fb24363831fdfd0101010101010101010\
1583+
0a2d3634393536d9a2d0aaba3823f442fb24363831fdfd0101010101010101010\
15841584
1010101010101010101010101010101010101016a01010101fdfdfdfdfdfdfdfd\
15851585
fdfdfdfdfd3ca059fdfdfb6a2000002323232323232323232323232323232\
15861586
3232323232323232321232323010151232323232323232323232323232323\
@@ -1598,7 +1598,7 @@ mod tests {
15981598

15991599
// Output with just one push and nothing else should be nulldata but not pegout
16001600
let output: TxOut = hex_deserialize!("\
1601-
c32d3634393536d9a2d0aaba3823f442fb24363831fdfd0101010101010101010\
1601+
0a2d3634393536d9a2d0aaba3823f442fb24363831fdfd0101010101010101010\
16021602
1010101010101010101010101010101010101016a01010101fdfdfdfdfdfdfdfd\
16031603
fdfdfdfdfd3ca059fdf2226a20000000000000000000000000000000000000000\
16041604
0000000000000000000000000\

0 commit comments

Comments
 (0)