From b4b7d0955a18b94a50517c784a4a9d44b46e3963 Mon Sep 17 00:00:00 2001 From: Harshil Jani Date: Mon, 10 Jul 2023 22:52:08 +0530 Subject: [PATCH] Adding brief explaination Signed-off-by: Harshil Jani --- examples/plan_spend.rs | 55 ++++++++++++++++++++++++---------- examples/psbt_sign_finalize.rs | 53 +++++++++++++++++++++----------- 2 files changed, 75 insertions(+), 33 deletions(-) diff --git a/examples/plan_spend.rs b/examples/plan_spend.rs index 1494bd08d..0580882e9 100644 --- a/examples/plan_spend.rs +++ b/examples/plan_spend.rs @@ -29,7 +29,25 @@ fn main() { "023fc33527afab09fa97135f2180bcd22ce637b1d2fbcb2db748b1f2c33f45b2b4", ]; - // Defining the taproot descriptor + // The taproot descriptor combines different spending paths and conditions, allowing the funds to be spent using + // different methods depending on the desired conditions. + + // tr({A},{{pkh({B}),{{multi_a(1,{C},{D}),and_v(v:pk({E}),after(10))}}}}) represents a taproot spending policy. + // Let's break it down: + // + // * Key Spend Path + // {A} represents the public key for the taproot key spending path. + // + // * Script Spend Paths + // {B} represents the public key for the pay-to-pubkey-hash (P2PKH) spending path. + // The multi_a(1,{C},{D}) construct represents a 1-of-2 multi-signature script condition. + // It requires at least one signature from {C} and {D} to spend funds using the script spend path. + // The and_v(v:pk({E}),after(10)) construct represents a combination of a P2PK script condition and a time lock. + // It requires a valid signature from {E} and enforces a time lock of 10 blocks on spending funds. + + // By constructing transactions using this taproot descriptor and signing them appropriately, + // you can create flexible spending policies that enable different spending paths and conditions depending on the + // transaction's inputs and outputs. let s = format!( "tr({},{{pkh({}),{{multi_a(1,{},{}),and_v(v:pk({}),after(10))}}}})", keys[0], keys[1], keys[2], keys[3], keys[4] @@ -127,17 +145,24 @@ fn main() { value: amount * 4 / 5, }); - // Plan the Transaction using available assets + // Consider that out of all the keys required to sign the descriptor spend path we only have some handful of assets. + // We can plan the PSBT with only few assets(keys or hashes) if that are enough for satisfying any policy. + // + // Here for example assume that we only have two keys available. + // Key A and Key B (as seen from the descriptor above) + // We have to add the keys to `Asset` and then obtain plan with only available signatures if the descriptor can be satisfied. let mut assets = Assets::new(); - assets = assets.add(DescriptorPublicKey::from_str(keys[0]).unwrap()); // Master Key for Key Spend Path - assets = assets.add(DescriptorPublicKey::from_str(keys[1]).unwrap()); // Script Spend Path + assets = assets.add(DescriptorPublicKey::from_str(keys[0]).unwrap()); // Master Key for Key Spend Path - Key A + assets = assets.add(DescriptorPublicKey::from_str(keys[1]).unwrap()); // Script Spend Path - Key B - // Obtain the result of the plan based on provided assets - let result = bridge_descriptor.clone().get_plan(&assets); + // Obtain the Plan based on available Assets + let plan = bridge_descriptor.clone().get_plan(&assets).unwrap(); // Creating PSBT Input let mut input = psbt::Input::default(); - result.unwrap().update_psbt_input(&mut input); + plan.update_psbt_input(&mut input); + + // Update the PSBT input from the result which we have obtained from the Plan. input .update_with_descriptor_unchecked(&bridge_descriptor) .unwrap(); @@ -148,15 +173,13 @@ fn main() { psbt.outputs.push(psbt::Output::default()); // Use private keys to sign - let sk1 = master_private_key.inner; - let sk2 = backup1_private.inner; - - // In the following example we have signed the descriptor with master key - // which will allow the transaction to be key spend type. - // Any other key apart from master key is part of script policies and it - // will sign for script spend path if it satisfies. - sign_taproot_psbt(&sk1, &mut psbt, &secp256k1); // Key Spend - sign_taproot_psbt(&sk2, &mut psbt, &secp256k1); // Script Spend + let key_a = master_private_key.inner; + let key_b = backup1_private.inner; + + // Taproot script can be signed when we have either Key spend or Script spend or both. + // Here you can try to verify by commenting one of the spend path or try signing with both. + sign_taproot_psbt(&key_a, &mut psbt, &secp256k1); // Key Spend - With Key A + sign_taproot_psbt(&key_b, &mut psbt, &secp256k1); // Script Spend - With Key B // Serializing and finalizing the PSBT Transaction let serialized = psbt.serialize(); diff --git a/examples/psbt_sign_finalize.rs b/examples/psbt_sign_finalize.rs index 6c23b000a..8108b4b7e 100644 --- a/examples/psbt_sign_finalize.rs +++ b/examples/psbt_sign_finalize.rs @@ -15,9 +15,29 @@ use miniscript::psbt::{PsbtExt, PsbtInputExt}; use miniscript::{Descriptor, DescriptorPublicKey}; fn main() { - // Defining the descriptor + // Defining the descriptor keys let secp256k1 = secp256k1::Secp256k1::new(); - let s = "wsh(t:or_c(pk(027a3565454fe1b749bccaef22aff72843a9c3efefd7b16ac54537a0c23f0ec0de),v:thresh(1,pkh(032d672a1a91cc39d154d366cd231983661b0785c7f27bc338447565844f4a6813),a:pkh(03417129311ed34c242c012cd0a3e0b9bca0065f742d0dfb63c78083ea6a02d4d9),a:pkh(025a687659658baeabdfc415164528065be7bcaade19342241941e556557f01e28))))#7hut9ukn"; + let keys = vec![ + "027a3565454fe1b749bccaef22aff72843a9c3efefd7b16ac54537a0c23f0ec0de", + "032d672a1a91cc39d154d366cd231983661b0785c7f27bc338447565844f4a6813", + "03417129311ed34c242c012cd0a3e0b9bca0065f742d0dfb63c78083ea6a02d4d9", + "025a687659658baeabdfc415164528065be7bcaade19342241941e556557f01e28", + ]; + // The wsh descriptor indicates a Witness Script Hash, meaning the descriptor is for a SegWit script. + // wsh(or(pk(A),thresh(1,pkh(B),pkh(C),pkh(D)))) + + // Let's break it down: + // t:or_c specifies an "or" construct, which means the script can be satisfied by one of the given conditions: + // pk(A) OR thresh(1,pkh(B),pkh(C),pkh(D)) + // Inside threshold condition atleast 1 out of all given conditions should satisfy. + + // By constructing transactions using this wsh descriptor and signing them appropriately, + // you can create flexible spending policies that enable different spending paths and conditions depending on the + // transaction's inputs and outputs. + let s = format!( + "wsh(t:or_c(pk({}),v:thresh(1,pkh({}),a:pkh({}),a:pkh({}))))#7hut9ukn", + keys[0], keys[1], keys[2], keys[3] + ); let bridge_descriptor = Descriptor::from_str(&s).expect("parse descriptor string"); assert!(bridge_descriptor.sanity_check().is_ok()); @@ -116,10 +136,12 @@ fn main() { value: amount * 4 / 5, }); - // Plan the Transaction using available assets - // The descriptor is : or(pk(A),thresh(1,pkh(B),pkh(C),pkh(D))) - // For the context of planning in this example, We will only provide the key A as an asset - // This will satisfy the pk(A) and since we have an OR, This should be sufficient to satisfy the given policy. + // Consider that out of all the keys required to sign the descriptor, we only have some handful of assets. + // We can plan the PSBT with only few assets(keys or hashes) if that are enough for satisfying any policy. + // + // Here for example assume that we only have one key available i.e Key A(as seen from the descriptor above) + // Key A is enough to satisfy the given descriptor because it is OR. + // We have to add the key to `Asset` and then obtain plan with only available signature if the descriptor can be satisfied. let mut assets = Assets::new(); assets = assets.add( DescriptorPublicKey::from_str( @@ -128,12 +150,14 @@ fn main() { .unwrap(), ); - // Obtain the result of the plan based on provided assets - let result = bridge_descriptor.clone().get_plan(&assets); + // Obtain the Plan based on available Assets + let plan = bridge_descriptor.clone().get_plan(&assets).unwrap(); // Creating a PSBT Input let mut input = psbt::Input::default(); - result.unwrap().update_psbt_input(&mut input); + + // Update the PSBT input from the result which we have obtained from the Plan. + plan.update_psbt_input(&mut input); input .update_with_descriptor_unchecked(&bridge_descriptor) .unwrap(); @@ -158,16 +182,11 @@ fn main() { // Finally construct the signature and add to psbt let sig1 = secp256k1.sign_ecdsa(&msg, &sk1); - let pk1 = backup1_private.public_key(&secp256k1); - assert!(secp256k1.verify_ecdsa(&msg, &sig1, &pk1.inner).is_ok()); - - // Second key just in case - let sig2 = secp256k1.sign_ecdsa(&msg, &sk2); - let pk2 = backup2_private.public_key(&secp256k1); - assert!(secp256k1.verify_ecdsa(&msg, &sig2, &pk2.inner).is_ok()); + let key_a = backup1_private.public_key(&secp256k1); + assert!(secp256k1.verify_ecdsa(&msg, &sig1, &key_a.inner).is_ok()); psbt.inputs[0].partial_sigs.insert( - pk1, + key_a, bitcoin::ecdsa::Signature { sig: sig1, hash_ty: hash_ty,