Skip to content

Commit d80aa1b

Browse files
committed
Merge #200: pset: input: add blinded issuance flag
908cb5e pset: input: merge: add missing fields (Leonardo Comandini) cd6ad48 pset: input: test issuance (Leonardo Comandini) 0453428 pset: input: handle blinded_issuance (Leonardo Comandini) 1bde7a1 pset: input: add blinded issuance flag (Leonardo Comandini) Pull request description: Added in ElementsProject/elements#1157 ACKs for top commit: apoelstra: ACK 908cb5e this looks good for now. Would be good to actually implement the blinding of course Tree-SHA512: 12039dcf0f68493cf959d84c64742b01367c847599c4960428f6813d9945933d526764e7787f8bb9836b34d217cb8a7d3003f38271ae79500dfc1b5477a720da
2 parents dd4ab1a + 908cb5e commit d80aa1b

File tree

3 files changed

+97
-1
lines changed

3 files changed

+97
-1
lines changed

src/pset/error.rs

+5
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ pub enum PsetBlindError {
250250
ConfidentialTxOutError(usize, ConfidentialTxOutError),
251251
/// Blinding proof creation error
252252
BlindingProofsCreationError(usize, secp256k1_zkp::Error),
253+
/// Blinding issuance unsupported
254+
BlindingIssuanceUnsupported(usize),
253255
}
254256

255257
impl fmt::Display for PsetBlindError {
@@ -291,6 +293,9 @@ impl fmt::Display for PsetBlindError {
291293
e, i
292294
)
293295
}
296+
PsetBlindError::BlindingIssuanceUnsupported(i) => {
297+
write!(f, "Blinding issuance is not supported, set blinded_issuance to 0 at input index {}", i)
298+
}
294299
}
295300
}
296301
}

src/pset/map/input.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ const PSBT_ELEMENTS_IN_EXPLICIT_ASSET: u8 = 0x13;
163163
/// the explicit asset in PSBT_ELEMENTS_IN_EXPLICIT_ASSET. If provided,
164164
/// PSBT_ELEMENTS_IN_EXPLICIT_ASSET must be provided too.
165165
const PSBT_ELEMENTS_IN_ASSET_PROOF: u8 = 0x14;
166+
/// A boolean flag. 0x00 indicates the issuance should not be blinded,
167+
/// 0x01 indicates it should be. If not specified, assumed to be 0x01.
168+
/// Note that this does not indicate actual blinding status,
169+
/// but rather the expected blinding status prior to signing.
170+
const PSBT_ELEMENTS_IN_BLINDED_ISSUANCE: u8 = 0x15;
166171
/// A key-value map for an input of the corresponding index in the unsigned
167172
/// transaction.
168173
#[derive(Clone, Debug, PartialEq)]
@@ -294,6 +299,8 @@ pub struct Input {
294299
pub asset: Option<AssetId>,
295300
/// The blind asset surjection proof
296301
pub blind_asset_proof: Option<Box<SurjectionProof>>,
302+
/// Whether the issuance is blinded
303+
pub blinded_issuance: Option<u8>,
297304
/// Other fields
298305
#[cfg_attr(
299306
feature = "serde",
@@ -310,7 +317,7 @@ pub struct Input {
310317

311318
impl Default for Input {
312319
fn default() -> Self {
313-
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() }
320+
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() }
314321
}
315322
}
316323

@@ -740,6 +747,9 @@ impl Map for Input {
740747
PSBT_ELEMENTS_IN_ASSET_PROOF => {
741748
impl_pset_prop_insert_pair!(self.blind_asset_proof <= <raw_key: _> | <raw_value : Box<SurjectionProof>>)
742749
}
750+
PSBT_ELEMENTS_IN_BLINDED_ISSUANCE => {
751+
impl_pset_prop_insert_pair!(self.blinded_issuance <= <raw_key: _> | <raw_value : u8>)
752+
}
743753
_ => match self.proprietary.entry(prop_key) {
744754
Entry::Vacant(empty_key) => {
745755
empty_key.insert(raw_value);
@@ -954,6 +964,10 @@ impl Map for Input {
954964
rv.push_prop(self.blind_asset_proof as <PSBT_ELEMENTS_IN_ASSET_PROOF, _>)
955965
}
956966

967+
impl_pset_get_pair! {
968+
rv.push_prop(self.blinded_issuance as <PSBT_ELEMENTS_IN_BLINDED_ISSUANCE, _>)
969+
}
970+
957971
for (key, value) in self.proprietary.iter() {
958972
rv.push(raw::Pair {
959973
key: key.to_key(),
@@ -1028,6 +1042,11 @@ impl Map for Input {
10281042
merge!(in_utxo_rangeproof, self, other);
10291043
merge!(in_issuance_blind_value_proof, self, other);
10301044
merge!(in_issuance_blind_inflation_keys_proof, self, other);
1045+
merge!(amount, self, other);
1046+
merge!(blind_value_proof, self, other);
1047+
merge!(asset, self, other);
1048+
merge!(blind_asset_proof, self, other);
1049+
merge!(blinded_issuance, self, other);
10311050
Ok(())
10321051
}
10331052
}

src/pset/mod.rs

+72
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,11 @@ impl PartiallySignedTransaction {
373373
),
374374
PsetBlindError,
375375
> {
376+
for (i, inp) in self.inputs.iter().enumerate() {
377+
if inp.has_issuance() && inp.blinded_issuance.unwrap_or(1) == 1 {
378+
return Err(PsetBlindError::BlindingIssuanceUnsupported(i));
379+
}
380+
}
376381
let mut blind_out_indices = Vec::new();
377382
for (i, out) in self.outputs.iter().enumerate() {
378383
if out.blinding_key.is_none() {
@@ -971,4 +976,71 @@ mod tests {
971976
assert_eq!(pset.n_inputs(), n_inputs - 1);
972977
assert_eq!(pset.n_outputs(), n_outputs - 1);
973978
}
979+
980+
#[test]
981+
fn pset_issuance() {
982+
use std::str::FromStr;
983+
use rand::{self, SeedableRng};
984+
let secp = secp256k1_zkp::Secp256k1::new();
985+
#[allow(deprecated)]
986+
let mut rng = rand::rngs::StdRng::seed_from_u64(0);
987+
988+
let policy = crate::AssetId::from_str("5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225").unwrap();
989+
let pk = bitcoin::key::PublicKey::from_str("020202020202020202020202020202020202020202020202020202020202020202").unwrap();
990+
let script = crate::Script::from_hex("0014d2bcde17e7744f6377466ca1bd35d212954674c8").unwrap();
991+
let sats_in = 10000;
992+
let sats_fee = 1000;
993+
let btc_txout_secrets = TxOutSecrets {
994+
asset_bf: AssetBlindingFactor::from_str("1111111111111111111111111111111111111111111111111111111111111111").unwrap(),
995+
value_bf: ValueBlindingFactor::from_str("2222222222222222222222222222222222222222222222222222222222222222").unwrap(),
996+
value: sats_in,
997+
asset: policy,
998+
};
999+
let previous_output = TxOut::default(); // Does not match btc_txout_secrets
1000+
let prevout = OutPoint::default();
1001+
let sats_asset = 10;
1002+
let sats_token = 1;
1003+
1004+
let mut pset = PartiallySignedTransaction::new_v2();
1005+
let mut input = Input::from_prevout(prevout);
1006+
input.witness_utxo = Some(previous_output);
1007+
input.issuance_value_amount = Some(sats_asset);
1008+
input.issuance_inflation_keys = Some(sats_token);
1009+
let (asset, token) = input.issuance_ids();
1010+
pset.add_input(input);
1011+
1012+
// Add asset
1013+
let mut output = Output::new_explicit(script.clone(), sats_asset, asset, Some(pk));
1014+
output.blinder_index = Some(0);
1015+
pset.add_output(output);
1016+
// Add token
1017+
let mut output = Output::new_explicit(script.clone(), sats_token, token, Some(pk));
1018+
output.blinder_index = Some(0);
1019+
pset.add_output(output);
1020+
// Add L-BTC
1021+
let mut output = Output::new_explicit(script.clone(), sats_in - sats_fee, policy, Some(pk));
1022+
output.blinder_index = Some(0);
1023+
pset.add_output(output);
1024+
// Add fee
1025+
let output = Output::new_explicit(crate::Script::new(), sats_fee, policy, None);
1026+
pset.add_output(output);
1027+
1028+
let mut inp_txout_sec = HashMap::new();
1029+
inp_txout_sec.insert(0, btc_txout_secrets);
1030+
1031+
let err = pset.blind_last(&mut rng, &secp, &inp_txout_sec).unwrap_err();
1032+
assert_eq!(err, PsetBlindError::BlindingIssuanceUnsupported(0));
1033+
1034+
let input = &mut pset.inputs_mut()[0];
1035+
input.blinded_issuance = Some(0x01);
1036+
let err = pset.blind_last(&mut rng, &secp, &inp_txout_sec).unwrap_err();
1037+
assert_eq!(err, PsetBlindError::BlindingIssuanceUnsupported(0));
1038+
1039+
let input = &mut pset.inputs_mut()[0];
1040+
input.blinded_issuance = Some(0x00);
1041+
pset.blind_last(&mut rng, &secp, &inp_txout_sec).unwrap();
1042+
let pset_bytes = encode::serialize(&pset);
1043+
let pset_des = encode::deserialize(&pset_bytes).unwrap();
1044+
assert_eq!(pset, pset_des);
1045+
}
9741046
}

0 commit comments

Comments
 (0)