Skip to content
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

pset: input: add blinded issuance flag #200

Merged
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
5 changes: 5 additions & 0 deletions src/pset/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ pub enum PsetBlindError {
ConfidentialTxOutError(usize, ConfidentialTxOutError),
/// Blinding proof creation error
BlindingProofsCreationError(usize, secp256k1_zkp::Error),
/// Blinding issuance unsupported
BlindingIssuanceUnsupported(usize),
}

impl fmt::Display for PsetBlindError {
Expand Down Expand Up @@ -291,6 +293,9 @@ impl fmt::Display for PsetBlindError {
e, i
)
}
PsetBlindError::BlindingIssuanceUnsupported(i) => {
write!(f, "Blinding issuance is not supported, set blinded_issuance to 0 at input index {}", i)
}
}
}
}
Expand Down
21 changes: 20 additions & 1 deletion src/pset/map/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ const PSBT_ELEMENTS_IN_EXPLICIT_ASSET: u8 = 0x13;
/// the explicit asset in PSBT_ELEMENTS_IN_EXPLICIT_ASSET. If provided,
/// PSBT_ELEMENTS_IN_EXPLICIT_ASSET must be provided too.
const PSBT_ELEMENTS_IN_ASSET_PROOF: u8 = 0x14;
/// A boolean flag. 0x00 indicates the issuance should not be blinded,
/// 0x01 indicates it should be. If not specified, assumed to be 0x01.
/// 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;
/// A key-value map for an input of the corresponding index in the unsigned
/// transaction.
#[derive(Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -294,6 +299,8 @@ pub struct Input {
pub asset: Option<AssetId>,
/// The blind asset surjection proof
pub blind_asset_proof: Option<Box<SurjectionProof>>,
/// Whether the issuance is blinded
pub blinded_issuance: Option<u8>,
/// Other fields
#[cfg_attr(
feature = "serde",
Expand All @@ -310,7 +317,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(), 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(), proprietary: Default::default(), unknown: Default::default() }
}
}

Expand Down Expand Up @@ -740,6 +747,9 @@ impl Map for Input {
PSBT_ELEMENTS_IN_ASSET_PROOF => {
impl_pset_prop_insert_pair!(self.blind_asset_proof <= <raw_key: _> | <raw_value : Box<SurjectionProof>>)
}
PSBT_ELEMENTS_IN_BLINDED_ISSUANCE => {
impl_pset_prop_insert_pair!(self.blinded_issuance <= <raw_key: _> | <raw_value : u8>)
}
_ => match self.proprietary.entry(prop_key) {
Entry::Vacant(empty_key) => {
empty_key.insert(raw_value);
Expand Down Expand Up @@ -954,6 +964,10 @@ impl Map for Input {
rv.push_prop(self.blind_asset_proof as <PSBT_ELEMENTS_IN_ASSET_PROOF, _>)
}

impl_pset_get_pair! {
rv.push_prop(self.blinded_issuance as <PSBT_ELEMENTS_IN_BLINDED_ISSUANCE, _>)
}

for (key, value) in self.proprietary.iter() {
rv.push(raw::Pair {
key: key.to_key(),
Expand Down Expand Up @@ -1028,6 +1042,11 @@ impl Map for Input {
merge!(in_utxo_rangeproof, self, other);
merge!(in_issuance_blind_value_proof, self, other);
merge!(in_issuance_blind_inflation_keys_proof, self, other);
merge!(amount, self, other);
merge!(blind_value_proof, self, other);
merge!(asset, self, other);
merge!(blind_asset_proof, self, other);
merge!(blinded_issuance, self, other);
Ok(())
}
}
Expand Down
72 changes: 72 additions & 0 deletions src/pset/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,11 @@ impl PartiallySignedTransaction {
),
PsetBlindError,
> {
for (i, inp) in self.inputs.iter().enumerate() {
if inp.has_issuance() && inp.blinded_issuance.unwrap_or(1) == 1 {
return Err(PsetBlindError::BlindingIssuanceUnsupported(i));
}
}
let mut blind_out_indices = Vec::new();
for (i, out) in self.outputs.iter().enumerate() {
if out.blinding_key.is_none() {
Expand Down Expand Up @@ -971,4 +976,71 @@ mod tests {
assert_eq!(pset.n_inputs(), n_inputs - 1);
assert_eq!(pset.n_outputs(), n_outputs - 1);
}

#[test]
fn pset_issuance() {
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 btc_txout_secrets = TxOutSecrets {
asset_bf: AssetBlindingFactor::from_str("1111111111111111111111111111111111111111111111111111111111111111").unwrap(),
value_bf: ValueBlindingFactor::from_str("2222222222222222222222222222222222222222222222222222222222222222").unwrap(),
value: sats_in,
asset: policy,
};
let previous_output = TxOut::default(); // Does not match btc_txout_secrets
let prevout = OutPoint::default();
let sats_asset = 10;
let sats_token = 1;

let mut pset = PartiallySignedTransaction::new_v2();
let mut input = Input::from_prevout(prevout);
input.witness_utxo = Some(previous_output);
input.issuance_value_amount = Some(sats_asset);
input.issuance_inflation_keys = Some(sats_token);
let (asset, token) = input.issuance_ids();
pset.add_input(input);

// Add asset
let mut output = Output::new_explicit(script.clone(), sats_asset, asset, Some(pk));
output.blinder_index = Some(0);
pset.add_output(output);
// Add token
let mut output = Output::new_explicit(script.clone(), sats_token, token, Some(pk));
output.blinder_index = Some(0);
pset.add_output(output);
// Add L-BTC
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);

let err = pset.blind_last(&mut rng, &secp, &inp_txout_sec).unwrap_err();
assert_eq!(err, PsetBlindError::BlindingIssuanceUnsupported(0));

let input = &mut pset.inputs_mut()[0];
input.blinded_issuance = Some(0x01);
let err = pset.blind_last(&mut rng, &secp, &inp_txout_sec).unwrap_err();
assert_eq!(err, PsetBlindError::BlindingIssuanceUnsupported(0));

let input = &mut pset.inputs_mut()[0];
input.blinded_issuance = Some(0x00);
pset.blind_last(&mut rng, &secp, &inp_txout_sec).unwrap();
let pset_bytes = encode::serialize(&pset);
let pset_des = encode::deserialize(&pset_bytes).unwrap();
assert_eq!(pset, pset_des);
}
}
Loading