Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -146,8 +145,8 @@ fn liquid_genesis_asset_tx(network_params: &NetworkParams) -> Option<Transaction
let asset_id = AssetId::from_entropy(asset_entropy);

let asset_issuance = AssetIssuance {
asset_blinding_nonce: Tweak::default(),
asset_entropy: [0u8; 32],
asset_blinding_nonce: AssetBlindingNonce::NEW_ISSUANCE,
asset_entropy: AssetEntropy::NEW_ISSUANCE,
amount: confidential::Value::Explicit(asset_amount),
inflation_keys: confidential::Value::Explicit(0),
};
Expand Down
8 changes: 4 additions & 4 deletions src/internal_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,22 +406,22 @@ macro_rules! impl_sha256_midstate_wrapper {

impl $ty {
/// Constructs this wrapper struct from raw bytes.
pub fn from_byte_array(inner: [u8; 32]) -> 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)
}
}
Expand Down
187 changes: 186 additions & 1 deletion src/issuance.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Rust Elements Library
// Rust Elements Libraryss
// Written in 2019 by
// The Elements developers
//
Expand All @@ -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;
Expand Down Expand Up @@ -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<W: io::Write>(&self, e: W) -> Result<usize, encode::Error> {
self.0.consensus_encode(e)
}
}

impl Decodable for AssetEntropy {
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, encode::Error> {
<[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<AssetBlindingFactor>`, 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<W: io::Write>(&self, e: W) -> Result<usize, encode::Error> {
self.0.consensus_encode(e)
}
}

impl Decodable for AssetBlindingNonce {
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, encode::Error> {
<[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]);
Expand Down Expand Up @@ -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::*;
Expand Down
7 changes: 6 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
40 changes: 21 additions & 19 deletions src/pset/map/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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};

Expand Down Expand Up @@ -258,9 +258,9 @@ pub struct Input {
/// Issuance inflation keys commitment
pub issuance_inflation_keys_comm: Option<secp256k1_zkp::PedersenCommitment>,
/// Issuance blinding nonce
pub issuance_blinding_nonce: Option<Tweak>,
pub issuance_blinding_nonce: Option<AssetBlindingNonce>,
/// Issuance asset entropy
pub issuance_asset_entropy: Option<[u8; 32]>,
pub issuance_asset_entropy: Option<AssetEntropy>,
/// input utxo rangeproof
pub in_utxo_rangeproof: Option<RangeProof>,
/// Proof that blinded issuance matches the commitment
Expand Down Expand Up @@ -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 =
Expand All @@ -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,
Expand Down Expand Up @@ -752,10 +754,10 @@ impl Map for Input {
impl_pset_prop_insert_pair!(self.issuance_inflation_keys_comm <= <raw_key: _> | <raw_value : secp256k1_zkp::PedersenCommitment>);
}
PSBT_ELEMENTS_IN_ISSUANCE_BLINDING_NONCE => {
impl_pset_prop_insert_pair!(self.issuance_blinding_nonce <= <raw_key: _> | <raw_value : Tweak>);
impl_pset_prop_insert_pair!(self.issuance_blinding_nonce <= <raw_key: _> | <raw_value : AssetBlindingNonce>);
}
PSBT_ELEMENTS_IN_ISSUANCE_ASSET_ENTROPY => {
impl_pset_prop_insert_pair!(self.issuance_asset_entropy <= <raw_key: _> | <raw_value : [u8;32]>);
impl_pset_prop_insert_pair!(self.issuance_asset_entropy <= <raw_key: _> | <raw_value : AssetEntropy>);
}
PSBT_ELEMENTS_IN_UTXO_RANGEPROOF => {
impl_pset_prop_insert_pair!(self.in_utxo_rangeproof <= <raw_key: _> | <raw_value : RangeProof>);
Expand Down Expand Up @@ -1182,20 +1184,20 @@ 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.
#[test]
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,
},
Expand Down
Loading
Loading