Skip to content

pset: add optional asset blinding factor to input and output #201

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 23, 2024
Merged
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
16 changes: 14 additions & 2 deletions src/pset/map/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use std::{
use crate::taproot::{ControlBlock, LeafVersion, TapNodeHash, TapLeafHash};
use crate::{schnorr, AssetId, ContractHash};

use crate::{confidential, locktime};
use crate::{confidential::{self, AssetBlindingFactor}, locktime};
use crate::encode::{self, Decodable};
use crate::hashes::{self, hash160, ripemd160, sha256, sha256d, Hash};
use crate::pset::map::Map;
Expand Down Expand Up @@ -168,6 +168,8 @@ const PSBT_ELEMENTS_IN_ASSET_PROOF: u8 = 0x14;
/// Note that this does not indicate actual blinding status,
/// but rather the expected blinding status prior to signing.
const PSBT_ELEMENTS_IN_BLINDED_ISSUANCE: u8 = 0x15;
/// The 32 byte asset blinding factor for the input being spent.
const PSBT_ELEMENTS_IN_ASSET_BLINDING_FACTOR: u8 = 0x16;
/// A key-value map for an input of the corresponding index in the unsigned
/// transaction.
#[derive(Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -301,6 +303,8 @@ pub struct Input {
pub blind_asset_proof: Option<Box<SurjectionProof>>,
/// Whether the issuance is blinded
pub blinded_issuance: Option<u8>,
/// The input asset blinding factor
pub asset_blinding_factor: Option<AssetBlindingFactor>,
/// Other fields
#[cfg_attr(
feature = "serde",
Expand All @@ -317,7 +321,7 @@ pub struct Input {

impl Default for Input {
fn default() -> Self {
Self { non_witness_utxo: Default::default(), witness_utxo: Default::default(), partial_sigs: Default::default(), sighash_type: Default::default(), redeem_script: Default::default(), witness_script: Default::default(), bip32_derivation: Default::default(), final_script_sig: Default::default(), final_script_witness: Default::default(), ripemd160_preimages: Default::default(), sha256_preimages: Default::default(), hash160_preimages: Default::default(), hash256_preimages: Default::default(), previous_txid: Txid::all_zeros(), previous_output_index: Default::default(), sequence: Default::default(), required_time_locktime: Default::default(), required_height_locktime: Default::default(), tap_key_sig: Default::default(), tap_script_sigs: Default::default(), tap_scripts: Default::default(), tap_key_origins: Default::default(), tap_internal_key: Default::default(), tap_merkle_root: Default::default(), issuance_value_amount: Default::default(), issuance_value_comm: Default::default(), issuance_value_rangeproof: Default::default(), issuance_keys_rangeproof: Default::default(), pegin_tx: Default::default(), pegin_txout_proof: Default::default(), pegin_genesis_hash: Default::default(), pegin_claim_script: Default::default(), pegin_value: Default::default(), pegin_witness: Default::default(), issuance_inflation_keys: Default::default(), issuance_inflation_keys_comm: Default::default(), issuance_blinding_nonce: Default::default(), issuance_asset_entropy: Default::default(), in_utxo_rangeproof: Default::default(), in_issuance_blind_value_proof: Default::default(), in_issuance_blind_inflation_keys_proof: Default::default(), amount: Default::default(), blind_value_proof: Default::default(), asset: Default::default(), blind_asset_proof: Default::default(), blinded_issuance: Default::default(), proprietary: Default::default(), unknown: Default::default() }
Self { non_witness_utxo: Default::default(), witness_utxo: Default::default(), partial_sigs: Default::default(), sighash_type: Default::default(), redeem_script: Default::default(), witness_script: Default::default(), bip32_derivation: Default::default(), final_script_sig: Default::default(), final_script_witness: Default::default(), ripemd160_preimages: Default::default(), sha256_preimages: Default::default(), hash160_preimages: Default::default(), hash256_preimages: Default::default(), previous_txid: Txid::all_zeros(), previous_output_index: Default::default(), sequence: Default::default(), required_time_locktime: Default::default(), required_height_locktime: Default::default(), tap_key_sig: Default::default(), tap_script_sigs: Default::default(), tap_scripts: Default::default(), tap_key_origins: Default::default(), tap_internal_key: Default::default(), tap_merkle_root: Default::default(), issuance_value_amount: Default::default(), issuance_value_comm: Default::default(), issuance_value_rangeproof: Default::default(), issuance_keys_rangeproof: Default::default(), pegin_tx: Default::default(), pegin_txout_proof: Default::default(), pegin_genesis_hash: Default::default(), pegin_claim_script: Default::default(), pegin_value: Default::default(), pegin_witness: Default::default(), issuance_inflation_keys: Default::default(), issuance_inflation_keys_comm: Default::default(), issuance_blinding_nonce: Default::default(), issuance_asset_entropy: Default::default(), in_utxo_rangeproof: Default::default(), in_issuance_blind_value_proof: Default::default(), in_issuance_blind_inflation_keys_proof: Default::default(), amount: Default::default(), blind_value_proof: Default::default(), asset: Default::default(), blind_asset_proof: Default::default(), blinded_issuance: Default::default(), asset_blinding_factor: Default::default(), proprietary: Default::default(), unknown: Default::default() }
}
}

Expand Down Expand Up @@ -750,6 +754,9 @@ impl Map for Input {
PSBT_ELEMENTS_IN_BLINDED_ISSUANCE => {
impl_pset_prop_insert_pair!(self.blinded_issuance <= <raw_key: _> | <raw_value : u8>)
}
PSBT_ELEMENTS_IN_ASSET_BLINDING_FACTOR => {
impl_pset_prop_insert_pair!(self.asset_blinding_factor <= <raw_key: _> | <raw_value : AssetBlindingFactor>)
}
_ => match self.proprietary.entry(prop_key) {
Entry::Vacant(empty_key) => {
empty_key.insert(raw_value);
Expand Down Expand Up @@ -968,6 +975,10 @@ impl Map for Input {
rv.push_prop(self.blinded_issuance as <PSBT_ELEMENTS_IN_BLINDED_ISSUANCE, _>)
}

impl_pset_get_pair! {
rv.push_prop(self.asset_blinding_factor as <PSBT_ELEMENTS_IN_ASSET_BLINDING_FACTOR, _>)
}

for (key, value) in self.proprietary.iter() {
rv.push(raw::Pair {
key: key.to_key(),
Expand Down Expand Up @@ -1047,6 +1058,7 @@ impl Map for Input {
merge!(asset, self, other);
merge!(blind_asset_proof, self, other);
merge!(blinded_issuance, self, other);
merge!(asset_blinding_factor, self, other);
Ok(())
}
}
Expand Down
14 changes: 13 additions & 1 deletion src/pset/map/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::encode::Decodable;
use crate::pset::map::Map;
use crate::pset::raw;
use crate::pset::Error;
use crate::{confidential, pset};
use crate::{confidential::{self, AssetBlindingFactor}, pset};
use crate::{encode, Script, TxOutWitness};
use bitcoin::bip32::KeySource;
use bitcoin::{PublicKey, key::XOnlyPublicKey};
Expand Down Expand Up @@ -83,6 +83,8 @@ const PSBT_ELEMENTS_OUT_BLIND_VALUE_PROOF: u8 = 0x09;
/// PSBT_ELEMENTS_OUT_ASSET. If provided, PSBT_ELEMENTS_OUT_ASSET_COMMITMENT must
/// be provided too.
const PSBT_ELEMENTS_OUT_BLIND_ASSET_PROOF: u8 = 0x0a;
/// The 32 byte asset blinding factor for this output.
const PSBT_ELEMENTS_OUT_ASSET_BLINDING_FACTOR: u8 = 0x0b;

/// A key-value map for an output of the corresponding index in the unsigned
/// transaction.
Expand Down Expand Up @@ -129,6 +131,8 @@ pub struct Output {
pub blind_value_proof: Option<Box<RangeProof>>,
/// The blind asset surjection proof
pub blind_asset_proof: Option<Box<SurjectionProof>>,
/// The 32 byte asset blinding factor
pub asset_blinding_factor: Option<AssetBlindingFactor>,
/// Pset
/// Other fields
#[cfg_attr(
Expand Down Expand Up @@ -374,6 +378,9 @@ impl Map for Output {
PSBT_ELEMENTS_OUT_BLIND_ASSET_PROOF => {
impl_pset_prop_insert_pair!(self.blind_asset_proof <= <raw_key: _> | <raw_value : Box<SurjectionProof>>)
}
PSBT_ELEMENTS_OUT_ASSET_BLINDING_FACTOR => {
impl_pset_prop_insert_pair!(self.asset_blinding_factor <= <raw_key: _> | <raw_value : AssetBlindingFactor>)
}
_ => match self.proprietary.entry(prop_key) {
Entry::Vacant(empty_key) => {
empty_key.insert(raw_value);
Expand Down Expand Up @@ -488,6 +495,10 @@ impl Map for Output {
rv.push_prop(self.blind_asset_proof as <PSBT_ELEMENTS_OUT_BLIND_ASSET_PROOF, _>)
}

impl_pset_get_pair! {
rv.push_prop(self.asset_blinding_factor as <PSBT_ELEMENTS_OUT_ASSET_BLINDING_FACTOR, _>)
}

for (key, value) in self.proprietary.iter() {
rv.push(raw::Pair {
key: key.to_key(),
Expand Down Expand Up @@ -525,6 +536,7 @@ impl Map for Output {
merge!(blinder_index, self, other);
merge!(blind_value_proof, self, other);
merge!(blind_asset_proof, self, other);
merge!(asset_blinding_factor, self, other);
Ok(())
}
}
Expand Down
49 changes: 49 additions & 0 deletions src/pset/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1043,4 +1043,53 @@ mod tests {
let pset_des = encode::deserialize(&pset_bytes).unwrap();
assert_eq!(pset, pset_des);
}

#[test]
fn pset_abf() {
use std::str::FromStr;
use rand::{self, SeedableRng};
let secp = secp256k1_zkp::Secp256k1::new();
#[allow(deprecated)]
let mut rng = rand::rngs::StdRng::seed_from_u64(0);

let policy = crate::AssetId::from_str("5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225").unwrap();
let pk = bitcoin::key::PublicKey::from_str("020202020202020202020202020202020202020202020202020202020202020202").unwrap();
let script = crate::Script::from_hex("0014d2bcde17e7744f6377466ca1bd35d212954674c8").unwrap();
let sats_in = 10000;
let sats_fee = 1000;
let asset_bf = AssetBlindingFactor::from_str("3311111111111111111111111111111111111111111111111111111111111111").unwrap();
let btc_txout_secrets = TxOutSecrets {
asset_bf,
value_bf: ValueBlindingFactor::from_str("2222222222222222222222222222222222222222222222222222222222222222").unwrap(),
value: sats_in,
asset: policy,
};
let previous_output = TxOut::default(); // Does not match btc_txout_secrets
let txid = Txid::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap();
let prevout = OutPoint::new(txid, 0);

let mut pset = PartiallySignedTransaction::new_v2();
let mut input = Input::from_prevout(prevout);
input.witness_utxo = Some(previous_output);
input.asset_blinding_factor = Some(asset_bf);
pset.add_input(input);

// Add policy
let mut output = Output::new_explicit(script.clone(), sats_in - sats_fee, policy, Some(pk));
output.blinder_index = Some(0);
pset.add_output(output);
// Add fee
let output = Output::new_explicit(crate::Script::new(), sats_fee, policy, None);
pset.add_output(output);

let mut inp_txout_sec = HashMap::new();
inp_txout_sec.insert(0, btc_txout_secrets);
pset.blind_last(&mut rng, &secp, &inp_txout_sec).unwrap();
let output = &mut pset.outputs_mut()[0];
// TODO: output the blinding factors and use the correct one
output.asset_blinding_factor = Some(asset_bf);
let pset_bytes = encode::serialize(&pset);
let pset_des = encode::deserialize(&pset_bytes).unwrap();
assert_eq!(pset, pset_des);
}
}
15 changes: 14 additions & 1 deletion src/pset/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
use std::convert::TryFrom;
use std::io;

use crate::confidential;
use crate::confidential::{self, AssetBlindingFactor};
use crate::encode::{self, deserialize, deserialize_partial, serialize, Decodable, Encodable};
use crate::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
use crate::{AssetId, BlockHash, Script, Transaction, TxOut, Txid};
Expand Down Expand Up @@ -96,6 +96,19 @@ impl Deserialize for Tweak {
}
}

impl Serialize for AssetBlindingFactor {
fn serialize(&self) -> Vec<u8> {
encode::serialize(self.into_inner().as_ref())
}
}

impl Deserialize for AssetBlindingFactor {
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
let x = deserialize::<[u8; 32]>(bytes)?;
AssetBlindingFactor::from_slice(&x).map_err(|_| encode::Error::ParseFailed("invalid AssetBlindingFactor"))
}
}

impl Serialize for Script {
fn serialize(&self) -> Vec<u8> {
self.to_bytes()
Expand Down
Loading