diff --git a/.github/workflows/cron-daily-fuzz.yml b/.github/workflows/cron-daily-fuzz.yml index 7a4f4314..7f3af38e 100644 --- a/.github/workflows/cron-daily-fuzz.yml +++ b/.github/workflows/cron-daily-fuzz.yml @@ -1,5 +1,5 @@ ###### -## DO NOT EDIT THIS FILE DIRECTLY. It is generated by generate-fuzz.sh. +## DO NOT EDIT THIS FILE DIRECTLY. It is generated by generate-files.sh. ## Edit that script instead and re-run it. ###### name: Fuzz diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index bf452ae6..14d0784b 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -1,5 +1,5 @@ ###### -## DO NOT EDIT THIS FILE DIRECTLY. It is generated by generate-fuzz.sh. +## DO NOT EDIT THIS FILE DIRECTLY. It is generated by generate-files.sh. ## Edit that script instead and re-run it. ###### [package] diff --git a/fuzz/generate-files.sh b/fuzz/generate-files.sh index cbe54f11..dc19fb28 100755 --- a/fuzz/generate-files.sh +++ b/fuzz/generate-files.sh @@ -11,7 +11,7 @@ source "$REPO_DIR/fuzz/fuzz-util.sh" # 1. Generate fuzz/Cargo.toml cat > "$REPO_DIR/fuzz/Cargo.toml" < "$REPO_DIR/.github/workflows/cron-daily-fuzz.yml" <, _>>()?; - let surjection_proof = SurjectionProof::new( - secp, - rng, - asset.into_tag(), - asset_bf.into_inner(), - inputs.as_ref(), - )?; + let surjection_proof = SurjectionProof::new(secp, rng, asset, asset_bf, inputs)?; Ok((out_asset, surjection_proof)) } @@ -657,7 +651,7 @@ impl TxOut { out_secrets.asset_bf, ); let exp_value = Value::Explicit(out_secrets.value); - let (out_value, nonce, range_proof) = exp_value.blind( + let (out_value, nonce, rangeproof) = exp_value.blind( secp, out_secrets.value_bf, receiver_blinding_pk, @@ -672,8 +666,8 @@ impl TxOut { nonce, script_pubkey: spk, witness: TxOutWitness { - surjection_proof: Some(Box::new(surjection_proof)), - rangeproof: Some(Box::new(range_proof)), + surjection_proof, + rangeproof, }, }; Ok(txout) @@ -977,10 +971,10 @@ impl TxIn { let (comm, prf) = v.blind_with_shared_secret(secp, bf, blind_sk, &spk, &msg)?; if i == 0 { self.asset_issuance.amount = comm; - self.witness.amount_rangeproof = Some(Box::new(prf)); + self.witness.amount_rangeproof = prf; } else { self.asset_issuance.inflation_keys = comm; - self.witness.inflation_keys_rangeproof = Some(Box::new(prf)); + self.witness.inflation_keys_rangeproof = prf; } } Ok(()) @@ -1365,122 +1359,6 @@ impl From for BlindError { } } -/// A trait to create and verify explicit rangeproofs -pub trait BlindValueProofs: Sized { - /// Outputs a `[RangeProof]` that blinded value - /// corresponfs to unblinded explicit value - fn blind_value_proof( - rng: &mut R, - secp: &Secp256k1, - explicit_val: u64, - value_commit: PedersenCommitment, - asset_gen: Generator, - vbf: ValueBlindingFactor, - ) -> Result; - - /// Verify that the Rangeproof proves that commitment - /// is actually bound to the explicit value - fn blind_value_proof_verify( - &self, - secp: &Secp256k1, - explicit_val: u64, - asset_gen: Generator, - value_commit: PedersenCommitment, - ) -> bool; -} - -impl BlindValueProofs for RangeProof { - /// Outputs a [`RangeProof`] that blinded `value_commit` - /// corresponds to explicit value - fn blind_value_proof( - rng: &mut R, - secp: &Secp256k1, - explicit_val: u64, - value_commit: PedersenCommitment, - asset_gen: Generator, - vbf: ValueBlindingFactor, - ) -> Result { - RangeProof::new( - secp, - explicit_val, // min_value - value_commit, // value_commit - explicit_val, // value - vbf.into_inner(), // blinding factor - &[], // message - &[], // add commitment - SecretKey::new(rng), // nonce - -1, // exp - 0, // min bits - asset_gen, // additional gen - ) - } - - /// Verify that the Rangeproof proves that commitment - /// is actually bound to the explicit value - fn blind_value_proof_verify( - &self, - secp: &Secp256k1, - explicit_val: u64, - asset_gen: Generator, - value_commit: PedersenCommitment, - ) -> bool { - let r = self.verify(secp, value_commit, &[], asset_gen); - match r { - Ok(e) => e.start == explicit_val && e.end - 1 == explicit_val, - Err(..) => false, - } - } -} - -/// A trait to create and verify explicit surjection proofs -pub trait BlindAssetProofs: Sized { - /// Outputs a `[SurjectionProof]` that blinded asset - /// corresponfs to unblinded explicit asset - fn blind_asset_proof( - rng: &mut R, - secp: &Secp256k1, - asset: AssetId, - abf: AssetBlindingFactor, - ) -> Result; - - /// Verify that the Surjection proves that asset commitment - /// is actually bound to the explicit asset - fn blind_asset_proof_verify( - &self, - secp: &Secp256k1, - asset: AssetId, - asset_commit: Generator, - ) -> bool; -} - -impl BlindAssetProofs for SurjectionProof { - fn blind_asset_proof( - rng: &mut R, - secp: &Secp256k1, - asset: AssetId, - abf: AssetBlindingFactor, - ) -> Result { - let gen = Generator::new_unblinded(secp, asset.into_tag()); - SurjectionProof::new( - secp, - rng, - asset.into_tag(), - abf.into_inner(), - &[(gen, asset.into_tag(), ZERO_TWEAK)], - ) - } - - fn blind_asset_proof_verify( - &self, - secp: &Secp256k1, - asset: AssetId, - asset_commit: Generator, - ) -> bool { - let gen = Generator::new_unblinded(secp, asset.into_tag()); - self.verify(secp, asset_commit, &[gen]) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/confidential.rs b/src/confidential.rs deleted file mode 100644 index 2e0ed39a..00000000 --- a/src/confidential.rs +++ /dev/null @@ -1,1458 +0,0 @@ -// Rust Elements Library -// Written in 2018 by -// Andrew Poelstra -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . -// - -//! # Confidential Commitments -//! -//! Structures representing Pedersen commitments of various types -//! - -use crate::hashes::sha256d; -use secp256k1_zkp::{self, CommitmentSecrets, Generator, PedersenCommitment, - PublicKey, Secp256k1, SecretKey, Signing, Tweak, ZERO_TWEAK, - compute_adaptive_blinding_factor, - rand::{CryptoRng, Rng, RngCore} -}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Deserializer, Serialize, Serializer}; - -use std::{fmt, io, ops::{AddAssign, Neg}, str}; - -use crate::encode::{self, Decodable, Encodable}; -use crate::issuance::AssetId; - -/// A CT commitment to an amount -#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)] -pub enum Value { - /// No value - #[default] - Null, - /// Value is explicitly encoded - Explicit(u64), - /// Value is committed - Confidential(PedersenCommitment), -} - -impl Value { - /// Create value commitment. - pub fn new_confidential( - secp: &Secp256k1, - value: u64, - asset: Generator, - bf: ValueBlindingFactor, - ) -> Self { - Value::Confidential(PedersenCommitment::new(secp, value, bf.0, asset)) - } - - /// Create value commitment from assetID, asset blinding factor, - /// value and value blinding factor - pub fn new_confidential_from_assetid( - secp: &Secp256k1, - value: u64, - asset: AssetId, - v_bf: ValueBlindingFactor, - a_bf: AssetBlindingFactor, - ) -> Self { - let generator = Generator::new_blinded(secp, asset.into_tag(), a_bf.0); - let comm = PedersenCommitment::new(secp, value, v_bf.0, generator); - - Value::Confidential(comm) - } - - /// Serialized length, in bytes - pub fn encoded_length(&self) -> usize { - match *self { - Value::Null => 1, - Value::Explicit(..) => 9, - Value::Confidential(..) => 33, - } - } - - /// Create from commitment. - pub fn from_commitment(bytes: &[u8]) -> Result { - Ok(Value::Confidential(PedersenCommitment::from_slice(bytes)?)) - } - - /// Check if the object is null. - pub fn is_null(&self) -> bool { - matches!(*self, Value::Null) - } - - /// Check if the object is explicit. - pub fn is_explicit(&self) -> bool { - matches!(*self, Value::Explicit(_)) - } - - /// Check if the object is confidential. - pub fn is_confidential(&self) -> bool { - matches!(*self, Value::Confidential(_)) - } - - /// Returns the explicit inner value. - /// Returns [None] if [`Value::is_explicit`] returns false. - pub fn explicit(&self) -> Option { - match *self { - Value::Explicit(i) => Some(i), - _ => None, - } - } - - /// Returns the confidential commitment in case of a confidential value. - /// Returns [None] if [`Value::is_confidential`] returns false. - pub fn commitment(&self) -> Option { - match *self { - Value::Confidential(i) => Some(i), - _ => None, - } - } -} - -impl From for Value { - fn from(from: PedersenCommitment) -> Self { - Value::Confidential(from) - } -} - -impl fmt::Display for Value { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Value::Null => f.write_str("null"), - Value::Explicit(n) => write!(f, "{}", n), - Value::Confidential(commitment) => write!(f, "{:02x}", commitment), - } - } -} - -impl Encodable for Value { - fn consensus_encode(&self, mut s: S) -> Result { - match *self { - Value::Null => 0u8.consensus_encode(s), - Value::Explicit(n) => { - 1u8.consensus_encode(&mut s)?; - Ok(1 + u64::swap_bytes(n).consensus_encode(&mut s)?) - } - Value::Confidential(commitment) => commitment.consensus_encode(&mut s), - } - } -} - -impl Encodable for PedersenCommitment { - fn consensus_encode(&self, mut e: W) -> Result { - e.write_all(&self.serialize())?; - Ok(33) - } -} - -impl Decodable for Value { - fn consensus_decode(mut d: D) -> Result { - let prefix = u8::consensus_decode(&mut d)?; - - match prefix { - 0 => Ok(Value::Null), - 1 => { - let explicit = u64::swap_bytes(Decodable::consensus_decode(&mut d)?); - Ok(Value::Explicit(explicit)) - } - p if p == 0x08 || p == 0x09 => { - let mut comm = [0u8; 33]; - comm[0] = p; - d.read_exact(&mut comm[1..])?; - Ok(Value::Confidential(PedersenCommitment::from_slice(&comm)?)) - } - p => Err(encode::Error::InvalidConfidentialPrefix(p)), - } - } -} - -impl Decodable for PedersenCommitment { - fn consensus_decode(d: D) -> Result { - let bytes = <[u8; 33]>::consensus_decode(d)?; - Ok(PedersenCommitment::from_slice(&bytes)?) - } -} - -#[cfg(feature = "serde")] -impl Serialize for Value { - fn serialize(&self, s: S) -> Result { - use serde::ser::SerializeSeq; - - let seq_len = match *self { - Value::Null => 1, - Value::Explicit(_) | Value::Confidential(_) => 2 - }; - let mut seq = s.serialize_seq(Some(seq_len))?; - - match *self { - Value::Null => seq.serialize_element(&0u8)?, - Value::Explicit(n) => { - seq.serialize_element(&1u8)?; - seq.serialize_element(&u64::swap_bytes(n))?; - } - Value::Confidential(commitment) => { - seq.serialize_element(&2u8)?; - seq.serialize_element(&commitment)?; - } - } - seq.end() - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for Value { - fn deserialize>(d: D) -> Result { - use serde::de::{Error, SeqAccess, Visitor}; - struct CommitVisitor; - - impl<'de> Visitor<'de> for CommitVisitor { - type Value = Value; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("a committed value") - } - - fn visit_seq>(self, mut access: A) -> Result { - let prefix = access.next_element::()?; - match prefix { - Some(0) => Ok(Value::Null), - Some(1) => { - match access.next_element()? { - Some(x) => Ok(Value::Explicit(u64::swap_bytes(x))), - None => Err(A::Error::custom("missing explicit value")), - } - } - Some(2) => { - match access.next_element()? { - Some(x) => Ok(Value::Confidential(x)), - None => Err(A::Error::custom("missing pedersen commitment")), - } - } - _ => Err(A::Error::custom("wrong or missing prefix")), - } - } - } - - d.deserialize_seq(CommitVisitor) - } -} - -/// A CT commitment to an asset -#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)] -pub enum Asset { - /// No value - #[default] - Null, - /// Asset entropy is explicitly encoded - Explicit(AssetId), - /// Asset is committed - Confidential(Generator), -} - -impl Asset { - /// Create asset commitment. - pub fn new_confidential( - secp: &Secp256k1, - asset: AssetId, - bf: AssetBlindingFactor, - ) -> Self { - Asset::Confidential(Generator::new_blinded( - secp, - asset.into_tag(), - bf.into_inner(), - )) - } - - /// Serialized length, in bytes - pub fn encoded_length(&self) -> usize { - match *self { - Asset::Null => 1, - Asset::Explicit(..) => 33, - Asset::Confidential(..) => 33, - } - } - - /// Create from commitment. - pub fn from_commitment(bytes: &[u8]) -> Result { - Ok(Asset::Confidential(Generator::from_slice(bytes)?)) - } - - /// Check if the object is null. - pub fn is_null(&self) -> bool { - matches!(*self, Asset::Null) - } - - /// Check if the object is explicit. - pub fn is_explicit(&self) -> bool { - matches!(*self, Asset::Explicit(_)) - } - - /// Check if the object is confidential. - pub fn is_confidential(&self) -> bool { - matches!(*self, Asset::Confidential(_)) - } - - /// Returns the explicit inner value. - /// Returns [None] if [`Asset::is_explicit`] returns false. - pub fn explicit(&self) -> Option { - match *self { - Asset::Explicit(i) => Some(i), - _ => None, - } - } - - /// Returns the confidential commitment in case of a confidential value. - /// Returns [None] if [`Asset::is_confidential`] returns false. - pub fn commitment(&self) -> Option { - match *self { - Asset::Confidential(i) => Some(i), - _ => None, - } - } - - /// Internally used function for getting the generator from asset - /// Used in the amount verification check - /// Returns [`None`] is the asset is [`Asset::Null`] - /// Converts a explicit asset into a generator and returns the confidential - /// generator as is. - pub fn into_asset_gen ( - self, - secp: &Secp256k1, - ) -> Option { - match self { - // Only error is Null error which is dealt with later - // when we have more context information about it. - Asset::Null => None, - Asset::Explicit(x) => { - Some(Generator::new_unblinded(secp, x.into_tag())) - } - Asset::Confidential(gen) => Some(gen), - } - } -} - -impl From for Asset { - fn from(from: Generator) -> Self { - Asset::Confidential(from) - } -} - -impl fmt::Display for Asset { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Asset::Null => f.write_str("null"), - Asset::Explicit(n) => write!(f, "{}", n), - Asset::Confidential(generator) => write!(f, "{:02x}", generator), - } - } -} - -impl Encodable for Asset { - fn consensus_encode(&self, mut s: S) -> Result { - match *self { - Asset::Null => 0u8.consensus_encode(s), - Asset::Explicit(n) => { - 1u8.consensus_encode(&mut s)?; - Ok(1 + n.consensus_encode(&mut s)?) - } - Asset::Confidential(generator) => generator.consensus_encode(&mut s) - } - } -} - -impl Encodable for Generator { - fn consensus_encode(&self, mut e: W) -> Result { - e.write_all(&self.serialize())?; - Ok(33) - } -} - -impl Decodable for Asset { - fn consensus_decode(mut d: D) -> Result { - let prefix = u8::consensus_decode(&mut d)?; - - match prefix { - 0 => Ok(Asset::Null), - 1 => { - let explicit = Decodable::consensus_decode(&mut d)?; - Ok(Asset::Explicit(explicit)) - } - p if p == 0x0a || p == 0x0b => { - let mut comm = [0u8; 33]; - comm[0] = p; - d.read_exact(&mut comm[1..])?; - Ok(Asset::Confidential(Generator::from_slice(&comm[..])?)) - } - p => Err(encode::Error::InvalidConfidentialPrefix(p)), - } - } -} - -impl Decodable for Generator { - fn consensus_decode(d: D) -> Result { - let bytes = <[u8; 33]>::consensus_decode(d)?; - Ok(Generator::from_slice(&bytes)?) - } -} - - -#[cfg(feature = "serde")] -impl Serialize for Asset { - fn serialize(&self, s: S) -> Result { - use serde::ser::SerializeSeq; - - let seq_len = match *self { - Asset::Null => 1, - Asset::Explicit(_) | Asset::Confidential(_) => 2 - }; - let mut seq = s.serialize_seq(Some(seq_len))?; - - match *self { - Asset::Null => seq.serialize_element(&0u8)?, - Asset::Explicit(n) => { - seq.serialize_element(&1u8)?; - seq.serialize_element(&n)?; - } - Asset::Confidential(commitment) => { - seq.serialize_element(&2u8)?; - seq.serialize_element(&commitment)?; - } - } - seq.end() - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for Asset { - fn deserialize>(d: D) -> Result { - use serde::de::{Error, SeqAccess, Visitor}; - struct CommitVisitor; - - impl<'de> Visitor<'de> for CommitVisitor { - type Value = Asset; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("a committed value") - } - - fn visit_seq>(self, mut access: A) -> Result { - let prefix = access.next_element::()?; - match prefix { - Some(0) => Ok(Asset::Null), - Some(1) => { - match access.next_element()? { - Some(x) => Ok(Asset::Explicit(x)), - None => Err(A::Error::custom("missing explicit asset")), - } - } - Some(2) => { - match access.next_element()? { - Some(x) => Ok(Asset::Confidential(x)), - None => Err(A::Error::custom("missing generator")), - } - } - _ => Err(A::Error::custom("wrong or missing prefix")), - } - } - } - - d.deserialize_seq(CommitVisitor) - } -} - -/// A CT commitment to an output nonce (i.e. a public key) -#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)] -pub enum Nonce { - /// No value - #[default] - Null, - /// There should be no such thing as an "explicit nonce", but Elements will deserialize - /// such a thing (and insists that its size be 32 bytes). So we stick a 32-byte type here - /// that implements all the traits we need. - Explicit([u8; 32]), - /// Nonce is committed - Confidential(PublicKey), -} - -impl Nonce { - /// Create nonce commitment. - pub fn new_confidential( - rng: &mut R, - secp: &Secp256k1, - receiver_blinding_pk: &PublicKey, - ) -> (Self, SecretKey) { - let ephemeral_sk = SecretKey::new(rng); - Self::with_ephemeral_sk(secp, ephemeral_sk, receiver_blinding_pk) - } - - /// Similar to [`Nonce::new_confidential`], but with a given `ephemeral_sk` - /// instead of sampling it from rng. - pub fn with_ephemeral_sk( - secp: &Secp256k1, - ephemeral_sk: SecretKey, - receiver_blinding_pk: &PublicKey - ) -> (Self, SecretKey) { - let sender_pk = PublicKey::from_secret_key(secp, &ephemeral_sk); - let shared_secret = Self::make_shared_secret(receiver_blinding_pk, &ephemeral_sk); - (Nonce::Confidential(sender_pk), shared_secret) - } - - /// Calculate the shared secret. - pub fn shared_secret(&self, receiver_blinding_sk: &SecretKey) -> Option { - match self { - Nonce::Confidential(sender_pk) => { - Some(Self::make_shared_secret(sender_pk, receiver_blinding_sk)) - } - _ => None, - } - } - - /// Create the shared secret. - fn make_shared_secret(pk: &PublicKey, sk: &SecretKey) -> SecretKey { - let xy = secp256k1_zkp::ecdh::shared_secret_point(pk, sk); - let shared_secret = { - // Yes, what follows is the compressed representation of a Bitcoin public key. - // However, this is more by accident then by design, see here: https://github.com/rust-bitcoin/rust-secp256k1/pull/255#issuecomment-744146282 - - let mut dh_secret = [0u8; 33]; - dh_secret[0] = if xy.last().unwrap() % 2 == 0 { - 0x02 - } else { - 0x03 - }; - dh_secret[1..].copy_from_slice(&xy[0..32]); - - sha256d::Hash::hash(&dh_secret).to_byte_array() - }; - - SecretKey::from_slice(&shared_secret[..32]).expect("always has exactly 32 bytes") - } - - /// Serialized length, in bytes - pub fn encoded_length(&self) -> usize { - match *self { - Nonce::Null => 1, - Nonce::Explicit(..) => 33, - Nonce::Confidential(..) => 33, - } - } - - /// Create from commitment. - pub fn from_commitment(bytes: &[u8]) -> Result { - Ok(Nonce::Confidential( - PublicKey::from_slice(bytes).map_err(secp256k1_zkp::Error::Upstream)?, - )) - } - - /// Check if the object is null. - pub fn is_null(&self) -> bool { - matches!(*self, Nonce::Null) - } - - /// Check if the object is explicit. - pub fn is_explicit(&self) -> bool { - matches!(*self, Nonce::Explicit(_)) - } - - /// Check if the object is confidential. - pub fn is_confidential(&self) -> bool { - matches!(*self, Nonce::Confidential(_)) - } - - /// Returns the explicit inner value. - /// Returns [None] if [`Nonce::is_explicit`] returns false. - pub fn explicit(&self) -> Option<[u8; 32]> { - match *self { - Nonce::Explicit(i) => Some(i), - _ => None, - } - } - - /// Returns the confidential commitment in case of a confidential value. - /// Returns [None] if [`Nonce::is_confidential`] returns false. - pub fn commitment(&self) -> Option { - match *self { - Nonce::Confidential(i) => Some(i), - _ => None, - } - } -} - -impl From for Nonce { - fn from(from: PublicKey) -> Self { - Nonce::Confidential(from) - } -} - -impl fmt::Display for Nonce { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Nonce::Null => f.write_str("null"), - Nonce::Explicit(n) => { - for b in &n { - write!(f, "{:02x}", b)?; - } - Ok(()) - } - Nonce::Confidential(pk) => write!(f, "{:02x}", pk), - } - } -} - -impl Encodable for Nonce { - fn consensus_encode(&self, mut s: S) -> Result { - match *self { - Nonce::Null => 0u8.consensus_encode(s), - Nonce::Explicit(n) => { - 1u8.consensus_encode(&mut s)?; - Ok(1 + n.consensus_encode(&mut s)?) - } - Nonce::Confidential(commitment) => commitment.consensus_encode(&mut s), - } - } -} - -impl Encodable for PublicKey { - fn consensus_encode(&self, mut e: W) -> Result { - e.write_all(&self.serialize())?; - Ok(33) - } -} - -impl Decodable for Nonce { - fn consensus_decode(mut d: D) -> Result { - let prefix = u8::consensus_decode(&mut d)?; - - match prefix { - 0 => Ok(Nonce::Null), - 1 => { - let explicit = Decodable::consensus_decode(&mut d)?; - Ok(Nonce::Explicit(explicit)) - } - p if p == 0x02 || p == 0x03 => { - let mut comm = [0u8; 33]; - comm[0] = p; - d.read_exact(&mut comm[1..])?; - Ok(Nonce::Confidential(PublicKey::from_slice(&comm)?)) - } - p => Err(encode::Error::InvalidConfidentialPrefix(p)), - } - } -} - -impl Decodable for PublicKey { - fn consensus_decode(d: D) -> Result { - let bytes = <[u8; 33]>::consensus_decode(d)?; - Ok(PublicKey::from_slice(&bytes)?) - } -} - -#[cfg(feature = "serde")] -impl Serialize for Nonce { - fn serialize(&self, s: S) -> Result { - use serde::ser::SerializeSeq; - - let seq_len = match *self { - Nonce::Null => 1, - Nonce::Explicit(_) | Nonce::Confidential(_) => 2 - }; - let mut seq = s.serialize_seq(Some(seq_len))?; - - match *self { - Nonce::Null => seq.serialize_element(&0u8)?, - Nonce::Explicit(n) => { - seq.serialize_element(&1u8)?; - seq.serialize_element(&n)?; - } - Nonce::Confidential(commitment) => { - seq.serialize_element(&2u8)?; - seq.serialize_element(&commitment)?; - } - } - seq.end() - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for Nonce { - fn deserialize>(d: D) -> Result { - use serde::de::{Error, SeqAccess, Visitor}; - struct CommitVisitor; - - impl<'de> Visitor<'de> for CommitVisitor { - type Value = Nonce; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("a committed value") - } - - fn visit_seq>(self, mut access: A) -> Result { - let prefix = access.next_element::()?; - match prefix { - Some(0) => Ok(Nonce::Null), - Some(1) => { - match access.next_element()? { - Some(x) => Ok(Nonce::Explicit(x)), - None => Err(A::Error::custom("missing explicit nonce")), - } - } - Some(2) => { - match access.next_element()? { - Some(x) => Ok(Nonce::Confidential(x)), - None => Err(A::Error::custom("missing nonce")), - } - } - _ => Err(A::Error::custom("wrong or missing prefix")) - } - } - } - - d.deserialize_seq(CommitVisitor) - } -} - -/// Error decoding hexadecimal string into tweak-like value. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum TweakHexDecodeError { - /// Invalid hexadecimal string. - InvalidHex(hex::DecodeFixedLengthBytesError), - /// Invalid tweak after decoding hexadecimal string. - InvalidTweak(secp256k1_zkp::Error), -} - -impl fmt::Display for TweakHexDecodeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - TweakHexDecodeError::InvalidHex(err) => { - write!(f, "Invalid hex: {}", err) - } - TweakHexDecodeError::InvalidTweak(err) => { - write!(f, "Invalid tweak: {}", err) - } - } - } -} - -#[doc(hidden)] -impl From for TweakHexDecodeError { - fn from(err: hex::DecodeFixedLengthBytesError) -> Self { - TweakHexDecodeError::InvalidHex(err) - } -} - -#[doc(hidden)] -impl From for TweakHexDecodeError { - fn from(err: secp256k1_zkp::Error) -> Self { - TweakHexDecodeError::InvalidTweak(err) - } -} - -impl From for encode::Error { - fn from(value: TweakHexDecodeError) -> Self { - match value { - TweakHexDecodeError::InvalidHex(err) => encode::Error::HexFixedError(err), - TweakHexDecodeError::InvalidTweak(err) => encode::Error::Secp256k1zkp(err), - } - } -} - -impl std::error::Error for TweakHexDecodeError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - TweakHexDecodeError::InvalidHex(err) => Some(err), - TweakHexDecodeError::InvalidTweak(err) => Some(err), - } - } -} - -/// Blinding factor used for asset commitments. -#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] -pub struct AssetBlindingFactor(pub(crate) Tweak); - -impl AssetBlindingFactor { - /// Generate random asset blinding factor. - pub fn new(rng: &mut R) -> Self { - AssetBlindingFactor(Tweak::new(rng)) - } - - /// Parse a blinding factor from a 64-character hex string. - #[deprecated(since = "0.27.0", note = "use s.parse() instead")] - pub fn from_hex(s: &str) -> Result { - s.parse() - } - - /// Create from bytes. - pub fn from_byte_array(bytes: [u8; 32]) -> Result { - Ok(AssetBlindingFactor(Tweak::from_inner(bytes)?)) - } - - /// Create from bytes. - pub fn from_slice(bytes: &[u8]) -> Result { - Ok(AssetBlindingFactor(Tweak::from_slice(bytes)?)) - } - - /// Returns the inner value. - pub fn into_inner(self) -> Tweak { - self.0 - } - - /// Get a unblinded/zero `AssetBlinding` factor - pub fn zero() -> Self { - AssetBlindingFactor(ZERO_TWEAK) - } -} - -impl core::borrow::Borrow<[u8]> for AssetBlindingFactor { - fn borrow(&self) -> &[u8] { &self.0[..] } -} - -hex::impl_fmt_traits! { - #[display_backward(true)] - impl fmt_traits for AssetBlindingFactor { - const LENGTH: usize = 32; - } -} - -impl str::FromStr for AssetBlindingFactor { - type Err = encode::Error; - - fn from_str(s: &str) -> Result { - let mut slice: [u8; 32] = hex::decode_to_array(s)?; - slice.reverse(); - - let inner = Tweak::from_inner(slice)?; - Ok(AssetBlindingFactor(inner)) - } -} - -#[cfg(feature = "serde")] -impl Serialize for AssetBlindingFactor { - fn serialize(&self, s: S) -> Result { - if s.is_human_readable() { - s.collect_str(&self) - } else { - s.serialize_bytes(&self.0[..]) - } - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for AssetBlindingFactor { - fn deserialize>(d: D) -> Result { - if d.is_human_readable() { - struct HexVisitor; - - impl ::serde::de::Visitor<'_> for HexVisitor { - type Value = AssetBlindingFactor; - - fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - formatter.write_str("an ASCII hex string") - } - - fn visit_bytes(self, v: &[u8]) -> Result - where - E: ::serde::de::Error, - { - if let Ok(hex) = ::std::str::from_utf8(v) { - hex.parse().map_err(E::custom) - } else { - Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self)) - } - } - - fn visit_str(self, v: &str) -> Result - where - E: ::serde::de::Error, - { - v.parse().map_err(E::custom) - } - } - - d.deserialize_str(HexVisitor) - } else { - struct BytesVisitor; - - impl ::serde::de::Visitor<'_> for BytesVisitor { - type Value = AssetBlindingFactor; - - fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - formatter.write_str("a bytestring") - } - - fn visit_bytes(self, v: &[u8]) -> Result - where - E: ::serde::de::Error, - { - use core::convert::TryFrom; - - match <[u8; 32]>::try_from(v) { - Ok(ret) => { - let inner = Tweak::from_inner(ret).map_err(E::custom)?; - Ok(AssetBlindingFactor(inner)) - } - Err(_) => Err(E::invalid_length(v.len(), &stringify!($len))), - } - } - } - - d.deserialize_bytes(BytesVisitor) - } - } -} - -/// Blinding factor used for value commitments. -#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] -pub struct ValueBlindingFactor(pub(crate) Tweak); - -impl ValueBlindingFactor { - /// Generate random value blinding factor. - pub fn new(rng: &mut R) -> Self { - ValueBlindingFactor(Tweak::new(rng)) - } - - /// Parse a blinding factor from a 64-character hex string. - #[deprecated(since = "0.27.0", note = "use s.parse() instead")] - pub fn from_hex(s: &str) -> Result { - s.parse() - } - - /// Create the value blinding factor of the last output of a transaction. - pub fn last( - secp: &Secp256k1, - value: u64, - abf: AssetBlindingFactor, - inputs: &[(u64, AssetBlindingFactor, ValueBlindingFactor)], - outputs: &[(u64, AssetBlindingFactor, ValueBlindingFactor)], - ) -> Self { - let set_a = inputs - .iter() - .map(|(value, abf, vbf)| CommitmentSecrets { - value: *value, - value_blinding_factor: vbf.0, - generator_blinding_factor: abf.into_inner(), - }) - .collect::>(); - let set_b = outputs - .iter() - .map(|(value, abf, vbf)| CommitmentSecrets { - value: *value, - value_blinding_factor: vbf.0, - generator_blinding_factor: abf.into_inner(), - }) - .collect::>(); - - ValueBlindingFactor(compute_adaptive_blinding_factor( - secp, value, abf.0, &set_a, &set_b, - )) - } - - /// Create from bytes. - pub fn from_slice(bytes: &[u8]) -> Result { - Ok(ValueBlindingFactor(Tweak::from_slice(bytes)?)) - } - - /// Returns the inner value. - pub fn into_inner(self) -> Tweak { - self.0 - } - - /// Get a unblinded/zero `AssetBlinding` factor - pub fn zero() -> Self { - ValueBlindingFactor(ZERO_TWEAK) - } -} - -impl AddAssign for ValueBlindingFactor { - fn add_assign(&mut self, other: Self) { - if self.0.as_ref() == &[0u8; 32] { - *self = other; - } else if other.0.as_ref() == &[0u8; 32] { - // nothing to do - } else { - // Since libsecp does not expose low level APIs - // for scalar arethematic, we need to abuse secret key - // operations for this - let sk2 = SecretKey::from_slice(self.into_inner().as_ref()).expect("Valid key"); - let sk = SecretKey::from_slice(other.into_inner().as_ref()).expect("Valid key"); - // The only reason that secret key addition can fail - // is when the keys add up to zero since we have already checked - // keys are in valid secret keys - match sk.add_tweak(&sk2.into()) { - Ok(sk_tweaked) => *self = ValueBlindingFactor::from_slice(sk_tweaked.as_ref()).expect("Valid Tweak"), - Err(_) => *self = Self::zero(), - } - } - } -} - -impl Neg for ValueBlindingFactor { - type Output = Self; - - fn neg(self) -> Self::Output { - if self.0.as_ref() == &[0u8; 32] { - self - } else { - let sk = SecretKey::from_slice(self.into_inner().as_ref()).expect("Valid key").negate(); - ValueBlindingFactor::from_slice(sk.as_ref()).expect("Valid Tweak") - } - } -} - -impl core::borrow::Borrow<[u8]> for ValueBlindingFactor { - fn borrow(&self) -> &[u8] { &self.0[..] } -} - -hex::impl_fmt_traits! { - #[display_backward(true)] - impl fmt_traits for ValueBlindingFactor { - const LENGTH: usize = 32; - } -} - -impl str::FromStr for ValueBlindingFactor { - type Err = encode::Error; - - fn from_str(s: &str) -> Result { - let mut slice: [u8; 32] = hex::decode_to_array(s)?; - slice.reverse(); - - let inner = Tweak::from_inner(slice)?; - Ok(ValueBlindingFactor(inner)) - } -} - -#[cfg(feature = "serde")] -impl Serialize for ValueBlindingFactor { - fn serialize(&self, s: S) -> Result { - if s.is_human_readable() { - s.collect_str(&self) - } else { - s.serialize_bytes(&self.0[..]) - } - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for ValueBlindingFactor { - fn deserialize>(d: D) -> Result { - if d.is_human_readable() { - struct HexVisitor; - - impl ::serde::de::Visitor<'_> for HexVisitor { - type Value = ValueBlindingFactor; - - fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - formatter.write_str("an ASCII hex string") - } - - fn visit_bytes(self, v: &[u8]) -> Result - where - E: ::serde::de::Error, - { - if let Ok(hex) = ::std::str::from_utf8(v) { - hex.parse().map_err(E::custom) - } else { - Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self)) - } - } - - fn visit_str(self, v: &str) -> Result - where - E: ::serde::de::Error, - { - v.parse().map_err(E::custom) - } - } - - d.deserialize_str(HexVisitor) - } else { - struct BytesVisitor; - - impl ::serde::de::Visitor<'_> for BytesVisitor { - type Value = ValueBlindingFactor; - - fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - formatter.write_str("a bytestring") - } - - fn visit_bytes(self, v: &[u8]) -> Result - where - E: ::serde::de::Error, - { - use core::convert::TryFrom; - - match <[u8; 32]>::try_from(v) { - Ok(ret) => { - let inner = Tweak::from_inner(ret).map_err(E::custom)?; - Ok(ValueBlindingFactor(inner)) - } - Err(_) => Err(E::invalid_length(v.len(), &stringify!($len))), - } - } - } - - d.deserialize_bytes(BytesVisitor) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[cfg(feature = "serde")] - use std::str::FromStr; - - #[cfg(feature = "serde")] - use bincode; - - #[test] - fn encode_length() { - let vals = [ - Value::Null, - Value::Explicit(1000), - Value::from_commitment(&[ - 0x08, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, - ]) - .unwrap(), - ]; - for v in &vals[..] { - let mut x = vec![]; - assert_eq!(v.consensus_encode(&mut x).unwrap(), v.encoded_length()); - assert_eq!(x.len(), v.encoded_length()); - } - - let nonces = [ - Nonce::Null, - Nonce::Explicit([0; 32]), - Nonce::from_commitment(&[ - 0x02, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, - ]) - .unwrap(), - ]; - for v in &nonces[..] { - let mut x = vec![]; - assert_eq!(v.consensus_encode(&mut x).unwrap(), v.encoded_length()); - assert_eq!(x.len(), v.encoded_length()); - } - - let assets = [ - Asset::Null, - Asset::Explicit(AssetId::from_byte_array([0; 32])), - Asset::from_commitment(&[ - 0x0a, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, - ]) - .unwrap(), - ]; - for v in &assets[..] { - let mut x = vec![]; - assert_eq!(v.consensus_encode(&mut x).unwrap(), v.encoded_length()); - assert_eq!(x.len(), v.encoded_length()); - } - } - - #[test] - fn commitments() { - let x = Value::from_commitment(&[ - 0x08, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, - ]) - .unwrap(); - let commitment = x.commitment().unwrap(); - let mut commitment = commitment.serialize(); - assert_eq!(x, Value::from_commitment(&commitment[..]).unwrap()); - commitment[0] = 42; - assert!(Value::from_commitment(&commitment[..]).is_err()); - - let x = Asset::from_commitment(&[ - 0x0a, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, - ]) - .unwrap(); - let commitment = x.commitment().unwrap(); - let mut commitment = commitment.serialize(); - assert_eq!(x, Asset::from_commitment(&commitment[..]).unwrap()); - commitment[0] = 42; - assert!(Asset::from_commitment(&commitment[..]).is_err()); - - let x = Nonce::from_commitment(&[ - 0x02, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, - ]) - .unwrap(); - let commitment = x.commitment().unwrap(); - let mut commitment = commitment.serialize(); - assert_eq!(x, Nonce::from_commitment(&commitment[..]).unwrap()); - commitment[0] = 42; - assert!(Nonce::from_commitment(&commitment[..]).is_err()); - } - - #[cfg(feature = "serde")] - #[test] - fn value_serde() { - use serde_test::{assert_tokens, Configure, Token}; - - let value = Value::Explicit(100_000_000); - assert_tokens( - &value, - &[ - Token::Seq { len: Some(2) }, - Token::U8(1), - Token::U64(63_601_271_583_539_200), - Token::SeqEnd - ] - ); - - let value = Value::from_commitment(&[ - 0x08, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - ]).unwrap(); - assert_tokens( - &value.readable(), - &[ - Token::Seq { len: Some(2) }, - Token::U8(2), - Token::Str( - "080101010101010101010101010101010101010101010101010101010101010101" - ), - Token::SeqEnd - ] - ); - assert_tokens( - &value.compact(), - &[ - Token::Seq { len: Some(2) }, - Token::U8(2), - Token::Bytes( - &[ - 8, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 - ] - ), - Token::SeqEnd - ] - ); - - let value = Value::Null; - assert_tokens( - &value, - &[ - Token::Seq { len: Some(1) }, - Token::U8(0), - Token::SeqEnd - ] - ); - } - - #[cfg(feature = "serde")] - #[test] - fn asset_serde() { - use serde_test::{assert_tokens, Configure, Token}; - - let asset_id = AssetId::from_str( - "630ed6f9b176af03c0cd3f8aa430f9e7b4d988cf2d0b2f204322488f03b00bf8" - ).unwrap(); - let asset = Asset::Explicit(asset_id); - assert_tokens( - &asset.readable(), - &[ - Token::Seq { len: Some(2) }, - Token::U8(1), - Token::Str( - "630ed6f9b176af03c0cd3f8aa430f9e7b4d988cf2d0b2f204322488f03b00bf8" - ), - Token::SeqEnd - ] - ); - assert_tokens( - &asset.compact(), - &[ - Token::Seq { len: Some(2) }, - Token::U8(1), - Token::Bytes( - &[ - 248, 11, 176, 3, 143, 72, 34, 67, 32, 47, 11, 45, 207, 136, 217, 180, - 231, 249, 48, 164, 138, 63, 205, 192, 3, 175, 118, 177, 249, 214, 14, 99 - ] - ), - Token::SeqEnd - ] - ); - - let asset = Asset::from_commitment(&[ - 0x0a, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - ]).unwrap(); - assert_tokens( - &asset.readable(), - &[ - Token::Seq { len: Some(2) }, - Token::U8(2), - Token::Str( - "0a0101010101010101010101010101010101010101010101010101010101010101" - ), - Token::SeqEnd - ] - ); - assert_tokens( - &asset.compact(), - &[ - Token::Seq { len: Some(2) }, - Token::U8(2), - Token::Bytes( - &[ - 10, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 - ] - ), - Token::SeqEnd - ] - ); - - let asset = Asset::Null; - assert_tokens( - &asset, - &[ - Token::Seq { len: Some(1) }, - Token::U8(0), - Token::SeqEnd - ] - ); - } - - #[cfg(feature = "serde")] - #[test] - fn nonce_serde() { - use serde_test::{assert_tokens, Configure, Token}; - - let nonce = Nonce::Explicit([ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - ]); - assert_tokens( - &nonce, - &[ - Token::Seq { len: Some(2) }, - Token::U8(1), - Token::Tuple { len: 32 }, - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::TupleEnd, - Token::SeqEnd - ] - ); - - let nonce = Nonce::from_commitment(&[ - 0x02, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - ]).unwrap(); - assert_tokens( - &nonce.readable(), - &[ - Token::Seq { len: Some(2) }, - Token::U8(2), - Token::Str( - "020101010101010101010101010101010101010101010101010101010101010101" - ), - Token::SeqEnd - ] - ); - assert_tokens( - &nonce.compact(), - &[ - Token::Seq { len: Some(2) }, - Token::U8(2), - Token::Tuple { len: 33 }, - Token::U8(2), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), - Token::U8(1), - Token::TupleEnd, - Token::SeqEnd - ] - ); - - let nonce = Nonce::Null; - assert_tokens( - &nonce, - &[ - Token::Seq { len: Some(1) }, - Token::U8(0), - Token::SeqEnd - ] - ); - } - - #[cfg(feature = "serde")] - #[test] - fn bf_serde() { - use serde_json; - use std::str::FromStr; - - let abf_str = "a5b3d111cdaa5fc111e2723df4caf315864f25fb4610cc737f10d5a55cd4096f"; - let abf_str_quoted = format!("\"{}\"", abf_str); - let abf_from_serde: AssetBlindingFactor = serde_json::from_str(&abf_str_quoted).unwrap(); - let abf_from_str = AssetBlindingFactor::from_str(abf_str).unwrap(); - assert_eq!(abf_from_serde, abf_from_str); - assert_eq!(abf_str_quoted, serde_json::to_string(&abf_from_serde).unwrap()); - - let vbf_str = "e36a4de359469f547571d117bc5509fb74fba73c84b0cdd6f4edfa7ff7fa457d"; - let vbf_str_quoted = format!("\"{}\"", vbf_str); - let vbf_from_serde: ValueBlindingFactor = serde_json::from_str(&vbf_str_quoted).unwrap(); - let vbf_from_str = ValueBlindingFactor::from_str(vbf_str).unwrap(); - assert_eq!(vbf_from_serde, vbf_from_str); - assert_eq!(vbf_str_quoted, serde_json::to_string(&vbf_from_serde).unwrap()); - } - - #[cfg(feature = "serde")] - #[test] - fn test_value_bincode_be() { - let value = Value::Explicit(500); - let bytes = bincode::serialize(&value).unwrap(); - let decoded: Value = bincode::deserialize(&bytes).unwrap(); - assert_eq!(value, decoded); - } - - #[cfg(feature = "serde")] - #[test] - fn test_value_bincode_le() { - use bincode::Options; - let value = Value::Explicit(500); - let bytes = bincode::DefaultOptions::default() - .with_little_endian() - .serialize(&value) - .unwrap(); - let decoded: Value = bincode::DefaultOptions::default() - .with_little_endian() - .deserialize(&bytes) - .unwrap(); - assert_eq!(value, decoded); - } -} diff --git a/src/confidential/asset.rs b/src/confidential/asset.rs new file mode 100644 index 00000000..d419ebde --- /dev/null +++ b/src/confidential/asset.rs @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Confiential Assets + +use core::{fmt, str}; +use std::io; + +use secp256k1_zkp::rand::Rng; +use secp256k1_zkp::{self, Generator, Secp256k1, Signing, Tweak, ZERO_TWEAK}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::encode::{self, Decodable, Encodable}; +use crate::issuance::AssetId; + +type ExplicitInner = AssetId; +type ConfInner = Generator; + +const EXPLICIT_LEN: usize = 32; +const CONFIDENTIAL_LEN: usize = 33; +const CONF_PREFIX_1: u8 = 0x0a; +const CONF_PREFIX_2: u8 = 0x0b; + +/// A CT commitment to an asset +#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub enum Asset { + /// No value + #[default] + Null, + /// Asset entropy is explicitly encoded + Explicit(ExplicitInner), + /// Asset is committed + Confidential(ConfInner), +} + +impl Asset { + /// Create asset commitment. + pub fn new_confidential( + secp: &Secp256k1, + asset: AssetId, + bf: BlindingFactor, + ) -> Self { + Self::Confidential(ConfInner::new_blinded(secp, asset.into_tag(), bf.into_inner())) + } + + /// Serialized length, in bytes + pub fn encoded_length(&self) -> usize { + match *self { + Self::Null => 1, + Self::Explicit(..) => 1 + EXPLICIT_LEN, + Self::Confidential(..) => CONFIDENTIAL_LEN, + } + } + + /// Create from commitment. + pub fn from_commitment(bytes: &[u8]) -> Result { + Ok(Self::Confidential(ConfInner::from_slice(bytes)?)) + } + + /// Check if the object is null. + pub fn is_null(&self) -> bool { matches!(*self, Self::Null) } + + /// Check if the object is explicit. + pub fn is_explicit(&self) -> bool { matches!(*self, Self::Explicit(_)) } + + /// Check if the object is confidential. + pub fn is_confidential(&self) -> bool { matches!(*self, Self::Confidential(_)) } + + /// Returns the explicit inner value. + /// Returns [None] if [`Self::is_explicit`] returns false. + pub fn explicit(&self) -> Option { + match *self { + Self::Explicit(i) => Some(i), + _ => None, + } + } + + /// Returns the confidential commitment in case of a confidential value. + /// Returns [None] if [`Self::is_confidential`] returns false. + pub fn commitment(&self) -> Option { + match *self { + Self::Confidential(i) => Some(i), + _ => None, + } + } + + /// Internally used function for getting the generator from asset + /// Used in the amount verification check + /// Returns [`None`] is the asset is [`Self::Null`] + /// Converts a explicit asset into a generator and returns the confidential + /// generator as is. + pub fn into_asset_gen( + self, + secp: &Secp256k1, + ) -> Option { + match self { + // Only error is Null error which is dealt with later + // when we have more context information about it. + Self::Null => None, + Self::Explicit(x) => Some(ConfInner::new_unblinded(secp, x.into_tag())), + Self::Confidential(gen) => Some(gen), + } + } +} + +impl From for Asset { + fn from(from: ConfInner) -> Self { Self::Confidential(from) } +} + +impl fmt::Display for Asset { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Null => f.write_str("null"), + Self::Explicit(n) => write!(f, "{}", n), + Self::Confidential(generator) => write!(f, "{:02x}", generator), + } + } +} + +impl Encodable for Asset { + fn consensus_encode(&self, mut s: S) -> Result { + match *self { + Self::Null => { + s.write_all(&[0u8])?; + Ok(1) + } + Self::Explicit(n) => { + s.write_all(&[1u8])?; + s.write_all(n.as_byte_array())?; + Ok(1 + EXPLICIT_LEN) + } + Self::Confidential(generator) => { + s.write_all(&generator.serialize())?; + Ok(CONFIDENTIAL_LEN) + } + } + } +} + +impl Decodable for Asset { + fn consensus_decode(mut d: D) -> Result { + let mut buf = [0u8; CONFIDENTIAL_LEN]; + d.read_exact(&mut buf[0..1])?; + + match buf[0] { + 0 => Ok(Self::Null), + 1 => { + let mut buf = [0; EXPLICIT_LEN]; + d.read_exact(&mut buf)?; + Ok(Self::Explicit(AssetId::from_byte_array(buf))) + } + p if p == CONF_PREFIX_1 || p == CONF_PREFIX_2 => { + d.read_exact(&mut buf[1..])?; + Ok(Self::Confidential(ConfInner::from_slice(&buf[..])?)) + } + p => Err(encode::Error::InvalidConfidentialPrefix(p)), + } + } +} + +#[cfg(feature = "serde")] +impl Serialize for Asset { + fn serialize(&self, s: S) -> Result { + use serde::ser::SerializeSeq; + + let seq_len = match *self { + Self::Null => 1, + Self::Explicit(_) | Self::Confidential(_) => 2, + }; + let mut seq = s.serialize_seq(Some(seq_len))?; + + match *self { + Self::Null => seq.serialize_element(&0u8)?, + Self::Explicit(n) => { + seq.serialize_element(&1u8)?; + seq.serialize_element(&n)?; + } + Self::Confidential(commitment) => { + seq.serialize_element(&2u8)?; + seq.serialize_element(&commitment)?; + } + } + seq.end() + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Asset { + fn deserialize>(d: D) -> Result { + use serde::de::{Error, SeqAccess, Visitor}; + struct CommitVisitor; + + impl<'de> Visitor<'de> for CommitVisitor { + type Value = Asset; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("a committed value") + } + + fn visit_seq>(self, mut access: A) -> Result { + let prefix = access.next_element::()?; + match prefix { + Some(0) => Ok(Self::Value::Null), + Some(1) => match access.next_element()? { + Some(x) => Ok(Self::Value::Explicit(x)), + None => Err(A::Error::custom("missing explicit asset")), + }, + Some(2) => match access.next_element()? { + Some(x) => Ok(Self::Value::Confidential(x)), + None => Err(A::Error::custom("missing generator")), + }, + _ => Err(A::Error::custom("wrong or missing prefix")), + } + } + } + + d.deserialize_seq(CommitVisitor) + } +} + +/// Blinding factor used for asset commitments. +#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub struct BlindingFactor(pub(crate) Tweak); + +impl BlindingFactor { + /// Generate random asset blinding factor. + pub fn new(rng: &mut R) -> Self { Self(Tweak::new(rng)) } + + /// Parse a blinding factor from a 64-character hex string. + #[deprecated(since = "0.27.0", note = "use s.parse() instead")] + pub fn from_hex(s: &str) -> Result { s.parse() } + + /// Create from bytes. + pub fn from_byte_array(bytes: [u8; 32]) -> Result { + Ok(Self(Tweak::from_inner(bytes)?)) + } + + /// Create from bytes. + pub fn from_slice(bytes: &[u8]) -> Result { + Ok(Self(Tweak::from_slice(bytes)?)) + } + + /// Returns the inner value. + pub fn into_inner(self) -> Tweak { self.0 } + + /// Get a unblinded/zero `AssetBlinding` factor + pub fn zero() -> Self { Self(ZERO_TWEAK) } +} + +impl core::borrow::Borrow<[u8]> for BlindingFactor { + fn borrow(&self) -> &[u8] { &self.0[..] } +} + +hex::impl_fmt_traits! { + #[display_backward(true)] + impl fmt_traits for BlindingFactor { + const LENGTH: usize = 32; + } +} + +impl str::FromStr for BlindingFactor { + type Err = encode::Error; + + fn from_str(s: &str) -> Result { + let mut slice: [u8; 32] = hex::decode_to_array(s)?; + slice.reverse(); + + let inner = Tweak::from_inner(slice)?; + Ok(Self(inner)) + } +} + +#[cfg(feature = "serde")] +impl Serialize for BlindingFactor { + fn serialize(&self, s: S) -> Result { + if s.is_human_readable() { + s.collect_str(&self) + } else { + s.serialize_bytes(&self.0[..]) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for BlindingFactor { + fn deserialize>(d: D) -> Result { + if d.is_human_readable() { + struct HexVisitor; + + impl ::serde::de::Visitor<'_> for HexVisitor { + type Value = BlindingFactor; + + fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + formatter.write_str("an ASCII hex string") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: ::serde::de::Error, + { + if let Ok(hex) = ::std::str::from_utf8(v) { + hex.parse().map_err(E::custom) + } else { + Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self)) + } + } + + fn visit_str(self, v: &str) -> Result + where + E: ::serde::de::Error, + { + v.parse().map_err(E::custom) + } + } + + d.deserialize_str(HexVisitor) + } else { + struct BytesVisitor; + + impl ::serde::de::Visitor<'_> for BytesVisitor { + type Value = BlindingFactor; + + fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + formatter.write_str("a bytestring") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: ::serde::de::Error, + { + use core::convert::TryFrom; + + match <[u8; 32]>::try_from(v) { + Ok(ret) => { + let inner = Tweak::from_inner(ret).map_err(E::custom)?; + Ok(BlindingFactor(inner)) + } + Err(_) => Err(E::invalid_length(v.len(), &stringify!($len))), + } + } + } + + d.deserialize_bytes(BytesVisitor) + } + } +} diff --git a/src/confidential/mod.rs b/src/confidential/mod.rs new file mode 100644 index 00000000..b6dbe807 --- /dev/null +++ b/src/confidential/mod.rs @@ -0,0 +1,435 @@ +// Rust Elements Library +// Written in 2018 by +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! # Confidential Commitments +//! +//! Structures representing Pedersen commitments of various types +//! + +#![warn(clippy::use_self)] + +mod asset; +mod nonce; +mod range_proof; +mod surjection_proof; +mod value; + +use core::fmt; + +use secp256k1_zkp; + +pub use self::asset::{Asset, BlindingFactor as AssetBlindingFactor}; +pub use self::nonce::Nonce; +pub use self::range_proof::RangeProof; +pub use self::surjection_proof::SurjectionProof; +pub use self::value::{BlindingFactor as ValueBlindingFactor, Value}; +use crate::encode; +use crate::issuance::AssetId; + +/// Error decoding hexadecimal string into tweak-like value. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TweakHexDecodeError { + /// Invalid hexadecimal string. + InvalidHex(hex::DecodeFixedLengthBytesError), + /// Invalid tweak after decoding hexadecimal string. + InvalidTweak(secp256k1_zkp::Error), +} + +impl fmt::Display for TweakHexDecodeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::InvalidHex(err) => { + write!(f, "Invalid hex: {}", err) + } + Self::InvalidTweak(err) => { + write!(f, "Invalid tweak: {}", err) + } + } + } +} + +#[doc(hidden)] +impl From for TweakHexDecodeError { + fn from(err: hex::DecodeFixedLengthBytesError) -> Self { Self::InvalidHex(err) } +} + +#[doc(hidden)] +impl From for TweakHexDecodeError { + fn from(err: secp256k1_zkp::Error) -> Self { Self::InvalidTweak(err) } +} + +impl From for encode::Error { + fn from(value: TweakHexDecodeError) -> Self { + match value { + TweakHexDecodeError::InvalidHex(err) => Self::HexFixedError(err), + TweakHexDecodeError::InvalidTweak(err) => Self::Secp256k1zkp(err), + } + } +} + +impl std::error::Error for TweakHexDecodeError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::InvalidHex(err) => Some(err), + Self::InvalidTweak(err) => Some(err), + } + } +} +#[cfg(test)] +mod tests { + #[cfg(feature = "serde")] + use std::str::FromStr; + + #[cfg(feature = "serde")] + use bincode; + + use super::*; + use crate::encode::Encodable as _; + + const VALUE_EXPLICIT: [u8; 9] = [1, 0, 0, 0, 0, 0, 0, 3, 232]; + + const VALUE_COMMITMENT1: [u8; 33] = [ + 0x08, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, + ]; + + const VALUE_COMMITMENT2: [u8; 33] = [ + 0x09, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, + ]; + + const NONCE_EXPLICIT: [u8; 33] = [ + 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ]; + + const NONCE_COMMITMENT1: [u8; 33] = [ + 0x02, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, + ]; + + const NONCE_COMMITMENT2: [u8; 33] = [ + 0x03, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, + ]; + + const ASSET_EXPLICIT: [u8; 33] = [ + 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ]; + + const ASSET_COMMITMENT1: [u8; 33] = [ + 0x0a, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, + ]; + + const ASSET_COMMITMENT2: [u8; 33] = [ + 0x0b, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, + ]; + + #[test] + fn encode_length() { + let val_encodings = [ + vec![0], + VALUE_EXPLICIT.to_vec(), + VALUE_COMMITMENT1.to_vec(), + VALUE_COMMITMENT2.to_vec(), + ]; + let vals = [ + Value::Null, + Value::Explicit(1000), + Value::from_commitment(&VALUE_COMMITMENT1).unwrap(), + Value::from_commitment(&VALUE_COMMITMENT2).unwrap(), + ]; + for (v, enc) in vals.iter().zip(val_encodings.iter()) { + let mut x = vec![]; + assert_eq!(v.consensus_encode(&mut x).unwrap(), v.encoded_length()); + assert_eq!(x.len(), v.encoded_length()); + assert_eq!(x, *enc); + } + + let nonce_encodings = [ + vec![0], + NONCE_EXPLICIT.to_vec(), + NONCE_COMMITMENT1.to_vec(), + NONCE_COMMITMENT2.to_vec(), + ]; + let nonces = [ + Nonce::Null, + Nonce::Explicit([0; 32]), + Nonce::from_commitment(&NONCE_COMMITMENT1).unwrap(), + Nonce::from_commitment(&NONCE_COMMITMENT2).unwrap(), + ]; + for (v, enc) in nonces.iter().zip(nonce_encodings.iter()) { + let mut x = vec![]; + assert_eq!(v.consensus_encode(&mut x).unwrap(), v.encoded_length()); + assert_eq!(x.len(), v.encoded_length()); + assert_eq!(x, *enc); + } + + let asset_encodings = [ + vec![0], + ASSET_EXPLICIT.to_vec(), + ASSET_COMMITMENT1.to_vec(), + ASSET_COMMITMENT2.to_vec(), + ]; + let assets = [ + Asset::Null, + Asset::Explicit(AssetId::from_byte_array([0; 32])), + Asset::from_commitment(&ASSET_COMMITMENT1).unwrap(), + Asset::from_commitment(&ASSET_COMMITMENT2).unwrap(), + ]; + for (v, enc) in assets.iter().zip(asset_encodings.iter()) { + let mut x = vec![]; + assert_eq!(v.consensus_encode(&mut x).unwrap(), v.encoded_length()); + assert_eq!(x.len(), v.encoded_length()); + assert_eq!(x, *enc); + } + } + + #[test] + fn commitments() { + let x = Value::from_commitment(&VALUE_COMMITMENT1).unwrap(); + let commitment = x.commitment().unwrap(); + let mut commitment = commitment.serialize(); + assert_eq!(x, Value::from_commitment(&commitment[..]).unwrap()); + commitment[0] = 42; + assert!(Value::from_commitment(&commitment[..]).is_err()); + + let x = Asset::from_commitment(&ASSET_COMMITMENT1).unwrap(); + let commitment = x.commitment().unwrap(); + let mut commitment = commitment.serialize(); + assert_eq!(x, Asset::from_commitment(&commitment[..]).unwrap()); + commitment[0] = 42; + assert!(Asset::from_commitment(&commitment[..]).is_err()); + + let x = Nonce::from_commitment(&NONCE_COMMITMENT1).unwrap(); + let commitment = x.commitment().unwrap(); + let mut commitment = commitment.serialize(); + assert_eq!(x, Nonce::from_commitment(&commitment[..]).unwrap()); + commitment[0] = 42; + assert!(Nonce::from_commitment(&commitment[..]).is_err()); + } + + #[cfg(feature = "serde")] + #[test] + fn value_serde() { + use serde_test::{assert_tokens, Configure, Token}; + + let value = Value::Explicit(100_000_000); + assert_tokens( + &value, + &[ + Token::Seq { len: Some(2) }, + Token::U8(1), + Token::U64(63_601_271_583_539_200), + Token::SeqEnd, + ], + ); + + let value = Value::from_commitment(&VALUE_COMMITMENT1).unwrap(); + assert_tokens( + &value.readable(), + &[ + Token::Seq { len: Some(2) }, + Token::U8(2), + Token::Str("080101010101010101010101010101010101010101010101010101010101010101"), + Token::SeqEnd, + ], + ); + assert_tokens( + &value.compact(), + &[ + Token::Seq { len: Some(2) }, + Token::U8(2), + Token::Bytes(&VALUE_COMMITMENT1), + Token::SeqEnd, + ], + ); + + let value = Value::Null; + assert_tokens(&value, &[Token::Seq { len: Some(1) }, Token::U8(0), Token::SeqEnd]); + } + + #[cfg(feature = "serde")] + #[test] + fn asset_serde() { + use serde_test::{assert_tokens, Configure, Token}; + + let asset_id = + AssetId::from_str("630ed6f9b176af03c0cd3f8aa430f9e7b4d988cf2d0b2f204322488f03b00bf8") + .unwrap(); + let asset = Asset::Explicit(asset_id); + assert_tokens( + &asset.readable(), + &[ + Token::Seq { len: Some(2) }, + Token::U8(1), + Token::Str("630ed6f9b176af03c0cd3f8aa430f9e7b4d988cf2d0b2f204322488f03b00bf8"), + Token::SeqEnd, + ], + ); + assert_tokens( + &asset.compact(), + &[ + Token::Seq { len: Some(2) }, + Token::U8(1), + Token::Bytes(&[ + 248, 11, 176, 3, 143, 72, 34, 67, 32, 47, 11, 45, 207, 136, 217, 180, 231, 249, + 48, 164, 138, 63, 205, 192, 3, 175, 118, 177, 249, 214, 14, 99, + ]), + Token::SeqEnd, + ], + ); + + let asset = Asset::from_commitment(&ASSET_COMMITMENT1).unwrap(); + assert_tokens( + &asset.readable(), + &[ + Token::Seq { len: Some(2) }, + Token::U8(2), + Token::Str("0a0101010101010101010101010101010101010101010101010101010101010101"), + Token::SeqEnd, + ], + ); + assert_tokens( + &asset.compact(), + &[ + Token::Seq { len: Some(2) }, + Token::U8(2), + Token::Bytes(&ASSET_COMMITMENT1), + Token::SeqEnd, + ], + ); + + let asset = Asset::Null; + assert_tokens(&asset, &[Token::Seq { len: Some(1) }, Token::U8(0), Token::SeqEnd]); + } + + #[cfg(feature = "serde")] + #[test] + #[rustfmt::skip] + fn nonce_serde() { + use serde_test::{assert_tokens, Configure, Token}; + + let nonce = Nonce::Explicit([ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + ]); + assert_tokens( + &nonce, + &[ + Token::Seq { len: Some(2) }, + Token::U8(1), + Token::Tuple { len: 32 }, + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::TupleEnd, + Token::SeqEnd + ] + ); + + let nonce = Nonce::from_commitment(&NONCE_COMMITMENT1).unwrap(); + assert_tokens( + &nonce.readable(), + &[ + Token::Seq { len: Some(2) }, + Token::U8(2), + Token::Str( + "020101010101010101010101010101010101010101010101010101010101010101" + ), + Token::SeqEnd + ] + ); + assert_tokens( + &nonce.compact(), + &[ + Token::Seq { len: Some(2) }, + Token::U8(2), + Token::Tuple { len: 33 }, + Token::U8(2), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), Token::U8(1), Token::U8(1), Token::U8(1), + Token::U8(1), + Token::TupleEnd, + Token::SeqEnd + ] + ); + + let nonce = Nonce::Null; + assert_tokens( + &nonce, + &[ + Token::Seq { len: Some(1) }, + Token::U8(0), + Token::SeqEnd + ] + ); + } + + #[cfg(feature = "serde")] + #[test] + fn bf_serde() { + use std::str::FromStr; + + use serde_json; + + let abf_str = "a5b3d111cdaa5fc111e2723df4caf315864f25fb4610cc737f10d5a55cd4096f"; + let abf_str_quoted = format!("\"{}\"", abf_str); + let abf_from_serde: AssetBlindingFactor = serde_json::from_str(&abf_str_quoted).unwrap(); + let abf_from_str = AssetBlindingFactor::from_str(abf_str).unwrap(); + assert_eq!(abf_from_serde, abf_from_str); + assert_eq!(abf_str_quoted, serde_json::to_string(&abf_from_serde).unwrap()); + + let vbf_str = "e36a4de359469f547571d117bc5509fb74fba73c84b0cdd6f4edfa7ff7fa457d"; + let vbf_str_quoted = format!("\"{}\"", vbf_str); + let vbf_from_serde: ValueBlindingFactor = serde_json::from_str(&vbf_str_quoted).unwrap(); + let vbf_from_str = ValueBlindingFactor::from_str(vbf_str).unwrap(); + assert_eq!(vbf_from_serde, vbf_from_str); + assert_eq!(vbf_str_quoted, serde_json::to_string(&vbf_from_serde).unwrap()); + } + + #[cfg(feature = "serde")] + #[test] + fn test_value_bincode_be() { + let value = Value::Explicit(500); + let bytes = bincode::serialize(&value).unwrap(); + let decoded: Value = bincode::deserialize(&bytes).unwrap(); + assert_eq!(value, decoded); + } + + #[cfg(feature = "serde")] + #[test] + fn test_value_bincode_le() { + use bincode::Options; + let value = Value::Explicit(500); + let bytes = + bincode::DefaultOptions::default().with_little_endian().serialize(&value).unwrap(); + let decoded: Value = + bincode::DefaultOptions::default().with_little_endian().deserialize(&bytes).unwrap(); + assert_eq!(value, decoded); + } +} diff --git a/src/confidential/nonce.rs b/src/confidential/nonce.rs new file mode 100644 index 00000000..0d9ca000 --- /dev/null +++ b/src/confidential/nonce.rs @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Confiential Nonces + +use core::fmt; +use std::io; + +use secp256k1_zkp::rand::{CryptoRng, RngCore}; +use secp256k1_zkp::{self, PublicKey, Secp256k1, SecretKey, Signing}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::encode::{self, Decodable, Encodable}; +use crate::hashes::sha256d; + +type ExplicitInner = [u8; 32]; +type ConfInner = PublicKey; + +const EXPLICIT_LEN: usize = 32; +const CONFIDENTIAL_LEN: usize = 33; +const CONF_PREFIX_1: u8 = 0x02; +const CONF_PREFIX_2: u8 = 0x03; + +/// A CT commitment to an output nonce (i.e. a public key) +#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub enum Nonce { + /// No value + #[default] + Null, + /// There should be no such thing as an "explicit nonce", but Elements will deserialize + /// such a thing (and insists that its size be 32 bytes). So we stick a 32-byte type here + /// that implements all the traits we need. + Explicit(ExplicitInner), + /// Nonce is committed + Confidential(ConfInner), +} + +impl Nonce { + /// Create nonce commitment. + pub fn new_confidential( + rng: &mut R, + secp: &Secp256k1, + receiver_blinding_pk: &ConfInner, + ) -> (Self, SecretKey) { + let ephemeral_sk = SecretKey::new(rng); + Self::with_ephemeral_sk(secp, ephemeral_sk, receiver_blinding_pk) + } + + /// Similar to [`Self::new_confidential`], but with a given `ephemeral_sk` + /// instead of sampling it from rng. + pub fn with_ephemeral_sk( + secp: &Secp256k1, + ephemeral_sk: SecretKey, + receiver_blinding_pk: &ConfInner, + ) -> (Self, SecretKey) { + let sender_pk = ConfInner::from_secret_key(secp, &ephemeral_sk); + let shared_secret = Self::make_shared_secret(receiver_blinding_pk, &ephemeral_sk); + (Self::Confidential(sender_pk), shared_secret) + } + + /// Calculate the shared secret. + pub fn shared_secret(&self, receiver_blinding_sk: &SecretKey) -> Option { + match self { + Self::Confidential(sender_pk) => + Some(Self::make_shared_secret(sender_pk, receiver_blinding_sk)), + _ => None, + } + } + + /// Create the shared secret. + fn make_shared_secret(pk: &ConfInner, sk: &SecretKey) -> SecretKey { + let xy = secp256k1_zkp::ecdh::shared_secret_point(pk, sk); + let shared_secret = { + // Yes, what follows is the compressed representation of a Bitcoin public key. + // However, this is more by accident then by design, see here: https://github.com/rust-bitcoin/rust-secp256k1/pull/255#issuecomment-744146282 + + let mut dh_secret = [0u8; CONFIDENTIAL_LEN]; + dh_secret[0] = if xy.last().unwrap() % 2 == 0 { CONF_PREFIX_1 } else { CONF_PREFIX_2 }; + dh_secret[1..].copy_from_slice(&xy[0..32]); + + sha256d::Hash::hash(&dh_secret).to_byte_array() + }; + + SecretKey::from_slice(&shared_secret[..32]).expect("always has exactly 32 bytes") + } + + /// Serialized length, in bytes + pub fn encoded_length(&self) -> usize { + match *self { + Self::Null => 1, + Self::Explicit(..) => 1 + EXPLICIT_LEN, + Self::Confidential(..) => CONFIDENTIAL_LEN, + } + } + + /// Create from commitment. + pub fn from_commitment(bytes: &[u8]) -> Result { + Ok(Self::Confidential( + ConfInner::from_slice(bytes).map_err(secp256k1_zkp::Error::Upstream)?, + )) + } + + /// Check if the object is null. + pub fn is_null(&self) -> bool { matches!(*self, Self::Null) } + + /// Check if the object is explicit. + pub fn is_explicit(&self) -> bool { matches!(*self, Self::Explicit(_)) } + + /// Check if the object is confidential. + pub fn is_confidential(&self) -> bool { matches!(*self, Self::Confidential(_)) } + + /// Returns the explicit inner value. + /// Returns [None] if [`Self::is_explicit`] returns false. + pub fn explicit(&self) -> Option { + match *self { + Self::Explicit(i) => Some(i), + _ => None, + } + } + + /// Returns the confidential commitment in case of a confidential value. + /// Returns [None] if [`Self::is_confidential`] returns false. + pub fn commitment(&self) -> Option { + match *self { + Self::Confidential(i) => Some(i), + _ => None, + } + } +} + +impl From for Nonce { + fn from(from: ConfInner) -> Self { Self::Confidential(from) } +} + +impl fmt::Display for Nonce { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Null => f.write_str("null"), + Self::Explicit(n) => { + for b in &n { + write!(f, "{:02x}", b)?; + } + Ok(()) + } + Self::Confidential(pk) => write!(f, "{:02x}", pk), + } + } +} + +impl Encodable for Nonce { + fn consensus_encode(&self, mut s: S) -> Result { + match *self { + Self::Null => { + s.write_all(&[0u8])?; + Ok(1) + } + Self::Explicit(n) => { + s.write_all(&[1u8])?; + s.write_all(&n)?; + Ok(1 + EXPLICIT_LEN) + } + Self::Confidential(commitment) => { + s.write_all(&commitment.serialize())?; + Ok(CONFIDENTIAL_LEN) + } + } + } +} + +impl Decodable for Nonce { + fn consensus_decode(mut d: D) -> Result { + let mut buf = [0u8; CONFIDENTIAL_LEN]; + d.read_exact(&mut buf[0..1])?; + + match buf[0] { + 0 => Ok(Self::Null), + 1 => { + let mut buf = [0; EXPLICIT_LEN]; + d.read_exact(&mut buf)?; + Ok(Self::Explicit(buf)) + } + p if p == CONF_PREFIX_1 || p == CONF_PREFIX_2 => { + d.read_exact(&mut buf[1..])?; + Ok(Self::Confidential(ConfInner::from_slice(&buf)?)) + } + p => Err(encode::Error::InvalidConfidentialPrefix(p)), + } + } +} + +#[cfg(feature = "serde")] +impl Serialize for Nonce { + fn serialize(&self, s: S) -> Result { + use serde::ser::SerializeSeq; + + let seq_len = match *self { + Self::Null => 1, + Self::Explicit(_) | Self::Confidential(_) => 2, + }; + let mut seq = s.serialize_seq(Some(seq_len))?; + + match *self { + Self::Null => seq.serialize_element(&0u8)?, + Self::Explicit(n) => { + seq.serialize_element(&1u8)?; + seq.serialize_element(&n)?; + } + Self::Confidential(commitment) => { + seq.serialize_element(&2u8)?; + seq.serialize_element(&commitment)?; + } + } + seq.end() + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Nonce { + fn deserialize>(d: D) -> Result { + use serde::de::{Error, SeqAccess, Visitor}; + struct CommitVisitor; + + impl<'de> Visitor<'de> for CommitVisitor { + type Value = Nonce; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("a committed value") + } + + fn visit_seq>(self, mut access: A) -> Result { + let prefix = access.next_element::()?; + match prefix { + Some(0) => Ok(Self::Value::Null), + Some(1) => match access.next_element()? { + Some(x) => Ok(Self::Value::Explicit(x)), + None => Err(A::Error::custom("missing explicit nonce")), + }, + Some(2) => match access.next_element()? { + Some(x) => Ok(Self::Value::Confidential(x)), + None => Err(A::Error::custom("missing nonce")), + }, + _ => Err(A::Error::custom("wrong or missing prefix")), + } + } + } + + d.deserialize_seq(CommitVisitor) + } +} diff --git a/src/confidential/range_proof.rs b/src/confidential/range_proof.rs new file mode 100644 index 00000000..89894333 --- /dev/null +++ b/src/confidential/range_proof.rs @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Range Proofs + +use core::convert::TryInto; +use std::io; + +use secp256k1_zkp::rand::{CryptoRng, RngCore}; +use secp256k1_zkp::{self, Generator, PedersenCommitment, Secp256k1, SecretKey, Signing, Tweak}; +#[cfg(feature = "serde")] +use serde::{Deserializer, Serializer}; + +use crate::confidential::ValueBlindingFactor; +use crate::encode; + +/// A range proof, which represents a proof that a confidential value lies within +/// some range (typically `[0, 2^64)`). +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct RangeProof { + inner: Option>, +} + +impl RangeProof { + /// No range proof. + pub const EMPTY: Self = Self { inner: None }; + + /// Constructs a new [`RangeProof`]. + #[allow(clippy::too_many_arguments)] + pub fn new( + secp: &Secp256k1, + min_value: u64, + commitment: PedersenCommitment, + value: u64, + commitment_blinding: Tweak, + message: &[u8], + additional_commitment: &[u8], + sk: SecretKey, + exp: i32, + min_bits: u8, + additional_generator: Generator, + ) -> Result { + secp256k1_zkp::RangeProof::new( + secp, + min_value, + commitment, + value, + commitment_blinding, + message, + additional_commitment, + sk, + exp, + min_bits, + additional_generator, + ) + .map(|inner| Self { inner: Some(Box::new(inner)) }) + } + + /// Parses a [`RangeProof`] from a byte slice (with no length prefix). + pub fn from_slice(sl: &[u8]) -> Result { + if sl.is_empty() { + Ok(Self { inner: None }) + } else { + secp256k1_zkp::RangeProof::from_slice(sl) + .map(|inner| Self { inner: Some(Box::new(inner)) }) + } + } + + /// Outputs a [`RangeProof`] proving that a commitment matches an exact value. + pub fn blind_value_proof( + rng: &mut R, + secp: &Secp256k1, + explicit_val: u64, + value_commit: PedersenCommitment, + asset_gen: Generator, + vbf: ValueBlindingFactor, + ) -> Result { + secp256k1_zkp::RangeProof::new( + secp, + explicit_val, // min_value + value_commit, // value_commit + explicit_val, // value + vbf.into_inner(), // blinding factor + &[], // message + &[], // add commitment + SecretKey::new(rng), // nonce + -1, // exp + 0, // min bits + asset_gen, // additional gen + ) + .map(|inner| Self { inner: Some(Box::new(inner)) }) + } + + /// Verifies a [`RangeProof`] proving that a commitment matches an exact value. + pub fn blind_value_proof_verify( + &self, + secp: &Secp256k1, + explicit_val: u64, + asset_gen: Generator, + value_commit: PedersenCommitment, + ) -> bool { + let Some(inner) = self.inner.as_deref() else { + return false; + }; + if explicit_val == u64::MAX { + // FIXME upstream will panic on this input; we should be able to validate + // proofs with this value. + return false; + } + + let Ok(range) = inner.verify(secp, value_commit, &[], asset_gen) else { + return false; + }; + range == (explicit_val..explicit_val + 1) + } + + /// The length of the range proof (zero if it is empty/absent). + pub fn len(&self) -> usize { self.inner.as_deref().map_or(0, secp256k1_zkp::RangeProof::len) } + + /// Whether the range proof is absent. + pub fn is_empty(&self) -> bool { self.inner.is_none() } + + /// Serializes the range proof as a byte vector. + pub fn to_vec(&self) -> Vec { + match self.inner.as_deref() { + Some(prf) => secp256k1_zkp::RangeProof::serialize(prf), + None => Vec::new(), + } + } + + /// Extracts the minimum value encoded in the range proof. + pub fn minimim_value(&self) -> Option { + // inefficient, consider implementing index on rangeproof + let prf = self.to_vec(); + let byte0 = prf.first()?; + + let has_nonzero_range = byte0 & 64 == 64; + let has_min = byte0 & 32 == 32; + + if !has_min { + None + } else if has_nonzero_range { + let bytes: [u8; 8] = prf.get(2..10)?.try_into().ok()?; + Some(u64::from_be_bytes(bytes)) + } else { + let bytes: [u8; 8] = prf.get(1..9)?.try_into().ok()?; + Some(u64::from_be_bytes(bytes)) + } + } + + /// Obtains a reference to the underlying secp256k1-zkp object. + pub fn as_ref(&self) -> Option<&secp256k1_zkp::RangeProof> { self.inner.as_deref() } +} + +impl crate::encode::Encodable for RangeProof { + fn consensus_encode(&self, e: W) -> Result { + self.to_vec().consensus_encode(e) + } +} + +impl crate::encode::Decodable for RangeProof { + fn consensus_decode(d: D) -> Result { + let v = Vec::::consensus_decode(d)?; + if v.is_empty() { + Ok(Self { inner: None }) + } else { + secp256k1_zkp::RangeProof::from_slice(&v) + .map(|inner| Self { inner: Some(Box::new(inner)) }) + .map_err(encode::Error::Secp256k1zkp) + } + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for RangeProof { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.inner.serialize(serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for RangeProof { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Option::::deserialize(deserializer) + .map(|inner| Self { inner: inner.map(Box::new) }) + } +} diff --git a/src/confidential/surjection_proof.rs b/src/confidential/surjection_proof.rs new file mode 100644 index 00000000..6fb6f8ea --- /dev/null +++ b/src/confidential/surjection_proof.rs @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Surjection Proofs + +use std::io; + +use secp256k1_zkp::rand::{CryptoRng, RngCore}; +use secp256k1_zkp::{self, Generator, Secp256k1, Signing, Tweak, ZERO_TWEAK}; +#[cfg(feature = "serde")] +use serde::{Deserializer, Serializer}; + +use crate::confidential::{AssetBlindingFactor, AssetId}; +use crate::encode; + +/// A surjection proof, proving that an asset commitment commits to the same asset ID +/// as a commitment from a given set. +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct SurjectionProof { + inner: Option>, +} + +impl SurjectionProof { + /// No surjection proof. + pub const EMPTY: Self = Self { inner: None }; + + /// Constructs a new [`SurjectionProof`]. + pub fn new( + secp: &Secp256k1, + rng: &mut R, + asset: AssetId, + asset_bf: AssetBlindingFactor, + inputs: S, + ) -> Result + where + R: RngCore + CryptoRng, + C: Signing, + S: AsRef<[(Generator, secp256k1_zkp::Tag, Tweak)]>, + { + secp256k1_zkp::SurjectionProof::new( + secp, + rng, + asset.into_tag(), + asset_bf.into_inner(), + inputs.as_ref(), + ) + .map(|inner| Self { inner: Some(Box::new(inner)) }) + } + + /// Parses a [`SurjectionProof`] from a byte slice (with no length prefix). + pub fn from_slice(sl: &[u8]) -> Result { + if sl.is_empty() { + Ok(Self { inner: None }) + } else { + secp256k1_zkp::SurjectionProof::from_slice(sl) + .map(|inner| Self { inner: Some(Box::new(inner)) }) + } + } + + /// Serializes the surjection proof as a byte vector. + pub fn to_vec(&self) -> Vec { + match self.inner.as_deref() { + Some(prf) => secp256k1_zkp::SurjectionProof::serialize(prf), + None => Vec::new(), + } + } + + /// Outputs a [`SurjectionProof`] proving that an asset matches an exact asset ID. + pub fn blind_asset_proof( + rng: &mut R, + secp: &Secp256k1, + asset: AssetId, + abf: AssetBlindingFactor, + ) -> Result { + let gen = Generator::new_unblinded(secp, asset.into_tag()); + Self::new(secp, rng, asset, abf, [(gen, asset.into_tag(), ZERO_TWEAK)]) + } + + /// Verifies a [`SurjectionProof`] proving that an asset matches an exact asset ID. + pub fn blind_asset_proof_verify( + &self, + secp: &Secp256k1, + asset: AssetId, + asset_commit: Generator, + ) -> bool { + let gen = Generator::new_unblinded(secp, asset.into_tag()); + match self.inner.as_deref() { + Some(inner) => inner.verify(secp, asset_commit, &[gen]), + None => false, + } + } + + /// The length of the range proof (zero if it is empty/absent). + pub fn len(&self) -> usize { + self.inner.as_deref().map_or(0, secp256k1_zkp::SurjectionProof::len) + } + + /// Whether the surjectionproof is absent. + pub fn is_empty(&self) -> bool { self.inner.is_none() } + + /// Obtains a reference to the underlying secp256k1-zkp object. + pub fn as_ref(&self) -> Option<&secp256k1_zkp::SurjectionProof> { self.inner.as_deref() } +} + +impl crate::encode::Encodable for SurjectionProof { + fn consensus_encode(&self, e: W) -> Result { + match self.inner.as_ref() { + Some(prf) => secp256k1_zkp::SurjectionProof::serialize(prf).consensus_encode(e), + None => <[u8]>::consensus_encode(&[], e), + } + } +} + +impl crate::encode::Decodable for SurjectionProof { + fn consensus_decode(d: D) -> Result { + let v = Vec::::consensus_decode(d)?; + if v.is_empty() { + Ok(Self { inner: None }) + } else { + secp256k1_zkp::SurjectionProof::from_slice(&v) + .map(|inner| Self { inner: Some(Box::new(inner)) }) + .map_err(encode::Error::Secp256k1zkp) + } + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for SurjectionProof { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.inner.serialize(serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for SurjectionProof { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Option::::deserialize(deserializer) + .map(|inner| Self { inner: inner.map(Box::new) }) + } +} diff --git a/src/confidential/value.rs b/src/confidential/value.rs new file mode 100644 index 00000000..a8267148 --- /dev/null +++ b/src/confidential/value.rs @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Confiential Values + +use core::ops::{AddAssign, Neg}; +use core::{fmt, str}; +use std::io; + +use secp256k1_zkp::rand::Rng; +use secp256k1_zkp::{ + self, compute_adaptive_blinding_factor, CommitmentSecrets, Generator, PedersenCommitment, + Secp256k1, SecretKey, Signing, Tweak, ZERO_TWEAK, +}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::confidential::AssetBlindingFactor; +use crate::encode::{self, Decodable, Encodable}; +use crate::issuance::AssetId; + +type ExplicitInner = u64; +type ConfInner = PedersenCommitment; + +const EXPLICIT_LEN: usize = 8; +const CONFIDENTIAL_LEN: usize = 33; +const CONF_PREFIX_1: u8 = 0x08; +const CONF_PREFIX_2: u8 = 0x09; + +/// A CT commitment to an amount +#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub enum Value { + /// No value + #[default] + Null, + /// Value is explicitly encoded + Explicit(ExplicitInner), + /// Value is committed + Confidential(ConfInner), +} + +impl Value { + /// Create value commitment. + pub fn new_confidential( + secp: &Secp256k1, + value: u64, + asset: Generator, + bf: BlindingFactor, + ) -> Self { + Self::Confidential(ConfInner::new(secp, value, bf.0, asset)) + } + + /// Create value commitment from assetID, asset blinding factor, + /// value and value blinding factor + pub fn new_confidential_from_assetid( + secp: &Secp256k1, + value: u64, + asset: AssetId, + v_bf: BlindingFactor, + a_bf: AssetBlindingFactor, + ) -> Self { + let generator = Generator::new_blinded(secp, asset.into_tag(), a_bf.0); + let comm = ConfInner::new(secp, value, v_bf.0, generator); + + Self::Confidential(comm) + } + + /// Serialized length, in bytes + pub fn encoded_length(&self) -> usize { + match *self { + Self::Null => 1, + Self::Explicit(..) => 1 + EXPLICIT_LEN, + Self::Confidential(..) => CONFIDENTIAL_LEN, + } + } + + /// Create from commitment. + pub fn from_commitment(bytes: &[u8]) -> Result { + Ok(Self::Confidential(ConfInner::from_slice(bytes)?)) + } + + /// Check if the object is null. + pub fn is_null(&self) -> bool { matches!(*self, Self::Null) } + + /// Check if the object is explicit. + pub fn is_explicit(&self) -> bool { matches!(*self, Self::Explicit(_)) } + + /// Check if the object is confidential. + pub fn is_confidential(&self) -> bool { matches!(*self, Self::Confidential(_)) } + + /// Returns the explicit inner value. + /// Returns [None] if [`Self::is_explicit`] returns false. + pub fn explicit(&self) -> Option { + match *self { + Self::Explicit(i) => Some(i), + _ => None, + } + } + + /// Returns the confidential commitment in case of a confidential value. + /// Returns [None] if [`Self::is_confidential`] returns false. + pub fn commitment(&self) -> Option { + match *self { + Self::Confidential(i) => Some(i), + _ => None, + } + } +} + +impl From for Value { + fn from(from: ConfInner) -> Self { Self::Confidential(from) } +} + +impl fmt::Display for Value { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Null => f.write_str("null"), + Self::Explicit(n) => write!(f, "{}", n), + Self::Confidential(commitment) => write!(f, "{:02x}", commitment), + } + } +} + +impl Encodable for Value { + fn consensus_encode(&self, mut s: S) -> Result { + match *self { + Self::Null => { + s.write_all(&[0u8])?; + Ok(1) + } + Self::Explicit(n) => { + s.write_all(&[1u8])?; + s.write_all(&n.to_be_bytes())?; + Ok(1 + EXPLICIT_LEN) + } + Self::Confidential(commitment) => { + s.write_all(&commitment.serialize())?; + Ok(CONFIDENTIAL_LEN) + } + } + } +} + +impl Decodable for Value { + fn consensus_decode(mut d: D) -> Result { + let mut buf = [0u8; CONFIDENTIAL_LEN]; + d.read_exact(&mut buf[0..1])?; + + match buf[0] { + 0 => Ok(Self::Null), + 1 => { + let mut buf = [0; EXPLICIT_LEN]; + d.read_exact(&mut buf)?; + Ok(Self::Explicit(u64::from_be_bytes(buf))) + } + p if p == CONF_PREFIX_1 || p == CONF_PREFIX_2 => { + d.read_exact(&mut buf[1..])?; + Ok(Self::Confidential(ConfInner::from_slice(&buf)?)) + } + p => Err(encode::Error::InvalidConfidentialPrefix(p)), + } + } +} + +#[cfg(feature = "serde")] +impl Serialize for Value { + fn serialize(&self, s: S) -> Result { + use serde::ser::SerializeSeq; + + let seq_len = match *self { + Self::Null => 1, + Self::Explicit(_) | Self::Confidential(_) => 2, + }; + let mut seq = s.serialize_seq(Some(seq_len))?; + + match *self { + Self::Null => seq.serialize_element(&0u8)?, + Self::Explicit(n) => { + seq.serialize_element(&1u8)?; + seq.serialize_element(&u64::swap_bytes(n))?; + } + Self::Confidential(commitment) => { + seq.serialize_element(&2u8)?; + seq.serialize_element(&commitment)?; + } + } + seq.end() + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Value { + fn deserialize>(d: D) -> Result { + use serde::de::{Error, SeqAccess, Visitor}; + struct CommitVisitor; + + impl<'de> Visitor<'de> for CommitVisitor { + type Value = Value; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("a committed value") + } + + fn visit_seq>(self, mut access: A) -> Result { + let prefix = access.next_element::()?; + match prefix { + Some(0) => Ok(Self::Value::Null), + Some(1) => match access.next_element()? { + Some(x) => Ok(Self::Value::Explicit(u64::swap_bytes(x))), + None => Err(A::Error::custom("missing explicit value")), + }, + Some(2) => match access.next_element()? { + Some(x) => Ok(Self::Value::Confidential(x)), + None => Err(A::Error::custom("missing pedersen commitment")), + }, + _ => Err(A::Error::custom("wrong or missing prefix")), + } + } + } + + d.deserialize_seq(CommitVisitor) + } +} + +/// Blinding factor used for value commitments. +#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub struct BlindingFactor(pub(crate) Tweak); + +impl BlindingFactor { + /// Generate random value blinding factor. + pub fn new(rng: &mut R) -> Self { Self(Tweak::new(rng)) } + + /// Parse a blinding factor from a 64-character hex string. + #[deprecated(since = "0.27.0", note = "use s.parse() instead")] + pub fn from_hex(s: &str) -> Result { s.parse() } + + /// Create the value blinding factor of the last output of a transaction. + pub fn last( + secp: &Secp256k1, + value: u64, + abf: AssetBlindingFactor, + inputs: &[(u64, AssetBlindingFactor, Self)], + outputs: &[(u64, AssetBlindingFactor, Self)], + ) -> Self { + let set_a = inputs + .iter() + .map(|(value, abf, vbf)| CommitmentSecrets { + value: *value, + value_blinding_factor: vbf.0, + generator_blinding_factor: abf.into_inner(), + }) + .collect::>(); + let set_b = outputs + .iter() + .map(|(value, abf, vbf)| CommitmentSecrets { + value: *value, + value_blinding_factor: vbf.0, + generator_blinding_factor: abf.into_inner(), + }) + .collect::>(); + + Self(compute_adaptive_blinding_factor(secp, value, abf.0, &set_a, &set_b)) + } + + /// Create from bytes. + pub fn from_slice(bytes: &[u8]) -> Result { + Ok(Self(Tweak::from_slice(bytes)?)) + } + + /// Returns the inner value. + pub fn into_inner(self) -> Tweak { self.0 } + + /// Get a unblinded/zero `AssetBlinding` factor + pub fn zero() -> Self { Self(ZERO_TWEAK) } +} + +impl AddAssign for BlindingFactor { + fn add_assign(&mut self, other: Self) { + if self.0.as_ref() == &[0u8; 32] { + *self = other; + } else if other.0.as_ref() == &[0u8; 32] { + // nothing to do + } else { + // Since libsecp does not expose low level APIs + // for scalar arethematic, we need to abuse secret key + // operations for this + let sk2 = SecretKey::from_slice(self.into_inner().as_ref()).expect("Valid key"); + let sk = SecretKey::from_slice(other.into_inner().as_ref()).expect("Valid key"); + // The only reason that secret key addition can fail + // is when the keys add up to zero since we have already checked + // keys are in valid secret keys + match sk.add_tweak(&sk2.into()) { + Ok(sk_tweaked) => + *self = Self::from_slice(sk_tweaked.as_ref()).expect("Valid Tweak"), + Err(_) => *self = Self::zero(), + } + } + } +} + +impl Neg for BlindingFactor { + type Output = Self; + + fn neg(self) -> Self::Output { + if self.0.as_ref() == &[0u8; 32] { + self + } else { + let sk = SecretKey::from_slice(self.into_inner().as_ref()).expect("Valid key").negate(); + Self::from_slice(sk.as_ref()).expect("Valid Tweak") + } + } +} + +impl core::borrow::Borrow<[u8]> for BlindingFactor { + fn borrow(&self) -> &[u8] { &self.0[..] } +} + +hex::impl_fmt_traits! { + #[display_backward(true)] + impl fmt_traits for BlindingFactor { + const LENGTH: usize = 32; + } +} + +impl str::FromStr for BlindingFactor { + type Err = encode::Error; + + fn from_str(s: &str) -> Result { + let mut slice: [u8; 32] = hex::decode_to_array(s)?; + slice.reverse(); + + let inner = Tweak::from_inner(slice)?; + Ok(Self(inner)) + } +} + +#[cfg(feature = "serde")] +impl Serialize for BlindingFactor { + fn serialize(&self, s: S) -> Result { + if s.is_human_readable() { + s.collect_str(&self) + } else { + s.serialize_bytes(&self.0[..]) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for BlindingFactor { + fn deserialize>(d: D) -> Result { + if d.is_human_readable() { + struct HexVisitor; + + impl ::serde::de::Visitor<'_> for HexVisitor { + type Value = BlindingFactor; + + fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + formatter.write_str("an ASCII hex string") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: ::serde::de::Error, + { + if let Ok(hex) = ::std::str::from_utf8(v) { + hex.parse().map_err(E::custom) + } else { + Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self)) + } + } + + fn visit_str(self, v: &str) -> Result + where + E: ::serde::de::Error, + { + v.parse().map_err(E::custom) + } + } + + d.deserialize_str(HexVisitor) + } else { + struct BytesVisitor; + + impl ::serde::de::Visitor<'_> for BytesVisitor { + type Value = BlindingFactor; + + fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + formatter.write_str("a bytestring") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: ::serde::de::Error, + { + use core::convert::TryFrom; + + match <[u8; 32]>::try_from(v) { + Ok(ret) => { + let inner = Tweak::from_inner(ret).map_err(E::custom)?; + Ok(BlindingFactor(inner)) + } + Err(_) => Err(E::invalid_length(v.len(), &stringify!($len))), + } + } + } + + d.deserialize_bytes(BytesVisitor) + } + } +} diff --git a/src/encode.rs b/src/encode.rs index 068a3c94..a1204062 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -20,7 +20,7 @@ use std::{any, error, fmt, io, mem}; use bitcoin::ScriptBuf; use hex::{DecodeFixedLengthBytesError, DecodeVariableLengthBytesError}; -use secp256k1_zkp::{self, RangeProof, SurjectionProof, Tweak}; +use secp256k1_zkp::{self, Tweak}; use crate::hashes::{sha256, Hash}; use crate::pset; @@ -417,31 +417,6 @@ impl_array!(20); impl_array!(32); impl_array!(33); -macro_rules! impl_box_option { - ($type: ty) => { - impl Encodable for Option> { - #[inline] - fn consensus_encode(&self, e: W) -> Result { - match self { - None => Vec::::new().consensus_encode(e), - Some(v) => v.serialize().consensus_encode(e), - } - } - } - - impl Decodable for Option> { - #[inline] - fn consensus_decode(mut d: D) -> Result { - let v: Vec = Decodable::consensus_decode(&mut d)?; - if v.is_empty() { - Ok(None) - } else { - Ok(Some(Box::new(<$type>::from_slice(&v)?))) - } - } - } - }; -} // special implementations for elements only fields impl Encodable for Tweak { fn consensus_encode(&self, e: W) -> Result { @@ -455,32 +430,6 @@ impl Decodable for Tweak { } } -impl Encodable for RangeProof { - fn consensus_encode(&self, e: W) -> Result { - self.serialize().consensus_encode(e) - } -} - -impl Decodable for RangeProof { - fn consensus_decode(d: D) -> Result { - Ok(RangeProof::from_slice(&>::consensus_decode(d)?)?) - } -} - -impl Encodable for SurjectionProof { - fn consensus_encode(&self, e: W) -> Result { - self.serialize().consensus_encode(e) - } -} - -impl Decodable for SurjectionProof { - fn consensus_decode(d: D) -> Result { - Ok(SurjectionProof::from_slice(&>::consensus_decode( - d, - )?)?) - } -} - impl Encodable for sha256::Hash { fn consensus_encode(&self, s: S) -> Result { self.to_byte_array().consensus_encode(s) @@ -508,6 +457,3 @@ impl Decodable for TapLeafHash { )) } } - -impl_box_option!(RangeProof); -impl_box_option!(SurjectionProof); diff --git a/src/lib.rs b/src/lib.rs index dfc51d3a..18c92cea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,11 +79,11 @@ pub mod genesis; // export everything at the top level so it can be used as `elements::Transaction` etc. pub use crate::address::{Address, AddressError, AddressParams}; pub use crate::blind::{ - BlindAssetProofs, BlindError, BlindValueProofs, ConfidentialTxOutError, RangeProofMessage, - SurjectionInput, TxOutError, TxOutSecrets, UnblindError, VerificationError, CtLocation, CtLocationType, + BlindError, ConfidentialTxOutError, CtLocation, CtLocationType, RangeProofMessage, + SurjectionInput, TxOutError, TxOutSecrets, UnblindError, VerificationError, }; -pub use crate::block::ExtData as BlockExtData; -pub use crate::block::{Block, BlockHeader, DynafedRoot}; +pub use crate::block::{Block, BlockHeader, DynafedRoot, ExtData as BlockExtData}; +pub use crate::confidential::{RangeProof, SurjectionProof}; pub use crate::ext::{ReadExt, WriteExt}; pub use crate::fast_merkle_root::fast_merkle_root; pub use crate::hash_types::*; @@ -92,8 +92,7 @@ pub use crate::locktime::LockTime; pub use crate::schnorr::{SchnorrSig, SchnorrSigError}; pub use crate::script::Script; pub use crate::sighash::SchnorrSighashType; -pub use crate::transaction::Sequence; pub use crate::transaction::{ - AssetIssuance, EcdsaSighashType, OutPoint, PeginData, PegoutData, Transaction, TxIn, + AssetIssuance, EcdsaSighashType, OutPoint, PeginData, PegoutData, Sequence, Transaction, TxIn, TxInWitness, TxOut, TxOutWitness, }; diff --git a/src/pset/map/input.rs b/src/pset/map/input.rs index ae67f854..ecd0af54 100644 --- a/src/pset/map/input.rs +++ b/src/pset/map/input.rs @@ -32,10 +32,10 @@ use crate::pset::raw; use crate::pset::serialize; use crate::pset::{self, error, Error}; use crate::{transaction::SighashTypeParseError, SchnorrSighashType}; -use crate::{AssetIssuance, BlockHash, EcdsaSighashType, Script, Transaction, TxIn, TxOut, Txid}; +use crate::{AssetIssuance, BlockHash, EcdsaSighashType, RangeProof, Script, Transaction, TxIn, TxOut, Txid, SurjectionProof}; use bitcoin::bip32::KeySource; use bitcoin::{PublicKey, key::XOnlyPublicKey}; -use secp256k1_zkp::{self, RangeProof, SurjectionProof, Tweak, ZERO_TWEAK}; +use secp256k1_zkp::{self, Tweak, ZERO_TWEAK}; use crate::{OutPoint, Sequence}; @@ -262,9 +262,9 @@ pub struct Input { /// The issuance value commitment pub issuance_value_comm: Option, /// Issuance value rangeproof - pub issuance_value_rangeproof: Option>, + pub issuance_value_rangeproof: Option, /// Issuance keys rangeproof - pub issuance_keys_rangeproof: Option>, + pub issuance_keys_rangeproof: Option, /// Pegin Transaction. Should be a `bitcoin::Transaction` pub pegin_tx: Option, /// Pegin Transaction proof @@ -287,19 +287,19 @@ pub struct Input { /// Issuance asset entropy pub issuance_asset_entropy: Option<[u8; 32]>, /// input utxo rangeproof - pub in_utxo_rangeproof: Option>, + pub in_utxo_rangeproof: Option, /// Proof that blinded issuance matches the commitment - pub in_issuance_blind_value_proof: Option>, + pub in_issuance_blind_value_proof: Option, /// Proof that blinded inflation keys matches the corresponding commitment - pub in_issuance_blind_inflation_keys_proof: Option>, + pub in_issuance_blind_inflation_keys_proof: Option, /// The explicit amount of the input pub amount: Option, /// The blind value rangeproof - pub blind_value_proof: Option>, + pub blind_value_proof: Option, /// The input explicit asset pub asset: Option, /// The blind asset surjection proof - pub blind_asset_proof: Option>, + pub blind_asset_proof: Option, /// Whether the issuance is blinded pub blinded_issuance: Option, /// Other fields @@ -540,8 +540,8 @@ impl Input { } // Witness - ret.issuance_keys_rangeproof = txin.witness.inflation_keys_rangeproof; - ret.issuance_value_rangeproof = txin.witness.amount_rangeproof; + ret.issuance_keys_rangeproof = Some(txin.witness.inflation_keys_rangeproof); + ret.issuance_value_rangeproof = Some(txin.witness.amount_rangeproof); } ret } @@ -746,10 +746,10 @@ impl Map for Input { impl_pset_prop_insert_pair!(self.issuance_value_comm <= | ); } PSBT_ELEMENTS_IN_ISSUANCE_VALUE_RANGEPROOF => { - impl_pset_prop_insert_pair!(self.issuance_value_rangeproof <= | >); + impl_pset_prop_insert_pair!(self.issuance_value_rangeproof <= | ); } PSBT_ELEMENTS_IN_ISSUANCE_KEYS_RANGEPROOF => { - impl_pset_prop_insert_pair!(self.issuance_keys_rangeproof <= | >); + impl_pset_prop_insert_pair!(self.issuance_keys_rangeproof <= | ); } PSBT_ELEMENTS_IN_PEG_IN_TX => { impl_pset_prop_insert_pair!(self.pegin_tx <= | ); @@ -783,25 +783,25 @@ impl Map for Input { impl_pset_prop_insert_pair!(self.issuance_asset_entropy <= | ); } PSBT_ELEMENTS_IN_UTXO_RANGEPROOF => { - impl_pset_prop_insert_pair!(self.in_utxo_rangeproof <= | >); + impl_pset_prop_insert_pair!(self.in_utxo_rangeproof <= | ); } PSBT_ELEMENTS_IN_ISSUANCE_BLIND_VALUE_PROOF => { - impl_pset_prop_insert_pair!(self.in_issuance_blind_value_proof <= | >); + impl_pset_prop_insert_pair!(self.in_issuance_blind_value_proof <= | ); } PSBT_ELEMENTS_IN_ISSUANCE_BLIND_INFLATION_KEYS_PROOF => { - impl_pset_prop_insert_pair!(self.in_issuance_blind_inflation_keys_proof <= | >); + impl_pset_prop_insert_pair!(self.in_issuance_blind_inflation_keys_proof <= | ); } PSBT_ELEMENTS_IN_EXPLICIT_VALUE => { impl_pset_prop_insert_pair!(self.amount <= | ); } PSBT_ELEMENTS_IN_VALUE_PROOF => { - impl_pset_prop_insert_pair!(self.blind_value_proof <= | >); + impl_pset_prop_insert_pair!(self.blind_value_proof <= | ); } PSBT_ELEMENTS_IN_EXPLICIT_ASSET => { impl_pset_prop_insert_pair!(self.asset <= | ); } PSBT_ELEMENTS_IN_ASSET_PROOF => { - impl_pset_prop_insert_pair!(self.blind_asset_proof <= | >); + impl_pset_prop_insert_pair!(self.blind_asset_proof <= | ); } PSBT_ELEMENTS_IN_BLINDED_ISSUANCE => { impl_pset_prop_insert_pair!(self.blinded_issuance <= | ); diff --git a/src/pset/map/output.rs b/src/pset/map/output.rs index 69e361c4..ef0be12c 100644 --- a/src/pset/map/output.rs +++ b/src/pset/map/output.rs @@ -23,10 +23,10 @@ use crate::pset::map::Map; use crate::pset::raw; use crate::pset::Error; use crate::{confidential, pset}; -use crate::{encode, Script, TxOutWitness}; +use crate::{encode, RangeProof, Script, TxOutWitness, SurjectionProof}; use bitcoin::bip32::KeySource; use bitcoin::{PublicKey, key::XOnlyPublicKey}; -use secp256k1_zkp::{self, Generator, RangeProof, SurjectionProof}; +use secp256k1_zkp::{self, Generator}; use crate::issuance; @@ -116,9 +116,9 @@ pub struct Output { pub asset_comm: Option, // Proprietary key-value pairs for this output. /// Output value rangeproof - pub value_rangeproof: Option>, + pub value_rangeproof: Option, /// Output Asset surjection proof - pub asset_surjection_proof: Option>, + pub asset_surjection_proof: Option, /// Blinding pubkey which is used in receiving address pub blinding_key: Option, /// The ephermal pk sampled by sender @@ -126,9 +126,9 @@ pub struct Output { /// The index of the input whose owner should blind this output pub blinder_index: Option, /// The blind value rangeproof - pub blind_value_proof: Option>, + pub blind_value_proof: Option, /// The blind asset surjection proof - pub blind_asset_proof: Option>, + pub blind_asset_proof: Option, /// Pset /// Other fields #[cfg_attr( @@ -233,8 +233,8 @@ impl Output { }); } rv.script_pubkey = txout.script_pubkey; - rv.value_rangeproof = txout.witness.rangeproof; - rv.asset_surjection_proof = txout.witness.surjection_proof; + rv.value_rangeproof = Some(txout.witness.rangeproof); + rv.asset_surjection_proof = Some(txout.witness.surjection_proof); rv } @@ -261,8 +261,12 @@ impl Output { .unwrap_or_default(), script_pubkey: self.script_pubkey.clone(), witness: TxOutWitness { - surjection_proof: self.asset_surjection_proof.clone(), - rangeproof: self.value_rangeproof.clone(), + surjection_proof: self.asset_surjection_proof + .clone() + .unwrap_or(SurjectionProof::EMPTY), + rangeproof: self.value_rangeproof + .clone() + .unwrap_or(RangeProof::EMPTY), }, } } @@ -354,10 +358,10 @@ impl Map for Output { impl_pset_prop_insert_pair!(self.asset_comm <= | ); } PSBT_ELEMENTS_OUT_VALUE_RANGEPROOF => { - impl_pset_prop_insert_pair!(self.value_rangeproof <= | >); + impl_pset_prop_insert_pair!(self.value_rangeproof <= | ); } PSBT_ELEMENTS_OUT_ASSET_SURJECTION_PROOF => { - impl_pset_prop_insert_pair!(self.asset_surjection_proof <= | >); + impl_pset_prop_insert_pair!(self.asset_surjection_proof <= | ); } PSBT_ELEMENTS_OUT_BLINDING_PUBKEY => { impl_pset_prop_insert_pair!(self.blinding_key <= | ); @@ -369,10 +373,10 @@ impl Map for Output { impl_pset_prop_insert_pair!(self.blinder_index <= | ); } PSBT_ELEMENTS_OUT_BLIND_VALUE_PROOF => { - impl_pset_prop_insert_pair!(self.blind_value_proof <= | >); + impl_pset_prop_insert_pair!(self.blind_value_proof <= | ); } PSBT_ELEMENTS_OUT_BLIND_ASSET_PROOF => { - impl_pset_prop_insert_pair!(self.blind_asset_proof <= | >); + impl_pset_prop_insert_pair!(self.blind_asset_proof <= | ); } _ => match self.proprietary.entry(prop_key) { Entry::Vacant(empty_key) => { diff --git a/src/pset/mod.rs b/src/pset/mod.rs index a61a3610..aeb3cea7 100644 --- a/src/pset/mod.rs +++ b/src/pset/mod.rs @@ -38,20 +38,19 @@ mod str; #[cfg(feature = "base64")] pub use self::str::ParseError; -use crate::blind::{BlindAssetProofs, BlindValueProofs}; use crate::confidential; use crate::encode::{self, Decodable, Encodable}; use crate::{ blind::RangeProofMessage, confidential::{AssetBlindingFactor, ValueBlindingFactor}, - TxOutSecrets, + RangeProof, SurjectionProof, TxOutSecrets, }; use crate::{ LockTime, OutPoint, Sequence, SurjectionInput, Transaction, TxIn, TxInWitness, TxOut, TxOutWitness, Txid, CtLocation, CtLocationType, }; use secp256k1_zkp::rand::{CryptoRng, RngCore}; -use secp256k1_zkp::{self, RangeProof, SecretKey, SurjectionProof}; +use secp256k1_zkp::{self, SecretKey}; pub use self::error::{Error, PsetBlindError, PsetHash}; use self::map::Map; @@ -300,8 +299,12 @@ impl PartiallySignedTransaction { sequence: psetin.sequence.unwrap_or(Sequence::MAX), asset_issuance: psetin.asset_issuance(), witness: TxInWitness { - amount_rangeproof: psetin.issuance_value_rangeproof.clone(), - inflation_keys_rangeproof: psetin.issuance_keys_rangeproof.clone(), + amount_rangeproof: psetin.issuance_value_rangeproof + .clone() + .unwrap_or(RangeProof::EMPTY), + inflation_keys_rangeproof: psetin.issuance_keys_rangeproof + .clone() + .unwrap_or(RangeProof::EMPTY), script_witness: psetin .final_script_witness .as_ref() @@ -335,8 +338,12 @@ impl PartiallySignedTransaction { .unwrap_or_default(), script_pubkey: out.script_pubkey.clone(), witness: TxOutWitness { - surjection_proof: out.asset_surjection_proof.clone(), - rangeproof: out.value_rangeproof.clone(), + surjection_proof: out.asset_surjection_proof + .clone() + .unwrap_or(SurjectionProof::EMPTY), + rangeproof: out.value_rangeproof + .clone() + .unwrap_or(RangeProof::EMPTY), }, }; outputs.push(txout); @@ -518,8 +525,8 @@ impl PartiallySignedTransaction { // mutate the pset { - self.outputs[i].value_rangeproof = txout.witness.rangeproof; - self.outputs[i].asset_surjection_proof = txout.witness.surjection_proof; + self.outputs[i].value_rangeproof = Some(txout.witness.rangeproof); + self.outputs[i].asset_surjection_proof = Some(txout.witness.surjection_proof); self.outputs[i].amount_comm = txout.value.commitment(); self.outputs[i].asset_comm = txout.asset.commitment(); self.outputs[i].ecdh_pubkey = @@ -530,10 +537,10 @@ impl PartiallySignedTransaction { let asset_id = self.outputs[i] .asset .ok_or(PsetBlindError::MustHaveExplicitTxOut(i))?; - self.outputs[i].blind_asset_proof = Some(Box::new( + self.outputs[i].blind_asset_proof = Some( SurjectionProof::blind_asset_proof(rng, secp, asset_id, abf) .map_err(|e| PsetBlindError::BlindingProofsCreationError(i, e))?, - )); + ); let asset_gen = self.outputs[i] .asset_comm @@ -541,10 +548,10 @@ impl PartiallySignedTransaction { let value_comm = self.outputs[i] .amount_comm .expect("Blinding proof successful"); - self.outputs[i].blind_value_proof = Some(Box::new( + self.outputs[i].blind_value_proof = Some( RangeProof::blind_value_proof(rng, secp, value, value_comm, asset_gen, vbf) .map_err(|e| PsetBlindError::BlindingProofsCreationError(i, e))?, - )); + ); } // return blinding factors used let location = CtLocation{ input_index: i, ty: CtLocationType::Input}; @@ -670,8 +677,8 @@ impl PartiallySignedTransaction { // mutate the pset { - self.outputs[last_out_index].value_rangeproof = Some(Box::new(rangeproof)); - self.outputs[last_out_index].asset_surjection_proof = Some(Box::new(surjection_proof)); + self.outputs[last_out_index].value_rangeproof = Some(rangeproof); + self.outputs[last_out_index].asset_surjection_proof = Some(surjection_proof); self.outputs[last_out_index].amount_comm = value_commitment.commitment(); self.outputs[last_out_index].asset_comm = out_asset_commitment.commitment(); self.outputs[last_out_index].ecdh_pubkey = @@ -682,10 +689,10 @@ impl PartiallySignedTransaction { let asset_id = self.outputs[last_out_index] .asset .ok_or(PsetBlindError::MustHaveExplicitTxOut(last_out_index))?; - self.outputs[last_out_index].blind_asset_proof = Some(Box::new( + self.outputs[last_out_index].blind_asset_proof = Some( SurjectionProof::blind_asset_proof(rng, secp, asset_id, out_abf) .map_err(|e| PsetBlindError::BlindingProofsCreationError(last_out_index, e))?, - )); + ); let asset_gen = self.outputs[last_out_index] .asset_comm @@ -693,10 +700,10 @@ impl PartiallySignedTransaction { let value_comm = self.outputs[last_out_index] .amount_comm .expect("Blinding proof successful"); - self.outputs[last_out_index].blind_value_proof = Some(Box::new( + self.outputs[last_out_index].blind_value_proof = Some( RangeProof::blind_value_proof(rng, secp, value, value_comm, asset_gen, final_vbf) .map_err(|e| PsetBlindError::BlindingProofsCreationError(last_out_index, e))?, - )); + ); self.global.scalars.clear(); } diff --git a/src/pset/serialize.rs b/src/pset/serialize.rs index 5b152c04..16e3b8b1 100644 --- a/src/pset/serialize.rs +++ b/src/pset/serialize.rs @@ -24,12 +24,12 @@ use crate::encode::{ self, deserialize, deserialize_partial, serialize, Decodable, Encodable, VarInt, }; use crate::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; -use crate::{AssetId, BlockHash, Script, Transaction, TxOut, Txid}; +use crate::{AssetId, BlockHash, RangeProof, Script, SurjectionProof, Transaction, TxOut, Txid}; use bitcoin; use bitcoin::bip32::{ChildNumber, Fingerprint, KeySource}; use bitcoin::{key::XOnlyPublicKey, PublicKey}; use internals::slice::SliceExt; -use secp256k1_zkp::{self, RangeProof, SurjectionProof, Tweak}; +use secp256k1_zkp::{self, Tweak}; use super::map::{PsbtSighashType, TapTree}; use crate::schnorr; @@ -282,31 +282,27 @@ impl Deserialize for confidential::Asset { } } -impl Serialize for Box { +impl Serialize for RangeProof { fn serialize(&self) -> Vec { - RangeProof::serialize(self) + self.to_vec() } } -impl Deserialize for Box { +impl Deserialize for RangeProof { fn deserialize(bytes: &[u8]) -> Result { - let prf = RangeProof::from_slice(bytes) - .map_err(|_| encode::Error::ParseFailed("Invalid Rangeproof"))?; - Ok(Box::new(prf)) + Self::from_slice(bytes).map_err(encode::Error::Secp256k1zkp) } } -impl Serialize for Box { +impl Serialize for SurjectionProof { fn serialize(&self) -> Vec { - SurjectionProof::serialize(self) + self.to_vec() } } -impl Deserialize for Box { +impl Deserialize for SurjectionProof { fn deserialize(bytes: &[u8]) -> Result { - let prf = SurjectionProof::from_slice(bytes) - .map_err(|_| encode::Error::ParseFailed("Invalid SurjectionProof"))?; - Ok(Box::new(prf)) + Self::from_slice(bytes).map_err(encode::Error::Secp256k1zkp) } } diff --git a/src/transaction.rs b/src/transaction.rs index 1ba454b7..694c43ac 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -30,9 +30,9 @@ use crate::issuance::{AssetEntropy, AssetId}; use crate::opcodes; use crate::parse::impl_parse_str_through_int; use crate::script::Instruction; -use crate::{LockTime, Script, Txid, Wtxid}; +use crate::{LockTime, RangeProof, Script, SurjectionProof, Txid, Wtxid}; use secp256k1_zkp::{ - RangeProof, SurjectionProof, Tweak, ZERO_TWEAK, + Tweak, ZERO_TWEAK, }; /// Description of an asset issuance in a transaction input @@ -362,9 +362,9 @@ impl std::error::Error for RelativeLockTimeError { #[derive(Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] pub struct TxInWitness { /// Amount rangeproof - pub amount_rangeproof: Option>, + pub amount_rangeproof: RangeProof, /// Rangeproof for inflation keys - pub inflation_keys_rangeproof: Option>, + pub inflation_keys_rangeproof: RangeProof, /// Traditional script witness pub script_witness: Vec>, /// Pegin witness, basically the same thing @@ -377,8 +377,8 @@ impl TxInWitness { /// Create an empty input witness. pub fn empty() -> Self { TxInWitness { - amount_rangeproof: None, - inflation_keys_rangeproof: None, + amount_rangeproof: RangeProof::EMPTY, + inflation_keys_rangeproof: RangeProof::EMPTY, script_witness: Vec::new(), pegin_witness: Vec::new(), } @@ -386,8 +386,8 @@ impl TxInWitness { /// Whether this witness is null pub fn is_empty(&self) -> bool { - self.amount_rangeproof.is_none() && - self.inflation_keys_rangeproof.is_none() && + self.amount_rangeproof.is_empty() && + self.inflation_keys_rangeproof.is_empty() && self.script_witness.is_empty() && self.pegin_witness.is_empty() } @@ -644,11 +644,11 @@ pub struct TxOutWitness { /// Surjection proof showing that the asset commitment is legitimate // We Box it because surjection proof internally is an array [u8; N] that // allocates on stack even when the surjection proof is empty - pub surjection_proof: Option>, + pub surjection_proof: SurjectionProof, /// Rangeproof showing that the value commitment is legitimate // We Box it because range proof internally is an array [u8; N] that // allocates on stack even when the range proof is empty - pub rangeproof: Option>, + pub rangeproof: RangeProof, } serde_struct_impl!(TxOutWitness, surjection_proof, rangeproof); impl_consensus_encoding!(TxOutWitness, surjection_proof, rangeproof); @@ -657,24 +657,24 @@ impl TxOutWitness { /// Create an empty output witness. pub fn empty() -> Self { TxOutWitness { - surjection_proof: None, - rangeproof: None, + surjection_proof: SurjectionProof::EMPTY, + rangeproof: RangeProof::EMPTY, } } /// Whether this witness is null pub fn is_empty(&self) -> bool { - self.surjection_proof.is_none() && self.rangeproof.is_none() + self.surjection_proof.is_empty() && self.rangeproof.is_empty() } /// The rangeproof len if is present, otherwise 0 pub fn rangeproof_len(&self) -> usize { - self.rangeproof.as_ref().map_or(0, |prf| prf.len()) + self.rangeproof.len() } /// The surjection proof len if is present, otherwise 0 pub fn surjectionproof_len(&self) -> usize { - self.surjection_proof.as_ref().map_or(0, |prf| prf.len()) + self.surjection_proof.len() } } @@ -839,29 +839,7 @@ impl TxOut { confidential::Value::Null => min_value, confidential::Value::Explicit(n) => n, confidential::Value::Confidential(..) => { - match &self.witness.rangeproof { - None => min_value, - Some(prf) => { - // inefficient, consider implementing index on rangeproof - let prf = prf.serialize(); - debug_assert!(prf.len() > 10); - - let has_nonzero_range = prf[0] & 64 == 64; - let has_min = prf[0] & 32 == 32; - - if !has_min { - min_value - } else if has_nonzero_range { - bitcoin::consensus::deserialize::(&prf[2..10]) - .expect("any 8 bytes is a u64") - .swap_bytes() // min-value is BE - } else { - bitcoin::consensus::deserialize::(&prf[1..9]) - .expect("any 8 bytes is a u64") - .swap_bytes() // min-value is BE - } - } - } + self.witness.rangeproof.minimim_value().unwrap_or(min_value) } } } @@ -974,10 +952,8 @@ impl Transaction { 0 } ) + if witness_flag { - let amt_prf_len = input.witness.amount_rangeproof.as_ref() - .map_or(0, |x| x.len()); - let keys_prf_len = input.witness.inflation_keys_rangeproof.as_ref() - .map_or(0, |x| x.len()); + let amt_prf_len = input.witness.amount_rangeproof.len(); + let keys_prf_len = input.witness.inflation_keys_rangeproof.len(); VarInt(amt_prf_len as u64).size() + amt_prf_len +