|
14 | 14 |
|
15 | 15 | //! Asset Issuance |
16 | 16 |
|
| 17 | +use core::fmt; |
17 | 18 | use std::io; |
18 | 19 |
|
| 20 | +use crate::confidential::AssetBlindingFactor; |
19 | 21 | use crate::encode::{self, Encodable, Decodable}; |
20 | 22 | use crate::hashes::{hash_newtype, sha256, sha256d}; |
21 | 23 | use crate::fast_merkle_root::fast_merkle_root; |
@@ -51,6 +53,158 @@ impl_sha256_midstate_wrapper! { |
51 | 53 | pub struct AssetEntropy([u8; 32]); |
52 | 54 | } |
53 | 55 |
|
| 56 | +impl AssetEntropy { |
| 57 | + /// The all-zeroes "entropy" used for new issuances (vs reissuances). |
| 58 | + pub const NEW_ISSUANCE: Self = Self([0; 32]); |
| 59 | + |
| 60 | + /// Re-interpret the asset entropy as a contract hash. |
| 61 | + pub fn into_contract_hash(self) -> ContractHash { |
| 62 | + ContractHash::from_byte_array(self.0) |
| 63 | + } |
| 64 | +} |
| 65 | + |
| 66 | +impl Encodable for AssetEntropy { |
| 67 | + fn consensus_encode<W: io::Write>(&self, e: W) -> Result<usize, encode::Error> { |
| 68 | + self.0.consensus_encode(e) |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +impl Decodable for AssetEntropy { |
| 73 | + fn consensus_decode<D: io::Read>(d: D) -> Result<Self, encode::Error> { |
| 74 | + <[u8; 32]>::consensus_decode(d).map(Self) |
| 75 | + } |
| 76 | +} |
| 77 | + |
| 78 | +encoding::encoder_newtype_exact! { |
| 79 | + /// Encoder for the [`AssetEntropyEncoder`] type. |
| 80 | + #[derive(Clone, Debug)] |
| 81 | + pub struct AssetEntropyEncoder<'e>(encoding::ArrayRefEncoder<'e, 32>); |
| 82 | +} |
| 83 | + |
| 84 | +impl encoding::Encode for AssetEntropy { |
| 85 | + type Encoder<'e> = AssetEntropyEncoder<'e>; |
| 86 | + |
| 87 | + fn encoder(&self) -> Self::Encoder<'_> { |
| 88 | + AssetEntropyEncoder::new(encoding::ArrayRefEncoder::without_length_prefix(&self.0)) |
| 89 | + } |
| 90 | +} |
| 91 | + |
| 92 | +decoder_newtype! { |
| 93 | + /// Decoder for the [`AssetEntropy`] type. |
| 94 | + #[derive(Default)] |
| 95 | + pub struct AssetEntropyDecoder(encoding::ArrayDecoder<32>); |
| 96 | + |
| 97 | + /// Decoder error for the [`AssetEntropy`] type. |
| 98 | + #[derive(Clone, PartialEq, Eq, Debug)] |
| 99 | + pub struct AssetEntropyDecoderError(encoding::UnexpectedEofError); |
| 100 | + const ERROR_DISPLAY = "error decoding asset entropy"; |
| 101 | + |
| 102 | + impl Decode for AssetEntropy { |
| 103 | + fn convert_inner(bytes) -> Result<_, UnexpectedEofError> { |
| 104 | + Ok(AssetEntropy::from_byte_array(bytes)) |
| 105 | + } |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +/// The blinding factor used to derive an asset commitment from an asset. |
| 110 | +/// |
| 111 | +/// This type represents either [`Self::NEW_ISSUANCE`], indicating that an asset |
| 112 | +/// issuance is of a new asset, or for a reissuance, the [`AssetBlindingFactor`] |
| 113 | +/// used to blind the reissuance token (which must be blinded in order to be |
| 114 | +/// spent, due to a quirk in the Elements consensus code.) |
| 115 | +/// |
| 116 | +/// Conceptually this can be thought of as an `Option<AssetBlindingFactor>`, except |
| 117 | +/// that there are no invalid values; [`AssetBlindingNonce::from_byte_array`] will |
| 118 | +/// always succeed. However, if an out-of-range value is used, the transaction will |
| 119 | +/// fail validation no matter what reissuance token is used. |
| 120 | +/// |
| 121 | +/// Also, **unlike [`AssetBlindingFactor`], this type represents public data**. You |
| 122 | +/// can convert a blinding factor into a "blinding nonce", and while this conversion |
| 123 | +/// is technically a no-op, conceptually it represents choosing to make the blinding |
| 124 | +/// factor public. |
| 125 | +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Default)] |
| 126 | +pub struct AssetBlindingNonce([u8; 32]); |
| 127 | + |
| 128 | +impl AssetBlindingNonce { |
| 129 | + /// A null blinding nonce, representing a new issuance (vs a reissuance). |
| 130 | + pub const NEW_ISSUANCE: Self = Self([0; 32]); |
| 131 | + |
| 132 | + /// Constructs this wrapper struct from raw bytes. |
| 133 | + pub const fn from_byte_array(inner: [u8; 32]) -> Self { |
| 134 | + Self(inner) |
| 135 | + } |
| 136 | + |
| 137 | + /// The raw bytes within the wrapper type. |
| 138 | + pub const fn as_byte_array(&self) -> &[u8; 32] { |
| 139 | + &self.0 |
| 140 | + } |
| 141 | + |
| 142 | + /// The raw bytes within the wrapper type. |
| 143 | + pub const fn to_byte_array(self) -> [u8; 32] { |
| 144 | + self.0 |
| 145 | + } |
| 146 | + |
| 147 | + /// Whether this is the null "new issuance" blinding nonce. |
| 148 | + pub fn is_null(&self) -> bool { |
| 149 | + // This is surprisingly annoying to make into a constfn, so we don't |
| 150 | + // bother for now. |
| 151 | + *self == Self::NEW_ISSUANCE |
| 152 | + } |
| 153 | + |
| 154 | + /// Reinterpret an asset blinding factor as a [`AssetBlindingNonce`]. |
| 155 | + /// |
| 156 | + /// This is something of a dangerous function, since in general blinding factors should |
| 157 | + /// be considered secret data, while blinding nonces are public (they are encoded on |
| 158 | + /// the blockchain). So callers of this function should be sure that this is a blinding |
| 159 | + /// factor that they intend to reveal.) |
| 160 | + pub fn from_blinding_factor(bf: AssetBlindingFactor) -> Self { |
| 161 | + Self(*bf.into_inner().as_ref()) |
| 162 | + } |
| 163 | +} |
| 164 | + |
| 165 | +impl Encodable for AssetBlindingNonce { |
| 166 | + fn consensus_encode<W: io::Write>(&self, e: W) -> Result<usize, encode::Error> { |
| 167 | + self.0.consensus_encode(e) |
| 168 | + } |
| 169 | +} |
| 170 | + |
| 171 | +impl Decodable for AssetBlindingNonce { |
| 172 | + fn consensus_decode<D: io::Read>(d: D) -> Result<Self, encode::Error> { |
| 173 | + <[u8; 32]>::consensus_decode(d).map(Self) |
| 174 | + } |
| 175 | +} |
| 176 | + |
| 177 | +encoding::encoder_newtype_exact! { |
| 178 | + /// Encoder for the [`AssetBlindingNonce`] type. |
| 179 | + #[derive(Clone, Debug)] |
| 180 | + pub struct AssetBlindingNonceEncoder<'e>(encoding::ArrayRefEncoder<'e, 32>); |
| 181 | +} |
| 182 | + |
| 183 | +impl encoding::Encode for AssetBlindingNonce { |
| 184 | + type Encoder<'e> = AssetBlindingNonceEncoder<'e>; |
| 185 | + |
| 186 | + fn encoder(&self) -> Self::Encoder<'_> { |
| 187 | + AssetBlindingNonceEncoder::new(encoding::ArrayRefEncoder::without_length_prefix(&self.0)) |
| 188 | + } |
| 189 | +} |
| 190 | + |
| 191 | +decoder_newtype! { |
| 192 | + /// Decoder for the [`AssetBlindingNonce`] type. |
| 193 | + #[derive(Default)] |
| 194 | + pub struct AssetBlindingNonceDecoder(encoding::ArrayDecoder<32>); |
| 195 | + |
| 196 | + /// Decoder error for the [`AssetBlindingNonce`] type. |
| 197 | + #[derive(Clone, PartialEq, Eq, Debug)] |
| 198 | + pub struct AssetBlindingNonceDecoderError(encoding::UnexpectedEofError); |
| 199 | + const ERROR_DISPLAY = "error decoding asset blinding nonce"; |
| 200 | + |
| 201 | + impl Decode for AssetBlindingNonce { |
| 202 | + fn convert_inner(bytes) -> Result<_, UnexpectedEofError> { |
| 203 | + Ok(AssetBlindingNonce::from_byte_array(bytes)) |
| 204 | + } |
| 205 | + } |
| 206 | +} |
| 207 | + |
54 | 208 | impl_sha256_midstate_wrapper! { |
55 | 209 | /// An issued asset ID. |
56 | 210 | pub struct AssetId([u8; 32]); |
@@ -189,6 +343,37 @@ impl Decodable for AssetId { |
189 | 343 | } |
190 | 344 | } |
191 | 345 |
|
| 346 | +encoding::encoder_newtype_exact! { |
| 347 | + /// Encoder for the [`AssetId`] type. |
| 348 | + #[derive(Clone, Debug)] |
| 349 | + pub struct AssetIdEncoder<'e>(encoding::ArrayRefEncoder<'e, 32>); |
| 350 | +} |
| 351 | + |
| 352 | +impl encoding::Encode for AssetId { |
| 353 | + type Encoder<'e> = AssetIdEncoder<'e>; |
| 354 | + |
| 355 | + fn encoder(&self) -> Self::Encoder<'_> { |
| 356 | + AssetIdEncoder::new(encoding::ArrayRefEncoder::without_length_prefix(&self.0)) |
| 357 | + } |
| 358 | +} |
| 359 | + |
| 360 | +decoder_newtype! { |
| 361 | + /// Decoder for the [`AssetId`] type. |
| 362 | + #[derive(Default)] |
| 363 | + pub struct AssetIdDecoder(encoding::ArrayDecoder<32>); |
| 364 | + |
| 365 | + /// Decoder error for the [`AssetId`] type. |
| 366 | + #[derive(Clone, PartialEq, Eq, Debug)] |
| 367 | + pub struct AssetIdDecoderError(encoding::UnexpectedEofError); |
| 368 | + const ERROR_DISPLAY = "error decoding asset ID"; |
| 369 | + |
| 370 | + impl Decode for AssetId { |
| 371 | + fn convert_inner(bytes) -> Result<_, UnexpectedEofError> { |
| 372 | + Ok(AssetId::from_byte_array(bytes)) |
| 373 | + } |
| 374 | + } |
| 375 | +} |
| 376 | + |
192 | 377 | #[cfg(test)] |
193 | 378 | mod test { |
194 | 379 | use super::*; |
|
0 commit comments