diff --git a/CHANGELOG.md b/CHANGELOG.md index 007ee907..7cd99548 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ +# 0.25.3 - 2025-08-28 + +* Semver-trick all types from 0.26.1 + # 0.25.2 - 2025-04-18 * [https://github.com/ElementsProject/rust-elements/pull/226](elip102: rename from elip101) diff --git a/Cargo.toml b/Cargo.toml index 12029a50..4c5fd21b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "elements" -version = "0.25.2" +version = "0.25.3" authors = ["Andrew Poelstra "] description = "Library with support for de/serialization, parsing and executing on data structures and network messages related to Elements" license = "CC0-1.0" @@ -12,19 +12,21 @@ edition = "2018" [features] default = ["json-contract"] -json-contract = ["serde_json"] +json-contract = ["serde_json", "elements26/json-contract" ] "serde" = [ "bitcoin/serde", "bitcoin/serde", + "elements26/serde", "secp256k1-zkp/serde", "actual-serde", ] -base64 = ["bitcoin/base64"] +base64 = ["bitcoin/base64", "elements26/base64"] [dependencies] bech32 = "0.11.0" bitcoin = "0.32.2" secp256k1-zkp = { version = "0.11.0", features = ["global-context", "hashes"] } +elements26 = { package = "elements", version = "0.26.1" } # Used for ContractHash::from_json_contract. serde_json = { version = "1.0", optional = true } diff --git a/src/address.rs b/src/address.rs deleted file mode 100644 index e4b5fda3..00000000 --- a/src/address.rs +++ /dev/null @@ -1,1019 +0,0 @@ -// Rust Elements Library -// Written by -// The Elements developers -// -// 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 . -// - -//! # Addresses -//! - -use std::convert::TryFrom as _; -use std::error; -use std::fmt; -use std::fmt::Write as _; -use std::str::FromStr; - -use bech32::{Bech32, Bech32m, ByteIterExt, Fe32, Fe32IterExt, Hrp}; -use crate::blech32::{Blech32, Blech32m}; -use crate::hashes::Hash; -use bitcoin::base58; -use bitcoin::PublicKey; -use secp256k1_zkp; -use secp256k1_zkp::Secp256k1; -use secp256k1_zkp::Verification; -#[cfg(feature = "serde")] -use serde; - -use crate::schnorr::{TapTweak, TweakedPublicKey, UntweakedPublicKey}; -use crate::taproot::TapNodeHash; - -use crate::{opcodes, script}; -use crate::{PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash}; - -/// Encoding error -#[derive(Debug, PartialEq)] -pub enum AddressError { - /// Base58 encoding error - Base58(base58::Error), - /// Bech32 encoding error - Bech32(bech32::primitives::decode::SegwitHrpstringError), - /// Blech32 encoding error - Blech32(crate::blech32::decode::SegwitHrpstringError), - /// Was unable to parse the address. - InvalidAddress(String), - /// Script version must be 0 to 16 inclusive - InvalidWitnessVersion(u8), - /// The witness program must be between 2 and 40 bytes in length. - InvalidWitnessProgramLength(usize), - /// A v0 witness program must be either of length 20 or 32. - InvalidSegwitV0ProgramLength(usize), - /// A v1+ witness program must use b(l)ech32m not b(l)ech32 - InvalidWitnessEncoding, - /// A v0 witness program must use b(l)ech32 not b(l)ech32m - InvalidSegwitV0Encoding, - - /// An invalid blinding pubkey was encountered. - InvalidBlindingPubKey(secp256k1_zkp::UpstreamError), - - /// The length (in bytes) of the object was not correct. - InvalidLength(usize), - - /// Address version byte were not recognized. - InvalidAddressVersion(u8), -} - -impl From for AddressError { - fn from(e: bech32::primitives::decode::SegwitHrpstringError) -> Self { - AddressError::Bech32(e) - } -} - -impl From for AddressError { - fn from(e: crate::blech32::decode::SegwitHrpstringError) -> Self { - AddressError::Blech32(e) - } -} - -impl fmt::Display for AddressError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - AddressError::Base58(ref e) => write!(f, "base58 error: {}", e), - AddressError::Bech32(ref e) => write!(f, "bech32 error: {}", e), - AddressError::Blech32(ref e) => write!(f, "blech32 error: {}", e), - AddressError::InvalidAddress(ref a) => { - write!(f, "was unable to parse the address: {}", a) - } - AddressError::InvalidWitnessVersion(ref wver) => { - write!(f, "invalid witness script version: {}", wver) - } - AddressError::InvalidWitnessProgramLength(ref len) => { - write!( - f, - "the witness program must be between 2 and 40 bytes in length, not {}", - len - ) - } - AddressError::InvalidSegwitV0ProgramLength(ref len) => { - write!( - f, - "a v0 witness program must be length 20 or 32, not {}", - len - ) - } - AddressError::InvalidBlindingPubKey(ref e) => { - write!(f, "an invalid blinding pubkey was encountered: {}", e) - } - AddressError::InvalidWitnessEncoding => { - write!(f, "v1+ witness program must use b(l)ech32m not b(l)ech32") - } - AddressError::InvalidSegwitV0Encoding => { - write!(f, "v0 witness program must use b(l)ech32 not b(l)ech32m") - } - AddressError::InvalidLength(len) => { - write!(f, "Address data has invalid length {}", len) - } - AddressError::InvalidAddressVersion(v) => { - write!(f, "address version {} is invalid for this type", v) - } - } - } -} - -impl error::Error for AddressError { - fn cause(&self) -> Option<&dyn error::Error> { - match *self { - AddressError::Base58(ref e) => Some(e), - AddressError::Bech32(ref e) => Some(e), - AddressError::Blech32(ref e) => Some(e), - AddressError::InvalidBlindingPubKey(ref e) => Some(e), - _ => None, - } - } -} - -#[doc(hidden)] -impl From for AddressError { - fn from(e: base58::Error) -> AddressError { - AddressError::Base58(e) - } -} - -/// The parameters to derive addresses. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct AddressParams { - /// The base58 prefix for p2pkh addresses. - pub p2pkh_prefix: u8, - /// The base58 prefix for p2sh addresses. - pub p2sh_prefix: u8, - /// The base58 prefix for blinded addresses. - pub blinded_prefix: u8, - /// The bech32 HRP for unblinded segwit addresses. - pub bech_hrp: Hrp, - /// The bech32 HRP for blinded segwit addresses. - pub blech_hrp: Hrp, -} - -impl AddressParams { - /// The Liquid network address parameters. - pub const LIQUID: AddressParams = AddressParams { - p2pkh_prefix: 57, - p2sh_prefix: 39, - blinded_prefix: 12, - bech_hrp: Hrp::parse_unchecked("ex"), - blech_hrp: Hrp::parse_unchecked("lq"), - }; - - /// The default Elements network address parameters. - pub const ELEMENTS: AddressParams = AddressParams { - p2pkh_prefix: 235, - p2sh_prefix: 75, - blinded_prefix: 4, - bech_hrp: Hrp::parse_unchecked("ert"), - blech_hrp: Hrp::parse_unchecked("el"), - }; - - /// The default liquid testnet network address parameters. - pub const LIQUID_TESTNET: AddressParams = AddressParams { - p2pkh_prefix: 36, - p2sh_prefix: 19, - blinded_prefix: 23, - bech_hrp: Hrp::parse_unchecked("tex"), - blech_hrp: Hrp::parse_unchecked("tlq"), - }; -} - -/// The method used to produce an address -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum Payload { - /// pay-to-pkhash address - PubkeyHash(PubkeyHash), - /// P2SH address - ScriptHash(ScriptHash), - /// Segwit address - WitnessProgram { - /// The segwit version. - version: Fe32, - /// The segwit program. - program: Vec, - }, -} - -/// An Elements address. -#[derive(Clone, PartialEq, Eq, Hash)] -pub struct Address { - /// the network - pub params: &'static AddressParams, - /// the traditional non-confidential payload - pub payload: Payload, - /// the blinding pubkey - pub blinding_pubkey: Option, -} - -impl Address { - /// Inspect if the address is a blinded address. - pub fn is_blinded(&self) -> bool { - self.blinding_pubkey.is_some() - } - - /// Return if the address is for the Liquid network - pub fn is_liquid(&self) -> bool { - self.params == &AddressParams::LIQUID - } - - /// Creates a pay to (compressed) public key hash address from a public key - /// This is the preferred non-witness type address - #[inline] - pub fn p2pkh( - pk: &PublicKey, - blinder: Option, - params: &'static AddressParams, - ) -> Address { - let mut hash_engine = PubkeyHash::engine(); - pk.write_into(&mut hash_engine) - .expect("engines don't error"); - - Address { - params, - payload: Payload::PubkeyHash(PubkeyHash::from_engine(hash_engine)), - blinding_pubkey: blinder, - } - } - - /// Creates a pay to script hash P2SH address from a script - /// This address type was introduced with BIP16 and is the popular type to implement multi-sig these days. - #[inline] - pub fn p2sh( - script: &script::Script, - blinder: Option, - params: &'static AddressParams, - ) -> Address { - Address { - params, - payload: Payload::ScriptHash(ScriptHash::hash(&script[..])), - blinding_pubkey: blinder, - } - } - - /// Create a witness pay to public key address from a public key - /// This is the native segwit address type for an output redeemable with a single signature - pub fn p2wpkh( - pk: &PublicKey, - blinder: Option, - params: &'static AddressParams, - ) -> Address { - let mut hash_engine = WPubkeyHash::engine(); - pk.write_into(&mut hash_engine) - .expect("engines don't error"); - - Address { - params, - payload: Payload::WitnessProgram { - version: Fe32::Q, - program: WPubkeyHash::from_engine(hash_engine)[..].to_vec(), - }, - blinding_pubkey: blinder, - } - } - - /// Create a pay to script address that embeds a witness pay to public key - /// This is a segwit address type that looks familiar (as p2sh) to legacy clients - pub fn p2shwpkh( - pk: &PublicKey, - blinder: Option, - params: &'static AddressParams, - ) -> Address { - let mut hash_engine = ScriptHash::engine(); - pk.write_into(&mut hash_engine) - .expect("engines don't error"); - - let builder = script::Builder::new() - .push_int(0) - .push_slice(&ScriptHash::from_engine(hash_engine)[..]); - - Address { - params, - payload: Payload::ScriptHash(ScriptHash::hash(builder.into_script().as_bytes())), - blinding_pubkey: blinder, - } - } - - /// Create a witness pay to script hash address - pub fn p2wsh( - script: &script::Script, - blinder: Option, - params: &'static AddressParams, - ) -> Address { - Address { - params, - payload: Payload::WitnessProgram { - version: Fe32::Q, - program: WScriptHash::hash(&script[..])[..].to_vec(), - }, - blinding_pubkey: blinder, - } - } - - /// Create a pay to script address that embeds a witness pay to script hash address - /// This is a segwit address type that looks familiar (as p2sh) to legacy clients - pub fn p2shwsh( - script: &script::Script, - blinder: Option, - params: &'static AddressParams, - ) -> Address { - let ws = script::Builder::new() - .push_int(0) - .push_slice(&WScriptHash::hash(&script[..])[..]) - .into_script(); - - Address { - params, - payload: Payload::ScriptHash(ScriptHash::hash(&ws[..])), - blinding_pubkey: blinder, - } - } - - /// Creates a pay to taproot address from an untweaked key. - pub fn p2tr( - secp: &Secp256k1, - internal_key: UntweakedPublicKey, - merkle_root: Option, - blinder: Option, - params: &'static AddressParams, - ) -> Address { - Address { - params, - payload: { - let (output_key, _parity) = internal_key.tap_tweak(secp, merkle_root); - Payload::WitnessProgram { - version: Fe32::P, - program: output_key.into_inner().serialize().to_vec(), - } - }, - blinding_pubkey: blinder, - } - } - - /// Creates a pay to taproot address from a pre-tweaked output key. - /// - /// This method is not recommended for use, [`Address::p2tr()`] should be used where possible. - pub fn p2tr_tweaked( - output_key: TweakedPublicKey, - blinder: Option, - params: &'static AddressParams, - ) -> Address { - Address { - params, - payload: Payload::WitnessProgram { - version: Fe32::P, - program: output_key.into_inner().serialize().to_vec(), - }, - blinding_pubkey: blinder, - } - } - - /// Get an [Address] from an output script. - pub fn from_script( - script: &script::Script, - blinder: Option, - params: &'static AddressParams, - ) -> Option
{ - Some(Address { - payload: if script.is_p2pkh() { - Payload::PubkeyHash(Hash::from_slice(&script.as_bytes()[3..23]).unwrap()) - } else if script.is_p2sh() { - Payload::ScriptHash(Hash::from_slice(&script.as_bytes()[2..22]).unwrap()) - } else if script.is_v0_p2wpkh() { - Payload::WitnessProgram { - version: Fe32::Q, - program: script.as_bytes()[2..22].to_vec(), - } - } else if script.is_v0_p2wsh() { - Payload::WitnessProgram { - version: Fe32::Q, - program: script.as_bytes()[2..34].to_vec(), - } - } else if script.is_v1plus_p2witprog() { - Payload::WitnessProgram { - version: Fe32::try_from(script.as_bytes()[0] - 0x50).expect("0<32"), - program: script.as_bytes()[2..].to_vec(), - } - } else { - return None; - }, - blinding_pubkey: blinder, - params, - }) - } - - /// Generates a script pubkey spending to this address - pub fn script_pubkey(&self) -> script::Script { - match self.payload { - Payload::PubkeyHash(ref hash) => script::Builder::new() - .push_opcode(opcodes::all::OP_DUP) - .push_opcode(opcodes::all::OP_HASH160) - .push_slice(&hash[..]) - .push_opcode(opcodes::all::OP_EQUALVERIFY) - .push_opcode(opcodes::all::OP_CHECKSIG), - Payload::ScriptHash(ref hash) => script::Builder::new() - .push_opcode(opcodes::all::OP_HASH160) - .push_slice(&hash[..]) - .push_opcode(opcodes::all::OP_EQUAL), - Payload::WitnessProgram { - version: witver, - program: ref witprog, - } => script::Builder::new() - .push_int(witver.to_u8() as i64) - .push_slice(witprog), - } - .into_script() - } - - /// Convert this address to an unconfidential address. - pub fn to_unconfidential(&self) -> Address { - Address { - params: self.params, - payload: self.payload.clone(), - blinding_pubkey: None, - } - } - - /// Convert this address to a confidential address with the given blinding pubkey. - pub fn to_confidential(&self, blinding_pubkey: secp256k1_zkp::PublicKey) -> Address { - Address { - params: self.params, - payload: self.payload.clone(), - blinding_pubkey: Some(blinding_pubkey), - } - } - - fn from_bech32( - s: &str, - blinded: bool, - params: &'static AddressParams, - ) -> Result { - let (version, data): (Fe32, Vec) = if blinded { - let hs = crate::blech32::decode::SegwitHrpstring::new(s)?; - (hs.witness_version(), hs.byte_iter().collect()) - } else { - let hs = bech32::primitives::decode::SegwitHrpstring::new(s)?; - (hs.witness_version(), hs.byte_iter().collect()) - }; - - let (blinding_pubkey, program) = match blinded { - true => ( - Some( - secp256k1_zkp::PublicKey::from_slice(&data[..33]) - .map_err(AddressError::InvalidBlindingPubKey)?, - ), - data[33..].to_vec(), - ), - false => (None, data), - }; - - Ok(Address { - params, - payload: Payload::WitnessProgram { version, program }, - blinding_pubkey, - }) - } - - // data.len() should be >= 1 when this method is called - fn from_base58(data: &[u8], params: &'static AddressParams) -> Result { - // When unblinded, the structure is: - // <1: regular prefix> <20: hash160> - // When blinded, the structure is: - // <1: blinding prefix> <1: regular prefix> <33: blinding pubkey> <20: hash160> - - let (blinded, prefix) = match data[0] == params.blinded_prefix { - true => { - if data.len() != 55 { - return Err(AddressError::InvalidLength(data.len())); - } - (true, data[1]) - } - false => { - if data.len() != 21 { - return Err(AddressError::InvalidLength(data.len())); - } - (false, data[0]) - } - }; - - let (blinding_pubkey, payload_data) = match blinded { - true => ( - Some( - secp256k1_zkp::PublicKey::from_slice(&data[2..35]) - .map_err(AddressError::InvalidBlindingPubKey)?, - ), - &data[35..], - ), - false => (None, &data[1..]), - }; - - let payload = if prefix == params.p2pkh_prefix { - Payload::PubkeyHash(PubkeyHash::from_slice(payload_data).unwrap()) - } else if prefix == params.p2sh_prefix { - Payload::ScriptHash(ScriptHash::from_slice(payload_data).unwrap()) - } else { - return Err(AddressError::InvalidAddressVersion(prefix)); - }; - - Ok(Address { - params, - payload, - blinding_pubkey, - }) - } - - /// Parse the address using the given parameters. - /// When using the built-in parameters, you can use [FromStr]. - pub fn parse_with_params( - s: &str, - params: &'static AddressParams, - ) -> Result { - // Bech32. - let prefix = find_prefix(s); - let b32_ex = match_prefix(prefix, params.bech_hrp); - let b32_bl = match_prefix(prefix, params.blech_hrp); - if b32_ex || b32_bl { - return Address::from_bech32(s, b32_bl, params); - } - - // Base58. - if s.len() > 150 { - return Err(AddressError::InvalidLength(s.len() * 11 / 15)); - } - let data = base58::decode_check(s)?; - Address::from_base58(&data, params) - } -} - -impl fmt::Display for Address { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match self.payload { - Payload::PubkeyHash(ref hash) => { - if let Some(ref blinder) = self.blinding_pubkey { - let mut prefixed = [0; 55]; // 1 + 1 + 33 + 20 - prefixed[0] = self.params.blinded_prefix; - prefixed[1] = self.params.p2pkh_prefix; - prefixed[2..35].copy_from_slice(&blinder.serialize()); - prefixed[35..].copy_from_slice(&hash[..]); - base58::encode_check_to_fmt(fmt, &prefixed[..]) - } else { - let mut prefixed = [0; 21]; - prefixed[0] = self.params.p2pkh_prefix; - prefixed[1..].copy_from_slice(&hash[..]); - base58::encode_check_to_fmt(fmt, &prefixed[..]) - } - } - Payload::ScriptHash(ref hash) => { - if let Some(ref blinder) = self.blinding_pubkey { - let mut prefixed = [0; 55]; // 1 + 1 + 33 + 20 - prefixed[0] = self.params.blinded_prefix; - prefixed[1] = self.params.p2sh_prefix; - prefixed[2..35].copy_from_slice(&blinder.serialize()); - prefixed[35..].copy_from_slice(&hash[..]); - base58::encode_check_to_fmt(fmt, &prefixed[..]) - } else { - let mut prefixed = [0; 21]; - prefixed[0] = self.params.p2sh_prefix; - prefixed[1..].copy_from_slice(&hash[..]); - base58::encode_check_to_fmt(fmt, &prefixed[..]) - } - } - Payload::WitnessProgram { - version: witver, - program: ref witprog, - } => { - let hrp = match self.blinding_pubkey.is_some() { - true => self.params.blech_hrp, - false => self.params.bech_hrp, - }; - - // FIXME: surely we can fix this logic to not be so repetitive. - if self.is_blinded() { - if let Some(ref blinder) = self.blinding_pubkey { - let byte_iter = IntoIterator::into_iter(blinder.serialize()) - .chain(witprog.iter().copied()); - let fe_iter = byte_iter.bytes_to_fes(); - if witver.to_u8() == 0 { - for c in fe_iter - .with_checksum::(&hrp) - .with_witness_version(witver) - .chars() - { - fmt.write_char(c)?; - } - } else { - for c in fe_iter - .with_checksum::(&hrp) - .with_witness_version(witver) - .chars() - { - fmt.write_char(c)?; - } - } - return Ok(()); - } - } - - let byte_iter = witprog.iter().copied(); - let fe_iter = byte_iter.bytes_to_fes(); - if witver.to_u8() == 0 { - for c in fe_iter - .with_checksum::(&hrp) - .with_witness_version(witver) - .chars() - { - fmt.write_char(c)?; - } - } else { - for c in fe_iter - .with_checksum::(&hrp) - .with_witness_version(witver) - .chars() - { - fmt.write_char(c)?; - } - } - Ok(()) - } - } - } -} - -impl fmt::Debug for Address { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -/// Extract the bech32 prefix. -/// Returns the same slice when no prefix is found. -fn find_prefix(bech32: &str) -> &str { - // Split at the last occurrence of the separator character '1'. - match bech32.rfind('1') { - None => bech32, - Some(sep) => bech32.split_at(sep).0, - } -} - -/// Checks if both prefixes match, regardless of case. -/// The first prefix can be mixed case, but the second one is expected in -/// lower case. -fn match_prefix(prefix_mixed: &str, target: Hrp) -> bool { - if target.len() != prefix_mixed.len() { - false - } else { - target - .lowercase_char_iter() - .zip(prefix_mixed.chars()) - .all(|(char_lower, char_mixed)| char_lower == char_mixed.to_ascii_lowercase()) - } -} - -impl FromStr for Address { - type Err = AddressError; - - fn from_str(s: &str) -> Result { - // shorthands - let liq = &AddressParams::LIQUID; - let ele = &AddressParams::ELEMENTS; - let liq_test = &AddressParams::LIQUID_TESTNET; - - let net_arr = [liq, ele, liq_test]; - - let prefix = find_prefix(s); - for net in net_arr.iter() { - // Bech32. - if match_prefix(prefix, net.bech_hrp) { - return Address::from_bech32(s, false, net); - } - if match_prefix(prefix, net.blech_hrp) { - return Address::from_bech32(s, true, net); - } - } - - // Base58. - if s.len() > 150 { - return Err(AddressError::InvalidLength(s.len() * 11 / 15)); - } - let data = base58::decode_check(s)?; - if data.is_empty() { - return Err(AddressError::InvalidLength(data.len())); - } - - let p = data[0]; - for net in net_arr.iter() { - if p == net.p2pkh_prefix || p == net.p2sh_prefix || p == net.blinded_prefix { - return Address::from_base58(&data, net); - } - } - - Err(AddressError::InvalidAddress(s.to_owned())) - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for Address { - #[inline] - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - use std::fmt::Formatter; - - struct Visitor; - impl<'de> serde::de::Visitor<'de> for Visitor { - type Value = Address; - - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - formatter.write_str("a Bitcoin address") - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - Address::from_str(v).map_err(E::custom) - } - - fn visit_borrowed_str(self, v: &'de str) -> Result - where - E: serde::de::Error, - { - self.visit_str(v) - } - - fn visit_string(self, v: String) -> Result - where - E: serde::de::Error, - { - self.visit_str(&v) - } - } - - deserializer.deserialize_str(Visitor) - } -} - -#[cfg(feature = "serde")] -impl serde::Serialize for Address { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::Script; - use bitcoin::key; - use secp256k1_zkp::{PublicKey, Secp256k1}; - #[cfg(feature = "serde")] - use serde_json; - - fn roundtrips(addr: &Address) { - assert_eq!( - Address::from_str(&addr.to_string()).ok().as_ref(), - Some(addr), - "string round-trip failed for {}", - addr, - ); - assert_eq!( - Address::from_script(&addr.script_pubkey(), addr.blinding_pubkey, addr.params).as_ref(), - Some(addr), - "script round-trip failed for {}", - addr, - ); - #[cfg(feature = "serde")] - assert_eq!( - serde_json::from_value::
(serde_json::to_value(addr).unwrap()) - .ok() - .as_ref(), - Some(addr) - ); - } - - #[test] - fn regression_188() { - // Tests that the `tlq` prefix was not accidentally changed, e.g. to `tlg` :). - let addr = Address::from_str("tlq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f3mmz5l7uw5pqmx6xf5xy50hsn6vhkm5euwt72x878eq6zxx2z58hd7zrsg9qn").unwrap(); - roundtrips(&addr); - } - - #[test] - fn exhaustive() { - let blinder_hex = "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166"; - let blinder = PublicKey::from_str(blinder_hex).unwrap(); - let sk_wif = "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy"; - let sk = key::PrivateKey::from_wif(sk_wif).unwrap(); - let pk = sk.public_key(&Secp256k1::new()); - let script: Script = vec![1u8, 2, 42, 255, 196].into(); - - let vectors = [ - /* #00 */ Address::p2pkh(&pk, None, &AddressParams::LIQUID), - /* #01 */ Address::p2pkh(&pk, None, &AddressParams::ELEMENTS), - /* #02 */ Address::p2pkh(&pk, Some(blinder), &AddressParams::LIQUID), - /* #03 */ Address::p2pkh(&pk, Some(blinder), &AddressParams::ELEMENTS), - /* #04 */ Address::p2sh(&script, None, &AddressParams::LIQUID), - /* #05 */ Address::p2sh(&script, None, &AddressParams::ELEMENTS), - /* #06 */ Address::p2sh(&script, Some(blinder), &AddressParams::LIQUID), - /* #07 */ - Address::p2sh(&script, Some(blinder), &AddressParams::ELEMENTS), - /* #08 */ Address::p2wpkh(&pk, None, &AddressParams::LIQUID), - /* #09 */ Address::p2wpkh(&pk, None, &AddressParams::ELEMENTS), - /* #10 */ Address::p2wpkh(&pk, Some(blinder), &AddressParams::LIQUID), - /* #11 */ Address::p2wpkh(&pk, Some(blinder), &AddressParams::ELEMENTS), - /* #12 */ Address::p2shwpkh(&pk, None, &AddressParams::LIQUID), - /* #13 */ Address::p2shwpkh(&pk, None, &AddressParams::ELEMENTS), - /* #14 */ Address::p2shwpkh(&pk, Some(blinder), &AddressParams::LIQUID), - /* #15 */ - Address::p2shwpkh(&pk, Some(blinder), &AddressParams::ELEMENTS), - /* #16 */ Address::p2wsh(&script, None, &AddressParams::LIQUID), - /* #17 */ Address::p2wsh(&script, None, &AddressParams::ELEMENTS), - /* #18 */ Address::p2wsh(&script, Some(blinder), &AddressParams::LIQUID), - /* #19 */ - Address::p2wsh(&script, Some(blinder), &AddressParams::ELEMENTS), - /* #20 */ Address::p2shwsh(&script, None, &AddressParams::LIQUID), - /* #21 */ Address::p2shwsh(&script, None, &AddressParams::ELEMENTS), - /* #22 */ - Address::p2shwsh(&script, Some(blinder), &AddressParams::LIQUID), - /* #23 */ - Address::p2shwsh(&script, Some(blinder), &AddressParams::ELEMENTS), - ]; - - for addr in &vectors { - roundtrips(addr); - } - } - - #[test] - fn test_actuals() { - // vectors: (address, blinded?, params) - let addresses = [ - // Elements - ("2dxmEBXc2qMYcLSKiDBxdEePY3Ytixmnh4E", false, AddressParams::ELEMENTS), - ("CTEo6VKG8xbe7HnfVW9mQoWTgtgeRSPktwTLbELzGw5tV8Ngzu53EBiasFMQKVbWmKWWTAdN5AUf4M6Y", true, AddressParams::ELEMENTS), - ("ert1qwhh2n5qypypm0eufahm2pvj8raj9zq5c27cysu", false, AddressParams::ELEMENTS), - ("el1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsmxh4xcjh2rse", true, AddressParams::ELEMENTS), - // Liquid - ("GqiQRsPEyJLAsEBFB5R34KHuqxDNkG3zur", false, AddressParams::LIQUID), - ("VJLDwMVWXg8RKq4mRe3YFNTAEykVN6V8x5MRUKKoC3nfRnbpnZeiG3jygMC6A4Gw967GY5EotJ4Rau2F", true, AddressParams::LIQUID), - ("ex1q7gkeyjut0mrxc3j0kjlt7rmcnvsh0gt45d3fud", false, AddressParams::LIQUID), - ("lq1qqf8er278e6nyvuwtgf39e6ewvdcnjupn9a86rzpx655y5lhkt0walu3djf9cklkxd3ryld97hu8h3xepw7sh2rlu7q45dcew5", true, AddressParams::LIQUID), - ]; - - for &(a, blinded, ref params) in &addresses { - let result = a.parse(); - assert!( - result.is_ok(), - "vector: {}, err: \"{}\"", - a, - result.unwrap_err() - ); - let addr: Address = result.unwrap(); - assert_eq!(a, &addr.to_string(), "vector: {}", a); - assert_eq!(blinded, addr.is_blinded()); - assert_eq!(params, addr.params); - roundtrips(&addr); - } - } - - #[test] - fn test_blech32_vectors() { - // taken from Elements test/functional/rpc_invalid_address_message.py - let address: Result = "el1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsmxh4xcjh2rse".parse(); - assert!(address.is_ok()); - - let address: Result = "el1pq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsxguu9nrdg2pc".parse(); - assert_eq!( - address.err().unwrap().to_string(), - "blech32 error: invalid checksum", // is valid blech32, but should be blech32m - ); - - let address: Result = "el1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsnnmzrstzt7de".parse(); - assert_eq!( - address.err().unwrap().to_string(), - "blech32 error: invalid checksum", // is valid blech32m, but should be blech32 - ); - - let address: Result = - "ert130xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqqu2tys".parse(); - assert_eq!( - address.err().unwrap().to_string(), - "bech32 error: invalid segwit witness version: 17 (bech32 character: '3')", - ); - - let address: Result = "el1pq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpe9jfn0gypaj".parse(); - assert_eq!( - address.err().unwrap().to_string(), - "blech32 error: invalid witness length", - ); - - // "invalid prefix" gives a weird error message because we do - // a dumb prefix check before even attempting bech32 decoding - let address: Result = "rrr1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfs2d9rp7meq4kg".parse(); - assert_eq!(address.err().unwrap().to_string(), "base58 error: decode",); - } - - #[test] - fn test_fixed_addresses() { - let pk = bitcoin::PublicKey::from_str( - "0212bf0ea45b733dfde8ecb5e896306c4165c666c99fc5d1ab887f71393a975cea", - ) - .unwrap(); - let script = Script::default(); - let secp = Secp256k1::verification_only(); - let internal_key = UntweakedPublicKey::from_str( - "93c7378d96518a75448821c4f7c8f4bae7ce60f804d03d1f0628dd5dd0f5de51", - ) - .unwrap(); - let tap_node_hash = TapNodeHash::all_zeros(); - - let mut expected = IntoIterator::into_iter([ - "2dszRCFv8Ub4ytKo1Q1vXXGgSx7mekNDwSJ", - "XToMocNywBYNSiXUe5xvoa2naAps9Ek1hq", - "ert1qew0l0emv7449u7hqgc8utzdzryhse79yhq2sxv", - "XZF6k8S6eoVxXMB4NpWjh2s7LjQUP7pw2R", - "ert1quwcvgs5clswpfxhm7nyfjmaeysn6us0yvjdexn9yjkv3k7zjhp2szaqlpq", - "ert1p8qs0qcn25l2y6yvtc5t95rr8w9pndcj64c8rkutnvkcvdp6gh02q2cqvj9", - "ert1pxrrurkg8j8pve97lffvv2y67cf7ux478h077c87qacqzhue7390sqkjp06", - "CTEkC79sYAvWNcxd8iTYnYo226FqRBbzBcMppq7L2dA8jVXJWoo1kKWB3UBLY6gBjiXf87ibs8c6mQyZ", - "AzpjUhKMLJi9y2oLt3ZdM3BP9nHdLPJfGMVxRBaRc2gDpeNqPMVpShTszJW7bX42vT2KoejYy8GtbcxH", - "el1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4jul7lnkeat2teawq3s0cky6yxf0pnu2gmz9ej9kyq5yc", - "AzpjUhKMLJi9y2oLt3ZdM3BP9nHdLPJfGMVxRBaRc2gDpeNvq6SLVpBVwtakF6nmUFundyW7YjUdVkpr", - "el1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4casc3pf3lquzjd0haxgn9hmjfp84eq7geymjdx2f9verdu99wz4h79u87cnxdzq", - "el1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5wpq7p3x4f75f5gch3gktgxxwu2rxm394tsw8dchxedsc6r53w75cj24fq2u2ls5", - "el1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5vx8c8vs0ywzejta7jjcc5f4asnacdtu0wlaas0upmsq90enaz2lhjd0k0q7qn4h", - "QFq3vvrr6Ub2KAyb3LdoCxEQvKukB6nN9i", - "GydeMhecNgrq17WMkyyTM4ETv1YubMVtLN", - "ex1qew0l0emv7449u7hqgc8utzdzryhse79ydjqgek", - "H55PJDhj6JpR5k9wViXGEX4nga8WmhXtnD", - "ex1quwcvgs5clswpfxhm7nyfjmaeysn6us0yvjdexn9yjkv3k7zjhp2s4sla8h", - "ex1p8qs0qcn25l2y6yvtc5t95rr8w9pndcj64c8rkutnvkcvdp6gh02qa4lw5j", - "ex1pxrrurkg8j8pve97lffvv2y67cf7ux478h077c87qacqzhue7390shmdrfd", - "VTptY6cqJbusNpL5xvo8VL38nLX9PGDjfYQfqhu9EaA7FtuidkWyQzMHY9jzZrpBcCXT437vM6V4N8kh", - "VJL64Ep3rcngP4cScRme15q9i8MCNiuqWeiG3YbtduUidVyorg7nRsgmmF714QtH3sNpWB2CqsVVciQh", - "lq1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4jul7lnkeat2teawq3s0cky6yxf0pnu2gs2923tg58xcz", - "VJL64Ep3rcngP4cScRme15q9i8MCNiuqWeiG3YbtduUidVyuJR4JUzQPiqBdhzd1bgGHLVnmRUjfHc68", - "lq1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4casc3pf3lquzjd0haxgn9hmjfp84eq7geymjdx2f9verdu99wz47jmkmgmr9a4s", - "lq1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5wpq7p3x4f75f5gch3gktgxxwu2rxm394tsw8dchxedsc6r53w75375l4kfvf08y", - "lq1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5vx8c8vs0ywzejta7jjcc5f4asnacdtu0wlaas0upmsq90enaz2l77n92erwrrz8", - "FojPFeboBgrd953mXXe72KWthjVwHWozqN", - "8vsafXgrB5bJeSidGbK5eYnjKvQ3RiB4BB", - "tex1qew0l0emv7449u7hqgc8utzdzryhse79yh5jp9a", - "92KKc3jxthYtj5ND1KrtY1d46UyeWV6XbP", - "tex1quwcvgs5clswpfxhm7nyfjmaeysn6us0yvjdexn9yjkv3k7zjhp2s5fd6kc", - "tex1p8qs0qcn25l2y6yvtc5t95rr8w9pndcj64c8rkutnvkcvdp6gh02quvdf9a", - "tex1pxrrurkg8j8pve97lffvv2y67cf7ux478h077c87qacqzhue7390skzlycz", - "vtS71VhcpFt978sha5d1L2gCzp3UL5kXacRpb3N4GTW5MwvBzz5HwxYyB8Pns4yM2dd2osmQkHSkp88u", - "vjTuLJ76nGi8PUopBVmGK8bLKPfBpaBWf6wKfn8z9Vdz6ubVhpvmMr6TK2RcqAYiujN1g1uwg8kejrM3", - "tlq1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4jul7lnkeat2teawq3s0cky6yxf0pnu2gq8g2kuxfj8ft", - "vjTuLJ76nGi8PUopBVmGK8bLKPfBpaBWf6wKfn8z9Vdz6ubb9ZsHQxp5GcWFUkHTTYFUWLgWFk1DN5Fe", - "tlq1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4casc3pf3lquzjd0haxgn9hmjfp84eq7geymjdx2f9verdu99wz4e6vcdfcyp5m8", - "tlq1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5wpq7p3x4f75f5gch3gktgxxwu2rxm394tsw8dchxedsc6r53w75kkr3rh2tdxfn", - "tlq1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5vx8c8vs0ywzejta7jjcc5f4asnacdtu0wlaas0upmsq90enaz2lekytucqf82vs", - ]); - - for params in [ - &AddressParams::ELEMENTS, - &AddressParams::LIQUID, - &AddressParams::LIQUID_TESTNET, - ] { - for blinder in [None, Some(pk.inner)] { - let addr = Address::p2pkh(&pk, blinder, params); - assert_eq!(&addr.to_string(), expected.next().unwrap()); - - let addr = Address::p2sh(&script, blinder, params); - assert_eq!(&addr.to_string(), expected.next().unwrap()); - - let addr = Address::p2wpkh(&pk, blinder, params); - assert_eq!(&addr.to_string(), expected.next().unwrap()); - - let addr = Address::p2shwpkh(&pk, blinder, params); - assert_eq!(&addr.to_string(), expected.next().unwrap()); - - let addr = Address::p2wsh(&script, blinder, params); - assert_eq!(&addr.to_string(), expected.next().unwrap()); - - let addr = Address::p2tr(&secp, internal_key, None, blinder, params); - assert_eq!(&addr.to_string(), expected.next().unwrap()); - - let addr = Address::p2tr(&secp, internal_key, Some(tap_node_hash), blinder, params); - assert_eq!(&addr.to_string(), expected.next().unwrap()); - } - } - } -} diff --git a/src/blech32/decode.rs b/src/blech32/decode.rs deleted file mode 100644 index b73c80d1..00000000 --- a/src/blech32/decode.rs +++ /dev/null @@ -1,924 +0,0 @@ -// -// This file is essentially a copy of src/primitives/decode.rs from the bech32 -// crate. It is not public-domain licensed. It is MIT-licensed. The changes are: -// * Imports changed to use the public bech32 crate -// * Bech32 changed to Blech32 by search-and-replace -// * Fe32::from_char_unchecked and .0 were changed to use public API -// * CheckedHrpstring::validate_witness_program_length replaced to use Elements limits -// * `std` feature gates were removed -// * a couple tests with fixed vectors were disabled since we replaced the checksum -// * doccomment examples with fixed vectors were removed -// - -// Copyright (c) 2023 Tobin Harding and Andrew Poelstra -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -//! Decoding of bech32 encoded strings as specified by [BIP-173] and [BIP-350]. -//! -//! You should only need to use this module directly if you want control over exactly what is -//! checked and when it is checked (correct bech32 characters, valid checksum, valid checksum for -//! specific checksum algorithm, etc). If you are parsing/validating modern (post BIP-350) bitcoin -//! segwit addresses consider using the higher crate level API. -//! -//! If you do find yourself using this module directly then consider using the most general type -//! that serves your purposes, each type can be created by parsing an address string to `new`. You -//! likely do not want to arbitrarily transition from one type to the next even though possible. And -//! be prepared to spend some time with the bips - you have been warned :) -//! -//! # Details -//! -//! A Blech32 string is at most 90 characters long and consists of: -//! -//! - The human-readable part, which is intended to convey the type of data, or anything else that -//! is relevant to the reader. This part MUST contain 1 to 83 US-ASCII characters. -//! - The separator, which is always "1". -//! - The data part, which is at least 6 characters long and only consists of alphanumeric -//! characters excluding "1", "b", "i", and "o". -//! -//! The types in this module heavily lean on the wording in BIP-173: *We first -//! describe the general checksummed base32 format called Blech32 and then define Segregated Witness -//! addresses using it.* -//! -//! - `UncheckedHrpstring`: Parses the general checksummed base32 format and provides checksum validation. -//! - `CheckedHrpstring`: Provides access to the data encoded by a general checksummed base32 string and segwit checks. -//! - `SegwitHrpstring`: Provides access to the data encoded by a segwit address. -//! -//! [BIP-173]: -//! [BIP-350]: - -use core::{fmt, iter, slice, str}; - -use crate::error::write_err; -use bech32::primitives::checksum::{self, Checksum}; -use bech32::primitives::gf32::Fe32; -use bech32::primitives::hrp::{self, Hrp}; -use bech32::primitives::iter::{Fe32IterExt, FesToBytes}; -use bech32::primitives::segwit::{WitnessLengthError, VERSION_0}; -use super::{Blech32, Blech32m}; - -/// Separator between the hrp and payload (as defined by BIP-173). -const SEP: char = '1'; - -/// An HRP string that has been parsed but not yet had the checksum checked. -/// -/// Parsing an HRP string only checks validity of the characters, it does not validate the -/// checksum in any way. -/// -/// Unless you are attempting to validate a string with multiple checksums then you likely do not -/// want to use this type directly, instead use [`CheckedHrpstring::new(s)`]. -#[derive(Debug)] -pub struct UncheckedHrpstring<'s> { - /// The human-readable part, guaranteed to be lowercase ASCII characters. - hrp: Hrp, - /// This is ASCII byte values of the parsed string, guaranteed to be valid bech32 characters. - /// - /// Contains the checksum if one was present in the parsed string. - data: &'s [u8], -} - -impl<'s> UncheckedHrpstring<'s> { - /// Parses an bech32 encode string and constructs a [`UncheckedHrpstring`] object. - /// - /// Checks for valid ASCII values, does not validate the checksum. - #[inline] - pub fn new(s: &'s str) -> Result { - let sep_pos = check_characters(s)?; - let (hrp, data) = s.split_at(sep_pos); - - let ret = UncheckedHrpstring { - hrp: Hrp::parse(hrp)?, - data: data[1..].as_bytes(), // Skip the separator. - }; - - Ok(ret) - } - - /// Returns the human-readable part. - #[inline] - pub fn hrp(&self) -> Hrp { self.hrp } - - /// Validates that data has a valid checksum for the `Ck` algorithm and returns a [`CheckedHrpstring`]. - #[inline] - pub fn validate_and_remove_checksum( - self, - ) -> Result, ChecksumError> { - self.validate_checksum::()?; - Ok(self.remove_checksum::()) - } - - /// Validates that data has a valid checksum for the `Ck` algorithm (this may mean an empty - /// checksum if `NoChecksum` is used). - /// - /// This is useful if you do not know which checksum algorithm was used and wish to validate - /// against multiple algorithms consecutively. If this function returns `true` then call - /// `remove_checksum` to get a [`CheckedHrpstring`]. - #[inline] - pub fn has_valid_checksum(&self) -> bool { - self.validate_checksum::().is_ok() - } - - /// Validates that data has a valid checksum for the `Ck` algorithm (this may mean an empty - /// checksum if `NoChecksum` is used). - #[inline] - pub fn validate_checksum(&self) -> Result<(), ChecksumError> { - use ChecksumError::*; - - if Ck::CHECKSUM_LENGTH == 0 { - // Called with NoChecksum - return Ok(()); - } - - if self.data.len() < Ck::CHECKSUM_LENGTH { - return Err(InvalidChecksumLength); - } - - let mut checksum_eng = checksum::Engine::::new(); - checksum_eng.input_hrp(self.hrp()); - - // Unwrap ok since we checked all characters in our constructor. - for fe in self.data.iter().map(|&b| Fe32::from_char(b.into()).unwrap()) { - checksum_eng.input_fe(fe); - } - - if checksum_eng.residue() != &Ck::TARGET_RESIDUE { - return Err(InvalidChecksum); - } - - Ok(()) - } - - /// Removes the checksum for the `Ck` algorithm and returns an [`CheckedHrpstring`]. - /// - /// Data must be valid (ie, first call `has_valid_checksum` or `validate_checksum()`). This - /// function is typically paired with `has_valid_checksum` when validating against multiple - /// checksum algorithms consecutively. - /// - /// # Panics - /// - /// May panic if data is not valid. - #[inline] - pub fn remove_checksum(self) -> CheckedHrpstring<'s> { - let data_len = self.data.len() - Ck::CHECKSUM_LENGTH; - - CheckedHrpstring { hrp: self.hrp(), data: &self.data[..data_len] } - } -} - -/// An HRP string that has been parsed and had the checksum validated. -/// -/// This type does not treat the first byte of the data in any special way i.e., as the witness -/// version byte. If you are parsing Bitcoin segwit addresses you likely want to use [`SegwitHrpstring`]. -/// -/// > We first describe the general checksummed base32 format called Blech32 and then -/// > define Segregated Witness addresses using it. -/// -/// This type abstracts over the general checksummed base32 format called Blech32. -#[derive(Debug)] -pub struct CheckedHrpstring<'s> { - /// The human-readable part, guaranteed to be lowercase ASCII characters. - hrp: Hrp, - /// This is ASCII byte values of the parsed string, guaranteed to be valid bech32 characters, - /// with the checksum removed. - data: &'s [u8], -} - -impl<'s> CheckedHrpstring<'s> { - /// Parses and validates an HRP string, without treating the first data character specially. - /// - /// If you are validating the checksum multiple times consider using [`UncheckedHrpstring`]. - /// - /// This is equivalent to `UncheckedHrpstring::new().validate_and_remove_checksum::()`. - #[inline] - pub fn new(s: &'s str) -> Result { - let unchecked = UncheckedHrpstring::new(s)?; - let checked = unchecked.validate_and_remove_checksum::()?; - Ok(checked) - } - - /// Returns the human-readable part. - #[inline] - pub fn hrp(&self) -> Hrp { self.hrp } - - /// Returns an iterator that yields the data part of the parsed bech32 encoded string. - /// - /// Converts the ASCII bytes representing field elements to the respective field elements, then - /// converts the stream of field elements to a stream of bytes. - #[inline] - pub fn byte_iter(&self) -> ByteIter { - ByteIter { iter: AsciiToFe32Iter { iter: self.data.iter().copied() }.fes_to_bytes() } - } - - /// Converts this type to a [`SegwitHrpstring`] after validating the witness and HRP. - #[inline] - pub fn validate_segwit(mut self) -> Result, SegwitHrpstringError> { - if self.data.is_empty() { - return Err(SegwitHrpstringError::MissingWitnessVersion); - } - // Unwrap ok since check_characters checked the bech32-ness of this char. - let witness_version = Fe32::from_char(self.data[0].into()).unwrap(); - self.data = &self.data[1..]; // Remove the witness version byte from data. - - self.validate_padding()?; - self.validate_witness_program_length(witness_version)?; - - Ok(SegwitHrpstring { hrp: self.hrp(), witness_version, data: self.data }) - } - - /// Validates the segwit padding rules. - /// - /// Must be called after the witness version byte is removed from the data. - /// - /// From BIP-173: - /// > Re-arrange those bits into groups of 8 bits. Any incomplete group at the - /// > end MUST be 4 bits or less, MUST be all zeroes, and is discarded. - fn validate_padding(&self) -> Result<(), PaddingError> { - if self.data.is_empty() { - return Ok(()); // Empty data implies correct padding. - } - - let fe_iter = AsciiToFe32Iter { iter: self.data.iter().copied() }; - let padding_len = fe_iter.len() * 5 % 8; - - if padding_len > 4 { - return Err(PaddingError::TooMuch)?; - } - - let last_fe = fe_iter.last().expect("checked above"); - let last_byte = last_fe.to_u8(); - - let padding_contains_non_zero_bits = match padding_len { - 0 => false, - 1 => last_byte & 0b0001 > 0, - 2 => last_byte & 0b0011 > 0, - 3 => last_byte & 0b0111 > 0, - 4 => last_byte & 0b1111 > 0, - _ => unreachable!("checked above"), - }; - if padding_contains_non_zero_bits { - Err(PaddingError::NonZero) - } else { - Ok(()) - } - } - - /// Validates the segwit witness length rules. - /// - /// Must be called after the witness version byte is removed from the data. - fn validate_witness_program_length( - &self, - witness_version: Fe32, - ) -> Result<(), WitnessLengthError> { - let len = self.byte_iter().len(); - if len < 2 { - Err(WitnessLengthError::TooShort) - } else if len > 40 + 33 { - Err(WitnessLengthError::TooLong) - } else if witness_version == Fe32::Q && len != 53 && len != 65 { - Err(WitnessLengthError::InvalidSegwitV0) - } else { - Ok(()) - } - } -} - -/// An HRP string that has been parsed, had the checksum validated, had the witness version -/// validated, had the witness data length checked, and the had witness version and checksum -/// removed. -/// -#[derive(Debug)] -pub struct SegwitHrpstring<'s> { - /// The human-readable part, valid for segwit addresses. - hrp: Hrp, - /// The first byte of the parsed data. - witness_version: Fe32, - /// This is ASCII byte values of the parsed string, guaranteed to be valid bech32 characters, - /// with the witness version and checksum removed. - data: &'s [u8], -} - -impl<'s> SegwitHrpstring<'s> { - /// Parses an HRP string, treating the first data character as a witness version. - /// - /// The version byte does not appear in the extracted binary data, but is covered by the - /// checksum. It can be accessed with [`Self::witness_version`]. - /// - /// NOTE: We do not enforce any restrictions on the HRP, use [`SegwitHrpstring::has_valid_hrp`] - /// to get strict BIP conformance (also [`Hrp::is_valid_on_mainnet`] and friends). - #[inline] - pub fn new(s: &'s str) -> Result { - let unchecked = UncheckedHrpstring::new(s)?; - - if unchecked.data.is_empty() { - return Err(SegwitHrpstringError::MissingWitnessVersion); - } - - // Unwrap ok since check_characters (in `Self::new`) checked the bech32-ness of this char. - let witness_version = Fe32::from_char(unchecked.data[0].into()).unwrap(); - if witness_version.to_u8() > 16 { - return Err(SegwitHrpstringError::InvalidWitnessVersion(witness_version)); - } - - let checked: CheckedHrpstring<'s> = match witness_version { - VERSION_0 => unchecked.validate_and_remove_checksum::()?, - _ => unchecked.validate_and_remove_checksum::()?, - }; - - checked.validate_segwit() - } - - /// Parses an HRP string, treating the first data character as a witness version. - /// - /// ## WARNING - /// - /// You almost certainly do not want to use this function. - /// - /// It is provided for backwards comparability to parse addresses that have an non-zero witness - /// version because [BIP-173] explicitly allows using the bech32 checksum with any witness - /// version however [BIP-350] specifies all witness version > 0 now MUST use bech32m. - /// - /// [BIP-173]: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki - /// [BIP-350]: https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki - #[inline] - pub fn new_bech32(s: &'s str) -> Result { - let unchecked = UncheckedHrpstring::new(s)?; - - // Unwrap ok since check_characters (in `Self::new`) checked the bech32-ness of this char. - let witness_version = Fe32::from_char(unchecked.data[0].into()).unwrap(); - if witness_version.to_u8() > 16 { - return Err(SegwitHrpstringError::InvalidWitnessVersion(witness_version)); - } - - let checked = unchecked.validate_and_remove_checksum::()?; - checked.validate_segwit() - } - - /// Returns `true` if the HRP is "bc" or "tb". - /// - /// BIP-173 requires that the HRP is "bc" or "tb" but software in the Bitcoin ecosystem uses - /// other HRPs, specifically "bcrt" for regtest addresses. We provide this function in order to - /// be BIP-173 compliant but their are no restrictions on the HRP of [`SegwitHrpstring`]. - #[inline] - pub fn has_valid_hrp(&self) -> bool { self.hrp().is_valid_segwit() } - - /// Returns the human-readable part. - #[inline] - pub fn hrp(&self) -> Hrp { self.hrp } - - /// Returns the witness version. - #[inline] - pub fn witness_version(&self) -> Fe32 { self.witness_version } - - /// Returns an iterator that yields the data part, excluding the witness version, of the parsed - /// bech32 encoded string. - /// - /// Converts the ASCII bytes representing field elements to the respective field elements, then - /// converts the stream of field elements to a stream of bytes. - /// - /// Use `self.witness_version()` to get the witness version. - #[inline] - pub fn byte_iter(&self) -> ByteIter { - ByteIter { iter: AsciiToFe32Iter { iter: self.data.iter().copied() }.fes_to_bytes() } - } -} - -/// Checks whether a given HRP string has data characters in the bech32 alphabet (incl. checksum -/// characters), and that the whole string has consistent casing (hrp, data, and checksum). -/// -/// # Returns -/// -/// The byte-index into the string where the '1' separator occurs, or an error if it does not. -fn check_characters(s: &str) -> Result { - use CharError::*; - - let mut has_upper = false; - let mut has_lower = false; - let mut req_bech32 = true; - let mut sep_pos = None; - for (n, ch) in s.char_indices().rev() { - if ch == SEP && sep_pos.is_none() { - req_bech32 = false; - sep_pos = Some(n); - } - if req_bech32 { - Fe32::from_char(ch).map_err(|_| InvalidChar(ch))?; - } - if ch.is_ascii_uppercase() { - has_upper = true; - } else if ch.is_ascii_lowercase() { - has_lower = true; - } - } - if has_upper && has_lower { - Err(MixedCase) - } else if let Some(pos) = sep_pos { - Ok(pos) - } else { - Err(MissingSeparator) - } -} - -/// An iterator over a parsed HRP string data as bytes. -pub struct ByteIter<'s> { - iter: FesToBytes>>>, -} - -impl Iterator for ByteIter<'_> { - type Item = u8; - #[inline] - fn next(&mut self) -> Option { self.iter.next() } - #[inline] - fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } -} - -impl ExactSizeIterator for ByteIter<'_> { - #[inline] - fn len(&self) -> usize { self.iter.len() } -} - -/// Helper iterator adaptor that maps an iterator of valid bech32 character ASCII bytes to an -/// iterator of field elements. -/// -/// # Panics -/// -/// If any `u8` in the input iterator is out of range for an [`Fe32`]. Should only be used on data -/// that has already been checked for validity (eg, by using `check_characters`). -struct AsciiToFe32Iter> { - iter: I, -} - -impl Iterator for AsciiToFe32Iter -where - I: Iterator, -{ - type Item = Fe32; - #[inline] - fn next(&mut self) -> Option { self.iter.next().map(|ch| Fe32::from_char(ch.into()).unwrap()) } - #[inline] - fn size_hint(&self) -> (usize, Option) { - // Each ASCII character is an fe32 so iterators are the same size. - self.iter.size_hint() - } -} - -impl ExactSizeIterator for AsciiToFe32Iter -where - I: Iterator + ExactSizeIterator, -{ - #[inline] - fn len(&self) -> usize { self.iter.len() } -} - -/// An error while constructing a [`SegwitHrpstring`] type. -#[derive(Debug, Clone, PartialEq, Eq)] -#[non_exhaustive] -pub enum SegwitHrpstringError { - /// Error while parsing the encoded address string. - Unchecked(UncheckedHrpstringError), - /// The witness version byte is missing. - MissingWitnessVersion, - /// Invalid witness version (must be 0-16 inclusive). - InvalidWitnessVersion(Fe32), - /// Invalid padding on the witness data. - Padding(PaddingError), - /// Invalid witness length. - WitnessLength(WitnessLengthError), - /// Invalid checksum. - Checksum(ChecksumError), -} - -impl fmt::Display for SegwitHrpstringError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use SegwitHrpstringError::*; - - match *self { - Unchecked(ref e) => write_err!(f, "parsing unchecked hrpstring failed"; e), - MissingWitnessVersion => write!(f, "the witness version byte is missing"), - InvalidWitnessVersion(fe) => write!(f, "invalid segwit witness version: {}", fe.to_u8()), - Padding(ref e) => write_err!(f, "invalid padding on the witness data"; e), - WitnessLength(ref e) => write_err!(f, "invalid witness length"; e), - Checksum(ref e) => write_err!(f, "invalid checksum"; e), - } - } -} - -impl std::error::Error for SegwitHrpstringError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use SegwitHrpstringError::*; - - match *self { - Unchecked(ref e) => Some(e), - Padding(ref e) => Some(e), - WitnessLength(ref e) => Some(e), - Checksum(ref e) => Some(e), - MissingWitnessVersion | InvalidWitnessVersion(_) => None, - } - } -} - -impl From for SegwitHrpstringError { - #[inline] - fn from(e: UncheckedHrpstringError) -> Self { Self::Unchecked(e) } -} - -impl From for SegwitHrpstringError { - #[inline] - fn from(e: WitnessLengthError) -> Self { Self::WitnessLength(e) } -} - -impl From for SegwitHrpstringError { - #[inline] - fn from(e: PaddingError) -> Self { Self::Padding(e) } -} - -impl From for SegwitHrpstringError { - #[inline] - fn from(e: ChecksumError) -> Self { Self::Checksum(e) } -} - -/// An error while constructing a [`CheckedHrpstring`] type. -#[derive(Debug, Clone, PartialEq, Eq)] -#[non_exhaustive] -pub enum CheckedHrpstringError { - /// Error while parsing the encoded address string. - Parse(UncheckedHrpstringError), - /// Invalid checksum. - Checksum(ChecksumError), -} - -impl fmt::Display for CheckedHrpstringError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use CheckedHrpstringError::*; - - match *self { - Parse(ref e) => write_err!(f, "parse failed"; e), - Checksum(ref e) => write_err!(f, "invalid checksum"; e), - } - } -} - -impl std::error::Error for CheckedHrpstringError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use CheckedHrpstringError::*; - - match *self { - Parse(ref e) => Some(e), - Checksum(ref e) => Some(e), - } - } -} - -impl From for CheckedHrpstringError { - #[inline] - fn from(e: UncheckedHrpstringError) -> Self { Self::Parse(e) } -} - -impl From for CheckedHrpstringError { - #[inline] - fn from(e: ChecksumError) -> Self { Self::Checksum(e) } -} - -/// Errors when parsing a bech32 encoded string. -#[derive(Debug, Clone, PartialEq, Eq)] -#[non_exhaustive] -pub enum UncheckedHrpstringError { - /// An error with the characters of the input string. - Char(CharError), - /// The human-readable part is invalid. - Hrp(hrp::Error), -} - -impl fmt::Display for UncheckedHrpstringError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use UncheckedHrpstringError::*; - - match *self { - Char(ref e) => write_err!(f, "character error"; e), - Hrp(ref e) => write_err!(f, "invalid human-readable part"; e), - } - } -} - -impl std::error::Error for UncheckedHrpstringError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use UncheckedHrpstringError::*; - - match *self { - Char(ref e) => Some(e), - Hrp(ref e) => Some(e), - } - } -} - -impl From for UncheckedHrpstringError { - #[inline] - fn from(e: CharError) -> Self { Self::Char(e) } -} - -impl From for UncheckedHrpstringError { - #[inline] - fn from(e: hrp::Error) -> Self { Self::Hrp(e) } -} - -/// Character errors in a bech32 encoded string. -#[derive(Debug, Clone, PartialEq, Eq)] -#[non_exhaustive] -pub enum CharError { - /// String does not contain the separator character. - MissingSeparator, - /// No characters after the separator. - NothingAfterSeparator, - /// The checksum does not match the rest of the data. - InvalidChecksum, - /// The checksum is not a valid length. - InvalidChecksumLength, - /// Some part of the string contains an invalid character. - InvalidChar(char), - /// The whole string must be of one case. - MixedCase, -} - -impl fmt::Display for CharError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use CharError::*; - - match *self { - MissingSeparator => write!(f, "missing human-readable separator, \"{}\"", SEP), - NothingAfterSeparator => write!(f, "invalid data - no characters after the separator"), - InvalidChecksum => write!(f, "invalid checksum"), - InvalidChecksumLength => write!(f, "the checksum is not a valid length"), - InvalidChar(n) => write!(f, "invalid character (code={})", n), - MixedCase => write!(f, "mixed-case strings not allowed"), - } - } -} - -impl std::error::Error for CharError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use CharError::*; - - match *self { - MissingSeparator - | NothingAfterSeparator - | InvalidChecksum - | InvalidChecksumLength - | InvalidChar(_) - | MixedCase => None, - } - } -} - -/// Errors in the checksum of a bech32 encoded string. -#[derive(Debug, Clone, PartialEq, Eq)] -#[non_exhaustive] -pub enum ChecksumError { - /// The checksum does not match the rest of the data. - InvalidChecksum, - /// The checksum is not a valid length. - InvalidChecksumLength, -} - -impl fmt::Display for ChecksumError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use ChecksumError::*; - - match *self { - InvalidChecksum => write!(f, "invalid checksum"), - InvalidChecksumLength => write!(f, "the checksum is not a valid length"), - } - } -} - -impl std::error::Error for ChecksumError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use ChecksumError::*; - - match *self { - InvalidChecksum | InvalidChecksumLength => None, - } - } -} - -/// Error validating the padding bits on the witness data. -#[derive(Debug, Clone, PartialEq, Eq)] -#[non_exhaustive] -pub enum PaddingError { - /// The data payload has too many bits of padding. - TooMuch, - /// The data payload is padded with non-zero bits. - NonZero, -} - -impl fmt::Display for PaddingError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use PaddingError::*; - - match *self { - TooMuch => write!(f, "the data payload has too many bits of padding"), - NonZero => write!(f, "the data payload is padded with non-zero bits"), - } - } -} - -impl std::error::Error for PaddingError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use PaddingError::*; - - match *self { - TooMuch | NonZero => None, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn bip_173_invalid_parsing_fails() { - use UncheckedHrpstringError::*; - - let invalid: Vec<(&str, UncheckedHrpstringError)> = vec!( - ("\u{20}1nwldj5", - // TODO: Rust >= 1.59.0 use Hrp(hrp::Error::InvalidAsciiByte('\u{20}'.try_into().unwrap()))), - Hrp(hrp::Error::InvalidAsciiByte(32))), - ("\u{7F}1axkwrx", - Hrp(hrp::Error::InvalidAsciiByte(127))), - ("\u{80}1eym55h", - Hrp(hrp::Error::NonAsciiChar('\u{80}'))), - ("an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4", - Hrp(hrp::Error::TooLong(84))), - ("pzry9x0s0muk", - Char(CharError::MissingSeparator)), - ("1pzry9x0s0muk", - Hrp(hrp::Error::Empty)), - ("x1b4n0q5v", - Char(CharError::InvalidChar('b'))), - // "li1dgmt3" in separate test because error is a checksum error. - ("de1lg7wt\u{ff}", - Char(CharError::InvalidChar('\u{ff}'))), - // "A1G7SGD8" in separate test because error is a checksum error. - ("10a06t8", - Hrp(hrp::Error::Empty)), - ("1qzzfhee", - Hrp(hrp::Error::Empty)), - ); - - for (s, want) in invalid { - let got = UncheckedHrpstring::new(s).unwrap_err(); - assert_eq!(got, want); - } - } - - /* - #[test] - fn bip_173_invalid_parsing_fails_invalid_checksum() { - use ChecksumError::*; - - let err = UncheckedHrpstring::new("li1dgmt3") - .expect("string parses correctly") - .validate_checksum::() - .unwrap_err(); - assert_eq!(err, InvalidChecksumLength); - - let err = UncheckedHrpstring::new("A1G7SGD8") - .expect("string parses correctly") - .validate_checksum::() - .unwrap_err(); - assert_eq!(err, InvalidChecksum); - } - */ - - #[test] - fn bip_350_invalid_parsing_fails() { - use UncheckedHrpstringError::*; - - let invalid: Vec<(&str, UncheckedHrpstringError)> = vec!( - ("\u{20}1xj0phk", - // TODO: Rust >= 1.59.0 use Hrp(hrp::Error::InvalidAsciiByte('\u{20}'.try_into().unwrap()))), - Hrp(hrp::Error::InvalidAsciiByte(32))), - ("\u{7F}1g6xzxy", - Hrp(hrp::Error::InvalidAsciiByte(127))), - ("\u{80}1g6xzxy", - Hrp(hrp::Error::NonAsciiChar('\u{80}'))), - ("an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx", - Hrp(hrp::Error::TooLong(84))), - ("qyrz8wqd2c9m", - Char(CharError::MissingSeparator)), - ("1qyrz8wqd2c9m", - Hrp(hrp::Error::Empty)), - ("y1b0jsk6g", - Char(CharError::InvalidChar('b'))), - ("lt1igcx5c0", - Char(CharError::InvalidChar('i'))), - // "in1muywd" in separate test because error is a checksum error. - ("mm1crxm3i", - Char(CharError::InvalidChar('i'))), - ("au1s5cgom", - Char(CharError::InvalidChar('o'))), - // "M1VUXWEZ" in separate test because error is a checksum error. - ("16plkw9", - Hrp(hrp::Error::Empty)), - ("1p2gdwpf", - Hrp(hrp::Error::Empty)), - - ); - - for (s, want) in invalid { - let got = UncheckedHrpstring::new(s).unwrap_err(); - assert_eq!(got, want); - } - } - - /* - #[test] - fn bip_350_invalid_because_of_invalid_checksum() { - use ChecksumError::*; - - // Note the "bc1p2" test case is not from the bip test vectors. - let invalid: Vec<&str> = vec!["in1muywd", "bc1p2"]; - - for s in invalid { - let err = - UncheckedHrpstring::new(s).unwrap().validate_checksum::().unwrap_err(); - assert_eq!(err, InvalidChecksumLength); - } - - let err = UncheckedHrpstring::new("M1VUXWEZ") - .unwrap() - .validate_checksum::() - .unwrap_err(); - assert_eq!(err, InvalidChecksum); - } - */ - - #[test] - fn check_hrp_uppercase_returns_lower() { - let addr = "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4"; - let unchecked = UncheckedHrpstring::new(addr).expect("failed to parse address"); - assert_eq!(unchecked.hrp(), Hrp::parse_unchecked("bc")); - } - - #[test] - fn check_hrp_max_length() { - let hrps = - "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio"; - - let hrp = Hrp::parse_unchecked(hrps); - let s = bech32::encode::(hrp, &[]).expect("failed to encode empty buffer"); - - let unchecked = UncheckedHrpstring::new(&s).expect("failed to parse address"); - assert_eq!(unchecked.hrp(), hrp); - } - - /* - #[test] - fn mainnet_valid_addresses() { - let addresses = vec![ - "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq", - "23451QAR0SRRR7XFKVY5L643LYDNW9RE59GTZZLKULZK", - ]; - for valid in addresses { - assert!(CheckedHrpstring::new::(valid).is_ok()) - } - } - */ - - macro_rules! check_invalid_segwit_addresses { - ($($test_name:ident, $reason:literal, $address:literal);* $(;)?) => { - $( - #[test] - fn $test_name() { - let res = SegwitHrpstring::new($address); - if res.is_ok() { - panic!("{} sting should not be valid: {}", $address, $reason); - } - } - )* - } - } - check_invalid_segwit_addresses! { - invalid_segwit_address_0, "missing hrp", "1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"; - invalid_segwit_address_1, "missing data-checksum", "91111"; - invalid_segwit_address_2, "invalid witness version", "bc14r0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"; - invalid_segwit_address_3, "invalid checksum length", "bc1q5mdq"; - invalid_segwit_address_4, "missing data", "bc1qwf5mdq"; - invalid_segwit_address_5, "invalid program length", "bc14r0srrr7xfkvy5l643lydnw9rewf5mdq"; - } -} diff --git a/src/blech32/mod.rs b/src/blech32/mod.rs deleted file mode 100644 index 69694dd3..00000000 --- a/src/blech32/mod.rs +++ /dev/null @@ -1,61 +0,0 @@ -// This file is an adaptation of the segwit-specific parts of the bech32 crate. -// Rust Elements Library -// Written in 2024 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 . -// - -//! Blech32-Encoding (Elements Segwit) Support -//! -//! A variation of the bech32 encoding for blinded Elements addresses. -//! - -pub mod decode; - -// *** Definitions of checksums *** - -/// The blech32 checksum algorithm. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Blech32 {} - -impl bech32::Checksum for Blech32 { - type MidstateRepr = u64; - const CHECKSUM_LENGTH: usize = 12; - const GENERATOR_SH: [u64; 5] = [ - 0x7d52fba40bd886, - 0x5e8dbf1a03950c, - 0x1c3a3c74072a18, - 0x385d72fa0e5139, - 0x7093e5a608865b, - ]; - const TARGET_RESIDUE: u64 = 1; - - const CODE_LENGTH: usize = 1024; -} - -/// The blech32m checksum algorithm. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Blech32m {} - -impl bech32::Checksum for Blech32m { - type MidstateRepr = u64; - const CHECKSUM_LENGTH: usize = 12; - const GENERATOR_SH: [u64; 5] = [ - 0x7d52fba40bd886, - 0x5e8dbf1a03950c, - 0x1c3a3c74072a18, - 0x385d72fa0e5139, - 0x7093e5a608865b, - ]; - const TARGET_RESIDUE: u64 = 0x455972a3350f7a1; - - const CODE_LENGTH: usize = 1024; -} diff --git a/src/blind.rs b/src/blind.rs index 650fcd74..b519ef48 100644 --- a/src/blind.rs +++ b/src/blind.rs @@ -15,449 +15,23 @@ //! # Transactions Blinding //! -use std::{self, collections::BTreeMap, fmt}; +use std::collections::BTreeMap; use secp256k1_zkp::{ self, rand::{CryptoRng, RngCore}, - PedersenCommitment, SecretKey, Tag, Tweak, Verification, ZERO_TWEAK, + PedersenCommitment, SecretKey, Verification, ZERO_TWEAK, }; use secp256k1_zkp::{Generator, RangeProof, Secp256k1, Signing, SurjectionProof}; -use crate::{AddressParams, Script, TxIn}; +use crate::{AddressParams, Script}; -use crate::hashes; use crate::{ - confidential::{Asset, AssetBlindingFactor, Nonce, Value, ValueBlindingFactor}, + confidential::{Asset, AssetBlindingFactor, Value, ValueBlindingFactor}, Address, AssetId, Transaction, TxOut, TxOutWitness, }; -/// Transaction Output related errors -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum TxOutError { - /// Unexpected Null Value - UnExpectedNullValue, - /// Unexpected Null asset - UnExpectedNullAsset, - /// Money should be between 0 and 21_000_000 - MoneyOutofRange, - /// Zero value explicit txout with non-provably unspendable script - NonUnspendableZeroValue, - /// Zero value pedersen commitment with provably unspendable script - ZeroValueCommitment, - /// Incorrect Blinding factors - IncorrectBlindingFactors, -} - -impl std::error::Error for TxOutError {} - -impl fmt::Display for TxOutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match self { - TxOutError::UnExpectedNullValue => write!(f, "UnExpected Null Value"), - TxOutError::UnExpectedNullAsset => write!(f, "UnExpected Null Asset"), - TxOutError::MoneyOutofRange => write!( - f, - "Explicit amount must be\ - less than 21 million" - ), - TxOutError::NonUnspendableZeroValue => { - write!( - f, - "Zero value explicit amounts must be provably unspendable.\ - See IsUnspendable in elements" - ) - } - TxOutError::ZeroValueCommitment => { - write!( - f, - "Tried to create pedersen commitment with zero value.\ - Zero value is only allowed for provable unspendable scripts, - in which case the verification check can ignore the txout" - ) - } - TxOutError::IncorrectBlindingFactors => { - write!(f, "Incorrect Blinding factors") - } - } - } -} - -/// Transaction Verification Errors -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum VerificationError { - /// Verification of rangeproof failed - RangeProofError(usize, secp256k1_zkp::Error), - /// Missing Range Proof - RangeProofMissing(usize), - /// Verification of SurjectionProof failed - SurjectionProofError(usize, secp256k1_zkp::Error), - /// Surjection Proof verification error - SurjectionProofVerificationError(usize), - /// Missing Range Proof - SurjectionProofMissing(usize), - /// Spent Txout error - SpentTxOutError(usize, TxOutError), - /// Current transaction txout error - TxOutError(usize, TxOutError), - /// Issuance transaction verification not supported yet - IssuanceTransactionInput(usize), - /// Spent input len must match the len of transaction input - UtxoInputLenMismatch, - /// Balance Check failed - BalanceCheckFailed, -} - -impl fmt::Display for VerificationError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - VerificationError::RangeProofError(i, e) => { - write!(f, "Rangeproof Error {} : for output index {}", i, e) - } - VerificationError::SurjectionProofError(i, e) => { - write!(f, "Surjection Proof Error {} : for output index {}", i, e) - } - VerificationError::SurjectionProofVerificationError(i) => { - write!( - f, - "Surjection proof verification failed for output index {}", - i - ) - } - VerificationError::IssuanceTransactionInput(i) => { - write!(f, "Issuance transaction input {} not supported yet", i) - } - VerificationError::UtxoInputLenMismatch => { - write!(f, "Utxo len must match the len of transaction inputs") - } - VerificationError::SpentTxOutError(i, e) => { - write!(f, "Input index {} spent utxo error: {}", i, e) - } - VerificationError::TxOutError(i, e) => { - write!(f, "Output index {} txout: {}", i, e) - } - VerificationError::BalanceCheckFailed => { - write!( - f, - "Confidential transaction verification balance check failed" - ) - } - VerificationError::RangeProofMissing(i) => { - write!(f, "Missing Rangeproof for output index {}", i) - } - VerificationError::SurjectionProofMissing(i) => { - write!(f, "Missing Surjection Proof for output index {}", i) - } - } - } -} - -impl std::error::Error for VerificationError {} - -/// Errors encountered when constructing confidential transaction outputs. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ConfidentialTxOutError { - /// The script pubkey does not represent a valid address - /// This is not a fundamental limitation, just a limitation of how - /// the code API is structured - InvalidAddress, - /// The address provided does not have a blinding key. - NoBlindingKeyInAddress, - /// Error originated in `secp256k1_zkp`. - Upstream(secp256k1_zkp::Error), - /// General TxOut errors - TxOutError(usize, TxOutError), - /// Expected Explicit Asset for blinding - ExpectedExplicitAsset, - /// Expected Explicit Value for blinding - ExpectedExplicitValue, -} - -impl fmt::Display for ConfidentialTxOutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match self { - ConfidentialTxOutError::NoBlindingKeyInAddress => { - write!(f, "address does not include a blinding key") - } - ConfidentialTxOutError::Upstream(e) => write!(f, "{}", e), - ConfidentialTxOutError::TxOutError(i, e) => { - write!(f, "Txout error {} at index: {}", e, i) - } - ConfidentialTxOutError::ExpectedExplicitAsset => { - write!(f, "Expected explicit asset for blinding") - } - ConfidentialTxOutError::ExpectedExplicitValue => { - write!(f, "Expected explicit value for blinding") - } - ConfidentialTxOutError::InvalidAddress => { - write!( - f, - "Only sending to valid addresses is supported as of now. \ - Manually construct transactions to send to custom script pubkeys" - ) - } - } - } -} - -impl std::error::Error for ConfidentialTxOutError {} - -impl From for ConfidentialTxOutError { - fn from(from: secp256k1_zkp::Error) -> Self { - ConfidentialTxOutError::Upstream(from) - } -} -/// The Rangeproof message -#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub struct RangeProofMessage { - /// The asset id - pub asset: AssetId, - /// The asset blinding factor - pub bf: AssetBlindingFactor, -} - -impl RangeProofMessage { - /// Converts the message to bytes - pub fn to_bytes(&self) -> [u8; 64] { - let mut message = [0u8; 64]; - - message[..32].copy_from_slice(self.asset.into_tag().as_ref()); - message[32..].copy_from_slice(self.bf.into_inner().as_ref()); - - message - } -} - -/// Information about Transaction Input Asset -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "actual_serde") -)] -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub struct TxOutSecrets { - /// Asset - pub asset: AssetId, - /// Asset Blinding Factor - pub asset_bf: AssetBlindingFactor, - /// Value - pub value: u64, - /// Value Blinding factor - pub value_bf: ValueBlindingFactor, -} - -impl TxOutSecrets { - /// Create a new [`TxOutSecrets`] - pub fn new( - asset: AssetId, - asset_bf: AssetBlindingFactor, - value: u64, - value_bf: ValueBlindingFactor, - ) -> Self { - Self { - asset, - asset_bf, - value, - value_bf, - } - } - - /// Gets the surjection inputs from [`TxOutSecrets`]. - /// - /// Returns a tuple (assetid, blind_factor, generator) if the blinds are - /// consistent with asset commitment - /// Otherwise, returns an error - pub fn surjection_inputs(&self, secp: &Secp256k1) -> (Generator, Tag, Tweak) { - let tag = self.asset.into_tag(); - let bf = self.asset_bf.into_inner(); - let gen = Generator::new_blinded(secp, tag, bf); - (gen, tag, bf) - } - - /// Gets the required fields for last value blinding factor calculation from [`TxOutSecrets`] - pub fn value_blind_inputs(&self) -> (u64, AssetBlindingFactor, ValueBlindingFactor) { - (self.value, self.asset_bf, self.value_bf) - } -} - -/// Data structure used to provide inputs to [`SurjectionProof`] methods. -/// -/// Inputs for which we don't know the secrets can be [`SurjectionInput::Unknown`], -/// while inputs from user's wallet should be [`SurjectionInput::Known`] -/// -/// Explicit assets can be provided as [`SurjectionInput::Unknown`]. There is no -/// need to construct a `Known` variant with secrets -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum SurjectionInput { - /// Unknown inputs for whom we don't know the secrets(asset tags/blinding factors) - Unknown(Asset), - /// Known inputs for whom we know blinding factors - Known { - /// Asset - asset: AssetId, - /// Asset Blinding Factor - asset_bf: AssetBlindingFactor, - }, -} - -impl From for SurjectionInput { - fn from(v: TxOutSecrets) -> Self { - Self::Known { - asset: v.asset, - asset_bf: v.asset_bf, - } - } -} - -impl From for SurjectionInput { - fn from(v: Asset) -> Self { - Self::Unknown(v) - } -} - -impl SurjectionInput { - /// Creates a new [`SurjectionInput`] from commitment - pub fn from_comm(asset: Asset) -> Self { - Self::Unknown(asset) - } - - /// Creates a new [`SurjectionInput`] from [`TxOutSecrets`] - pub fn from_txout_secrets(secrets: TxOutSecrets) -> Self { - Self::from(secrets) - } - - /// Handy method to convert [`SurjectionInput`] into a surjection target - /// that can be used while creating a new [SurjectionProof]. - /// - /// Only errors when the input asset is Null. - pub fn surjection_target( - &self, - secp: &Secp256k1, - ) -> Result<(Generator, Tag, Tweak), TxOutError> { - match self { - SurjectionInput::Unknown(asset) => { - let gen = asset - .into_asset_gen(secp) - .ok_or(TxOutError::UnExpectedNullAsset)?; - // Return the input as 0 tag and 0 tweak. This also correctly handles explicit case - Ok((gen, Tag::default(), ZERO_TWEAK)) - } - SurjectionInput::Known { asset, asset_bf } => { - let tag = asset.into_tag(); - let bf = asset_bf.into_inner(); - let gen = Generator::new_blinded(secp, tag, bf); - Ok((gen, tag, bf)) - } - } - } -} - -impl Asset { - /// Blinds the asset such that there is a surjection proof between - /// the input assets and the output blinded asset. - /// - /// # Returns: - /// - /// A pair of blinded asset and corresponding proof as ([`Asset`], [`SurjectionProof`]) - pub fn blind( - self, - rng: &mut R, - secp: &Secp256k1, - asset_bf: AssetBlindingFactor, - spent_utxo_secrets: &[S], - ) -> Result<(Self, SurjectionProof), ConfidentialTxOutError> - where - R: RngCore + CryptoRng, - C: Signing, - S: Into + Copy, - { - let asset = self - .explicit() - .ok_or(ConfidentialTxOutError::ExpectedExplicitAsset)?; - let out_asset = Asset::new_confidential(secp, asset, asset_bf); - - let inputs = spent_utxo_secrets - .iter() - .enumerate() - .map(|(i, surject_inp)| { - (*surject_inp) - .into() - .surjection_target(secp) - .map_err(|e| ConfidentialTxOutError::TxOutError(i, e)) - }) - .collect::, _>>()?; - - let surjection_proof = SurjectionProof::new( - secp, - rng, - asset.into_tag(), - asset_bf.into_inner(), - inputs.as_ref(), - )?; - - Ok((out_asset, surjection_proof)) - } -} - -impl Value { - /// Blinds the values and outputs the blinded value along with [`RangeProof`]. - /// This computes the nonce by doing an ECDH with `receiver_blinding_pk` and `ephemeral_sk` - /// - /// # Returns: - /// - /// A pair of blinded value, nonce and corresponding proof as ([`Value`], [`Nonce`], [`RangeProof`]) - /// The nonce here refers to public key corresponding to the input `ephemeral_sk` - pub fn blind( - self, - secp: &Secp256k1, - vbf: ValueBlindingFactor, - receiver_blinding_pk: secp256k1_zkp::PublicKey, - ephemeral_sk: SecretKey, - spk: &Script, - msg: &RangeProofMessage, - ) -> Result<(Self, Nonce, RangeProof), ConfidentialTxOutError> { - let (nonce, shared_secret) = - Nonce::with_ephemeral_sk(secp, ephemeral_sk, &receiver_blinding_pk); - - let (value_commit, rangeproof) = - self.blind_with_shared_secret(secp, vbf, shared_secret, spk, msg)?; - Ok((value_commit, nonce, rangeproof)) - } - - /// Blinds with the given shared_secret(instead of computing it via ECDH) - /// This is useful while blinding assets as there is no counter party to provide - /// the blinding key. - pub fn blind_with_shared_secret( - self, - secp: &Secp256k1, - vbf: ValueBlindingFactor, - shared_secret: SecretKey, - spk: &Script, - msg: &RangeProofMessage, - ) -> Result<(Self, RangeProof), ConfidentialTxOutError> { - let value = self - .explicit() - .ok_or(ConfidentialTxOutError::ExpectedExplicitValue)?; - let out_asset_commitment = - Generator::new_blinded(secp, msg.asset.into_tag(), msg.bf.into_inner()); - let value_commitment = Value::new_confidential(secp, value, out_asset_commitment, vbf); - - let rangeproof = RangeProof::new( - secp, - TxOut::RANGEPROOF_MIN_VALUE, - value_commitment.commitment().expect("confidential value"), - value, - vbf.into_inner(), - &msg.to_bytes(), - spk.as_bytes(), - shared_secret, - TxOut::RANGEPROOF_EXP_SHIFT, - TxOut::RANGEPROOF_MIN_PRIV_BITS, - out_asset_commitment, - )?; - Ok((value_commitment, rangeproof)) - } -} +use crate::{BlindError, ConfidentialTxOutError, RangeProofMessage, SurjectionInput, TxOutError, TxOutSecrets, UnblindError, VerificationError}; impl TxOut { /// Rangeproof minimum value @@ -779,7 +353,7 @@ impl TxOut { let asset_bf = AssetBlindingFactor::from_slice(&asset_bf[..32])?; let value = opening.value; - let value_bf = ValueBlindingFactor(opening.blinding_factor); + let value_bf = ValueBlindingFactor::from_slice(opening.blinding_factor.as_ref()).expect("correct length"); Ok(TxOutSecrets { asset, @@ -790,131 +364,6 @@ impl TxOut { } } -/// Errors encountered when unblinding `TxOut`s. -#[derive(Debug)] -pub enum UnblindError { - /// The `TxOut` is not fully confidential. - NotConfidential, - /// Transaction output does not have a nonce commitment. - MissingNonce, - /// Transaction output does not have a rangeproof. - MissingRangeproof, - /// Malformed asset ID. - MalformedAssetId(hashes::FromSliceError), - /// Error originated in `secp256k1_zkp`. - Upstream(secp256k1_zkp::Error), -} - -impl fmt::Display for UnblindError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match self { - UnblindError::MissingNonce => write!(f, "missing nonce in txout"), - UnblindError::MalformedAssetId(_) => write!(f, "malformed asset id"), - UnblindError::Upstream(e) => write!(f, "{}", e), - UnblindError::NotConfidential => write!(f, "cannot unblind non-confidential txout"), - UnblindError::MissingRangeproof => write!(f, "missing rangeproof in txout"), - } - } -} - -impl std::error::Error for UnblindError { - fn cause(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - UnblindError::MissingNonce => None, - UnblindError::MalformedAssetId(e) => Some(e), - UnblindError::Upstream(e) => Some(e), - UnblindError::NotConfidential => None, - UnblindError::MissingRangeproof => None, - } - } -} - -impl From for UnblindError { - fn from(from: secp256k1_zkp::Error) -> Self { - UnblindError::Upstream(from) - } -} - -impl From for UnblindError { - fn from(from: hashes::FromSliceError) -> Self { - UnblindError::MalformedAssetId(from) - } -} - -impl TxIn { - /// Blind issuances for this [`TxIn`]. Asset amount and token amount must be - /// set in [`AssetIssuance`](crate::AssetIssuance) field for this input - pub fn blind_issuances_with_bfs( - &mut self, - secp: &Secp256k1, - issue_vbf: ValueBlindingFactor, - token_vbf: ValueBlindingFactor, - issue_sk: SecretKey, - token_sk: SecretKey, - ) -> Result<(), BlindError> { - if !self.has_issuance() { - return Err(BlindError::NoIssuanceToBlind); - } - let (asset_id, token_id) = self.issuance_ids(); - let arr = vec![ - (issue_vbf, self.asset_issuance.amount, issue_sk, asset_id), - ( - token_vbf, - self.asset_issuance.inflation_keys, - token_sk, - token_id, - ), - ]; - for (i, (bf, amt, blind_sk, asset)) in arr.into_iter().enumerate() { - let v = match amt { - Value::Null => continue, // nothing to blind - Value::Explicit(0) => return Err(BlindError::ZeroValueBlindingNotAllowed), - Value::Confidential(_) => return Err(BlindError::IssuanceAmountMustBeExplicit), - Value::Explicit(v) => Value::Explicit(v), - }; - let spk = Script::new(); - let msg = RangeProofMessage { - asset, - bf: AssetBlindingFactor::zero(), - }; - 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)); - } else { - self.asset_issuance.inflation_keys = comm; - self.witness.inflation_keys_rangeproof = Some(Box::new(prf)); - } - } - Ok(()) - } - - /// Blind issuances for this [`TxIn`]. Asset amount and token amount must be - /// set in [`AssetIssuance`](crate::AssetIssuance) field for this input - /// - /// Returns (issuance_blinding_factor, issue_blind_sec_key, token_blinding_factor, token_blind_sec_key) - pub fn blind_issuances( - &mut self, - secp: &Secp256k1, - rng: &mut R, - ) -> Result< - ( - ValueBlindingFactor, - SecretKey, - ValueBlindingFactor, - SecretKey, - ), - BlindError, - > { - let issue_vbf = ValueBlindingFactor::new(rng); - let token_vbf = ValueBlindingFactor::new(rng); - let issue_sk = SecretKey::new(rng); - let token_sk = SecretKey::new(rng); - self.blind_issuances_with_bfs(secp, issue_vbf, token_vbf, issue_sk, token_sk)?; - Ok((issue_vbf, issue_sk, token_vbf, token_sk)) - } -} - /// Data structure for Unifying inputs and pseudo-inputs. #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash)] pub enum TxInType { @@ -1189,70 +638,6 @@ impl Transaction { } } -/// Errors encountered when blinding transaction outputs. -#[derive(Debug, Clone, Copy)] -pub enum BlindError { - /// The script pubkey does not represent a valid address - /// This is not a fundamental limitation, just a limitation of how - /// the code API is structured - InvalidAddress, - /// Too few blinding inputs - TooFewBlindingOutputs, - /// All outputs must be explicit asset/amounts - MustHaveAllExplicitTxOuts, - /// General TxOut errors - ConfidentialTxOutError(ConfidentialTxOutError), - /// No Issuances to blind in this TxIn - NoIssuanceToBlind, - /// Zero Value Blinding not allowed - ZeroValueBlindingNotAllowed, - /// Issuance Amount must be explicit - IssuanceAmountMustBeExplicit, -} - -impl fmt::Display for BlindError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match self { - BlindError::InvalidAddress => { - write!( - f, - "Only sending to valid addresses is supported as of now. \ - Manually construct transactions to send to custom script pubkeys" - ) - } - BlindError::TooFewBlindingOutputs => { - write!( - f, - "Transactions must have atleast confidential outputs \ - marked for blinding. To mark a output for blinding set nonce field\ - with a blinding pubkey" - ) - } - BlindError::MustHaveAllExplicitTxOuts => { - write!(f, "Transaction must all outputs explicit") - } - BlindError::ConfidentialTxOutError(e) => { - write!(f, "{}", e) - } - BlindError::NoIssuanceToBlind => write!(f, "No Issuance present"), - BlindError::ZeroValueBlindingNotAllowed => { - write!(f, "Zero value blinding is not allowed") - } - BlindError::IssuanceAmountMustBeExplicit => { - write!(f, "Issuance amount must be explicit to blind") - } - } - } -} - -impl std::error::Error for BlindError {} - -impl From for BlindError { - fn from(from: ConfidentialTxOutError) -> Self { - BlindError::ConfidentialTxOutError(from) - } -} - /// A trait to create and verify explicit rangeproofs pub trait BlindValueProofs: Sized { /// Outputs a `[RangeProof]` that blinded value @@ -1265,16 +650,6 @@ pub trait BlindValueProofs: Sized { 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 { @@ -1302,22 +677,6 @@ impl BlindValueProofs for RangeProof { 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 @@ -1330,15 +689,6 @@ pub trait BlindAssetProofs: Sized { 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 { @@ -1357,22 +707,12 @@ impl BlindAssetProofs for SurjectionProof { &[(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::*; - use crate::confidential; + use crate::confidential::{self, Nonce}; use crate::encode; use crate::encode::deserialize; use crate::hex::FromHex; @@ -1533,7 +873,7 @@ mod tests { let vbf = ValueBlindingFactor::new(&mut thread_rng()); let v = confidential::Value::new_confidential(SECP256K1, explicit_val, asset_gen, vbf); let value_comm = v.commitment().unwrap(); - let proof = RangeProof::blind_value_proof( + let _ = RangeProof::blind_value_proof( &mut thread_rng(), SECP256K1, explicit_val, @@ -1542,9 +882,6 @@ mod tests { vbf, ) .unwrap(); - - let res = proof.blind_value_proof_verify(SECP256K1, explicit_val, asset_gen, value_comm); - assert!(res); } #[test] @@ -1553,13 +890,10 @@ mod tests { let abf = AssetBlindingFactor::new(&mut thread_rng()); let asset = confidential::Asset::new_confidential(SECP256K1, id, abf); - let asset_comm = asset.commitment().unwrap(); + let _ = asset.commitment().unwrap(); // Create the proof - let proof = + let _ = SurjectionProof::blind_asset_proof(&mut thread_rng(), SECP256K1, id, abf).unwrap(); - - let res = proof.blind_asset_proof_verify(SECP256K1, id, asset_comm); - assert!(res); } #[test] diff --git a/src/block.rs b/src/block.rs index 0ab9c623..a96fe7a2 100644 --- a/src/block.rs +++ b/src/block.rs @@ -15,337 +15,13 @@ //! # Blocks //! -use std::io; -#[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; -#[cfg(feature = "serde")] use std::fmt; - -use crate::dynafed; -use crate::hashes::{Hash, sha256}; use crate::Transaction; -use crate::encode::{self, serialize, Decodable, Encodable, VarInt}; -use crate::{BlockHash, Script, TxMerkleNode}; - -/// Data related to block signatures -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub enum ExtData { - /// Liquid v1-style static `signblockscript` and witness - Proof { - /// Block "public key" - challenge: Script, - /// Satisfying witness to the above challenge, or nothing - solution: Script, - }, - /// Dynamic federations - Dynafed { - /// Current dynamic federation parameters - current: dynafed::Params, - /// Proposed dynamic federation parameters - proposed: dynafed::Params, - /// Witness satisfying the current blocksigning script - signblock_witness: Vec>, - }, -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for ExtData { - fn deserialize>(d: D) -> Result { - use serde::de; - - enum Enum { Unknown, Challenge, Solution, Current, Proposed, Witness } - struct EnumVisitor; - - impl de::Visitor<'_> for EnumVisitor { - type Value = Enum; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("a field name") - } - - fn visit_str(self, v: &str) -> Result { - match v { - "challenge" => Ok(Enum::Challenge), - "solution" => Ok(Enum::Solution), - "current" => Ok(Enum::Current), - "proposed" => Ok(Enum::Proposed), - "signblock_witness" => Ok(Enum::Witness), - _ => Ok(Enum::Unknown), - } - } - } - - impl<'de> Deserialize<'de> for Enum { - fn deserialize>(d: D) -> Result { - d.deserialize_str(EnumVisitor) - } - } - - struct Visitor; - impl<'de> de::Visitor<'de> for Visitor { - type Value = ExtData; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("block header extra data") - } - - fn visit_map(self, mut map: A) -> Result - where - A: de::MapAccess<'de>, - { - let mut challenge = None; - let mut solution = None; - let mut current = None; - let mut proposed = None; - let mut witness = None; - - loop { - match map.next_key::()? { - Some(Enum::Unknown) => { - map.next_value::()?; - }, - Some(Enum::Challenge) => challenge = Some(map.next_value()?), - Some(Enum::Solution) => solution = Some(map.next_value()?), - Some(Enum::Current) => current = Some(map.next_value()?), - Some(Enum::Proposed) => proposed = Some(map.next_value()?), - Some(Enum::Witness) => witness = Some(map.next_value()?), - None => { break; } - } - } - - let challenge_missing = challenge.is_some(); - if let (Some(chal), Some(soln)) = (challenge, solution) { - Ok(ExtData::Proof { - challenge: chal, - solution: soln, - }) - } else if let (Some(cur), Some(prop), Some(wit)) - = (current, proposed, witness) - { - Ok(ExtData::Dynafed { - current: cur, - proposed: prop, - signblock_witness: wit, - }) - } else if challenge_missing { - Err(de::Error::missing_field("challenge")) - } else { - Err(de::Error::missing_field("solution")) - } - } - } - - static FIELDS: &[&str] = &[ - "challenge", - "solution", - "current", - "proposed", - "signblock_witness", - ]; - d.deserialize_struct("ExtData", FIELDS, Visitor) - } -} - -#[cfg(feature = "serde")] -impl Serialize for ExtData { - fn serialize(&self, s: S) -> Result { - use serde::ser::SerializeStruct; +use crate::encode::{serialize, VarInt}; +use crate::BlockHash; - match *self { - ExtData::Proof { ref challenge, ref solution } => { - let mut st = s.serialize_struct("ExtData", 2)?; - st.serialize_field("challenge", challenge)?; - st.serialize_field("solution", solution)?; - st.end() - }, - ExtData::Dynafed { ref current, ref proposed, ref signblock_witness } => { - let mut st = s.serialize_struct("ExtData", 3)?; - st.serialize_field("current", current)?; - st.serialize_field("proposed", proposed)?; - st.serialize_field("signblock_witness", signblock_witness)?; - st.end() - }, - } - } -} - -impl Encodable for ExtData { - fn consensus_encode(&self, mut s: S) -> Result { - Ok(match *self { - ExtData::Proof { - ref challenge, - ref solution, - } => { - challenge.consensus_encode(&mut s)? + - solution.consensus_encode(&mut s)? - }, - ExtData::Dynafed { - ref current, - ref proposed, - ref signblock_witness, - } => { - current.consensus_encode(&mut s)? + - proposed.consensus_encode(&mut s)? + - signblock_witness.consensus_encode(&mut s)? - }, - }) - } -} - -impl Default for ExtData { - fn default() -> ExtData { - ExtData::Dynafed { - current: dynafed::Params::Null, - proposed: dynafed::Params::Null, - signblock_witness: vec![], - } - } -} - -/// Elements block header -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct BlockHeader { - /// Version - should be 0x20000000 except when versionbits signalling - pub version: u32, - /// Previous blockhash - pub prev_blockhash: BlockHash, - /// Transaction Merkle root - pub merkle_root: TxMerkleNode, - /// Block timestamp - pub time: u32, - /// Block height - pub height: u32, - /// Block signature and dynamic federation-related data - pub ext: ExtData, -} -serde_struct_impl!(BlockHeader, version, prev_blockhash, merkle_root, time, height, ext); - -impl BlockHeader { - /// Return the block hash. - pub fn block_hash(&self) -> BlockHash { - - let version = if let ExtData::Dynafed { .. } = self.ext { - self.version | 0x8000_0000 - } else { - self.version - }; - - // Everything except the signblock witness goes into the hash - let mut enc = BlockHash::engine(); - version.consensus_encode(&mut enc).unwrap(); - self.prev_blockhash.consensus_encode(&mut enc).unwrap(); - self.merkle_root.consensus_encode(&mut enc).unwrap(); - self.time.consensus_encode(&mut enc).unwrap(); - self.height.consensus_encode(&mut enc).unwrap(); - match self.ext { - ExtData::Proof { ref challenge, .. } => { - challenge.consensus_encode(&mut enc).unwrap(); - }, - ExtData::Dynafed { ref current, ref proposed, .. } => { - current.consensus_encode(&mut enc).unwrap(); - proposed.consensus_encode(&mut enc).unwrap(); - }, - } - BlockHash::from_engine(enc) - } - - /// Returns true if this is a block with dynamic federations enabled. - pub fn is_dynafed(&self) -> bool { - matches!(self.ext, ExtData::Dynafed { .. }) - } - - /// Remove the witness data of the block header. - /// This is all the data that can be removed without changing - /// the block hash. - pub fn clear_witness(&mut self) { - match &mut self.ext { - ExtData::Proof { ref mut solution, .. } => { - *solution = Script::new(); - }, - ExtData::Dynafed { ref mut signblock_witness, .. } => { - signblock_witness.clear(); - }, - } - } - - /// Calculate the root of the dynafed params. Returns [None] when not dynafed. - pub fn calculate_dynafed_params_root(&self) -> Option { - match self.ext { - ExtData::Proof { .. } => None, - ExtData::Dynafed { ref current, ref proposed, .. } => { - let leaves = [ - current.calculate_root().to_byte_array(), - proposed.calculate_root().to_byte_array(), - ]; - Some(crate::fast_merkle_root::fast_merkle_root(&leaves[..])) - } - } - } - - /// Get the current dynafed parameters if it's a dynafed header. - pub fn dynafed_current(&self) -> Option<&dynafed::Params> { - match self.ext { - ExtData::Dynafed { ref current, .. } => Some(current), - ExtData::Proof { .. } => None, - } - } - - /// Get the proposed dynafed parameters if it's a dynafed header. - pub fn dynafed_proposed(&self) -> Option<&dynafed::Params> { - match self.ext { - ExtData::Dynafed { ref proposed, .. } => Some(proposed), - ExtData::Proof { .. } => None, - } - } -} - -impl Encodable for BlockHeader { - fn consensus_encode(&self, mut s: S) -> Result { - let version = if let ExtData::Dynafed { .. } = self.ext { - self.version | 0x8000_0000 - } else { - self.version - }; - - Ok(version.consensus_encode(&mut s)? + - self.prev_blockhash.consensus_encode(&mut s)? + - self.merkle_root.consensus_encode(&mut s)? + - self.time.consensus_encode(&mut s)? + - self.height.consensus_encode(&mut s)? + - self.ext.consensus_encode(&mut s)?) - } -} - -impl Decodable for BlockHeader { - fn consensus_decode(mut d: D) -> Result { - let mut version: u32 = Decodable::consensus_decode(&mut d)?; - let is_dyna = if version >> 31 == 1 { - version &= 0x7fff_ffff; - true - } else { - false - }; - - Ok(BlockHeader { - version, - prev_blockhash: Decodable::consensus_decode(&mut d)?, - merkle_root: Decodable::consensus_decode(&mut d)?, - time: Decodable::consensus_decode(&mut d)?, - height: Decodable::consensus_decode(&mut d)?, - ext: if is_dyna { - ExtData::Dynafed { - current: Decodable::consensus_decode(&mut d)?, - proposed: Decodable::consensus_decode(&mut d)?, - signblock_witness: Decodable::consensus_decode(&mut d)?, - } - } else { - ExtData::Proof { - challenge: Decodable::consensus_decode(&mut d)?, - solution: Decodable::consensus_decode(&mut d)?, - } - }, - }) - } -} +pub use elements26::BlockExtData as ExtData; +pub use elements26::BlockHeader; /// Elements block #[derive(Clone, Debug, Eq, Hash, PartialEq)] @@ -411,6 +87,7 @@ mod tests { 7d45000000000000012000000000000000000000000000000000000000000000\ 000000000000000000000000000000\ "; + #[cfg(feature = "serde")] const DYNAFED_BLOCK: &str = "\ 000000a0da9d569617d1d65c3390a01c18c4fa7c4d0f4738b6fc2b5c5faf2e8a\ 463abbaa46eb9123808e1e2ff75e9472fa0f0589b53b7518a69d3d6fcb9228ed\ @@ -715,88 +392,11 @@ mod tests { } } - #[test] - fn dynafed_block() { - // Copied from elements RPC during a functionary integration test run - let block: Block = hex_deserialize!(DYNAFED_BLOCK); - - // Test that this is a block with compact current params and null proposed params - if let ExtData::Dynafed { current, proposed, .. } = block.clone().header.ext { - if let dynafed::Params::Compact { signblock_witness_limit, .. } = current { - assert_eq!(signblock_witness_limit, 258); - } else { - panic!("Current block dynafed params not compact"); - } - if let dynafed::Params::Null { .. } = proposed { - /* pass */ - } else { - panic!("Proposed block dynafed params not compact"); - } - } else { - panic!("No dynafed params"); - } - - assert_eq!( - block.block_hash().to_string(), - "4961df970cf12d789383974e6ab439f780d956b5a50162ca9d281362e46c605a" - ); - assert_eq!(block.header.version, 0x20000000); - - // Full current and proposal - let block: Block = hex_deserialize!("\ - 000000a01ecf88cda4d9e6339109c685417c526e8316fe0d3ea058765634dcbb\ - 205d3081bd83073b1f1793154ab820c70a1fda32a0d45bb0e1f40c0c61ae0350\ - 7f49c293debcc45d1400000002220020a6794de47a1612cc94c1b978d5bd1b25\ - 873f4cab0b1a76260b0b8af9ad954dc74b0100002200204ae81572f06e1b88fd\ - 5ced7a1a000945432e83e1551e6f721ee9c00b8cc33260015100022200204cb4\ - 0d59d6e1bbe963f3a63021b0d7d5474b87206978a1129fbffc4d1c1cf7e44b01\ - 00002200204ae81572f06e1b88fd5ced7a1a000945432e83e1551e6f721ee9c0\ - 0b8cc332600151000500483045022100ab2203a8a68d7eca8a3a0fac91e7c780\ - 2656d937535800da82f7102e1c06f7b80220576f004cb14b95178c71e36bf947\ - bfea496b00b66c18d2eb8febf46362d50e2b0147304402202d4630887d661a50\ - b76b7b32555fd76906ad298ce24483df42310ffbf62d451802200e0d64069e58\ - 047c271c1b4051c1ff3d1cba7d32e56abb0d8b8bc30e1bed075b014830450221\ - 00c6b196967c661c4543802a895ae731af44862e75d9e3c65b8efdd668727a34\ - af022041ff4d67029052eb6305d25d0fc4813d21a939ff5316a12562d0c90389\ - 76f8e1016953210296db75c11ea3a292a372f6c94f5013eaeb379f701857a702\ - f3b83f88da21be6f21021f0d8638c413ef7769cd711ce84c8f192f5a85f0fd6d\ - 8e63ddb4d2cf6740b23b2103cadff18e928133df2e670a3715c4e7a81d357de3\ - 6ddaa5016628e70a3e6a452f53ae010200000001010000000000000000000000\ - 000000000000000000000000000000000000000000ffffffff0401140101ffff\ - ffff020137c495f58d698979ff9124e8c7455fe79b13ddb96afa25c45894eb05\ - 9868a8c001000000000000000000016a0137c495f58d698979ff9124e8c7455f\ - e79b13ddb96afa25c45894eb059868a8c001000000000000000000266a24aa21\ - a9ed94f15ed3a62165e4a0b99699cc28b48e19cb5bc1b1f47155db62d63f1e04\ - 7d45000000000000012000000000000000000000000000000000000000000000\ - 000000000000000000000000000000\ - "); - - // Test that this is a block with full current params and full proposed params - if let ExtData::Dynafed { current, proposed, .. } = block.clone().header.ext { - if let dynafed::Params::Full { .. } = current { - /* pass */ - } else { - panic!("Current block dynafed params not full"); - } - if let dynafed::Params::Full { .. } = proposed { - /* pass */ - } else { - panic!("Proposed block dynafed params not full"); - } - } else { - panic!("No dynafed params"); - } - assert_eq!( - block.block_hash().to_string(), - "e9a5176b1690a448f76fb691ab4d516e60e13a6e7a49454c62dbf0d611ffcce7" - ); - } - #[test] fn test_failed_block() { let block_str = include_str!("../tests/data/failedblock.hex"); let bytes = Vec::::from_hex(block_str).unwrap(); - let _block = encode::deserialize::(&bytes).unwrap(); + let _block = crate::encode::deserialize::(&bytes).unwrap(); } } diff --git a/src/confidential.rs b/src/confidential.rs deleted file mode 100644 index ba6c8543..00000000 --- a/src/confidential.rs +++ /dev/null @@ -1,1431 +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, Hash}; -use crate::hex; -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, Eq, Hash, PartialEq, PartialOrd, Ord)] -pub enum Value { - /// No value - 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 Default for Value { - fn default() -> Self { - Value::Null - } -} - -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, Eq, Hash, PartialEq, PartialOrd, Ord)] -pub enum Asset { - /// No value - 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 Default for Asset { - fn default() -> Self { - Asset::Null - } -} - - -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, Eq, Hash, PartialEq, PartialOrd, Ord)] -pub enum Nonce { - /// No value - 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.iter() { - write!(f, "{:02x}", b)?; - } - Ok(()) - } - Nonce::Confidential(pk) => write!(f, "{:02x}", pk), - } - } -} - -impl Default for Nonce { - fn default() -> Self { - Nonce::Null - } -} - -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) - } -} - -/// Blinding factor used for asset commitments. -#[derive(Copy, Clone, Debug, 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)) - } - - /// 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 hex::FromHex for AssetBlindingFactor { - fn from_byte_iter(iter: I) -> Result - where I: Iterator> + - ExactSizeIterator + - DoubleEndedIterator - { - let slice = <[u8; 32]>::from_byte_iter(iter.rev())?; - // Incorrect Return Error - // See: https://github.com/rust-bitcoin/bitcoin_hashes/issues/124 - let inner = Tweak::from_inner(slice) - .map_err(|_e| hex::Error::InvalidChar(0))?; - Ok(AssetBlindingFactor(inner)) - } -} - -impl fmt::Display for AssetBlindingFactor { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - hex::format_hex_reverse(self.0.as_ref(), f) - } -} - -impl fmt::LowerHex for AssetBlindingFactor { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - hex::format_hex_reverse(self.0.as_ref(), f) - } -} - -impl str::FromStr for AssetBlindingFactor { - type Err = encode::Error; - - fn from_str(s: &str) -> Result { - Ok(hex::FromHex::from_hex(s)?) - } -} - -#[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 { - use crate::hex::FromHex; - - 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) { - AssetBlindingFactor::from_hex(hex).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, - { - AssetBlindingFactor::from_hex(v).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, - { - if v.len() != 32 { - Err(E::invalid_length(v.len(), &stringify!($len))) - } else { - let mut ret = [0; 32]; - ret.copy_from_slice(v); - let inner = Tweak::from_inner(ret).map_err(E::custom)?; - Ok(AssetBlindingFactor(inner)) - } - } - } - - d.deserialize_bytes(BytesVisitor) - } - } -} - -/// Blinding factor used for value commitments. -#[derive(Copy, Clone, Debug, 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)) - } - - /// 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 hex::FromHex for ValueBlindingFactor { - fn from_byte_iter(iter: I) -> Result - where I: Iterator> + - ExactSizeIterator + - DoubleEndedIterator - { - let slice = <[u8; 32]>::from_byte_iter(iter.rev())?; - // Incorrect Return Error - // See: https://github.com/rust-bitcoin/bitcoin_hashes/issues/124 - let inner = Tweak::from_inner(slice) - .map_err(|_e| hex::Error::InvalidChar(0))?; - Ok(ValueBlindingFactor(inner)) - } -} - -impl fmt::Display for ValueBlindingFactor { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - hex::format_hex_reverse(self.0.as_ref(), f) - } -} - -impl fmt::LowerHex for ValueBlindingFactor { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - hex::format_hex_reverse(self.0.as_ref(), f) - } -} - -impl str::FromStr for ValueBlindingFactor { - type Err = encode::Error; - - fn from_str(s: &str) -> Result { - Ok(hex::FromHex::from_hex(s)?) - } -} - -#[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 { - use crate::hex::FromHex; - - 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) { - ValueBlindingFactor::from_hex(hex).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, - { - ValueBlindingFactor::from_hex(v).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, - { - if v.len() != 32 { - Err(E::invalid_length(v.len(), &stringify!($len))) - } else { - let mut ret = [0; 32]; - ret.copy_from_slice(v); - let inner = Tweak::from_inner(ret).map_err(E::custom)?; - Ok(ValueBlindingFactor(inner)) - } - } - } - - d.deserialize_bytes(BytesVisitor) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::hashes::sha256; - - #[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_inner(sha256::Midstate::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(63601271583539200), - 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/dynafed.rs b/src/dynafed.rs deleted file mode 100644 index 2c402825..00000000 --- a/src/dynafed.rs +++ /dev/null @@ -1,804 +0,0 @@ -// Rust Elements Library -// Written in 2019 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 . -// - -//! Dynamic Federations - -use std::{fmt, io}; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -#[cfg(feature = "serde")] -use serde::ser::{SerializeSeq, SerializeStruct}; - -use crate::encode::{self, Encodable, Decodable}; -use crate::hashes::{Hash, sha256, sha256d}; -use crate::Script; - -/// ad-hoc struct to fmt in hex -struct HexBytes<'a>(&'a [u8]); -impl fmt::Display for HexBytes<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - crate::hex::format_hex(self.0, f) - } -} -impl fmt::Debug for HexBytes<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self, f) - } -} -#[cfg(feature = "serde")] -impl Serialize for HexBytes<'_> { - fn serialize(&self, s: S) -> Result { - if s.is_human_readable() { - s.collect_str(self) - } else { - s.serialize_bytes(self.0) - } - } -} - -/// ad-hoc struct to fmt in hex -struct HexBytesArray<'a>(&'a [Vec]); -impl fmt::Display for HexBytesArray<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "[")?; - for (i, e) in self.0.iter().enumerate() { - if i != 0 { - write!(f, ", ")?; - } - crate::hex::format_hex(&e[..], f)?; - } - write!(f, "]") - } -} -impl fmt::Debug for HexBytesArray<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self, f) - } -} -#[cfg(feature = "serde")] -impl Serialize for HexBytesArray<'_> { - fn serialize(&self, s: S) -> Result { - let mut seq = s.serialize_seq(Some(self.0.len()))?; - for b in self.0 { - seq.serialize_element(&HexBytes(&b[..]))?; - } - seq.end() - } -} - -/// Full dynafed parameters with all fields. -#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)] -pub struct FullParams { - /// "scriptPubKey" used for block signing - signblockscript: Script, - /// Maximum, in bytes, of the size of a blocksigning witness - signblock_witness_limit: u32, - /// Untweaked `scriptPubKey` used for pegins - fedpeg_program: bitcoin::ScriptBuf, - /// For v0 fedpeg programs, the witness script of the untweaked - /// pegin address. For future versions, this data has no defined - /// meaning and will be considered "anyone can spend". - fedpegscript: Vec, - /// "Extension space" used by Liquid for PAK key entries - extension_space: Vec>, -} - -impl FullParams { - /// Construct a set of FullParams - pub fn new( - signblockscript: Script, - signblock_witness_limit: u32, - fedpeg_program: bitcoin::ScriptBuf, - fedpegscript: Vec, - extension_space: Vec>, - ) -> Self { - Self { - signblockscript, - signblock_witness_limit, - fedpeg_program, - fedpegscript, - extension_space, - } - } - - /// Return the `extra root` of this params. - /// The extra root commits to the consensus parameters unrelated to - /// blocksigning: `fedpeg_program`, `fedpegscript` and `extension_space`. - fn extra_root(&self) -> sha256::Midstate { - fn serialize_hash(obj: &E) -> sha256d::Hash { - let mut engine = sha256d::Hash::engine(); - obj.consensus_encode(&mut engine).expect("engines don't error"); - sha256d::Hash::from_engine(engine) - } - - let leaves = [ - serialize_hash(&self.fedpeg_program).to_byte_array(), - serialize_hash(&self.fedpegscript).to_byte_array(), - serialize_hash(&self.extension_space).to_byte_array(), - ]; - crate::fast_merkle_root::fast_merkle_root(&leaves[..]) - } - - /// Calculate the root of this [FullParams]. - pub fn calculate_root(&self) -> sha256::Midstate { - fn serialize_hash(obj: &E) -> sha256d::Hash { - let mut engine = sha256d::Hash::engine(); - obj.consensus_encode(&mut engine).expect("engines don't error"); - sha256d::Hash::from_engine(engine) - } - - let leaves = [ - serialize_hash(&self.signblockscript).to_byte_array(), - serialize_hash(&self.signblock_witness_limit).to_byte_array(), - ]; - let compact_root = crate::fast_merkle_root::fast_merkle_root(&leaves[..]); - - let leaves = [ - compact_root.to_byte_array(), - self.extra_root().to_byte_array(), - ]; - crate::fast_merkle_root::fast_merkle_root(&leaves[..]) - } - - /// Turns paramers into compact parameters. - /// This returns self for compact params and [None] for null ones. - pub fn into_compact(self) -> Params { - Params::Compact { - elided_root: self.extra_root(), - signblockscript: self.signblockscript, - signblock_witness_limit: self.signblock_witness_limit, - } - } - - /// Format for [fmt::Debug]. - fn fmt_debug(&self, f: &mut fmt::Formatter, name: &'static str) -> fmt::Result { - let mut s = f.debug_struct(name); - s.field("signblockscript", &HexBytes(&self.signblockscript[..])); - s.field("signblock_witness_limit", &self.signblock_witness_limit); - s.field("fedpeg_program", &HexBytes(self.fedpeg_program.as_ref())); - s.field("fedpegscript", &HexBytes(&self.fedpegscript[..])); - s.field("extension_space", &HexBytesArray(&self.extension_space)); - s.finish() - } - - - #[cfg(feature = "serde")] - fn serde_serialize(&self, s: S, name: &'static str) -> Result { - let mut st = s.serialize_struct(name, 5)?; - st.serialize_field("signblockscript", &self.signblockscript)?; - st.serialize_field("signblock_witness_limit", &self.signblock_witness_limit)?; - st.serialize_field("fedpeg_program", &self.fedpeg_program)?; - st.serialize_field("fedpegscript", &HexBytes(&self.fedpegscript))?; - st.serialize_field("extension_space", &HexBytesArray(&self.extension_space))?; - st.end() - } -} - -impl fmt::Debug for FullParams { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.fmt_debug(f, "FullParams") - } -} - -impl Encodable for FullParams { - fn consensus_encode(&self, mut s: S) -> Result { - let ret = Encodable::consensus_encode(&self.signblockscript, &mut s)? + - Encodable::consensus_encode(&self.signblock_witness_limit, &mut s)? + - Encodable::consensus_encode(&self.fedpeg_program, &mut s)? + - Encodable::consensus_encode(&self.fedpegscript, &mut s)? + - Encodable::consensus_encode(&self.extension_space, &mut s)?; - Ok(ret) - } -} - -impl Decodable for FullParams { - fn consensus_decode(mut d: D) -> Result { - Ok(FullParams { - signblockscript: Decodable::consensus_decode(&mut d)?, - signblock_witness_limit: Decodable::consensus_decode(&mut d)?, - fedpeg_program: Decodable::consensus_decode(&mut d)?, - fedpegscript: Decodable::consensus_decode(&mut d)?, - extension_space: Decodable::consensus_decode(&mut d)?, - }) - } -} - -/// Dynamic federations parameters, as encoded in a block header -#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)] -pub enum Params { - /// Null entry, used to signal "no vote" as a proposal - Null, - /// Compact params where the fedpeg data and extension space - /// are not included, and are assumed to be equal to the values - /// from the previous block - Compact { - /// "scriptPubKey" used for block signing - signblockscript: Script, - /// Maximum, in bytes, of the size of a blocksigning witness - signblock_witness_limit: u32, - /// Merkle root of extra data - elided_root: sha256::Midstate, - }, - /// Full dynamic federations parameters - Full(FullParams), -} - -impl fmt::Debug for Params { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Params::Null => write!(f, "Null"), - Params::Compact { signblockscript, signblock_witness_limit, elided_root } => { - let mut s = f.debug_struct("Compact"); - s.field("signblockscript", &HexBytes(&signblockscript[..])); - s.field("signblock_witness_limit", signblock_witness_limit); - s.field("elided_root", elided_root); - s.finish() - } - Params::Full(ref full) => full.fmt_debug(f, "Full"), - } - } -} - -impl Params { - /// Check whether this is [Params::Null]. - pub fn is_null(&self) -> bool { - match *self { - Params::Null => true, - Params::Compact { .. } => false, - Params::Full(..) => false, - } - } - - /// Check whether this is [Params::Compact]. - pub fn is_compact(&self) -> bool { - match *self { - Params::Null => false, - Params::Compact { .. } => true, - Params::Full(..) => false, - } - } - - /// Check whether this is [Params::Full]. - pub fn is_full(&self) -> bool { - match *self { - Params::Null => false, - Params::Compact { .. } => false, - Params::Full(..) => true, - } - } - - /// Get the signblockscript. Is [None] for [Params::Null] params. - pub fn signblockscript(&self) -> Option<&Script> { - match *self { - Params::Null => None, - Params::Compact { ref signblockscript, ..} => Some(signblockscript), - Params::Full(ref f) => Some(&f.signblockscript), - } - } - - /// Get the signblock_witness_limit. Is [None] for [Params::Null] params. - pub fn signblock_witness_limit(&self) -> Option { - match *self { - Params::Null => None, - Params::Compact { signblock_witness_limit, ..} => Some(signblock_witness_limit), - Params::Full(ref f) => Some(f.signblock_witness_limit), - } - } - - /// Get the fedpeg_program. Is [None] for non-[Params::Full] params. - pub fn fedpeg_program(&self) -> Option<&bitcoin::ScriptBuf> { - match *self { - Params::Null => None, - Params::Compact { .. } => None, - Params::Full(ref f) => Some(&f.fedpeg_program), - } - } - - /// Get the fedpegscript. Is [None] for non-[Params::Full] params. - pub fn fedpegscript(&self) -> Option<&Vec> { - match *self { - Params::Null => None, - Params::Compact { .. } => None, - Params::Full(ref f) => Some(&f.fedpegscript), - } - } - - /// Get the extension_space. Is [None] for non-[Params::Full] params. - pub fn extension_space(&self) -> Option<&Vec>> { - match *self { - Params::Null => None, - Params::Compact { .. } => None, - Params::Full(ref f) => Some(&f.extension_space), - } - } - - /// Get the elided_root. Is [None] for non-[Params::Compact] params. - pub fn elided_root(&self) -> Option<&sha256::Midstate> { - match *self { - Params::Null => None, - Params::Compact { ref elided_root, ..} => Some(elided_root), - Params::Full(..) => None, - } - } - - /// Return the `extra root` of this params. - /// The extra root commits to the consensus parameters unrelated to - /// blocksigning: `fedpeg_program`, `fedpegscript` and `extension_space`. - fn extra_root(&self) -> sha256::Midstate { - match *self { - Params::Null => sha256::Midstate::from_byte_array([0u8; 32]), - Params::Compact { ref elided_root, .. } => *elided_root, - Params::Full(ref f) => f.extra_root(), - } - } - - /// Calculate the root of this [Params]. - pub fn calculate_root(&self) -> sha256::Midstate { - fn serialize_hash(obj: &E) -> sha256d::Hash { - let mut engine = sha256d::Hash::engine(); - obj.consensus_encode(&mut engine).expect("engines don't error"); - sha256d::Hash::from_engine(engine) - } - - if self.is_null() { - return sha256::Midstate::from_byte_array([0u8; 32]); - } - - let leaves = [ - serialize_hash(self.signblockscript().unwrap()).to_byte_array(), - serialize_hash(&self.signblock_witness_limit().unwrap()).to_byte_array(), - ]; - let compact_root = crate::fast_merkle_root::fast_merkle_root(&leaves[..]); - - let leaves = [ - compact_root.to_byte_array(), - self.extra_root().to_byte_array(), - ]; - crate::fast_merkle_root::fast_merkle_root(&leaves[..]) - } - - /// Get the full params when this params are full. - pub fn full(&self) -> Option<&FullParams> { - match self { - Params::Null => None, - Params::Compact { .. } => None, - Params::Full(ref f) => Some(f), - } - } - - /// Convert into the full params when this params are full. - pub fn into_full(self) -> Option { - match self { - Params::Null => None, - Params::Compact { .. } => None, - Params::Full(f) => Some(f), - } - } - - /// Turns paramers into compact parameters. - /// This returns self for compact params and [None] for null ones. - pub fn into_compact(self) -> Option { - match self { - Params::Null => None, - s @ Params::Compact { .. } => Some(s), - Params::Full(f) => Some(f.into_compact()), - } - } -} - -impl Default for Params { - fn default() -> Params { - Params::Null - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for Params { - fn deserialize>(d: D) -> Result { - use serde::de; - - enum Enum { - Unknown, - SignblockScript, - SignblockWitnessLimit, - ElidedRoot, - FedpegProgram, - FedpegScript, - ExtSpace, - } - struct EnumVisitor; - - impl de::Visitor<'_> for EnumVisitor { - type Value = Enum; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("a field name") - } - - fn visit_str(self, v: &str) -> Result { - match v { - "signblockscript" => Ok(Enum::SignblockScript), - "signblock_witness_limit" => Ok(Enum::SignblockWitnessLimit), - "elided_root" => Ok(Enum::ElidedRoot), - "fedpeg_program" => Ok(Enum::FedpegProgram), - "fedpegscript" => Ok(Enum::FedpegScript), - "extension_space" => Ok(Enum::ExtSpace), - _ => Ok(Enum::Unknown), - } - } - } - - impl<'de> Deserialize<'de> for Enum { - fn deserialize>(d: D) -> Result { - d.deserialize_str(EnumVisitor) - } - } - - struct Visitor; - impl<'de> de::Visitor<'de> for Visitor { - type Value = Params; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("block header extra data") - } - - fn visit_map(self, mut map: A) -> Result - where - A: de::MapAccess<'de>, - { - /// Utility type to parse bytes from either hex or array notation. - struct HexBytes(Vec); - impl<'de> Deserialize<'de> for HexBytes { - fn deserialize>(d: D) -> Result { - struct Visitor; - impl<'de> de::Visitor<'de> for Visitor { - type Value = HexBytes; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("bytes in either hex or array format") - } - - fn visit_str(self, v: &str) -> Result { - use crate::hex::FromHex; - - Ok(HexBytes(FromHex::from_hex(v).map_err(E::custom)?)) - } - - fn visit_bytes(self, v: &[u8]) -> Result { - Ok(HexBytes(v.to_vec())) - } - - fn visit_seq(self, mut seq: A) -> Result where - A: de::SeqAccess<'de>, - { - let mut ret = if let Some(l) = seq.size_hint() { - Vec::with_capacity(l) - } else { - Vec::new() - }; - - while let Some(e) = seq.next_element()? { - ret.push(e); - } - Ok(HexBytes(ret)) - } - } - - d.deserialize_any(Visitor) - } - } - - let mut signblockscript = None; - let mut signblock_witness_limit = None; - let mut elided_root = None; - let mut fedpeg_program = None; - let mut fedpegscript: Option = None; - let mut extension_space: Option> = None; - - loop { - match map.next_key::()? { - Some(Enum::Unknown) => { - map.next_value::()?; - }, - Some(Enum::SignblockScript) => { - signblockscript = Some(map.next_value()?); - }, - Some(Enum::SignblockWitnessLimit) => { - signblock_witness_limit = Some(map.next_value()?); - }, - Some(Enum::ElidedRoot) => { - elided_root = Some(map.next_value()?); - }, - Some(Enum::FedpegProgram) => { - fedpeg_program = Some(map.next_value()?); - }, - Some(Enum::FedpegScript) => { - fedpegscript = Some(map.next_value()?); - }, - Some(Enum::ExtSpace) => { - extension_space = Some(map.next_value()?); - }, - None => { break; } - } - } - - match ( - signblockscript, - signblock_witness_limit, - elided_root, - fedpeg_program, - fedpegscript, - extension_space, - ) { - ( - Some(signblockscript), - Some(signblock_witness_limit), - _, - Some(fedpeg_program), - Some(HexBytes(fedpegscript)), - Some(extension_space), - ) => Ok(Params::Full(FullParams { - signblockscript, - signblock_witness_limit, - fedpeg_program, - fedpegscript, - extension_space: extension_space.into_iter().map(|h| h.0).collect(), - })), - ( - Some(signblockscript), - Some(signblock_witness_limit), - Some(elided_root), - _, - _, - _ - ) => Ok(Params::Compact { - signblockscript, - signblock_witness_limit, - elided_root, - }), - // We should probably be stricter about errors here - _ => Ok(Params::Null), - } - } - } - - static FIELDS: &[&str] = &[ - "signblockscript", - "signblock_witness_limit", - "fedpeg_program", - "fedpegscript", - "extension_space", - "elided_root", - ]; - d.deserialize_struct("Params", FIELDS, Visitor) - } -} - -#[cfg(feature = "serde")] -impl Serialize for Params { - fn serialize(&self, s: S) -> Result { - match *self { - Params::Null => { - let st = s.serialize_struct("Params", 0)?; - st.end() - }, - Params::Compact { - ref signblockscript, - ref signblock_witness_limit, - ref elided_root, - } => { - let mut st = s.serialize_struct("Params", 3)?; - st.serialize_field("signblockscript", signblockscript)?; - st.serialize_field("signblock_witness_limit", signblock_witness_limit)?; - st.serialize_field("elided_root", elided_root)?; - st.end() - }, - Params::Full(ref full) => full.serde_serialize(s, "Params"), - } - } -} - -impl Encodable for Params { - fn consensus_encode(&self, mut s: S) -> Result { - Ok(match *self { - Params::Null => Encodable::consensus_encode(&0u8, &mut s)?, - Params::Compact { - ref signblockscript, - ref signblock_witness_limit, - ref elided_root, - } => { - Encodable::consensus_encode(&1u8, &mut s)? + - Encodable::consensus_encode(signblockscript, &mut s)? + - Encodable::consensus_encode(signblock_witness_limit, &mut s)? + - Encodable::consensus_encode(&elided_root.to_byte_array(), &mut s)? - }, - Params::Full(ref f) => { - Encodable::consensus_encode(&2u8, &mut s)? + - Encodable::consensus_encode(f, &mut s)? - }, - }) - } -} - -impl Decodable for Params { - fn consensus_decode(mut d: D) -> Result { - let ser_type: u8 = Decodable::consensus_decode(&mut d)?; - match ser_type { - 0 => Ok(Params::Null), - 1 => Ok(Params::Compact { - signblockscript: Decodable::consensus_decode(&mut d)?, - signblock_witness_limit: Decodable::consensus_decode(&mut d)?, - elided_root: sha256::Midstate::from_byte_array(Decodable::consensus_decode(&mut d)?), - }), - 2 => Ok(Params::Full(Decodable::consensus_decode(&mut d)?)), - _ => Err(encode::Error::ParseFailed( - "bad serialize type for dynafed parameters" - )), - } - } -} - -#[cfg(test)] -mod tests { - use std::fmt::{self, Write}; - - use crate::hashes::sha256; - use crate::hex::ToHex; - use crate::{BlockHash, TxMerkleNode}; - - use super::*; - - #[test] - fn test_param_roots() { - // Taken from the following Elements Core test: - - // CScript signblockscript(opcodetype(1)); - // uint32_t signblock_wl(2); - // CScript fp_program(opcodetype(3)); - // CScript fp_script(opcodetype(4)); - // std::vector> ext{ {5, 6}, {7} }; - // - // DynaFedParamEntry compact_entry = DynaFedParamEntry(signblockscript, signblock_wl); - // BOOST_CHECK_EQUAL( - // compact_entry.CalculateRoot().GetHex(), - // "dff5f3793abc06a6d75e80fe3cfd47406f732fa4ec9305960ae2a229222a1ad5" - // ); - // - // DynaFedParamEntry full_entry = - // DynaFedParamEntry(signblockscript, signblock_wl, fp_program, fp_script, ext); - // BOOST_CHECK_EQUAL( - // full_entry.CalculateRoot().GetHex(), - // "175be2087ba7cc0e33348bef493bd3e34f31f64bf9226e5881ab310dafa432ff" - // ); - // - // DynaFedParams params = DynaFedParams(compact_entry, full_entry); - // BOOST_CHECK_EQUAL( - // params.CalculateRoot().GetHex(), - // "e56cf79487952dfa85fe6a85829600adc19714ba6ab1157fdff02b25ae60cee2" - // ); - - let signblockscript: Script = vec![1].into(); - let signblock_wl = 2; - let fp_program: bitcoin::ScriptBuf = vec![3].into(); - let fp_script = vec![4]; - let ext = vec![vec![5, 6], vec![7]]; - - let compact_entry = Params::Compact { - signblockscript: signblockscript.clone(), - signblock_witness_limit: signblock_wl, - elided_root: sha256::Midstate::from_byte_array([0; 32]), - }; - assert_eq!( - compact_entry.calculate_root().to_hex(), - "f98f149fd11da6fbe26d0ee53cadd28372fa9eed2cb7080f41da7ca311531777" - ); - - let full_entry = Params::Full(FullParams::new( - signblockscript, - signblock_wl, - fp_program, - fp_script, - ext, - )); - assert_eq!( - full_entry.calculate_root().to_hex(), - "8eb1b83cce69a3d8b0bfb7fbe77ae8f1d24b57a9cae047b8c0aba084ad878249" - ); - - let header = crate::block::BlockHeader{ - ext: crate::block::ExtData::Dynafed { - current: compact_entry, - proposed: full_entry, - signblock_witness: vec![], - }, - version: Default::default(), - prev_blockhash: BlockHash::all_zeros(), - merkle_root: TxMerkleNode::all_zeros(), - time: Default::default(), - height: Default::default(), - }; - assert_eq!( - header.calculate_dynafed_params_root().unwrap().to_hex(), - "113160f76dc17fe367a2def79aefe06feeea9c795310c9e88aeedc23e145982e" - ); - } - - fn to_debug_string(o: &O) -> String { - let mut s = String::new(); - write!(&mut s, "{:?}", o).unwrap(); - s - } - - #[test] - fn into_compact_test() { - let full = FullParams { - signblockscript: vec![0x01, 0x02].into(), - signblock_witness_limit: 3, - fedpeg_program: vec![0x04, 0x05].into(), - fedpegscript: vec![0x06, 0x07], - extension_space: vec![vec![0x08, 0x09], vec![0x0a]], - }; - let extra_root = full.extra_root(); - let root = full.calculate_root(); - let params = Params::Full(full.clone()); - assert_eq!(params.full(), Some(&full)); - assert_eq!(params.clone().into_full(), Some(full.clone())); - assert_eq!(params.extra_root(), extra_root); - assert_eq!(params.calculate_root(), root); - - assert_eq!( - to_debug_string(¶ms), - "Full { signblockscript: 0102, signblock_witness_limit: 3, fedpeg_program: 0405, fedpegscript: 0607, extension_space: [0809, 0a] }", - ); - - let compact = params.into_compact().unwrap(); - assert_eq!( - to_debug_string(&compact), - "Compact { signblockscript: 0102, signblock_witness_limit: 3, elided_root: 0xc3058c822b22a13bb7c47cf50d3f3c7817e7d9075ff55a7d16c85b9673e7e553 }", - ); - assert_eq!(compact.calculate_root(), full.calculate_root()); - assert_eq!(compact.elided_root(), Some(&extra_root)); - assert_eq!(compact.extra_root(), extra_root); - } - - #[cfg(feature = "serde")] - #[test] - fn test_serde_roundtrip() { - use serde_json; - - let full = Params::Full(FullParams { - signblockscript: vec![0x01, 0x02].into(), - signblock_witness_limit: 3, - fedpeg_program: vec![0x04, 0x05].into(), - fedpegscript: vec![0x06, 0x07], - extension_space: vec![vec![0x08, 0x09], vec![0x0a]], - }); - let encoded = serde_json::to_string(&full).unwrap(); - let decoded: Params = serde_json::from_str(&encoded).unwrap(); - assert_eq!(full, decoded); - - // test old encoded format - let old_encoded = { - let s1 = encoded.replace("\"0607\"", "[6,7]"); - assert_ne!(s1, encoded); - let s2 = s1.replace("\"0809\",\"0a\"", "[8,9],[10]"); - assert_ne!(s2, s1); - s2 - }; - assert_ne!(old_encoded, encoded); - let decoded: Params = serde_json::from_str(&old_encoded).unwrap(); - assert_eq!(full, decoded); - } -} diff --git a/src/encode.rs b/src/encode.rs deleted file mode 100644 index 08735f10..00000000 --- a/src/encode.rs +++ /dev/null @@ -1,511 +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 . -// - -//! Consensus-encodable types -//! - -use std::io::Cursor; -use std::{error, fmt, io, mem}; - -use bitcoin::ScriptBuf; -use secp256k1_zkp::{self, RangeProof, SurjectionProof, Tweak}; - -use crate::hashes::{sha256, Hash}; -use crate::pset; -use crate::transaction::{Transaction, TxIn, TxOut}; - -pub use bitcoin::{self, consensus::encode::MAX_VEC_SIZE}; - -use crate::taproot::TapLeafHash; - -/// Encoding error -#[derive(Debug)] -pub enum Error { - /// And I/O error - Io(io::Error), - /// A Bitcoin encoding error. - Bitcoin(bitcoin::consensus::encode::Error), - /// Tried to allocate an oversized vector - OversizedVectorAllocation { - /// The capacity requested - requested: usize, - /// The maximum capacity - max: usize, - }, - /// Parsing error - ParseFailed(&'static str), - /// We unexpectedly hit the end of the buffer - UnexpectedEOF, - /// Invalid prefix for the confidential type. - InvalidConfidentialPrefix(u8), - /// Parsing within libsecp256k1 failed - Secp256k1(secp256k1_zkp::UpstreamError), - /// Parsing within libsecp256k1-zkp failed - Secp256k1zkp(secp256k1_zkp::Error), - /// Pset related Errors - PsetError(pset::Error), - /// Hex parsing errors - HexError(crate::hex::Error), - /// Got a time-based locktime when expecting a height-based one, or vice-versa - BadLockTime(crate::LockTime), - /// VarInt was encoded in a non-minimal way. - NonMinimalVarInt, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Io(ref e) => write!(f, "I/O error: {}", e), - Error::Bitcoin(ref e) => write!(f, "a Bitcoin type encoding error: {}", e), - Error::OversizedVectorAllocation { - requested: ref r, - max: ref m, - } => write!( - f, - "oversized vector allocation: requested {}, maximum {}", - r, m - ), - Error::ParseFailed(ref e) => write!(f, "parse failed: {}", e), - Error::UnexpectedEOF => write!(f, "unexpected EOF"), - Error::InvalidConfidentialPrefix(p) => { - write!(f, "invalid confidential prefix: 0x{:02x}", p) - } - Error::Secp256k1(ref e) => write!(f, "{}", e), - Error::Secp256k1zkp(ref e) => write!(f, "{}", e), - Error::PsetError(ref e) => write!(f, "Pset Error: {}", e), - Error::HexError(ref e) => write!(f, "Hex error {}", e), - Error::BadLockTime(ref lt) => write!(f, "Invalid locktime {}", lt), - Error::NonMinimalVarInt => write!(f, "non-minimal varint"), - } - } -} - -impl error::Error for Error { - fn cause(&self) -> Option<&dyn error::Error> { - match *self { - Error::Secp256k1zkp(ref e) => Some(e), - _ => None, - } - } -} -#[doc(hidden)] -impl From for Error { - fn from(e: bitcoin::consensus::encode::Error) -> Error { - Error::Bitcoin(e) - } -} - -#[doc(hidden)] -impl From for Error { - fn from(error: io::Error) -> Self { - Error::Io(error) - } -} - -#[doc(hidden)] -impl From for Error { - fn from(e: pset::Error) -> Error { - Error::PsetError(e) - } -} - -#[doc(hidden)] -impl From for Error { - fn from(e: secp256k1_zkp::UpstreamError) -> Self { - Error::Secp256k1(e) - } -} - -#[doc(hidden)] -impl From for Error { - fn from(e: secp256k1_zkp::Error) -> Self { - Error::Secp256k1zkp(e) - } -} - -#[doc(hidden)] -impl From for Error { - fn from(e: crate::hex::Error) -> Self { - Error::HexError(e) - } -} - -/// Data which can be encoded in a consensus-consistent way -pub trait Encodable { - /// Encode an object with a well-defined format, should only ever error if - /// the underlying `Write` errors. Returns the number of bytes written on - /// success - fn consensus_encode(&self, e: W) -> Result; -} - -/// Data which can be encoded in a consensus-consistent way -pub trait Decodable: Sized { - /// Decode an object with a well-defined format - fn consensus_decode(d: D) -> Result; -} - -/// Encode an object into a vector -pub fn serialize(data: &T) -> Vec { - let mut encoder = Cursor::new(vec![]); - data.consensus_encode(&mut encoder).unwrap(); - encoder.into_inner() -} - -/// Encode an object into a hex-encoded string -pub fn serialize_hex(data: &T) -> String { - crate::hex::ToHex::to_hex(&serialize(data)[..]) -} - -/// Deserialize an object from a vector, will error if said deserialization -/// doesn't consume the entire vector. -pub fn deserialize(data: &[u8]) -> Result { - let (rv, consumed) = deserialize_partial(data)?; - - // Fail if data are not consumed entirely. - if consumed == data.len() { - Ok(rv) - } else { - Err(Error::ParseFailed( - "data not consumed entirely when explicitly deserializing", - )) - } -} - -/// Deserialize an object from a vector, but will not report an error if said deserialization -/// doesn't consume the entire vector. -pub fn deserialize_partial(data: &[u8]) -> Result<(T, usize), Error> { - let mut decoder = Cursor::new(data); - let rv = Decodable::consensus_decode(&mut decoder)?; - let consumed = decoder.position() as usize; - - Ok((rv, consumed)) -} - -impl Encodable for sha256::Midstate { - fn consensus_encode(&self, e: W) -> Result { - self.to_byte_array().consensus_encode(e) - } -} - -impl Decodable for sha256::Midstate { - fn consensus_decode(d: D) -> Result { - Ok(Self::from_byte_array(<[u8; 32]>::consensus_decode(d)?)) - } -} - -pub(crate) fn consensus_encode_with_size( - data: &[u8], - mut s: S, -) -> Result { - let vi_len = VarInt(data.len() as u64).consensus_encode(&mut s)?; - s.emit_slice(data)?; - Ok(vi_len + data.len()) -} - -// Specific locktime types (which appear in PSET/PSBT2 but not in rust-bitcoin PSBT) -impl Encodable for crate::locktime::Height { - fn consensus_encode(&self, s: S) -> Result { - crate::LockTime::from(*self).consensus_encode(s) - } -} -impl Decodable for crate::locktime::Height { - fn consensus_decode(d: D) -> Result { - match crate::LockTime::consensus_decode(d)? { - crate::LockTime::Blocks(h) => Ok(h), - x @ crate::LockTime::Seconds(_) => Err(Error::BadLockTime(x)), - } - } -} - -impl Encodable for crate::locktime::Time { - fn consensus_encode(&self, s: S) -> Result { - crate::LockTime::from(*self).consensus_encode(s) - } -} -impl Decodable for crate::locktime::Time { - fn consensus_decode(d: D) -> Result { - match crate::LockTime::consensus_decode(d)? { - crate::LockTime::Seconds(t) => Ok(t), - x @ crate::LockTime::Blocks(_) => Err(Error::BadLockTime(x)), - } - } -} - -/// A variable sized integer. -pub struct VarInt(pub u64); -impl Encodable for VarInt { - fn consensus_encode(&self, mut e: W) -> Result { - Ok(e.emit_varint(self.0)?) - } -} -impl Decodable for VarInt { - fn consensus_decode(mut d: D) -> Result { - Ok(VarInt(d.read_varint()?)) - } -} -impl VarInt { - /// returns the byte size used if this var int is serialized - pub fn size(&self) -> usize { - match self.0 { - 0..=0xFC => 1, - 0xFD..=0xFFFF => 3, - 0x10000..=0xFFFFFFFF => 5, - _ => 9, - } - } -} - -// Primitive types -macro_rules! impl_int { - ($ty:ident, $meth_dec:ident, $meth_enc:ident) => { - impl Encodable for $ty { - fn consensus_encode(&self, mut w: W) -> Result { - w.$meth_enc(*self)?; - Ok(mem::size_of::<$ty>()) - } - } - impl Decodable for $ty { - fn consensus_decode(mut r: R) -> Result { - crate::ReadExt::$meth_dec(&mut r) - } - } - }; -} - -impl_int!(u8, read_u8, emit_u8); -impl_int!(u16, read_u16, emit_u16); -impl_int!(u32, read_u32, emit_u32); -impl_int!(u64, read_u64, emit_u64); - -impl Encodable for bitcoin::ScriptBuf { - fn consensus_encode(&self, w: W) -> Result { - consensus_encode_with_size(self.as_script().as_bytes(), w) - } -} -impl Decodable for bitcoin::ScriptBuf { - fn consensus_decode(d: D) -> Result { - let bytes = Vec::::consensus_decode(d)?; - Ok(ScriptBuf::from_bytes(bytes)) - } -} - -impl Encodable for bitcoin::hashes::sha256d::Hash { - fn consensus_encode(&self, mut w: W) -> Result { - self.as_byte_array().consensus_encode(&mut w) - } -} -impl Decodable for bitcoin::hashes::sha256d::Hash { - fn consensus_decode(d: D) -> Result { - Ok(Self::from_byte_array( - <::Bytes>::consensus_decode(d)?, - )) - } -} - -// Vectors -macro_rules! impl_vec { - ($type: ty) => { - impl Encodable for Vec<$type> { - #[inline] - fn consensus_encode(&self, mut s: S) -> Result { - let mut len = 0; - len += VarInt(self.len() as u64).consensus_encode(&mut s)?; - for c in self.iter() { - len += c.consensus_encode(&mut s)?; - } - Ok(len) - } - } - - impl Decodable for Vec<$type> { - #[inline] - fn consensus_decode(mut d: D) -> Result { - let len = VarInt::consensus_decode(&mut d)?.0; - let byte_size = (len as usize) - .checked_mul(mem::size_of::<$type>()) - .ok_or(self::Error::ParseFailed("Invalid length"))?; - if byte_size > MAX_VEC_SIZE { - return Err(self::Error::OversizedVectorAllocation { - requested: byte_size, - max: MAX_VEC_SIZE, - }); - } - let mut ret = Vec::with_capacity(len as usize); - for _ in 0..len { - ret.push(Decodable::consensus_decode(&mut d)?); - } - Ok(ret) - } - } - }; -} -impl_vec!(TxIn); -impl_vec!(TxOut); -impl_vec!(Transaction); -impl_vec!(TapLeafHash); -impl_vec!(Vec); // Vec> - -macro_rules! impl_array { - ( $size:literal ) => { - impl Encodable for [u8; $size] { - #[inline] - fn consensus_encode( - &self, - mut w: W, - ) -> core::result::Result { - w.emit_slice(&self[..])?; - Ok($size) - } - } - - impl Decodable for [u8; $size] { - #[inline] - fn consensus_decode(mut r: R) -> core::result::Result { - let mut ret = [0; $size]; - r.read_slice(&mut ret)?; - Ok(ret) - } - } - }; -} -impl_array!(4); -impl_array!(32); -impl_array!(33); - -impl Encodable for Box<[u8]> { - fn consensus_encode(&self, mut w: W) -> Result { - consensus_encode_with_size(&self[..], &mut w) - } -} -impl Decodable for Box<[u8]> { - fn consensus_decode(d: D) -> Result { - let v = Vec::::consensus_decode(d)?; - Ok(v.into()) - } -} - -impl Encodable for Vec { - fn consensus_encode(&self, mut w: W) -> Result { - consensus_encode_with_size(&self[..], &mut w) - } -} -impl Decodable for Vec { - fn consensus_decode(mut d: D) -> Result { - let s = VarInt::consensus_decode(&mut d)?.0 as usize; - if s > MAX_VEC_SIZE { - return Err(self::Error::OversizedVectorAllocation { - requested: s, - max: MAX_VEC_SIZE, - }); - } - let mut v = vec![0; s]; - d.read_slice(&mut v)?; - Ok(v) - } -} - -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 { - self.as_ref().consensus_encode(e) - } -} - -impl Decodable for Tweak { - fn consensus_decode(d: D) -> Result { - Ok(Tweak::from_inner(<[u8; 32]>::consensus_decode(d)?)?) - } -} - -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) - } -} - -impl Decodable for sha256::Hash { - fn consensus_decode(d: D) -> Result { - Ok(Self::from_byte_array( - <::Bytes>::consensus_decode(d)?, - )) - } -} - -impl Encodable for TapLeafHash { - fn consensus_encode(&self, s: S) -> Result { - self.to_byte_array().consensus_encode(s) - } -} - -impl Decodable for TapLeafHash { - fn consensus_decode(d: D) -> Result { - Ok(Self::from_byte_array( - <::Bytes>::consensus_decode(d)?, - )) - } -} - -impl_box_option!(RangeProof); -impl_box_option!(SurjectionProof); diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 0d56617f..00000000 --- a/src/error.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! Contains error types and other error handling tools. - -pub use crate::parse::ParseIntError; - -/// Impls std::error::Error for the specified type with appropriate attributes, possibly returning -/// source. -macro_rules! impl_std_error { - // No source available - ($type:ty) => { - impl std::error::Error for $type {} - }; - // Struct with $field as source - ($type:ty, $field:ident) => { - impl std::error::Error for $type { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - Some(&self.$field) - } - } - }; -} -pub(crate) use impl_std_error; - -/// Formats error. If `std` feature is OFF appends error source (delimited by `: `). We do this -/// because `e.source()` is only available in std builds, without this macro the error source is -/// lost for no-std builds. -macro_rules! write_err { - ($writer:expr, $string:literal $(, $args:expr)*; $source:expr) => { - { - let _ = &$source; // Prevents clippy warnings. - write!($writer, $string $(, $args)*) - } - } -} -pub(crate) use write_err; - - diff --git a/src/ext.rs b/src/ext.rs deleted file mode 100644 index b7c1c543..00000000 --- a/src/ext.rs +++ /dev/null @@ -1,194 +0,0 @@ -use std::io; - -use crate::encode; - -/// Extensions of `Write` to encode data as per Bitcoin consensus. -pub trait WriteExt: io::Write { - /// Outputs a 64-bit unsigned integer. - fn emit_u64(&mut self, v: u64) -> Result<(), io::Error>; - /// Outputs a 32-bit unsigned integer. - fn emit_u32(&mut self, v: u32) -> Result<(), io::Error>; - /// Outputs a 16-bit unsigned integer. - fn emit_u16(&mut self, v: u16) -> Result<(), io::Error>; - /// Outputs an 8-bit unsigned integer. - fn emit_u8(&mut self, v: u8) -> Result<(), io::Error>; - - /// Outputs a 64-bit signed integer. - fn emit_i64(&mut self, v: i64) -> Result<(), io::Error>; - /// Outputs a 32-bit signed integer. - fn emit_i32(&mut self, v: i32) -> Result<(), io::Error>; - /// Outputs a 16-bit signed integer. - fn emit_i16(&mut self, v: i16) -> Result<(), io::Error>; - /// Outputs an 8-bit signed integer. - fn emit_i8(&mut self, v: i8) -> Result<(), io::Error>; - - /// Outputs a variable sized integer. - fn emit_varint(&mut self, v: u64) -> Result; - - /// Outputs a boolean. - fn emit_bool(&mut self, v: bool) -> Result<(), io::Error>; - - /// Outputs a byte slice. - fn emit_slice(&mut self, v: &[u8]) -> Result; -} - -/// Extensions of `Read` to decode data as per Bitcoin consensus. -pub trait ReadExt: io::Read { - /// Reads a 64-bit unsigned integer. - fn read_u64(&mut self) -> Result; - /// Reads a 32-bit unsigned integer. - fn read_u32(&mut self) -> Result; - /// Reads a 16-bit unsigned integer. - fn read_u16(&mut self) -> Result; - /// Reads an 8-bit unsigned integer. - fn read_u8(&mut self) -> Result; - - /// Reads a 64-bit signed integer. - fn read_i64(&mut self) -> Result; - /// Reads a 32-bit signed integer. - fn read_i32(&mut self) -> Result; - /// Reads a 16-bit signed integer. - fn read_i16(&mut self) -> Result; - /// Reads an 8-bit signed integer. - fn read_i8(&mut self) -> Result; - - /// Reads a variable sized integer. - fn read_varint(&mut self) -> Result; - - /// Reads a boolean. - fn read_bool(&mut self) -> Result; - - /// Reads a byte slice. - fn read_slice(&mut self, slice: &mut [u8]) -> Result<(), encode::Error>; -} - -macro_rules! encoder_fn { - ($name:ident, $val_type:ty) => { - #[inline] - fn $name(&mut self, v: $val_type) -> core::result::Result<(), io::Error> { - self.write_all(&v.to_le_bytes()) - } - }; -} - -macro_rules! decoder_fn { - ($name:ident, $val_type:ty, $byte_len: expr) => { - #[inline] - fn $name(&mut self) -> core::result::Result<$val_type, encode::Error> { - let mut val = [0; $byte_len]; - self.read_exact(&mut val[..]).map_err(encode::Error::Io)?; - Ok(<$val_type>::from_le_bytes(val)) - } - }; -} - -impl WriteExt for W { - encoder_fn!(emit_u64, u64); - encoder_fn!(emit_u32, u32); - encoder_fn!(emit_u16, u16); - encoder_fn!(emit_i64, i64); - encoder_fn!(emit_i32, i32); - encoder_fn!(emit_i16, i16); - - #[inline] - fn emit_i8(&mut self, v: i8) -> Result<(), io::Error> { - self.write_all(&[v as u8]) - } - #[inline] - fn emit_u8(&mut self, v: u8) -> Result<(), io::Error> { - self.write_all(&[v]) - } - #[inline] - fn emit_bool(&mut self, v: bool) -> Result<(), io::Error> { - self.write_all(&[v as u8]) - } - #[inline] - fn emit_slice(&mut self, v: &[u8]) -> Result { - self.write_all(v)?; - Ok(v.len()) - } - #[inline] - fn emit_varint(&mut self, v: u64) -> Result { - match v { - i @ 0..=0xFC => { - self.emit_u8(i as u8)?; - Ok(1) - } - i @ 0xFD..=0xFFFF => { - self.emit_u8(0xFD)?; - self.emit_u16(i as u16)?; - Ok(3) - } - i @ 0x10000..=0xFFFFFFFF => { - self.emit_u8(0xFE)?; - self.emit_u32(i as u32)?; - Ok(5) - } - i => { - self.emit_u8(0xFF)?; - self.emit_u64(i)?; - Ok(9) - } - } - } -} - -impl ReadExt for R { - decoder_fn!(read_u64, u64, 8); - decoder_fn!(read_u32, u32, 4); - decoder_fn!(read_u16, u16, 2); - decoder_fn!(read_i64, i64, 8); - decoder_fn!(read_i32, i32, 4); - decoder_fn!(read_i16, i16, 2); - - #[inline] - fn read_u8(&mut self) -> Result { - let mut slice = [0u8; 1]; - self.read_exact(&mut slice)?; - Ok(slice[0]) - } - #[inline] - fn read_i8(&mut self) -> Result { - let mut slice = [0u8; 1]; - self.read_exact(&mut slice)?; - Ok(slice[0] as i8) - } - #[inline] - fn read_bool(&mut self) -> Result { - ReadExt::read_i8(self).map(|bit| bit != 0) - } - #[inline] - fn read_slice(&mut self, slice: &mut [u8]) -> Result<(), encode::Error> { - self.read_exact(slice).map_err(encode::Error::Io) - } - #[inline] - fn read_varint(&mut self) -> Result { - match self.read_u8()? { - 0xFF => { - let x = self.read_u64()?; - if x < 0x100000000 { - Err(encode::Error::NonMinimalVarInt) - } else { - Ok(x) - } - } - 0xFE => { - let x = self.read_u32()?; - if x < 0x10000 { - Err(encode::Error::NonMinimalVarInt) - } else { - Ok(x as u64) - } - } - 0xFD => { - let x = self.read_u16()?; - if x < 0xFD { - Err(encode::Error::NonMinimalVarInt) - } else { - Ok(x as u64) - } - } - n => Ok(n as u64), - } - } -} diff --git a/src/fast_merkle_root.rs b/src/fast_merkle_root.rs deleted file mode 100644 index 098bbde1..00000000 --- a/src/fast_merkle_root.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Rust Elements Library -// Written in 2019 by -// The Elements developers -// -// 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 . -// - -use crate::hashes::{sha256, Hash, HashEngine}; - -/// Calculate a single sha256 midstate hash of the given left and right leaves. -#[inline] -fn sha256midstate(left: &[u8], right: &[u8]) -> sha256::Midstate { - let mut engine = sha256::Hash::engine(); - engine.input(left); - engine.input(right); - engine.midstate() -} - -/// Compute the Merkle root of the give hashes using mid-state only. -/// -/// The inputs must be byte slices of length 32. -/// Note that the merkle root calculated with this method is not the same as the -/// one computed by a normal SHA256(d) merkle root. -pub fn fast_merkle_root(leaves: &[[u8; 32]]) -> sha256::Midstate { - let mut result_hash = Default::default(); - // Implementation based on ComputeFastMerkleRoot method in Elements Core. - if leaves.is_empty() { - return result_hash; - } - - // inner is an array of eagerly computed subtree hashes, indexed by tree - // level (0 being the leaves). - // For example, when count is 25 (11001 in binary), inner[4] is the hash of - // the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to - // the last leaf. The other inner entries are undefined. - // - // First process all leaves into 'inner' values. - let mut inner: [sha256::Midstate; 32] = Default::default(); - let mut count: u32 = 0; - while (count as usize) < leaves.len() { - let mut temp_hash = sha256::Midstate::from_byte_array(leaves[count as usize]); - count += 1; - // For each of the lower bits in count that are 0, do 1 step. Each - // corresponds to an inner value that existed before processing the - // current leaf, and each needs a hash to combine it. - let mut level = 0; - while count & (1u32 << level) == 0 { - temp_hash = sha256midstate(&inner[level][..], &temp_hash[..]); - level += 1; - } - // Store the resulting hash at inner position level. - inner[level] = temp_hash; - } - - // Do a final 'sweep' over the rightmost branch of the tree to process - // odd levels, and reduce everything to a single top value. - // Level is the level (counted from the bottom) up to which we've sweeped. - // - // As long as bit number level in count is zero, skip it. It means there - // is nothing left at this level. - let mut level = 0; - while count & (1u32 << level) == 0 { - level += 1; - } - result_hash = inner[level]; - - while count != (1u32 << level) { - // If we reach this point, hash is an inner value that is not the top. - // We combine it with itself (Bitcoin's special rule for odd levels in - // the tree) to produce a higher level one. - - // Increment count to the value it would have if two entries at this - // level had existed and propagate the result upwards accordingly. - count += 1 << level; - level += 1; - while count & (1u32 << level) == 0 { - result_hash = sha256midstate(&inner[level][..], &result_hash[..]); - level += 1; - } - } - // Return result. - result_hash -} - -#[cfg(test)] -mod tests { - use super::fast_merkle_root; - use crate::hashes::sha256; - use std::str::FromStr; - - #[test] - fn test_fast_merkle_root() { - // unit test vectors from Elements Core - let test_leaves = [ - "b66b041650db0f297b53f8d93c0e8706925bf3323f8c59c14a6fac37bfdcd06f", - "99cb2fa68b2294ae133550a9f765fc755d71baa7b24389fed67d1ef3e5cb0255", - "257e1b2fa49dd15724c67bac4df7911d44f6689860aa9f65a881ae0a2f40a303", - "b67b0b9f093fa83d5e44b707ab962502b7ac58630e556951136196e65483bb80", - ]; - - let test_roots = [ - "0000000000000000000000000000000000000000000000000000000000000000", - "b66b041650db0f297b53f8d93c0e8706925bf3323f8c59c14a6fac37bfdcd06f", - "f752938da0cb71c051aabdd5a86658e8d0b7ac00e1c2074202d8d2a79d8a6cf6", - "245d364a28e9ad20d522c4a25ffc6a7369ab182f884e1c7dcd01aa3d32896bd3", - "317d6498574b6ca75ee0368ec3faec75e096e245bdd5f36e8726fa693f775dfc", - ]; - - let mut leaves = vec![]; - for i in 0..4 { - let root = fast_merkle_root(&leaves); - assert_eq!(root, FromStr::from_str(test_roots[i]).unwrap(), "root #{}", i); - leaves.push(sha256::Midstate::from_str(test_leaves[i]).unwrap().to_byte_array()); - } - assert_eq!(fast_merkle_root(&leaves), FromStr::from_str(test_roots[4]).unwrap()); - } -} diff --git a/src/hash_types.rs b/src/hash_types.rs deleted file mode 100644 index 8220dd7f..00000000 --- a/src/hash_types.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Rust Elements Library -// Written in 2020 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 . -// - -//! File defines types for hashes used throughout the library. -//! -//! These types are needed in order -//! to avoid mixing data of the same hash format (like SHA256d) but of different meaning -//! (transaction id, block hash etc). - -use crate::hashes::{hash160, hash_newtype, sha256, sha256d, Hash}; - -macro_rules! impl_hashencode { - ($hashtype:ident) => { - impl $crate::encode::Encodable for $hashtype { - fn consensus_encode( - &self, - w: W, - ) -> Result { - self.0.consensus_encode(w) - } - } - - impl $crate::encode::Decodable for $hashtype { - fn consensus_decode(r: R) -> Result { - Ok(Self::from_byte_array( - <<$hashtype as $crate::hashes::Hash>::Bytes>::consensus_decode(r)?, - )) - } - } - }; -} - -hash_newtype! { - /// An elements transaction ID - pub struct Txid(sha256d::Hash); - /// An elements witness transaction ID - pub struct Wtxid(sha256d::Hash); - /// An elements blockhash - pub struct BlockHash(sha256d::Hash); - - /// "Hash of the transaction according to the signature algorithm" - pub struct Sighash(sha256d::Hash); - - /// A hash of a public key. - pub struct PubkeyHash(hash160::Hash); - /// A hash of Bitcoin Script bytecode. - pub struct ScriptHash(hash160::Hash); - /// SegWit version of a public key hash. - pub struct WPubkeyHash(hash160::Hash); - /// SegWit version of a Bitcoin Script bytecode hash. - pub struct WScriptHash(sha256::Hash); - - /// A hash of the Merkle tree branch or root for transactions - pub struct TxMerkleNode(sha256d::Hash); -} - -impl_hashencode!(Txid); -impl_hashencode!(Wtxid); -impl_hashencode!(Sighash); -impl_hashencode!(BlockHash); -impl_hashencode!(TxMerkleNode); diff --git a/src/hex.rs b/src/hex.rs deleted file mode 100644 index bbca409e..00000000 --- a/src/hex.rs +++ /dev/null @@ -1,420 +0,0 @@ -// Rust Elements Library -// Written in 2023 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 . -// - -//! Hex Encoding and Decoding - -// The rust-bitcoin hex stury is such a mess right now that the most -// straightforward thing is to just reimplement everything here. -// -// This is a copy/paste of the bitcoin_hashes hex module, with the -// std/alloc feature gates, and the benchmarks, removed. -// - -use std::{fmt, io, str}; - -/// Hex decoding error. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Error { - /// Non-hexadecimal character. - InvalidChar(u8), - /// Purported hex string had odd length. - OddLengthString(usize), - /// Tried to parse fixed-length hash from a string with the wrong type (expected, got). - InvalidLength(usize, usize), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::InvalidChar(ch) => write!(f, "invalid hex character {}", ch), - Error::OddLengthString(ell) => write!(f, "odd hex string length {}", ell), - Error::InvalidLength(ell, ell2) => write!(f, "bad hex string length {} (expected {})", ell2, ell), - } - } -} - -/// Trait for objects that can be serialized as hex strings. -pub trait ToHex { - /// Converts to a hexadecimal representation of the object. - fn to_hex(&self) -> String; -} - -/// Trait for objects that can be deserialized from hex strings. -pub trait FromHex: Sized { - /// Produces an object from a byte iterator. - fn from_byte_iter(iter: I) -> Result - where - I: Iterator> + ExactSizeIterator + DoubleEndedIterator; - - /// Produces an object from a hex string. - fn from_hex(s: &str) -> Result { - Self::from_byte_iter(HexIterator::new(s)?) - } -} - -impl ToHex for T { - /// Outputs the hash in hexadecimal form. - fn to_hex(&self) -> String { - format!("{:x}", self) - } -} - -/// Iterator over a hex-encoded string slice which decodes hex and yields bytes. -pub struct HexIterator<'a> { - /// The `Bytes` iterator whose next two bytes will be decoded to yield - /// the next byte. - iter: str::Bytes<'a>, -} - -impl<'a> HexIterator<'a> { - /// Constructs a new `HexIterator` from a string slice. - /// - /// # Errors - /// - /// If the input string is of odd length. - pub fn new(s: &'a str) -> Result, Error> { - if s.len() % 2 != 0 { - Err(Error::OddLengthString(s.len())) - } else { - Ok(HexIterator { iter: s.bytes() }) - } - } -} - -fn chars_to_hex(hi: u8, lo: u8) -> Result { - let hih = (hi as char) - .to_digit(16) - .ok_or(Error::InvalidChar(hi))?; - let loh = (lo as char) - .to_digit(16) - .ok_or(Error::InvalidChar(lo))?; - - let ret = (hih << 4) + loh; - Ok(ret as u8) -} - -impl Iterator for HexIterator<'_> { - type Item = Result; - - fn next(&mut self) -> Option> { - let hi = self.iter.next()?; - let lo = self.iter.next().unwrap(); - Some(chars_to_hex(hi, lo)) - } - - fn size_hint(&self) -> (usize, Option) { - let (min, max) = self.iter.size_hint(); - (min / 2, max.map(|x| x / 2)) - } -} - -impl io::Read for HexIterator<'_> { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let mut bytes_read = 0usize; - for dst in buf { - match self.next() { - Some(Ok(src)) => { - *dst = src; - bytes_read += 1; - }, - _ => break, - } - } - Ok(bytes_read) - } -} - -impl DoubleEndedIterator for HexIterator<'_> { - fn next_back(&mut self) -> Option> { - let lo = self.iter.next_back()?; - let hi = self.iter.next_back().unwrap(); - Some(chars_to_hex(hi, lo)) - } -} - -impl ExactSizeIterator for HexIterator<'_> {} - -/// Outputs hex into an object implementing `fmt::Write`. -/// -/// This is usually more efficient than going through a `String` using [`ToHex`]. -pub fn format_hex(data: &[u8], f: &mut fmt::Formatter) -> fmt::Result { - let prec = f.precision().unwrap_or(2 * data.len()); - let width = f.width().unwrap_or(2 * data.len()); - for _ in (2 * data.len())..width { - f.write_str("0")?; - } - for ch in data.iter().take(prec / 2) { - write!(f, "{:02x}", *ch)?; - } - if prec < 2 * data.len() && prec % 2 == 1 { - write!(f, "{:x}", data[prec / 2] / 16)?; - } - Ok(()) -} - -/// Outputs hex in reverse order. -/// -/// Used for `sha256d::Hash` whose standard hex encoding has the bytes reversed. -pub fn format_hex_reverse(data: &[u8], f: &mut fmt::Formatter) -> fmt::Result { - let prec = f.precision().unwrap_or(2 * data.len()); - let width = f.width().unwrap_or(2 * data.len()); - for _ in (2 * data.len())..width { - f.write_str("0")?; - } - for ch in data.iter().rev().take(prec / 2) { - write!(f, "{:02x}", *ch)?; - } - if prec < 2 * data.len() && prec % 2 == 1 { - write!(f, "{:x}", data[data.len() - 1 - prec / 2] / 16)?; - } - Ok(()) -} - -impl ToHex for [u8] { - fn to_hex(&self) -> String { - use core::fmt::Write; - let mut ret = String::with_capacity(2 * self.len()); - for ch in self { - write!(ret, "{:02x}", ch).expect("writing to string"); - } - ret - } -} - -/// A struct implementing [`io::Write`] that converts what's written to it into -/// a hex String. -/// -/// If you already have the data to be converted in a `Vec` use [`ToHex`] -/// but if you have an encodable object, by using this you avoid the -/// serialization to `Vec` by going directly to `String`. -/// -/// Note that to achieve better performance than [`ToHex`] the struct must be -/// created with the right `capacity` of the final hex string so that the inner -/// `String` doesn't re-allocate. -pub struct HexWriter(String); - -impl HexWriter { - /// Creates a new [`HexWriter`] with the `capacity` of the inner `String` - /// that will contain final hex value. - pub fn new(capacity: usize) -> Self { - HexWriter(String::with_capacity(capacity)) - } - - /// Returns the resulting hex string. - pub fn result(self) -> String { - self.0 - } -} - -impl io::Write for HexWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - use core::fmt::Write; - for ch in buf { - write!(self.0, "{:02x}", ch).expect("writing to string"); - } - Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl FromHex for Vec { - fn from_byte_iter(iter: I) -> Result - where - I: Iterator> + ExactSizeIterator + DoubleEndedIterator, - { - iter.collect() - } -} - -macro_rules! impl_fromhex_array { - ($len:expr) => { - impl FromHex for [u8; $len] { - fn from_byte_iter(iter: I) -> Result - where - I: Iterator> + ExactSizeIterator + DoubleEndedIterator, - { - if iter.len() == $len { - let mut ret = [0; $len]; - for (n, byte) in iter.enumerate() { - ret[n] = byte?; - } - Ok(ret) - } else { - Err(Error::InvalidLength(2 * $len, 2 * iter.len())) - } - } - } - } -} - -impl_fromhex_array!(2); -impl_fromhex_array!(4); -impl_fromhex_array!(6); -impl_fromhex_array!(8); -impl_fromhex_array!(10); -impl_fromhex_array!(12); -impl_fromhex_array!(14); -impl_fromhex_array!(16); -impl_fromhex_array!(20); -impl_fromhex_array!(24); -impl_fromhex_array!(28); -impl_fromhex_array!(32); -impl_fromhex_array!(33); -impl_fromhex_array!(64); -impl_fromhex_array!(65); -impl_fromhex_array!(128); -impl_fromhex_array!(256); -impl_fromhex_array!(384); -impl_fromhex_array!(512); - -#[cfg(test)] -mod tests { - use super::*; - - use core::fmt; - use std::io::Write; - - #[test] - fn hex_roundtrip() { - let expected = "0123456789abcdef"; - let expected_up = "0123456789ABCDEF"; - - let parse: Vec = FromHex::from_hex(expected).expect("parse lowercase string"); - assert_eq!(parse, vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); - let ser = parse.to_hex(); - assert_eq!(ser, expected); - - let parse: Vec = FromHex::from_hex(expected_up).expect("parse uppercase string"); - assert_eq!(parse, vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); - let ser = parse.to_hex(); - assert_eq!(ser, expected); - - let parse: [u8; 8] = FromHex::from_hex(expected_up).expect("parse uppercase string"); - assert_eq!(parse, [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); - let ser = parse.to_hex(); - assert_eq!(ser, expected); - } - - #[test] - fn hex_truncate() { - struct HexBytes(Vec); - impl fmt::LowerHex for HexBytes { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - format_hex(&self.0, f) - } - } - - let bytes = HexBytes(vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - - assert_eq!( - format!("{:x}", bytes), - "0102030405060708090a" - ); - - for i in 0..20 { - assert_eq!( - format!("{:.prec$x}", bytes, prec = i), - &"0102030405060708090a"[0..i] - ); - } - - assert_eq!( - format!("{:25x}", bytes), - "000000102030405060708090a" - ); - assert_eq!( - format!("{:26x}", bytes), - "0000000102030405060708090a" - ); - } - - #[test] - fn hex_truncate_rev() { - struct HexBytes(Vec); - impl fmt::LowerHex for HexBytes { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - format_hex_reverse(&self.0, f) - } - } - - let bytes = HexBytes(vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - - assert_eq!( - format!("{:x}", bytes), - "0a090807060504030201" - ); - - for i in 0..20 { - assert_eq!( - format!("{:.prec$x}", bytes, prec = i), - &"0a090807060504030201"[0..i] - ); - } - - assert_eq!( - format!("{:25x}", bytes), - "000000a090807060504030201" - ); - assert_eq!( - format!("{:26x}", bytes), - "0000000a090807060504030201" - ); - } - - #[test] - fn hex_error() { - let oddlen = "0123456789abcdef0"; - let badchar1 = "Z123456789abcdef"; - let badchar2 = "012Y456789abcdeb"; - let badchar3 = "«23456789abcdef"; - - assert_eq!( - Vec::::from_hex(oddlen), - Err(Error::OddLengthString(17)) - ); - assert_eq!( - <[u8; 4]>::from_hex(oddlen), - Err(Error::OddLengthString(17)) - ); - assert_eq!( - <[u8; 8]>::from_hex(oddlen), - Err(Error::OddLengthString(17)) - ); - assert_eq!( - Vec::::from_hex(badchar1), - Err(Error::InvalidChar(b'Z')) - ); - assert_eq!( - Vec::::from_hex(badchar2), - Err(Error::InvalidChar(b'Y')) - ); - assert_eq!( - Vec::::from_hex(badchar3), - Err(Error::InvalidChar(194)) - ); - } - - - #[test] - fn hex_writer() { - let vec: Vec<_> = (0u8..32).collect(); - let mut writer = HexWriter::new(64); - writer.write_all(&vec[..]).unwrap(); - assert_eq!(vec.to_hex(), writer.result()); - } -} - diff --git a/src/internal_macros.rs b/src/internal_macros.rs index 1936c6c2..2eb41cbd 100644 --- a/src/internal_macros.rs +++ b/src/internal_macros.rs @@ -154,249 +154,6 @@ macro_rules! serde_struct_impl { ) } -macro_rules! serde_string_impl { - ($name:ident, $expecting:expr) => { - #[cfg(feature = "serde")] - impl<'de> $crate::serde::Deserialize<'de> for $name { - fn deserialize(deserializer: D) -> Result<$name, D::Error> - where - D: $crate::serde::de::Deserializer<'de>, - { - use ::std::fmt::{self, Formatter}; - use ::std::str::FromStr; - - struct Visitor; - impl<'de> $crate::serde::de::Visitor<'de> for Visitor { - type Value = $name; - - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - formatter.write_str($expecting) - } - - fn visit_str(self, v: &str) -> Result - where - E: $crate::serde::de::Error, - { - $name::from_str(v).map_err(E::custom) - } - - fn visit_borrowed_str(self, v: &'de str) -> Result - where - E: $crate::serde::de::Error, - { - self.visit_str(v) - } - - fn visit_string(self, v: String) -> Result - where - E: $crate::serde::de::Error, - { - self.visit_str(&v) - } - } - - deserializer.deserialize_str(Visitor) - } - } - - #[cfg(feature = "serde")] - impl<'de> $crate::serde::Serialize for $name { - fn serialize(&self, serializer: S) -> Result - where - S: $crate::serde::Serializer, - { - serializer.collect_str(&self) - } - } - }; -} - -/// A combination of serde_struct_impl and serde_string_impl where string is -/// used for human-readable serialization and struct is used for -/// non-human-readable serialization. -macro_rules! serde_struct_human_string_impl { - ($name:ident, $expecting:expr, $($fe:ident),*) => ( - #[cfg(feature = "serde")] - impl<'de> $crate::serde::Deserialize<'de> for $name { - fn deserialize(deserializer: D) -> Result<$name, D::Error> - where - D: $crate::serde::de::Deserializer<'de>, - { - if deserializer.is_human_readable() { - use ::std::fmt::{self, Formatter}; - use ::std::str::FromStr; - - struct Visitor; - impl<'de> $crate::serde::de::Visitor<'de> for Visitor { - type Value = $name; - - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - formatter.write_str($expecting) - } - - fn visit_str(self, v: &str) -> Result - where - E: $crate::serde::de::Error, - { - $name::from_str(v).map_err(E::custom) - } - - fn visit_borrowed_str(self, v: &'de str) -> Result - where - E: $crate::serde::de::Error, - { - self.visit_str(v) - } - - fn visit_string(self, v: String) -> Result - where - E: $crate::serde::de::Error, - { - self.visit_str(&v) - } - } - - deserializer.deserialize_str(Visitor) - } else { - use ::std::fmt::{self, Formatter}; - use $crate::serde::de::IgnoredAny; - - #[allow(non_camel_case_types)] - enum Enum { Unknown__Field, $($fe),* } - - struct EnumVisitor; - impl<'de> $crate::serde::de::Visitor<'de> for EnumVisitor { - type Value = Enum; - - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - formatter.write_str("a field name") - } - - fn visit_str(self, v: &str) -> Result - where - E: $crate::serde::de::Error, - { - match v { - $( - stringify!($fe) => Ok(Enum::$fe) - ),*, - _ => Ok(Enum::Unknown__Field) - } - } - } - - impl<'de> $crate::serde::Deserialize<'de> for Enum { - fn deserialize(deserializer: D) -> Result - where - D: $crate::serde::de::Deserializer<'de>, - { - deserializer.deserialize_str(EnumVisitor) - } - } - - struct Visitor; - - impl<'de> $crate::serde::de::Visitor<'de> for Visitor { - type Value = $name; - - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - formatter.write_str("a struct") - } - - fn visit_seq(self, mut seq: V) -> Result - where - V: $crate::serde::de::SeqAccess<'de>, - { - use $crate::serde::de::Error; - - let length = 0; - $( - let $fe = seq.next_element()?.ok_or_else(|| { - Error::invalid_length(length, &self) - })?; - #[allow(unused_variables)] - let length = length + 1; - )* - - let ret = $name { - $($fe),* - }; - - Ok(ret) - } - - fn visit_map(self, mut map: A) -> Result - where - A: $crate::serde::de::MapAccess<'de>, - { - use $crate::serde::de::Error; - - $(let mut $fe = None;)* - - loop { - match map.next_key::()? { - Some(Enum::Unknown__Field) => { - map.next_value::()?; - } - $( - Some(Enum::$fe) => { - $fe = Some(map.next_value()?); - } - )* - None => { break; } - } - } - - $( - let $fe = match $fe { - Some(x) => x, - None => return Err(A::Error::missing_field(stringify!($fe))), - }; - )* - - let ret = $name { - $($fe),* - }; - - Ok(ret) - } - } - // end type defs - - static FIELDS: &'static [&'static str] = &[$(stringify!($fe)),*]; - - deserializer.deserialize_struct(stringify!($name), FIELDS, Visitor) - } - } - } - - #[cfg(feature = "serde")] - impl<'de> $crate::serde::Serialize for $name { - fn serialize(&self, serializer: S) -> Result - where - S: $crate::serde::Serializer, - { - if serializer.is_human_readable() { - serializer.collect_str(&self) - } else { - use $crate::serde::ser::SerializeStruct; - - // Only used to get the struct length. - static FIELDS: &'static [&'static str] = &[$(stringify!($fe)),*]; - - let mut st = serializer.serialize_struct(stringify!($name), FIELDS.len())?; - - $( - st.serialize_field(stringify!($fe), &self.$fe)?; - )* - - st.end() - } - } - } - ) -} - #[cfg(test)] macro_rules! hex_deserialize( ($e:expr) => ({ @@ -436,12 +193,3 @@ macro_rules! hex_deserialize( deserialize(&ret).expect("deserialize object") }); ); - -#[cfg(test)] -macro_rules! hex_script( - ($e:expr) => ({ - let v: Vec = crate::hex::FromHex::from_hex($e) - .expect("hex decoding"); - crate::Script::from(v) - }) -); diff --git a/src/issuance.rs b/src/issuance.rs deleted file mode 100644 index de478e7b..00000000 --- a/src/issuance.rs +++ /dev/null @@ -1,372 +0,0 @@ -// Rust Elements Library -// Written in 2019 by -// The Elements developers -// -// 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 . -// - -//! Asset Issuance - -use std::io; -use std::str::FromStr; - -use crate::encode::{self, Encodable, Decodable}; -use crate::hashes::{self, hash_newtype, sha256, sha256d, Hash}; -use crate::fast_merkle_root::fast_merkle_root; -use secp256k1_zkp::Tag; -use crate::transaction::OutPoint; - -/// The zero hash. -const ZERO32: [u8; 32] = [ - 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, -]; -/// The one hash. -const ONE32: [u8; 32] = [ - 1, 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, -]; -/// The two hash. -const TWO32: [u8; 32] = [ - 2, 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, -]; - -hash_newtype!( - /// The hash of an asset contract.", tru) - #[hash_newtype(backward)] - pub struct ContractHash(sha256::Hash); -); - -impl ContractHash { - /// Calculate the contract hash of a JSON contract object. - /// - /// This method does not perform any validation of the contents of the contract. - /// After basic JSON syntax validation, the object is formatted in a standard way to calculate - /// the hash. - #[cfg(feature = "json-contract")] - pub fn from_json_contract(json: &str) -> Result { - // Parsing the JSON into a BTreeMap will recursively order object keys - // lexicographically. This order is respected when we later serialize - // it again. - let ordered: ::std::collections::BTreeMap = - ::serde_json::from_str(json)?; - - let mut engine = ContractHash::engine(); - ::serde_json::to_writer(&mut engine, &ordered).expect("engines don't error"); - Ok(ContractHash::from_engine(engine)) - } -} - -/// An issued asset ID. -#[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)] -pub struct AssetId(sha256::Midstate); - -impl AssetId { - /// The asset ID for L-BTC, Bitcoin on the Liquid network. - pub const LIQUID_BTC: AssetId = AssetId(sha256::Midstate([ - 0x6d, 0x52, 0x1c, 0x38, 0xec, 0x1e, 0xa1, 0x57, - 0x34, 0xae, 0x22, 0xb7, 0xc4, 0x60, 0x64, 0x41, - 0x28, 0x29, 0xc0, 0xd0, 0x57, 0x9f, 0x0a, 0x71, - 0x3d, 0x1c, 0x04, 0xed, 0xe9, 0x79, 0x02, 0x6f, - ])); - - /// Create an [AssetId] from its inner type. - pub const fn from_inner(midstate: sha256::Midstate) -> AssetId { - AssetId(midstate) - } - - /// Convert the [AssetId] into its inner type. - pub fn into_inner(self) -> sha256::Midstate { - self.0 - } - - /// Copies a byte slice into an AssetId object - pub fn from_slice(sl: &[u8]) -> Result { - sha256::Midstate::from_slice(sl).map(AssetId) - } - - /// Generate the asset entropy from the issuance prevout and the contract hash. - pub fn generate_asset_entropy( - prevout: OutPoint, - contract_hash: ContractHash, - ) -> sha256::Midstate { - // E : entropy - // I : prevout - // C : contract - // E = H( H(I) || H(C) ) - let prevout_hash = { - let mut enc = sha256d::Hash::engine(); - prevout.consensus_encode(&mut enc).unwrap(); - sha256d::Hash::from_engine(enc) - }; - fast_merkle_root(&[prevout_hash.to_byte_array(), contract_hash.to_byte_array()]) - } - - /// Calculate the asset ID from the asset entropy. - pub fn from_entropy(entropy: sha256::Midstate) -> AssetId { - // H_a : asset tag - // E : entropy - // H_a = H( E || 0 ) - AssetId(fast_merkle_root(&[entropy.to_byte_array(), ZERO32])) - } - - /// Computes the asset ID when issuing asset from issuing input and contract hash - pub fn new_issuance(prevout: OutPoint, contract_hash: ContractHash) -> Self { - let entropy = AssetId::generate_asset_entropy(prevout, contract_hash); - AssetId::from_entropy(entropy) - } - - /// Computes the re-issuance token from input and contract hash - pub fn new_reissuance_token(prevout: OutPoint, contract_hash: ContractHash, confidential: bool) -> Self { - let entropy = AssetId::generate_asset_entropy(prevout, contract_hash); - AssetId::reissuance_token_from_entropy(entropy, confidential) - } - - /// Calculate the reissuance token asset ID from the asset entropy. - pub fn reissuance_token_from_entropy(entropy: sha256::Midstate, confidential: bool) -> AssetId { - // H_a : asset reissuance tag - // E : entropy - // if not fConfidential: - // H_a = H( E || 1 ) - // else - // H_a = H( E || 2 ) - let second = match confidential { - false => ONE32, - true => TWO32, - }; - AssetId(fast_merkle_root(&[entropy.to_byte_array(), second])) - } - - /// Convert an asset into [Tag] - pub fn into_tag(self) -> Tag { - self.0.to_byte_array().into() - } -} - -impl ::std::fmt::Display for AssetId { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - ::std::fmt::Display::fmt(&self.0, f) - } -} - -impl ::std::fmt::Debug for AssetId { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - ::std::fmt::Display::fmt(&self, f) - } -} - -impl ::std::fmt::LowerHex for AssetId { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - ::std::fmt::LowerHex::fmt(&self.0, f) - } -} - -impl FromStr for AssetId { - type Err = crate::hashes::hex::HexToArrayError; - fn from_str(s: &str) -> Result { - sha256::Midstate::from_str(s).map(AssetId) - } -} - -impl Encodable for AssetId { - fn consensus_encode(&self, e: W) -> Result { - self.0.consensus_encode(e) - } -} - -impl Decodable for AssetId { - fn consensus_decode(d: D) -> Result { - Ok(Self::from_inner(sha256::Midstate::consensus_decode(d)?)) - } -} - -#[cfg(feature = "serde")] -impl ::serde::Serialize for AssetId { - fn serialize(&self, s: S) -> Result { - use crate::hex::ToHex; - if s.is_human_readable() { - s.serialize_str(&self.to_hex()) - } else { - s.serialize_bytes(&self.0[..]) - } - } -} - -#[cfg(feature = "serde")] -impl<'de> ::serde::Deserialize<'de> for AssetId { - fn deserialize>(d: D) -> Result { - if d.is_human_readable() { - struct HexVisitor; - - impl ::serde::de::Visitor<'_> for HexVisitor { - type Value = AssetId; - - 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) { - AssetId::from_str(hex).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, - { - AssetId::from_str(v).map_err(E::custom) - } - } - - d.deserialize_str(HexVisitor) - } else { - struct BytesVisitor; - - impl ::serde::de::Visitor<'_> for BytesVisitor { - type Value = AssetId; - - 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, - { - if v.len() != 32 { - Err(E::invalid_length(v.len(), &stringify!($len))) - } else { - let mut ret = [0; 32]; - ret.copy_from_slice(v); - Ok(AssetId(sha256::Midstate::from_byte_array(ret))) - } - } - } - - d.deserialize_bytes(BytesVisitor) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use std::str::FromStr; - - use crate::hashes::sha256; - - #[test] - fn example_elements_core() { - // example test data from Elements Core 0.17 - let prevout_str = "05a047c98e82a848dee94efcf32462b065198bebf2404d201ba2e06db30b28f4:0"; - let entropy_hex = "746f447f691323502cad2ef646f932613d37a83aeaa2133185b316648df4b70a"; - let asset_id_hex = "dcd60818d863b5c026c40b2bc3ba6fdaf5018bcc8606c18adf7db4da0bcd8533"; - let token_id_hex = "c1adb114f4f87d33bf9ce90dd4f9ca523dd414d6cd010a7917903e2009689530"; - - let contract_hash = ContractHash::from_byte_array(ZERO32); - let prevout = OutPoint::from_str(prevout_str).unwrap(); - let entropy = sha256::Midstate::from_str(entropy_hex).unwrap(); - assert_eq!(AssetId::generate_asset_entropy(prevout, contract_hash), entropy); - let asset_id = AssetId::from_str(asset_id_hex).unwrap(); - assert_eq!(AssetId::from_entropy(entropy), asset_id); - let token_id = AssetId::from_str(token_id_hex).unwrap(); - assert_eq!(AssetId::reissuance_token_from_entropy(entropy, false), token_id); - - // example test data from Elements Core 0.21 with prevout vout = 1 - let prevout_str = "c76664aa4be760056dcc39b59637eeea8f3c3c3b2aeefb9f23a7b99945a2931e:1"; - let entropy_hex = "bc67a13736341d8ad19e558433483a38cae48a44a5a8b5598ca0b01b5f9f9f41"; - let asset_id_hex = "2ec6c1a06e895b06fffb8dc36084255f890467fb906565b0c048d4c807b4a129"; - let token_id_hex = "d09d205ff7c626ca98c91fed24787ff747fec62194ed1b7e6ef6cc775a1a1fdc"; - - let contract_hash = ContractHash::from_byte_array(ZERO32); - let prevout = OutPoint::from_str(prevout_str).unwrap(); - let entropy = sha256::Midstate::from_str(entropy_hex).unwrap(); - assert_eq!(AssetId::generate_asset_entropy(prevout, contract_hash), entropy); - let asset_id = AssetId::from_str(asset_id_hex).unwrap(); - assert_eq!(AssetId::from_entropy(entropy), asset_id); - let token_id = AssetId::from_str(token_id_hex).unwrap(); - assert_eq!(AssetId::reissuance_token_from_entropy(entropy, true), token_id); - - - // example test data from Elements Core 0.21 with a given contract hash and non-blinded issuance - let prevout_str = "ee45365ddb62e8822182fbdd132fb156b4991e0b7411cff4aab576fd964f2edb:0"; // txid parsed reverse - let contract_hash_hex = "e06e6d4933e76afd7b9cc6a013e0855aa60bbe6d2fca1c27ec6951ff5f1a20c9"; // parsed reverse - let entropy_hex = "1922da340705eef526640b49d28b08928630d1ad52db0f945f3c389267e292c9"; // parsed reverse - let asset_id_hex = "8eebf6109bca0331fe559f0cbd1ef846a2bbb6812f3ae3d8b0b610170cc21a4e"; // parsed reverse - let token_id_hex = "eb02cbc591c9ede071625c129f0a1fab386202cb27a894a45be0d564e961d6bc"; // parsed reverse - - let contract_hash = ContractHash::from_str(contract_hash_hex).unwrap(); - let prevout = OutPoint::from_str(prevout_str).unwrap(); - let entropy = sha256::Midstate::from_str(entropy_hex).unwrap(); - assert_eq!(AssetId::generate_asset_entropy(prevout, contract_hash), entropy); - let asset_id = AssetId::from_str(asset_id_hex).unwrap(); - assert_eq!(AssetId::from_entropy(entropy), asset_id); - let token_id = AssetId::from_str(token_id_hex).unwrap(); - assert_eq!(AssetId::reissuance_token_from_entropy(entropy, false), token_id); - - // example test data from Elements Core 0.21 - // with confidential re-issuance - let prevout_str = "8903ee739b52859877fbfedc58194c2d59d0f5a4ea3c2774dc3cba3031cec757:0"; - let entropy_hex = "b9789de8589dc1b664e4f2bda4d04af9d4d2180394a8c47b1f889acfb5e0acc4"; - let asset_id_hex = "bdab916e8cda17781bcdb84505452e44d0ab2f080e9e5dd7765ffd5ce0c07cd9"; - let token_id_hex = "f144868169dfc7afc024c4d8f55607ac8dfe925e67688650a9cdc54c3cfa5b1c"; - - let contract_hash = ContractHash::from_byte_array(ZERO32); - let prevout = OutPoint::from_str(prevout_str).unwrap(); - let entropy = sha256::Midstate::from_str(entropy_hex).unwrap(); - assert_eq!(AssetId::generate_asset_entropy(prevout, contract_hash), entropy); - let asset_id = AssetId::from_str(asset_id_hex).unwrap(); - assert_eq!(AssetId::from_entropy(entropy), asset_id); - let token_id = AssetId::from_str(token_id_hex).unwrap(); - assert_eq!(AssetId::reissuance_token_from_entropy(entropy, true), token_id); - } - - #[cfg(feature = "json-contract")] - #[test] - fn test_json_contract() { - let tether = ContractHash::from_str("3c7f0a53c2ff5b99590620d7f6604a7a3a7bfbaaa6aa61f7bfc7833ca03cde82").unwrap(); - - let correct = r#"{"entity":{"domain":"tether.to"},"issuer_pubkey":"0337cceec0beea0232ebe14cba0197a9fbd45fcf2ec946749de920e71434c2b904","name":"Tether USD","precision":8,"ticker":"USDt","version":0}"#; - let expected = ContractHash::hash(correct.as_bytes()); - assert_eq!(tether, expected); - assert_eq!(expected, ContractHash::from_json_contract(correct).unwrap()); - - let invalid_json = r#"{"entity":{"domain":"tether.to"},"issuer_pubkey:"#; - assert!(ContractHash::from_json_contract(invalid_json).is_err()); - - let unordered = r#"{"precision":8,"ticker":"USDt","entity":{"domain":"tether.to"},"issuer_pubkey":"0337cceec0beea0232ebe14cba0197a9fbd45fcf2ec946749de920e71434c2b904","name":"Tether USD","version":0}"#; - assert_eq!(expected, ContractHash::from_json_contract(unordered).unwrap()); - - let unordered = r#"{"precision":8,"name":"Tether USD","ticker":"USDt","entity":{"domain":"tether.to"},"issuer_pubkey":"0337cceec0beea0232ebe14cba0197a9fbd45fcf2ec946749de920e71434c2b904","version":0}"#; - assert_eq!(expected, ContractHash::from_json_contract(unordered).unwrap()); - - let spaces = r#"{"precision":8, "name" : "Tether USD", "ticker":"USDt", "entity":{"domain":"tether.to" }, "issuer_pubkey" :"0337cceec0beea0232ebe14cba0197a9fbd45fcf2ec946749de920e71434c2b904","version":0} "#; - assert_eq!(expected, ContractHash::from_json_contract(spaces).unwrap()); - - let nested_correct = r#"{"entity":{"author":"Tether Inc","copyright":2020,"domain":"tether.to","hq":"Mars"},"issuer_pubkey":"0337cceec0beea0232ebe14cba0197a9fbd45fcf2ec946749de920e71434c2b904","name":"Tether USD","precision":8,"ticker":"USDt","version":0}"#; - let nested_expected = ContractHash::hash(nested_correct.as_bytes()); - assert_eq!(nested_expected, ContractHash::from_json_contract(nested_correct).unwrap()); - - let nested_unordered = r#"{"ticker":"USDt","entity":{"domain":"tether.to","hq":"Mars","author":"Tether Inc","copyright":2020},"issuer_pubkey":"0337cceec0beea0232ebe14cba0197a9fbd45fcf2ec946749de920e71434c2b904","name":"Tether USD","precision":8,"version":0}"#; - assert_eq!(nested_expected, ContractHash::from_json_contract(nested_unordered).unwrap()); - } - - #[test] - fn liquid() { - assert_eq!( - AssetId::LIQUID_BTC.to_string(), - "6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d", - ); - } -} diff --git a/src/lib.rs b/src/lib.rs index b54fc1f5..e9822037 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,52 +46,41 @@ extern crate serde_json; #[macro_use] mod internal_macros; -pub mod address; -pub mod blech32; + mod blind; mod block; -pub mod confidential; -pub mod dynafed; -pub mod encode; -mod error; -mod ext; -mod fast_merkle_root; -pub mod hash_types; -pub mod hex; -pub mod issuance; -pub mod locktime; -pub mod opcodes; -mod parse; +mod endian; pub mod pset; -pub mod schnorr; -pub mod script; #[cfg(feature = "serde")] mod serde_utils; pub mod sighash; -pub mod taproot; mod transaction; -// consider making upstream public -mod endian; + +pub use elements26::{address, blech32, confidential, dynafed, encode, hex, issuance, locktime, opcodes}; +pub use elements26::{schnorr, script, taproot}; // re-export bitcoin deps which we re-use pub use bitcoin::hashes; // 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::{ +pub use elements26::{Address, AddressError, AddressParams}; +pub use elements26::{ BlindAssetProofs, BlindError, BlindValueProofs, ConfidentialTxOutError, RangeProofMessage, SurjectionInput, TxOutError, TxOutSecrets, UnblindError, VerificationError, }; +pub use elements26::{ReadExt, WriteExt}; +pub use elements26::fast_merkle_root; +pub use elements26::hash_types::{self, *}; +pub use elements26::{AssetId, ContractHash}; +pub use elements26::LockTime; +pub use elements26::{SchnorrSig, SchnorrSigError}; +pub use elements26::Script; +pub use elements26::Sequence; +pub use elements26::{ + AssetIssuance, EcdsaSighashType, OutPoint, PeginData, PegoutData, TxIn, + TxInWitness, TxOutWitness, +}; + pub use crate::block::ExtData as BlockExtData; pub use crate::block::{Block, BlockHeader}; -pub use crate::ext::{ReadExt, WriteExt}; -pub use crate::fast_merkle_root::fast_merkle_root; -pub use crate::hash_types::*; -pub use crate::issuance::{AssetId, ContractHash}; -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, - TxInWitness, TxOut, TxOutWitness, -}; +pub use crate::transaction::{TxOut, Transaction}; + diff --git a/src/locktime.rs b/src/locktime.rs deleted file mode 100644 index 5ffddf8c..00000000 --- a/src/locktime.rs +++ /dev/null @@ -1,617 +0,0 @@ -// Rust Bitcoin Library -// Written in 2022 by -// Tobin C. Harding -// 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 . -// - -//! Provides type [`LockTime`] that implements the logic around nLockTime/OP_CHECKLOCKTIMEVERIFY. -//! -//! There are two types of lock time: lock-by-blockheight and lock-by-blocktime, distinguished by -//! whether `LockTime < LOCKTIME_THRESHOLD`. -//! - -use std::{mem, fmt}; -use std::cmp::{PartialOrd, Ordering}; -use std::convert::TryFrom; -use std::str::FromStr; -use std::io::{Read, Write}; -use crate::error::ParseIntError; -use crate::parse; - -use crate::encode::{self, Decodable, Encodable}; -use crate::error::write_err; -use crate::parse::impl_parse_str_through_int; - -/// The Threshold for deciding whether a lock time value is a height or a time (see [Bitcoin Core]). -/// -/// `LockTime` values _below_ the threshold are interpreted as block heights, values _above_ (or -/// equal to) the threshold are interpreted as block times (UNIX timestamp, seconds since epoch). -/// -/// Elements is able to safely use this value because a block height greater than 500,000,000 would -/// never occur because it would represent a height in approximately 950 years. Conversely, block -/// times under 500,000,000 will never happen because they would represent times before 1986 which -/// are, for obvious reasons, not useful within any Elements network. -/// -/// [Bitcoin Core]: https://github.com/bitcoin/bitcoin/blob/9ccaee1d5e2e4b79b0a7c29aadb41b97e4741332/src/script/script.h#L39 -pub const LOCK_TIME_THRESHOLD: u32 = 500_000_000; - -/// A lock time value, representing either a block height or a UNIX timestamp (seconds since epoch). -/// -/// Used for transaction lock time (`nLockTime` in Bitcoin Core and [`crate::Transaction::lock_time`] -/// in this library) and also for the argument to opcode 'OP_CHECKLOCKTIMEVERIFY`. -/// -/// ### Relevant BIPs -/// -/// * [BIP-65 OP_CHECKLOCKTIMEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki) -/// * [BIP-113 Median time-past as endpoint for lock-time calculations](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki) -/// -/// # Examples -/// ``` -/// # use elements::{LockTime, LockTime::*}; -/// # let n = LockTime::from_consensus(100); // n OP_CHECKLOCKTIMEVERIFY -/// # let lock_time = LockTime::from_consensus(100); // nLockTime -/// // To compare lock times there are various `is_satisfied_*` methods, you may also use: -/// let is_satisfied = match (n, lock_time) { -/// (Blocks(n), Blocks(lock_time)) => n <= lock_time, -/// (Seconds(n), Seconds(lock_time)) => n <= lock_time, -/// _ => panic!("handle invalid comparison error"), -/// }; -/// ``` -#[allow(clippy::derive_ord_xor_partial_ord)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))] -pub enum LockTime { - /// A block height lock time value. - /// - /// # Examples - /// ```rust - /// use elements::LockTime; - /// - /// let block: u32 = 741521; - /// let n = LockTime::from_height(block).expect("valid height"); - /// assert!(n.is_block_height()); - /// assert_eq!(n.to_consensus_u32(), block); - /// ``` - Blocks(Height), - /// A UNIX timestamp lock time value. - /// - /// # Examples - /// ```rust - /// use elements::LockTime; - /// - /// let seconds: u32 = 1653195600; // May 22nd, 5am UTC. - /// let n = LockTime::from_time(seconds).expect("valid time"); - /// assert!(n.is_block_time()); - /// assert_eq!(n.to_consensus_u32(), seconds); - /// ``` - Seconds(Time), -} - -impl LockTime { - /// If [`crate::Transaction::lock_time`] is set to zero it is ignored, in other words a - /// transaction with nLocktime==0 is able to be included immediately in any block. - pub const ZERO: LockTime = LockTime::Blocks(Height(0)); - - /// Constructs a `LockTime` from an nLockTime value or the argument to OP_CHEKCLOCKTIMEVERIFY. - /// - /// # Examples - /// - /// ```rust - /// use elements::LockTime; - /// let n = LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY - /// - /// // `from_consensus` roundtrips as expected with `to_consensus_u32`. - /// let n_lock_time: u32 = 741521; - /// let lock_time = LockTime::from_consensus(n_lock_time); - /// assert_eq!(lock_time.to_consensus_u32(), n_lock_time); - #[inline] - pub fn from_consensus(n: u32) -> Self { - if is_block_height(n) { - Self::Blocks(Height::from_consensus(n).expect("n is valid")) - } else { - Self::Seconds(Time::from_consensus(n).expect("n is valid")) - } - } - - /// Constructs a `LockTime` from `n`, expecting `n` to be a valid block height. - /// - /// See [`LOCK_TIME_THRESHOLD`] for definition of a valid height value. - /// - /// # Examples - /// ```rust - /// use elements::LockTime; - /// assert!(LockTime::from_height(741521).is_ok()); - /// assert!(LockTime::from_height(1653195600).is_err()); - /// ``` - #[inline] - pub fn from_height(n: u32) -> Result { - let height = Height::from_consensus(n)?; - Ok(LockTime::Blocks(height)) - } - - /// Constructs a `LockTime` from `n`, expecting `n` to be a valid block time. - /// - /// See [`LOCK_TIME_THRESHOLD`] for definition of a valid time value. - /// - /// # Examples - /// ```rust - /// use elements::LockTime; - /// assert!(LockTime::from_time(1653195600).is_ok()); - /// assert!(LockTime::from_time(741521).is_err()); - /// ``` - #[inline] - pub fn from_time(n: u32) -> Result { - let time = Time::from_consensus(n)?; - Ok(LockTime::Seconds(time)) - } - - /// Returns true if both lock times use the same unit i.e., both height based or both time based. - #[inline] - pub fn is_same_unit(&self, other: LockTime) -> bool { - mem::discriminant(self) == mem::discriminant(&other) - } - - /// Returns true if this lock time value is a block height. - #[inline] - pub fn is_block_height(&self) -> bool { - match *self { - LockTime::Blocks(_) => true, - LockTime::Seconds(_) => false, - } - } - - /// Returns true if this lock time value is a block time (UNIX timestamp). - #[inline] - pub fn is_block_time(&self) -> bool { - !self.is_block_height() - } - - /// Returns true if this timelock constraint is satisfied by the respective `height`/`time`. - /// - /// If `self` is a blockheight based lock then it is checked against `height` and if `self` is a - /// blocktime based lock it is checked against `time`. - /// - /// A 'timelock constraint' refers to the `n` from `n OP_CHEKCLOCKTIMEVERIFY`, this constraint - /// is satisfied if a transaction with nLockTime ([`crate::Transaction::lock_time`]) set to - /// `height`/`time` is valid. - /// - /// # Examples - /// ```no_run - /// # use elements::locktime::{LockTime, Height, Time}; - /// // Can be implemented if block chain data is available. - /// fn get_height() -> Height { todo!("return the current block height") } - /// fn get_time() -> Time { todo!("return the current block time") } - /// - /// let n = LockTime::from_consensus(741521); // `n OP_CHEKCLOCKTIMEVERIFY`. - /// if n.is_satisfied_by(get_height(), get_time()) { - /// // Can create and mine a transaction that satisfies the OP_CLTV timelock constraint. - /// } - /// ```` - #[inline] - pub fn is_satisfied_by(&self, height: Height, time: Time) -> bool { - use LockTime::*; - - match *self { - Blocks(n) => n <= height, - Seconds(n) => n <= time, - } - } - - /// Returns the inner `u32` value. This is the value used when creating this `LockTime` - /// i.e., `n OP_CHECKLOCKTIMEVERIFY` or nLockTime. - /// - /// # Warning - /// - /// Do not compare values return by this method. The whole point of the `LockTime` type is to - /// assist in doing correct comparisons. Either use `is_satisfied_by` or use the pattern below: - /// - /// # Examples - /// - /// ```rust - /// # use elements::{LockTime, LockTime::*}; - /// # let n = LockTime::from_consensus(100); // n OP_CHECKLOCKTIMEVERIFY - /// # let lock_time = LockTime::from_consensus(100); // nLockTime - /// - /// let is_satisfied = match (n, lock_time) { - /// (Blocks(n), Blocks(lock_time)) => n <= lock_time, - /// (Seconds(n), Seconds(lock_time)) => n <= lock_time, - /// _ => panic!("invalid comparison"), - /// }; - /// - /// // Or, if you have Rust 1.53 or greater - /// // let is_satisfied = n.partial_cmp(&lock_time).expect("invalid comparison").is_le(); - /// ``` - #[inline] - pub fn to_consensus_u32(self) -> u32 { - match self { - LockTime::Blocks(ref h) => h.to_consensus_u32(), - LockTime::Seconds(ref t) => t.to_consensus_u32(), - } - } -} - -impl_parse_str_through_int!(LockTime, from_consensus); - -impl From for LockTime { - fn from(h: Height) -> Self { - LockTime::Blocks(h) - } -} - -impl From