diff --git a/Cargo.toml b/Cargo.toml index 4ff2578c..79d9dd0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,10 +19,10 @@ default = ["json-contract"] json-contract = ["serde_json"] "serde" = [ + "dep:serde", "bitcoin/serde", "bitcoin/serde", "secp256k1-zkp/serde", - "actual-serde", ] base64 = ["bitcoin/base64"] @@ -33,10 +33,7 @@ secp256k1-zkp = { version = "0.11.0", features = ["global-context", "hashes"] } # Used for ContractHash::from_json_contract. serde_json = { version = "1.0", optional = true } - -actual-serde = { package = "serde", version = "1.0.103", features = [ - "derive", -], optional = true } +serde = { version = "1.0.103", features = [ "derive" ], optional = true } hex = { package = "hex-conservative", version = "1.1.0" } diff --git a/src/address.rs b/src/address.rs index 296fa63b..5b83abc3 100644 --- a/src/address.rs +++ b/src/address.rs @@ -36,7 +36,7 @@ use crate::schnorr::{TapTweak, TweakedPublicKey, UntweakedPublicKey}; use crate::taproot::TapNodeHash; use crate::{opcodes, script}; -use crate::{PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash}; +use crate::{PubkeyHash, ScriptHash, WScriptHash}; /// Encoding error #[derive(Debug, PartialEq)] @@ -236,13 +236,9 @@ impl Address { 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)), + payload: Payload::PubkeyHash(pk.pubkey_hash()), blinding_pubkey: blinder, } } @@ -257,27 +253,27 @@ impl Address { ) -> Address { Address { params, - payload: Payload::ScriptHash(ScriptHash::hash(&script[..])), + payload: Payload::ScriptHash(ScriptHash::hash_script(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 + /// + /// # Panics + /// + /// Panics if the provided public key is not compressed. 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(), + program: pk.wpubkey_hash().expect("public key must be uncompressed").as_byte_array().to_vec(), }, blinding_pubkey: blinder, } @@ -285,22 +281,23 @@ impl Address { /// 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 + /// + /// # Panics + /// + /// Panics if the provided public key is not compressed. 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 pkh = pk.wpubkey_hash().expect("public key must be uncompressed"); let builder = script::Builder::new() .push_int(0) - .push_slice(&ScriptHash::from_engine(hash_engine)[..]); + .push_slice(pkh.as_ref()); Address { params, - payload: Payload::ScriptHash(ScriptHash::hash(builder.into_script().as_bytes())), + payload: Payload::ScriptHash(ScriptHash::hash_script(&builder.into_script())), blinding_pubkey: blinder, } } @@ -315,7 +312,7 @@ impl Address { params, payload: Payload::WitnessProgram { version: Fe32::Q, - program: WScriptHash::hash(&script[..])[..].to_vec(), + program: WScriptHash::hash_script(script).as_byte_array().to_vec(), }, blinding_pubkey: blinder, } diff --git a/src/blind.rs b/src/blind.rs index bd611259..1d31a36c 100644 --- a/src/blind.rs +++ b/src/blind.rs @@ -15,6 +15,7 @@ //! # Transactions Blinding //! +use core::convert::TryFrom; use std::{self, collections::BTreeMap, fmt}; use secp256k1_zkp::{ @@ -26,7 +27,6 @@ use secp256k1_zkp::{Generator, RangeProof, Secp256k1, Signing, SurjectionProof}; use crate::{AddressParams, Script, TxIn}; -use crate::hashes; use crate::{ confidential::{Asset, AssetBlindingFactor, Nonce, Value, ValueBlindingFactor}, Address, AssetId, Transaction, TxOut, TxOutWitness, @@ -224,8 +224,7 @@ impl RangeProofMessage { /// Information about Transaction Input Asset #[cfg_attr( feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "actual_serde") + derive(serde::Serialize, serde::Deserialize), )] #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub struct TxOutSecrets { @@ -763,7 +762,8 @@ impl TxOut { )?; let (asset, asset_bf) = opening.message.as_ref().split_at(32); - let asset = AssetId::from_slice(asset)?; + let asset = <[u8; 32]>::try_from(asset).map_err(UnblindError::MalformedAssetId)?; + let asset = AssetId::from_byte_array(asset); let asset_bf = AssetBlindingFactor::from_slice(&asset_bf[..32])?; let value = opening.value; @@ -788,7 +788,7 @@ pub enum UnblindError { /// Transaction output does not have a rangeproof. MissingRangeproof, /// Malformed asset ID. - MalformedAssetId(hashes::FromSliceError), + MalformedAssetId(core::array::TryFromSliceError), /// Error originated in `secp256k1_zkp`. Upstream(secp256k1_zkp::Error), } @@ -823,12 +823,6 @@ impl From for UnblindError { } } -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 @@ -1516,7 +1510,7 @@ mod tests { #[test] fn blind_value_proof_test() { - let id = AssetId::from_slice(&[1u8; 32]).unwrap(); + let id = AssetId::from_byte_array([1u8; 32]); let abf = AssetBlindingFactor::new(&mut thread_rng()); let asset = confidential::Asset::new_confidential(SECP256K1, id, abf); @@ -1542,7 +1536,7 @@ mod tests { #[test] fn blind_asset_proof_test() { - let id = AssetId::from_slice(&[1u8; 32]).unwrap(); + let id = AssetId::from_byte_array([1u8; 32]); let abf = AssetBlindingFactor::new(&mut thread_rng()); let asset = confidential::Asset::new_confidential(SECP256K1, id, abf); diff --git a/src/block.rs b/src/block.rs index 0b72b69e..0f9b1ba5 100644 --- a/src/block.rs +++ b/src/block.rs @@ -21,11 +21,16 @@ use std::io; #[cfg(feature = "serde")] use std::fmt; use crate::dynafed; -use crate::hashes::{Hash, sha256}; +use crate::hashes::Hash; use crate::Transaction; use crate::encode::{self, serialize, Decodable, Encodable, VarInt}; use crate::{BlockHash, Script, TxMerkleNode}; +impl_sha256_midstate_wrapper! { + /// The Merkle root of a set of dynafed parameters. + pub struct DynafedRoot([u8; 32]); +} + /// Data related to block signatures #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum ExtData { @@ -268,7 +273,7 @@ impl BlockHeader { } /// Calculate the root of the dynafed params. Returns [None] when not dynafed. - pub fn calculate_dynafed_params_root(&self) -> Option { + pub fn calculate_dynafed_params_root(&self) -> Option { match self.ext { ExtData::Proof { .. } => None, ExtData::Dynafed { ref current, ref proposed, .. } => { @@ -276,7 +281,7 @@ impl BlockHeader { current.calculate_root().to_byte_array(), proposed.calculate_root().to_byte_array(), ]; - Some(crate::fast_merkle_root::fast_merkle_root(&leaves[..])) + Some(DynafedRoot::from_midstate(crate::fast_merkle_root::fast_merkle_root(&leaves[..]))) } } } diff --git a/src/confidential.rs b/src/confidential.rs index 91d692ad..fa0e6dca 100644 --- a/src/confidential.rs +++ b/src/confidential.rs @@ -1102,7 +1102,6 @@ impl<'de> Deserialize<'de> for ValueBlindingFactor { #[cfg(test)] mod tests { use super::*; - use crate::hashes::sha256; #[cfg(feature = "serde")] use std::str::FromStr; @@ -1144,7 +1143,7 @@ mod tests { let assets = [ Asset::Null, - Asset::Explicit(AssetId::from_inner(sha256::Midstate::from_byte_array([0; 32]))), + Asset::Explicit(AssetId::from_byte_array([0; 32])), Asset::from_commitment(&[ 0x0a, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, diff --git a/src/dynafed.rs b/src/dynafed.rs index 9316eca6..3acaae5a 100644 --- a/src/dynafed.rs +++ b/src/dynafed.rs @@ -23,9 +23,19 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::ser::{SerializeSeq, SerializeStruct}; use crate::encode::{self, Encodable, Decodable}; -use crate::hashes::{Hash, sha256, sha256d}; +use crate::hashes::{Hash, sha256d}; use crate::Script; +impl_sha256_midstate_wrapper! { + /// The Merkle root of a set of dynafed parameters. + pub struct ParamsRoot([u8; 32]); +} + +impl_sha256_midstate_wrapper! { + /// A hash of elided dynafed parameter data. + pub struct ElidedRoot([u8; 32]); +} + /// ad-hoc struct to fmt in hex #[cfg(feature = "serde")] struct HexBytes<'a>(&'a [u8]); @@ -109,7 +119,7 @@ impl FullParams { /// 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 extra_root(&self) -> ElidedRoot { fn serialize_hash(obj: &E) -> sha256d::Hash { let mut engine = sha256d::Hash::engine(); obj.consensus_encode(&mut engine).expect("engines don't error"); @@ -121,11 +131,11 @@ impl FullParams { serialize_hash(&self.fedpegscript).to_byte_array(), serialize_hash(&self.extension_space).to_byte_array(), ]; - crate::fast_merkle_root::fast_merkle_root(&leaves[..]) + ElidedRoot::from_midstate(crate::fast_merkle_root::fast_merkle_root(&leaves[..])) } /// Calculate the root of this [`FullParams`]. - pub fn calculate_root(&self) -> sha256::Midstate { + pub fn calculate_root(&self) -> ParamsRoot { fn serialize_hash(obj: &E) -> sha256d::Hash { let mut engine = sha256d::Hash::engine(); obj.consensus_encode(&mut engine).expect("engines don't error"); @@ -142,7 +152,7 @@ impl FullParams { compact_root.to_byte_array(), self.extra_root().to_byte_array(), ]; - crate::fast_merkle_root::fast_merkle_root(&leaves[..]) + ParamsRoot::from_midstate(crate::fast_merkle_root::fast_merkle_root(&leaves[..])) } /// Turns parameters into compact parameters. @@ -223,7 +233,7 @@ pub enum Params { /// Maximum, in bytes, of the size of a blocksigning witness signblock_witness_limit: u32, /// Merkle root of extra data - elided_root: sha256::Midstate, + elided_root: ElidedRoot, }, /// Full dynamic federations parameters Full(FullParams), @@ -319,7 +329,7 @@ impl Params { } /// Get the `elided_root`. Is [None] for non-[`Params::Compact`] params. - pub fn elided_root(&self) -> Option<&sha256::Midstate> { + pub fn elided_root(&self) -> Option<&ElidedRoot> { match *self { Params::Null => None, Params::Compact { ref elided_root, ..} => Some(elided_root), @@ -330,16 +340,16 @@ impl Params { /// 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 extra_root(&self) -> ElidedRoot { match *self { - Params::Null => sha256::Midstate::from_byte_array([0u8; 32]), + Params::Null => ElidedRoot::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 { + pub fn calculate_root(&self) -> ParamsRoot { fn serialize_hash(obj: &E) -> sha256d::Hash { let mut engine = sha256d::Hash::engine(); obj.consensus_encode(&mut engine).expect("engines don't error"); @@ -347,7 +357,7 @@ impl Params { } if self.is_null() { - return sha256::Midstate::from_byte_array([0u8; 32]); + return ParamsRoot::from_byte_array([0u8; 32]); } let leaves = [ @@ -360,7 +370,7 @@ impl Params { compact_root.to_byte_array(), self.extra_root().to_byte_array(), ]; - crate::fast_merkle_root::fast_merkle_root(&leaves[..]) + ParamsRoot::from_midstate(crate::fast_merkle_root::fast_merkle_root(&leaves[..])) } /// Get the full params when this params are full. @@ -626,7 +636,7 @@ impl Decodable for Params { 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)?), + elided_root: ElidedRoot::from_byte_array(Decodable::consensus_decode(&mut d)?), }), 2 => Ok(Params::Full(Decodable::consensus_decode(&mut d)?)), _ => Err(encode::Error::ParseFailed( @@ -640,7 +650,6 @@ impl Decodable for Params { mod tests { use std::fmt::{self, Write}; - use crate::hashes::sha256; use crate::{BlockHash, TxMerkleNode}; use super::*; @@ -683,7 +692,7 @@ mod tests { let compact_entry = Params::Compact { signblockscript: signblockscript.clone(), signblock_witness_limit: signblock_wl, - elided_root: sha256::Midstate::from_byte_array([0; 32]), + elided_root: ElidedRoot::from_byte_array([0; 32]), }; assert_eq!( format!("{:x}", compact_entry.calculate_root()), @@ -751,7 +760,7 @@ mod tests { let compact = params.into_compact().unwrap(); assert_eq!( to_debug_string(&compact), - "Compact { signblockscript: 0102, signblock_witness_limit: 3, elided_root: 0xc3058c822b22a13bb7c47cf50d3f3c7817e7d9075ff55a7d16c85b9673e7e553 }", + "Compact { signblockscript: 0102, signblock_witness_limit: 3, elided_root: c3058c822b22a13bb7c47cf50d3f3c7817e7d9075ff55a7d16c85b9673e7e553 }", ); assert_eq!(compact.calculate_root(), full.calculate_root()); assert_eq!(compact.elided_root(), Some(&extra_root)); diff --git a/src/encode.rs b/src/encode.rs index d140eef2..a0bbc0af 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -202,18 +202,6 @@ pub fn deserialize_partial(data: &[u8]) -> Result<(T, usize), Erro 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, @@ -427,6 +415,7 @@ macro_rules! impl_array { }; } impl_array!(4); +impl_array!(20); impl_array!(32); impl_array!(33); diff --git a/src/hash_types.rs b/src/hash_types.rs index 6231c34b..b4ce1861 100644 --- a/src/hash_types.rs +++ b/src/hash_types.rs @@ -20,6 +20,9 @@ use crate::hashes::{hash160, hash_newtype, sha256, sha256d, Hash}; +// Re-export bitcoin's pubkeyhash types. We already re-export bitcoin's `PublicKey` type. +pub use bitcoin::{PubkeyHash, WPubkeyHash}; + macro_rules! impl_hashencode { ($hashtype:ident) => { impl $crate::encode::Encodable for $hashtype { @@ -52,12 +55,8 @@ hash_newtype! { /// "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); @@ -70,3 +69,117 @@ impl_hashencode!(Wtxid); impl_hashencode!(Sighash); impl_hashencode!(BlockHash); impl_hashencode!(TxMerkleNode); + +impl ScriptHash { + /// Computes the `ScriptHash` of a script. + pub fn hash_script(s: &crate::Script) -> Self { + Self(hash160::Hash::hash(s.as_bytes())) + } +} + +impl WScriptHash { + /// Computes the `WScriptHash` of a script. + pub fn hash_script(s: &crate::Script) -> Self { + Self(sha256::Hash::hash(s.as_bytes())) + } +} + +#[cfg(test)] +#[cfg(feature = "serde")] +mod serde_tests { + use super::*; + use crate::{AssetEntropy, AssetId, ContractHash, DynafedRoot}; + use crate::dynafed::{ElidedRoot, ParamsRoot}; + + /// An arbitrary test vector. + /// + /// Mainly its goal is to make sure we don't mess up and reverse stuff we don't + /// intend to reverse, or vice-versa, so its only requirement is that it not be + /// invariant under reversal. + const TEST_VECTOR: [u8; 32] = [ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, + 0xff, 0xff, 0xff, 0xff, 0x11, 0x22, 0x33, 0x44, + ]; + + /// The vector encoded as CBOR + const CBOR: [u8; 34] = [ + 0x58, 0x20, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, + 0xff, 0xff, 0xff, 0xff, 0x11, 0x22, 0x33, 0x44, + ]; + /// The vector encoded as JSON (non-reversed hash) + const FORWARD_JSON: &str = "\"0102030405060708090a0b0c0d0e0f100000000011223344ffffffff11223344\""; + /// The vector encoded as JSON (reversed hash) + const REVERSED_JSON: &str = "\"44332211ffffffff4433221100000000100f0e0d0c0b0a090807060504030201\""; + + /// Constructs a test which attempts to (de-)serialize the 32-byte hash `TEST_VECTOR`, + /// + /// Checks that serialization works, matches the given target, and can deserialize + /// to match the original value. + macro_rules! serde_rtt_test32 { + ($testname:ident, $ty:ty, $json:expr) => { + #[test] + fn $testname() { + let obj = <$ty>::from_byte_array(TEST_VECTOR); + // JSON (human-readable) round-trip + let enc_json = serde_json::to_string(&obj).expect("encode json"); + assert_eq!( + enc_json, + $json, + "encoded JSON did not match target '{}'", $json + ); + let dec_json: $ty = serde_json::from_str(&enc_json).expect("decode json"); + assert_eq!( + dec_json, + obj, + "decoded JSON did not match object '{}'", obj + ); + + // CBOR (non-human-readable) round-trip + let enc_cbor = serde_cbor::to_vec(&obj).expect("encode cbor"); + assert_eq!( + enc_cbor, + CBOR, + "encoded CBOR did not match target '{:?}'", CBOR + ); + let dec_cbor: $ty = serde_cbor::from_slice(&enc_cbor).expect("decode cbor"); + assert_eq!( + dec_cbor, + obj, + "decoded CBOR did not match object '{}'", obj + ); + + // While we're here, do a string round-trip test + let s = obj.to_string(); + assert_eq!( + s, + $json[1..65], + "encoded string did not match target '{:?}'", $json + ); + let dec_s: $ty = s.parse().expect("parsing string"); + assert_eq!( + dec_s, + obj, + "decoded string did not match object '{}'", obj + ); + } + } + } + + serde_rtt_test32!(serde_rtt_txid, Txid, REVERSED_JSON); + serde_rtt_test32!(serde_rtt_wtxid, Wtxid, REVERSED_JSON); + serde_rtt_test32!(serde_rtt_blockhash, BlockHash, REVERSED_JSON); + serde_rtt_test32!(serde_rtt_wscripthash, WScriptHash, FORWARD_JSON); + serde_rtt_test32!(serde_rtt_merklenode, TxMerkleNode, REVERSED_JSON); + serde_rtt_test32!(serde_rtt_assetid, AssetId, REVERSED_JSON); + serde_rtt_test32!(serde_rtt_contracthash, ContractHash, REVERSED_JSON); + + serde_rtt_test32!(serde_rtt_entropy, AssetEntropy, REVERSED_JSON); + serde_rtt_test32!(serde_rtt_dynaroot, DynafedRoot, REVERSED_JSON); + serde_rtt_test32!(serde_rtt_elidedroot, ElidedRoot, REVERSED_JSON); + serde_rtt_test32!(serde_rtt_paramsroot, ParamsRoot, REVERSED_JSON); +} diff --git a/src/internal_macros.rs b/src/internal_macros.rs index 1972a6c8..7dfbcf38 100644 --- a/src/internal_macros.rs +++ b/src/internal_macros.rs @@ -395,6 +395,94 @@ macro_rules! serde_struct_human_string_impl { ) } +macro_rules! impl_sha256_midstate_wrapper { + { + #[$($type_attrs:meta)*] + pub struct $ty:ident([u8; 32]); + } => { + $(#[$type_attrs])* + #[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)] + pub struct $ty([u8; 32]); + + impl $ty { + /// Constructs this wrapper struct from raw bytes. + pub fn from_byte_array(inner: [u8; 32]) -> Self { + Self(inner) + } + + /// The raw bytes within the wrapper type. + pub fn as_byte_array(&self) -> &[u8; 32] { + &self.0 + } + + /// The raw bytes within the wrapper type. + pub fn to_byte_array(self) -> [u8; 32] { + self.0 + } + + /// (Private) convert a sha256 midstate to an object. + fn from_midstate(value: crate::hashes::sha256::Midstate) -> Self { + Self(value.to_byte_array()) + } + } + + impl ::std::fmt::Display for $ty { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + ::std::fmt::LowerHex::fmt(&self, f) + } + } + + impl ::std::fmt::LowerHex for $ty { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + hex::fmt_hex_exact!(f, 32, self.0.iter().rev(), hex::Case::Lower) + } + } + + impl ::std::fmt::UpperHex for $ty { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + hex::fmt_hex_exact!(f, 32, self.0.iter().rev(), hex::Case::Upper) + } + } + + impl ::std::fmt::Debug for $ty { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + ::std::fmt::Display::fmt(&self, f) + } + } + + impl ::core::str::FromStr for $ty { + type Err = hex::DecodeFixedLengthBytesError; + + fn from_str(s: &str) -> Result { + let mut arr = hex::decode_to_array(s)?; + arr.reverse(); + Ok(Self(arr)) + } + } + + #[cfg(feature = "serde")] + impl ::serde::Serialize for $ty { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer { + // "cheat" by just copying sha256d serde serialization + crate::hashes::sha256d::Hash::from_byte_array(self.to_byte_array()).serialize(serializer) + } + } + + #[cfg(feature = "serde")] + impl<'de> ::serde::Deserialize<'de> for $ty { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de> { + // "cheat" by just copying sha256d serde serialization + let hash = crate::hashes::sha256d::Hash::deserialize(deserializer)?; + Ok(Self::from_byte_array(hash.to_byte_array())) + } + } + } +} + #[cfg(test)] macro_rules! hex_deserialize( ($e:expr) => ({ diff --git a/src/issuance.rs b/src/issuance.rs index fd59a542..762f02e6 100644 --- a/src/issuance.rs +++ b/src/issuance.rs @@ -15,10 +15,9 @@ //! 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::hashes::{hash_newtype, sha256, sha256d, Hash}; use crate::fast_merkle_root::fast_merkle_root; use secp256k1_zkp::Tag; use crate::transaction::OutPoint; @@ -37,11 +36,22 @@ const TWO32: [u8; 32] = [ ]; hash_newtype!( - /// The hash of an asset contract.", tru) + /// The hash of an asset contract. #[hash_newtype(backward)] pub struct ContractHash(sha256::Hash); ); + +impl_sha256_midstate_wrapper! { + /// A hash of some data used as "asset entropy" to seed the ID of a new asset. + pub struct AssetEntropy([u8; 32]); +} + +impl_sha256_midstate_wrapper! { + /// An issued asset ID. + pub struct AssetId([u8; 32]); +} + impl ContractHash { /// Calculate the contract hash of a JSON contract object. /// @@ -62,39 +72,20 @@ impl ContractHash { } } -/// 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([ + pub const LIQUID_BTC: AssetId = AssetId([ 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 { + ) -> AssetEntropy { // E : entropy // I : prevout // C : contract @@ -104,15 +95,15 @@ impl AssetId { prevout.consensus_encode(&mut enc).unwrap(); sha256d::Hash::from_engine(enc) }; - fast_merkle_root(&[prevout_hash.to_byte_array(), contract_hash.to_byte_array()]) + AssetEntropy::from_midstate(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 { + pub fn from_entropy(entropy: AssetEntropy) -> AssetId { // H_a : asset tag // E : entropy // H_a = H( E || 0 ) - AssetId(fast_merkle_root(&[entropy.to_byte_array(), ZERO32])) + AssetId::from_midstate(fast_merkle_root(&[entropy.to_byte_array(), ZERO32])) } /// Computes the asset ID when issuing asset from issuing input and contract hash @@ -128,7 +119,7 @@ impl AssetId { } /// Calculate the reissuance token asset ID from the asset entropy. - pub fn reissuance_token_from_entropy(entropy: sha256::Midstate, confidential: bool) -> AssetId { + pub fn reissuance_token_from_entropy(entropy: AssetEntropy, confidential: bool) -> AssetId { // H_a : asset reissuance tag // E : entropy // if not fConfidential: @@ -139,37 +130,12 @@ impl AssetId { false => ONE32, true => TWO32, }; - AssetId(fast_merkle_root(&[entropy.to_byte_array(), second])) + AssetId::from_midstate(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) + self.0.into() } } @@ -181,78 +147,7 @@ impl Encodable for AssetId { 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 { - if s.is_human_readable() { - s.collect_str(self) - } 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, - { - use core::convert::TryFrom; - match <[u8; 32]>::try_from(v) { - Ok(ret) => Ok(AssetId(sha256::Midstate::from_byte_array(ret))), - Err(_) => Err(E::invalid_length(v.len(), &stringify!($len))), - } - } - } - - d.deserialize_bytes(BytesVisitor) - } + Decodable::consensus_decode(d).map(Self) } } @@ -261,8 +156,6 @@ 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 @@ -273,7 +166,7 @@ mod test { 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(); + let entropy = AssetEntropy::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); @@ -288,7 +181,7 @@ mod test { 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(); + let entropy = AssetEntropy::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); @@ -305,7 +198,7 @@ mod test { 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(); + let entropy = AssetEntropy::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); @@ -321,7 +214,7 @@ mod test { 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(); + let entropy = AssetEntropy::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); diff --git a/src/lib.rs b/src/lib.rs index 5002c4ad..c94940dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,8 +34,7 @@ pub extern crate hex; pub extern crate secp256k1_zkp; /// Re-export of serde crate #[cfg(feature = "serde")] -#[macro_use] -pub extern crate actual_serde as serde; +pub extern crate serde; #[cfg(all(test, feature = "serde"))] extern crate serde_test; @@ -82,11 +81,11 @@ pub use crate::blind::{ SurjectionInput, TxOutError, TxOutSecrets, UnblindError, VerificationError, CtLocation, CtLocationType, }; pub use crate::block::ExtData as BlockExtData; -pub use crate::block::{Block, BlockHeader}; +pub use crate::block::{Block, BlockHeader, DynafedRoot}; 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::issuance::{AssetEntropy, AssetId, ContractHash}; pub use crate::locktime::LockTime; pub use crate::schnorr::{SchnorrSig, SchnorrSigError}; pub use crate::script::Script; diff --git a/src/locktime.rs b/src/locktime.rs index e93f10b6..0fd8c043 100644 --- a/src/locktime.rs +++ b/src/locktime.rs @@ -66,8 +66,7 @@ pub const LOCK_TIME_THRESHOLD: u32 = 500_000_000; /// ``` #[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"))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum LockTime { /// A block height lock time value. /// @@ -293,8 +292,7 @@ impl Decodable for LockTime { /// An absolute block height, guaranteed to always contain a valid height value. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Height(u32); impl Height { @@ -379,8 +377,7 @@ impl TryFrom for Height { /// `to_consensus_u32()`. Said another way, `Time(x)` means 'x seconds since epoch' _not_ '(x - /// threshold) seconds since epoch'. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Time(u32); impl Time { diff --git a/src/pset/elip102.rs b/src/pset/elip102.rs index 89331603..8504fc25 100644 --- a/src/pset/elip102.rs +++ b/src/pset/elip102.rs @@ -124,7 +124,7 @@ mod test { pset.add_input(input); let mut output = Output { amount: Some(1), - asset: Some(AssetId::from_slice(&[9; 32]).unwrap()), + asset: Some(AssetId::from_byte_array([9; 32])), ..Default::default() }; output.set_abf(abf); diff --git a/src/pset/error.rs b/src/pset/error.rs index 1f6d4564..0241f0ac 100644 --- a/src/pset/error.rs +++ b/src/pset/error.rs @@ -20,7 +20,6 @@ use crate::Txid; use super::raw; use crate::blind::ConfidentialTxOutError; -use crate::hashes; use secp256k1_zkp; #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -52,12 +51,6 @@ pub enum Error { /// PSET has an input exclusively requiring a height-based locktime and also /// an input requiring a time-based locktime LocktimeConflict, - /// The scriptSigs for the unsigned transaction must be empty. - UnsignedTxHasScriptSigs, - /// The scriptWitnesses for the unsigned transaction must be empty. - UnsignedTxHasScriptWitnesses, - /// A PSET must have an unsigned transaction. - MustHaveUnsignedTx, /// Signals that there are no more key-value pairs in a key-value map. NoMorePairs, /// Attempting to merge with a PSET describing a different unsigned @@ -68,10 +61,6 @@ pub enum Error { /// Actual actual: Txid, }, - /// Unable to parse as a standard Sighash type. - NonStandardSighashType(u32), - /// Parsing errors from `bitcoin_hashes` - HashParseError(hashes::FromSliceError), /// The pre-image must hash to the corresponding pset hash InvalidPreimageHashPair { /// Hash-type @@ -101,8 +90,6 @@ pub enum Error { MissingInputPrevTxId, /// Missing Input Prev Out MissingInputPrevVout, - /// Global scalar must be 32 bytes - SecpScalarSizeError(usize), /// Missing Output Value MissingOutputValue, /// Missing Output Asset @@ -133,22 +120,9 @@ impl fmt::Display for Error { expected: ref e, actual: ref a, } => write!(f, "different id: expected {}, actual {}", e, a), - Error::NonStandardSighashType(ref sht) => { - write!(f, "non-standard sighash type: {}", sht) - } Error::InvalidMagic => f.write_str("invalid magic"), Error::InvalidSeparator => f.write_str("invalid separator"), - Error::UnsignedTxHasScriptSigs => { - f.write_str("the unsigned transaction has script sigs") - } - Error::UnsignedTxHasScriptWitnesses => { - f.write_str("the unsigned transaction has script witnesses") - } - Error::MustHaveUnsignedTx => { - f.write_str("partially signed transactions must have an unsigned transaction") - } Error::NoMorePairs => f.write_str("no more key-value pairs for this pset map"), - Error::HashParseError(e) => write!(f, "Hash Parse Error: {}", e), Error::InvalidPreimageHashPair { ref preimage, ref hash, @@ -177,13 +151,6 @@ impl fmt::Display for Error { Error::MissingOutputCount => f.write_str("PSET missing output count"), Error::MissingInputPrevTxId => f.write_str("PSET input missing previous txid"), Error::MissingInputPrevVout => f.write_str("PSET input missing previous output index"), - Error::SecpScalarSizeError(actual) => { - write!( - f, - "PSET blinding scalars must be 32 bytes. Found {} bytes", - actual - ) - } Error::MissingOutputValue => f.write_str( "PSET output missing value. Must have \ at least one of explicit/confidential value set", @@ -217,13 +184,6 @@ impl fmt::Display for Error { impl error::Error for Error {} -#[doc(hidden)] -impl From for Error { - fn from(e: hashes::FromSliceError) -> Error { - Error::HashParseError(e) - } -} - impl From for Error { fn from(err: encode::Error) -> Self { match err { diff --git a/src/pset/macros.rs b/src/pset/macros.rs index a6e637ed..7928c746 100644 --- a/src/pset/macros.rs +++ b/src/pset/macros.rs @@ -211,8 +211,7 @@ macro_rules! impl_pset_hash_deserialize { ($hash_type:ty) => { impl $crate::pset::serialize::Deserialize for $hash_type { fn deserialize(bytes: &[u8]) -> Result { - <$hash_type>::from_slice(&bytes[..]) - .map_err(|e| $crate::pset::Error::from(e).into()) + $crate::encode::deserialize(bytes).map(Self::from_byte_array) } } }; diff --git a/src/pset/map/global.rs b/src/pset/map/global.rs index 4f9b0e24..0c1cb018 100644 --- a/src/pset/map/global.rs +++ b/src/pset/map/global.rs @@ -58,7 +58,7 @@ const PSBT_ELEMENTS_GLOBAL_TX_MODIFIABLE: u8 = 0x01; /// Global transaction data #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "actual_serde"))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TxData { /// Transaction version. Must be 2. pub version: u32, @@ -93,7 +93,7 @@ impl Default for TxData { /// A key-value map for global data. #[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "actual_serde"))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Global { /// Global transaction data #[cfg_attr(feature = "serde", serde(flatten))] diff --git a/src/pset/map/input.rs b/src/pset/map/input.rs index 72702525..ca54d56a 100644 --- a/src/pset/map/input.rs +++ b/src/pset/map/input.rs @@ -26,6 +26,7 @@ use crate::{schnorr, AssetId, ContractHash}; use crate::{confidential, locktime}; use crate::encode::{self, Decodable}; use crate::hashes::{self, hash160, ripemd160, sha256, sha256d, Hash}; +use crate::issuance::AssetEntropy; use crate::pset::map::Map; use crate::pset::raw; use crate::pset::serialize; @@ -171,7 +172,7 @@ const PSBT_ELEMENTS_IN_BLINDED_ISSUANCE: u8 = 0x15; /// A key-value map for an input of the corresponding index in the unsigned /// transaction. #[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "actual_serde"))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Input { /// The non-witness transaction this input spends from. Should only be /// [`std::option::Option::Some`] for inputs which spend non-segwit outputs or @@ -560,7 +561,7 @@ impl Input { AssetId::generate_asset_entropy(prevout, contract_hash) } else { // re-issuance - sha256::Midstate::from_byte_array(self.issuance_asset_entropy.unwrap_or_default()) + AssetEntropy::from_byte_array(self.issuance_asset_entropy.unwrap_or_default()) }; let asset_id = AssetId::from_entropy(entropy); let token_id = diff --git a/src/pset/map/output.rs b/src/pset/map/output.rs index d86d3389..69e361c4 100644 --- a/src/pset/map/output.rs +++ b/src/pset/map/output.rs @@ -87,7 +87,7 @@ const PSBT_ELEMENTS_OUT_BLIND_ASSET_PROOF: u8 = 0x0a; /// A key-value map for an output of the corresponding index in the unsigned /// transaction. #[derive(Clone, Default, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "actual_serde"))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Output { /// The redeem script for this output. pub redeem_script: Option