diff --git a/src/genesis.rs b/src/genesis.rs index 4aed10ae..d10367eb 100644 --- a/src/genesis.rs +++ b/src/genesis.rs @@ -15,12 +15,11 @@ //! Helpers to calculate the genesis block for a given network. use bitcoin::secp256k1::impl_array_newtype; -use secp256k1_zkp::Tweak; use crate::hashes::{sha256, HashEngine}; use crate::opcodes::all::OP_RETURN; use crate::opcodes::OP_TRUE; use crate::{confidential, script, AssetId, Block, BlockExtData, BlockHash, BlockHeader, LockTime, Script, Sequence, Transaction, TxIn, TxInWitness, TxOut, TxOutWitness}; -use crate::{AssetIssuance, ContractHash, OutPoint, Txid}; +use crate::{AssetBlindingNonce, AssetEntropy, AssetIssuance, ContractHash, OutPoint, Txid}; use crate::confidential::Nonce; /// Parameters that influence chain consensus. The contents of the genesis block for a given network @@ -146,8 +145,8 @@ fn liquid_genesis_asset_tx(network_params: &NetworkParams) -> Option Self { + pub const 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] { + pub const fn as_byte_array(&self) -> &[u8; 32] { &self.0 } /// The raw bytes within the wrapper type. - pub fn to_byte_array(self) -> [u8; 32] { + pub const 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 { + const fn from_midstate(value: crate::hashes::sha256::Midstate) -> Self { Self(value.to_parts().0) } } diff --git a/src/issuance.rs b/src/issuance.rs index 82e31e26..755be132 100644 --- a/src/issuance.rs +++ b/src/issuance.rs @@ -1,4 +1,4 @@ -// Rust Elements Library +// Rust Elements Libraryss // Written in 2019 by // The Elements developers // @@ -14,8 +14,10 @@ //! Asset Issuance +use core::fmt; use std::io; +use crate::confidential::AssetBlindingFactor; use crate::encode::{self, Encodable, Decodable}; use crate::hashes::{hash_newtype, sha256, sha256d}; use crate::fast_merkle_root::fast_merkle_root; @@ -51,6 +53,158 @@ impl_sha256_midstate_wrapper! { pub struct AssetEntropy([u8; 32]); } +impl AssetEntropy { + /// The all-zeroes "entropy" used for new issuances (vs reissuances). + pub const NEW_ISSUANCE: Self = Self([0; 32]); + + /// Re-interpret the asset entropy as a contract hash. + pub fn into_contract_hash(self) -> ContractHash { + ContractHash::from_byte_array(self.0) + } +} + +impl Encodable for AssetEntropy { + fn consensus_encode(&self, e: W) -> Result { + self.0.consensus_encode(e) + } +} + +impl Decodable for AssetEntropy { + fn consensus_decode(d: D) -> Result { + <[u8; 32]>::consensus_decode(d).map(Self) + } +} + +encoding::encoder_newtype_exact! { + /// Encoder for the [`OutPoint`] type. + #[derive(Clone, Debug)] + pub struct AssetEntropyEncoder<'e>(encoding::ArrayRefEncoder<'e, 32>); +} + +impl encoding::Encode for AssetEntropy { + type Encoder<'e> = AssetEntropyEncoder<'e>; + + fn encoder(&self) -> Self::Encoder<'_> { + AssetEntropyEncoder::new(encoding::ArrayRefEncoder::without_length_prefix(&self.0)) + } +} + +decoder_newtype! { + /// Decoder for the [`AssetEntropy`] type. + #[derive(Default)] + pub struct AssetEntropyDecoder(encoding::ArrayDecoder<32>); + + /// Decoder error for the [`AssetEntropy`] type. + #[derive(Clone, PartialEq, Eq, Debug)] + pub struct AssetEntropyDecoderError(encoding::UnexpectedEofError); + const ERROR_DISPLAY = "error decoding asset entropy"; + + impl Decode for AssetEntropy { + fn convert_inner(bytes) -> Result<_, UnexpectedEofError> { + Ok(AssetEntropy::from_byte_array(bytes)) + } + } +} + +/// The blinding factor used to derive an asset commitment from an asset. +/// +/// This type represents either [`Self::NEW_ISSUANCE`], indicating that an asset +/// issuance is of a new asset, or for a reissuance, the [`AssetBlindingFactor`] +/// used to blind the reissuance token (which must be blinded in order to be +/// spent, due to a quirk in the Elements consensus code.) +/// +/// Conceputally this can be thought of as an `Option`, except +/// that there are no invalid values; [`AssetBlindingNonce::from_byte_array`] will +/// always succeed. However, if an out-of-range value is used, the transaction will +/// fail validation no matter what reissuance token is used. +/// +/// Also, **unlike [`AssetBlindingFactor`], this type represents public data**. You +/// can convert a blinding factor into a "blinding nonce", and while this conversion +/// is technically a no-op, conceptually it represents choosing to make the blinding +/// factor public. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Default)] +pub struct AssetBlindingNonce([u8; 32]); + +impl AssetBlindingNonce { + /// A null blinding nonce, representing a new issuance (vs a reissuance). + pub const NEW_ISSUANCE: Self = Self([0; 32]); + + /// Constructs this wrapper struct from raw bytes. + pub const fn from_byte_array(inner: [u8; 32]) -> Self { + Self(inner) + } + + /// The raw bytes within the wrapper type. + pub const fn as_byte_array(&self) -> &[u8; 32] { + &self.0 + } + + /// The raw bytes within the wrapper type. + pub const fn to_byte_array(self) -> [u8; 32] { + self.0 + } + + /// Whether this is the null "new issuance" blinding nonce. + pub fn is_null(&self) -> bool { + // This is surprisingly annoying to make into a constfn, so we don't + // bother for now. + *self == Self::NEW_ISSUANCE + } + + /// Reinterpret an asset blinding factor as a [`AssetBlindingNonce`]. + /// + /// This is something of a dangerous function, since in general blinding factors should + /// be considered secret data, while blinding nonces are public (they are encoded on + /// the blockchain). So callers of this function should be sure that this is a blinding + /// factor that they intend to reveal.) + pub fn from_blinding_factor(bf: AssetBlindingFactor) -> Self { + Self(*bf.into_inner().as_ref()) + } +} + +impl Encodable for AssetBlindingNonce { + fn consensus_encode(&self, e: W) -> Result { + self.0.consensus_encode(e) + } +} + +impl Decodable for AssetBlindingNonce { + fn consensus_decode(d: D) -> Result { + <[u8; 32]>::consensus_decode(d).map(Self) + } +} + +encoding::encoder_newtype_exact! { + /// Encoder for the [`OutPoint`] type. + #[derive(Clone, Debug)] + pub struct AssetBlindingNonceEncoder<'e>(encoding::ArrayRefEncoder<'e, 32>); +} + +impl encoding::Encode for AssetBlindingNonce { + type Encoder<'e> = AssetBlindingNonceEncoder<'e>; + + fn encoder(&self) -> Self::Encoder<'_> { + AssetBlindingNonceEncoder::new(encoding::ArrayRefEncoder::without_length_prefix(&self.0)) + } +} + +decoder_newtype! { + /// Decoder for the [`AssetBlindingNonce`] type. + #[derive(Default)] + pub struct AssetBlindingNonceDecoder(encoding::ArrayDecoder<32>); + + /// Decoder error for the [`AssetBlindingNonce`] type. + #[derive(Clone, PartialEq, Eq, Debug)] + pub struct AssetBlindingNonceDecoderError(encoding::UnexpectedEofError); + const ERROR_DISPLAY = "error decoding asset blinding nonce"; + + impl Decode for AssetBlindingNonce { + fn convert_inner(bytes) -> Result<_, UnexpectedEofError> { + Ok(AssetBlindingNonce::from_byte_array(bytes)) + } + } +} + impl_sha256_midstate_wrapper! { /// An issued asset ID. pub struct AssetId([u8; 32]); @@ -189,6 +343,37 @@ impl Decodable for AssetId { } } +encoding::encoder_newtype_exact! { + /// Encoder for the [`OutPoint`] type. + #[derive(Clone, Debug)] + pub struct AssetIdEncoder<'e>(encoding::ArrayRefEncoder<'e, 32>); +} + +impl encoding::Encode for AssetId { + type Encoder<'e> = AssetIdEncoder<'e>; + + fn encoder(&self) -> Self::Encoder<'_> { + AssetIdEncoder::new(encoding::ArrayRefEncoder::without_length_prefix(&self.0)) + } +} + +decoder_newtype! { + /// Decoder for the [`AssetId`] type. + #[derive(Default)] + pub struct AssetIdDecoder(encoding::ArrayDecoder<32>); + + /// Decoder error for the [`AssetId`] type. + #[derive(Clone, PartialEq, Eq, Debug)] + pub struct AssetIdDecoderError(encoding::UnexpectedEofError); + const ERROR_DISPLAY = "error decoding asset ID"; + + impl Decode for AssetId { + fn convert_inner(bytes) -> Result<_, UnexpectedEofError> { + Ok(AssetId::from_byte_array(bytes)) + } + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/lib.rs b/src/lib.rs index 68b2a1c7..ea5b713f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,7 +89,12 @@ pub use crate::confidential::{RangeProof, SurjectionProof}; pub use crate::ext::{ReadExt, WriteExt}; pub use crate::fast_merkle_root::fast_merkle_root; pub use crate::hash_types::*; -pub use crate::issuance::{AssetEntropy, AssetId, ContractHash}; +pub use crate::issuance::{ + AssetBlindingNonce, AssetBlindingNonceDecoder, AssetBlindingNonceDecoderError, + AssetBlindingNonceEncoder, AssetEntropy, AssetEntropyDecoder, AssetEntropyDecoderError, + AssetEntropyEncoder, AssetId, AssetIdDecoder, AssetIdDecoderError, AssetIdEncoder, + ContractHash, +}; pub use crate::locktime::LockTime; pub use crate::schnorr::{SchnorrSig, SchnorrSigError}; pub use crate::script::Script; diff --git a/src/pset/map/input.rs b/src/pset/map/input.rs index 1aa02d75..9c0a65b3 100644 --- a/src/pset/map/input.rs +++ b/src/pset/map/input.rs @@ -21,7 +21,7 @@ use std::{ }; use crate::taproot::{ControlBlock, LeafVersion, TapNodeHash, TapLeafHash}; -use crate::{schnorr, AssetId, ContractHash}; +use crate::{schnorr, AssetId}; use crate::{confidential, locktime}; use crate::encode::{self, Decodable}; @@ -32,10 +32,10 @@ use crate::pset::raw; use crate::pset::serialize; use crate::pset::{self, error, Error}; use crate::{transaction::SighashTypeParseError, SchnorrSighashType}; -use crate::{AssetIssuance, BlockHash, EcdsaSighashType, PeginWitness, RangeProof, Script, Transaction, TxIn, TxOut, Txid, SurjectionProof}; +use crate::{AssetBlindingNonce, AssetIssuance, BlockHash, EcdsaSighashType, PeginWitness, RangeProof, Script, Transaction, TxIn, TxOut, Txid, SurjectionProof}; use bitcoin::bip32::KeySource; use bitcoin::{PublicKey, key::XOnlyPublicKey}; -use secp256k1_zkp::{self, Tweak, ZERO_TWEAK}; +use secp256k1_zkp; use crate::{OutPoint, Sequence}; @@ -258,9 +258,9 @@ pub struct Input { /// Issuance inflation keys commitment pub issuance_inflation_keys_comm: Option, /// Issuance blinding nonce - pub issuance_blinding_nonce: Option, + pub issuance_blinding_nonce: Option, /// Issuance asset entropy - pub issuance_asset_entropy: Option<[u8; 32]>, + pub issuance_asset_entropy: Option, /// input utxo rangeproof pub in_utxo_rangeproof: Option, /// Proof that blinded issuance matches the commitment @@ -524,19 +524,21 @@ impl Input { /// Compute the issuance asset ids from pset. This function does not check /// whether there is an issuance in this input. Returns (`asset_id`, `token_id`) pub fn issuance_ids(&self) -> (AssetId, AssetId) { - let issue_nonce = self.issuance_blinding_nonce.unwrap_or(ZERO_TWEAK); - let entropy = if issue_nonce == ZERO_TWEAK { + let issue_nonce = self.issuance_blinding_nonce.unwrap_or_default(); + let entropy = if issue_nonce.is_null() { // new issuance let prevout = OutPoint { txid: self.previous_txid, vout: self.previous_output_index, }; - let contract_hash = - ContractHash::from_byte_array(self.issuance_asset_entropy.unwrap_or_default()); + let contract_hash = self + .issuance_asset_entropy + .unwrap_or_default() + .into_contract_hash(); AssetId::generate_asset_entropy(prevout, contract_hash) } else { // re-issuance - AssetEntropy::from_byte_array(self.issuance_asset_entropy.unwrap_or_default()) + self.issuance_asset_entropy.unwrap_or_default() }; let asset_id = AssetId::from_entropy(entropy); let token_id = @@ -558,7 +560,7 @@ impl Input { /// Get the issuance for this tx input pub fn asset_issuance(&self) -> AssetIssuance { AssetIssuance { - asset_blinding_nonce: *self.issuance_blinding_nonce.as_ref().unwrap_or(&ZERO_TWEAK), + asset_blinding_nonce: self.issuance_blinding_nonce.unwrap_or_default(), asset_entropy: self.issuance_asset_entropy.unwrap_or_default(), amount: match (self.issuance_value_amount, self.issuance_value_comm) { (None, None) => confidential::Value::Null, @@ -752,10 +754,10 @@ impl Map for Input { impl_pset_prop_insert_pair!(self.issuance_inflation_keys_comm <= | ); } PSBT_ELEMENTS_IN_ISSUANCE_BLINDING_NONCE => { - impl_pset_prop_insert_pair!(self.issuance_blinding_nonce <= | ); + impl_pset_prop_insert_pair!(self.issuance_blinding_nonce <= | ); } PSBT_ELEMENTS_IN_ISSUANCE_ASSET_ENTROPY => { - impl_pset_prop_insert_pair!(self.issuance_asset_entropy <= | ); + impl_pset_prop_insert_pair!(self.issuance_asset_entropy <= | ); } PSBT_ELEMENTS_IN_UTXO_RANGEPROOF => { impl_pset_prop_insert_pair!(self.in_utxo_rangeproof <= | ); @@ -1182,11 +1184,11 @@ where #[cfg(test)] mod tests { - use secp256k1_zkp::ZERO_TWEAK; - - use crate::confidential; + use crate::{AssetBlindingNonce, confidential}; use crate::pset::PartiallySignedTransaction; - use crate::{AssetIssuance, LockTime, Transaction, TxIn, TxInWitness}; + use crate::{AssetEntropy, AssetIssuance, LockTime, Transaction, TxIn, TxInWitness}; + + const DUMMY_ENTROPY: AssetEntropy = AssetEntropy::from_byte_array([1; 32]); // See `pset::map::output::tests::from_tx_does_not_spuriously_set_proofs_on_unblinded_outputs`. // Same principle, but for the asset issuance rangeproofs in the input witnesses. @@ -1194,8 +1196,8 @@ mod tests { fn from_tx_does_not_spuriously_set_proofs_on_explicit_issuance() { let txin = TxIn { asset_issuance: AssetIssuance { - asset_blinding_nonce: ZERO_TWEAK, - asset_entropy: [1u8; 32], + asset_blinding_nonce: AssetBlindingNonce::NEW_ISSUANCE, + asset_entropy: DUMMY_ENTROPY, amount: confidential::Value::Explicit(1000), inflation_keys: confidential::Value::Null, }, diff --git a/src/pset/serialize.rs b/src/pset/serialize.rs index 28b52e05..cf39eaca 100644 --- a/src/pset/serialize.rs +++ b/src/pset/serialize.rs @@ -24,12 +24,12 @@ use crate::encode::{ self, deserialize, deserialize_partial, serialize, Decodable, Encodable, VarInt, }; use crate::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; -use crate::{AssetId, BlockHash, RangeProof, Script, SurjectionProof, Transaction, TxOut, Txid}; +use crate::{AssetBlindingNonce, AssetEntropy, AssetId, BlockHash, RangeProof, Script, SurjectionProof, Transaction, TxOut, Txid}; use bitcoin; use bitcoin::bip32::{ChildNumber, Fingerprint, KeySource}; use bitcoin::{key::XOnlyPublicKey, PublicKey}; use internals::slice::SliceExt; -use secp256k1_zkp::{self, Tweak}; +use secp256k1_zkp; use super::map::{PsbtSighashType, TapTree}; use crate::schnorr; @@ -59,6 +59,8 @@ pub fn serialize_hex(data: &T) -> String { impl_pset_de_serialize!(Transaction); impl_pset_de_serialize!(TxOut); +impl_pset_de_serialize!(AssetBlindingNonce); +impl_pset_de_serialize!(AssetEntropy); impl_pset_de_serialize!(AssetId); impl_pset_de_serialize!(u8); impl_pset_de_serialize!(u32); @@ -108,19 +110,6 @@ impl Deserialize for VarInt { } } -impl Serialize for Tweak { - fn serialize(&self) -> Vec { - encode::serialize(self.as_ref()) - } -} - -impl Deserialize for Tweak { - fn deserialize(bytes: &[u8]) -> Result { - let x = deserialize::<[u8; 32]>(bytes)?; - Tweak::from_slice(&x).map_err(|_| encode::Error::ParseFailed("invalid Tweak")) - } -} - impl Serialize for AssetBlindingFactor { fn serialize(&self) -> Vec { encode::serialize(self.into_inner().as_ref()) diff --git a/src/transaction/decoders.rs b/src/transaction/decoders.rs index 9e34e931..26df90e7 100644 --- a/src/transaction/decoders.rs +++ b/src/transaction/decoders.rs @@ -80,30 +80,26 @@ decoder_newtype! { /// Decoder for the [`AssetIssuance`] type. #[derive(Default)] pub struct AssetIssuanceDecoder(Decoder4< - ArrayDecoder<32>, - ArrayDecoder<32>, + crate::AssetBlindingNonceDecoder, + crate::AssetEntropyDecoder, crate::confidential::ValueDecoder, crate::confidential::ValueDecoder, >); /// Decoder error for the [`AssetIssuance`] type. #[derive(Clone, PartialEq, Eq, Debug)] - pub struct AssetIssuanceDecoderError(enum AssetIssuanceDecoderErrorInner { - Decode(Decoder4Error< - UnexpectedEofError, - UnexpectedEofError, - crate::confidential::ValueDecoderError, - crate::confidential::ValueDecoderError, - >), - InvalidTweak(secp256k1_zkp::Error), - }); + pub struct AssetIssuanceDecoderError(Decoder4Error< + crate::AssetBlindingNonceDecoderError, + crate::AssetEntropyDecoderError, + crate::confidential::ValueDecoderError, + crate::confidential::ValueDecoderError, + >); + const ERROR_DISPLAY = "error decoding asset issuance"; impl Decode for AssetIssuance { fn convert_inner(output) -> Result<_, AssetIssuanceDecoderError> { let (asset_blinding_nonce, asset_entropy, amount, inflation_keys) = output; Ok(AssetIssuance { - asset_blinding_nonce: secp256k1_zkp::Tweak::from_inner(asset_blinding_nonce) - .map_err(AssetIssuanceDecoderErrorInner::InvalidTweak) - .map_err(AssetIssuanceDecoderError)?, + asset_blinding_nonce, asset_entropy, amount, inflation_keys, @@ -112,26 +108,6 @@ decoder_newtype! { } } -impl fmt::Display for AssetIssuanceDecoderError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use AssetIssuanceDecoderErrorInner as Inner; - match self.0 { - Inner::Decode(_) => f.write_str("error decoding asset issuance"), - Inner::InvalidTweak(_) => f.write_str("asset issuance had out-of-range blinding nonce"), - } - } -} - -impl std::error::Error for AssetIssuanceDecoderError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use AssetIssuanceDecoderErrorInner as Inner; - match self.0 { - Inner::Decode(ref e) => Some(e), - Inner::InvalidTweak(ref e) => Some(e), - } - } -} - decoder_state_machine! { /// Decoder for the [`TxIn`] type. pub struct TxInDecoder(enum TxInDecoderInner { diff --git a/src/transaction/encoders.rs b/src/transaction/encoders.rs index 17bcc0dd..0dc6164d 100644 --- a/src/transaction/encoders.rs +++ b/src/transaction/encoders.rs @@ -64,8 +64,8 @@ encoder_newtype_exact! { /// Encoder for the [`AssetIssuance`] type. #[derive(Clone, Debug)] pub struct AssetIssuanceEncoder<'e>(Encoder4< - ArrayRefEncoder<'e, 32>, - ArrayRefEncoder<'e, 32>, + crate::AssetBlindingNonceEncoder<'e>, + crate::AssetEntropyEncoder<'e>, crate::confidential::ValueEncoder<'e>, crate::confidential::ValueEncoder<'e>, >); @@ -76,8 +76,8 @@ impl Encode for AssetIssuance { fn encoder(&self) -> Self::Encoder<'_> { AssetIssuanceEncoder::new(Encoder4::new( - ArrayRefEncoder::without_length_prefix(self.asset_blinding_nonce.as_ref()), - ArrayRefEncoder::without_length_prefix(&self.asset_entropy), + self.asset_blinding_nonce.encoder(), + self.asset_entropy.encoder(), self.amount.encoder(), self.inflation_keys.encoder(), )) diff --git a/src/transaction/mod.rs b/src/transaction/mod.rs index 1f3c6847..8b9ad701 100644 --- a/src/transaction/mod.rs +++ b/src/transaction/mod.rs @@ -28,16 +28,13 @@ use bitcoin::{self, VarInt}; use bitcoin::hashes::Hash as _; use crate::hashes::{sha256d, HashEngine as _}; -use crate::{confidential, ContractHash}; +use crate::confidential; use crate::encode::{self, Encodable, Decodable}; -use crate::issuance::{AssetEntropy, AssetId}; +use crate::issuance::{AssetBlindingNonce, AssetEntropy, AssetId}; use crate::opcodes; use crate::parse::impl_parse_str_through_int; use crate::script::Instruction; use crate::{LockTime, RangeProof, Script, SurjectionProof, Txid, Wtxid}; -use secp256k1_zkp::{ - Tweak, ZERO_TWEAK, -}; pub use self::decoders::{AssetIssuanceDecoder, AssetIssuanceDecoderError, SequenceDecoder, SequenceDecoderError, TransactionDecoder, TransactionDecoderError, TxInDecoder, TxInDecoderError, TxInWitnessDecoder, TxInWitnessDecoderError, TxOutDecoder, TxOutDecoderError, TxOutWitnessDecoder, TxOutWitnessDecoderError}; pub use self::encoders::{AssetIssuanceEncoder, SequenceEncoder, TransactionEncoder, TxInEncoder, TxInWitnessEncoder, TxOutEncoder, TxOutWitnessEncoder}; @@ -50,9 +47,9 @@ pub use self::witness::{Witness, WitnessDecoder, WitnessDecoderError, WitnessEnc #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] pub struct AssetIssuance { /// Zero for a new asset issuance; otherwise a blinding factor for the input - pub asset_blinding_nonce: Tweak, + pub asset_blinding_nonce: AssetBlindingNonce, /// Freeform entropy field - pub asset_entropy: [u8; 32], + pub asset_entropy: AssetEntropy, /// Amount of asset to issue pub amount: confidential::Value, /// Amount of inflation keys to issue @@ -63,8 +60,8 @@ impl AssetIssuance { /// Create a null issuance. pub fn null() -> Self { AssetIssuance { - asset_blinding_nonce: ZERO_TWEAK, - asset_entropy: [0; 32], + asset_blinding_nonce: AssetBlindingNonce::NEW_ISSUANCE, + asset_entropy: AssetEntropy::NEW_ISSUANCE, amount: confidential::Value::Null, inflation_keys: confidential::Value::Null, } @@ -548,13 +545,15 @@ impl TxIn { /// Compute the issuance asset ids from this [`TxIn`]. This function does not check /// whether there is an issuance in this input. Returns (`asset_id`, `token_id`) pub fn issuance_ids(&self) -> (AssetId, AssetId) { - let entropy = if self.asset_issuance.asset_blinding_nonce == ZERO_TWEAK { - let contract_hash = - ContractHash::from_byte_array(self.asset_issuance.asset_entropy); + let entropy = if self.asset_issuance.asset_blinding_nonce.is_null() { + let contract_hash = self + .asset_issuance + .asset_entropy + .into_contract_hash(); AssetId::generate_asset_entropy(self.previous_output, contract_hash) } else { // re-issuance - AssetEntropy::from_byte_array(self.asset_issuance.asset_entropy) + self.asset_issuance.asset_entropy }; let asset_id = AssetId::from_entropy(entropy); let token_id = @@ -1198,9 +1197,9 @@ impl ::std::error::Error for SighashTypeParseError {} mod tests { use std::str::FromStr; - use crate::{encode::serialize, pset::PartiallySignedTransaction}; + use crate::issuance::AssetBlindingNonce; +use crate::{encode::serialize, pset::PartiallySignedTransaction}; use crate::confidential; - use secp256k1_zkp::{self, ZERO_TWEAK}; use crate::script; use super::*; @@ -2045,8 +2044,8 @@ mod tests { assert_eq!( tx.input[0].asset_issuance, AssetIssuance { - asset_blinding_nonce: ZERO_TWEAK, - asset_entropy: [0; 32], + asset_blinding_nonce: AssetBlindingNonce::NEW_ISSUANCE, + asset_entropy: AssetEntropy::NEW_ISSUANCE, amount: confidential::Value::from_commitment( &[ 0x09, 0x81, 0x65, 0x4e, 0xb5, 0xcc, 0xd9, 0x92, 0x7b, 0x8b, 0xea, 0x94, 0x99, 0x7d, 0xce, 0x4a, diff --git a/src/transaction/pegin_witness.rs b/src/transaction/pegin_witness.rs index c80d66b8..9b2326fd 100644 --- a/src/transaction/pegin_witness.rs +++ b/src/transaction/pegin_witness.rs @@ -347,7 +347,7 @@ pub struct PeginDataDecoder { encoding::ArrayDecoder<8>, // asset ID ExactLengthDecoder, - encoding::ArrayDecoder<32>, + crate::AssetIdDecoder, // genesis hash ExactLengthDecoder, GenesisHashDecoder, @@ -372,7 +372,7 @@ impl Default for PeginDataDecoder { ExactLengthDecoder::new(8), encoding::ArrayDecoder::new(), ExactLengthDecoder::new(32), - encoding::ArrayDecoder::new(), + crate::AssetIdDecoder::default(), ExactLengthDecoder::new(32), GenesisHashDecoder::new(), ), @@ -388,6 +388,7 @@ impl Default for PeginDataDecoder { #[derive(Clone, PartialEq, Eq, Debug)] enum PeginDataDecoderErrorInner { + AssetId(crate::AssetIdDecoderError), ClaimScript(bitcoin::blockdata::script::ScriptBufDecoderError), GenesisHash(GenesisHashDecoderError), Length(ExactLengthDecoderError), @@ -421,6 +422,7 @@ impl std::error::Error for PeginWitnessDecoderError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { use PeginDataDecoderErrorInner as Inner; match self.inner { + Inner::AssetId(ref e) => Some(e), Inner::ClaimScript(ref e) => Some(e), Inner::GenesisHash(ref e) => Some(e), Inner::Length(ref e) => Some(e), @@ -441,7 +443,7 @@ impl PeginWitnessDecoderError { ExactLengthDecoderError, UnexpectedEofError, ExactLengthDecoderError, - UnexpectedEofError, + crate::AssetIdDecoderError, ExactLengthDecoderError, GenesisHashDecoderError, >, @@ -464,7 +466,7 @@ impl PeginWitnessDecoderError { Dec3Err::Second(Dec6Err::Third(error)) => Self { field: "asset ID", inner: PeginDataDecoderErrorInner::Length(error) }, Dec3Err::Second(Dec6Err::Fourth(error)) => - Self { field: "asset ID", inner: PeginDataDecoderErrorInner::Eof(error) }, + Self { field: "asset ID", inner: PeginDataDecoderErrorInner::AssetId(error) }, Dec3Err::Second(Dec6Err::Fifth(error)) => Self { field: "genesis hash", inner: PeginDataDecoderErrorInner::Length(error) }, Dec3Err::Second(Dec6Err::Sixth(error)) => Self { @@ -514,7 +516,7 @@ impl encoding::Decoder for PeginDataDecoder { Ok(PeginData { value: u64::from_le_bytes(value), - asset_id: AssetId::from_byte_array(asset_id), + asset_id, genesis_hash, claim_script, transaction,