|  | 
|  | 1 | +use std::str::FromStr; | 
|  | 2 | + | 
|  | 3 | +use bitcoin::util::address::WitnessVersion; | 
|  | 4 | +use bitcoin::Network; | 
|  | 5 | +use miniscript::descriptor::DescriptorType; | 
|  | 6 | +use miniscript::policy::Concrete; | 
|  | 7 | +use miniscript::{Descriptor, Miniscript, Tap}; | 
|  | 8 | +use secp256k1::{rand, KeyPair}; | 
|  | 9 | + | 
|  | 10 | +// Refer to https://github.com/sanket1729/adv_btc_workshop/blob/master/workshop.md#creating-a-taproot-descriptor | 
|  | 11 | +// for a detailed explanation of the policy and it's compilation | 
|  | 12 | + | 
|  | 13 | +fn main() { | 
|  | 14 | +    let pubkeys = hardcoded_xonlypubkeys(); | 
|  | 15 | +    let pol_str = format!( | 
|  | 16 | +        "or( | 
|  | 17 | +        99@thresh(2, | 
|  | 18 | +            pk({}), | 
|  | 19 | +            pk({}) | 
|  | 20 | +        ),1@or( | 
|  | 21 | +            99@pk({}), | 
|  | 22 | +            1@and(pk({}), | 
|  | 23 | +            older(9)) | 
|  | 24 | +            ) | 
|  | 25 | +        )", | 
|  | 26 | +        pubkeys[0], pubkeys[1], pubkeys[2], pubkeys[3] | 
|  | 27 | +    ) | 
|  | 28 | +    .replace(&[' ', '\n', '\t'][..], ""); | 
|  | 29 | + | 
|  | 30 | +    let pol: Concrete<bitcoin::XOnlyPublicKey> = Concrete::from_str(&pol_str).unwrap(); | 
|  | 31 | + | 
|  | 32 | +    // We require secp for generating a random XOnlyPublicKey | 
|  | 33 | +    let secp = secp256k1::Secp256k1::new(); | 
|  | 34 | +    let key_pair = KeyPair::new(&secp, &mut rand::thread_rng()); | 
|  | 35 | +    // Random unspendable XOnlyPublicKey provided for compilation to Taproot Descriptor | 
|  | 36 | +    let unspendable_key = bitcoin::XOnlyPublicKey::from_keypair(&key_pair); | 
|  | 37 | + | 
|  | 38 | +    let private_desc = pol.compile_tr_private(Some(unspendable_key)).unwrap(); | 
|  | 39 | +    // let opt_desc = pol.compile_tr(Some(unspendable_key.clone())).unwrap(); | 
|  | 40 | +    let expected_desc = Descriptor::<bitcoin::XOnlyPublicKey>::from_str(&format!( | 
|  | 41 | +        "tr({},{{and_v(v:pk({}),older(9)),multi_a(2,{},{})}})", | 
|  | 42 | +        pubkeys[2], pubkeys[3], pubkeys[0], pubkeys[1] | 
|  | 43 | +    )) | 
|  | 44 | +    .unwrap(); | 
|  | 45 | +    assert_eq!(private_desc, expected_desc); | 
|  | 46 | +    // assert_eq!(opt_desc, expected_desc); | 
|  | 47 | + | 
|  | 48 | +    // Check whether the descriptors are safe. | 
|  | 49 | +    assert!(private_desc.sanity_check().is_ok()); | 
|  | 50 | +    // assert!(opt_desc.sanity_check().is_ok()); | 
|  | 51 | + | 
|  | 52 | +    // Descriptor Type and Version should match respectively for Taproot | 
|  | 53 | +    let priv_desc_type = private_desc.desc_type(); | 
|  | 54 | +    assert_eq!(priv_desc_type, DescriptorType::Tr); | 
|  | 55 | +    // let opt_desc_type = opt_desc.desc_type(); | 
|  | 56 | +    // assert_eq!(opt_desc_type, DescriptorType::Tr); | 
|  | 57 | +    assert_eq!(priv_desc_type.segwit_version().unwrap(), WitnessVersion::V1); | 
|  | 58 | +    // assert_eq!(opt_desc_type.segwit_version().unwrap(), WitnessVersion::V1); | 
|  | 59 | + | 
|  | 60 | +    if let Descriptor::Tr(ref p) = private_desc { | 
|  | 61 | +        // Check if internal key is correctly inferred as Ca | 
|  | 62 | +        assert_eq!(p.internal_key(), &pubkeys[2]); | 
|  | 63 | + | 
|  | 64 | +        // Iterate through scripts | 
|  | 65 | +        let mut iter = p.iter_scripts(); | 
|  | 66 | +        assert_eq!( | 
|  | 67 | +            iter.next().unwrap(), | 
|  | 68 | +            ( | 
|  | 69 | +                1u8, | 
|  | 70 | +                &Miniscript::<bitcoin::XOnlyPublicKey, Tap>::from_str(&format!( | 
|  | 71 | +                    "and_v(vc:pk_k({}),older(9))", | 
|  | 72 | +                    pubkeys[3] | 
|  | 73 | +                )) | 
|  | 74 | +                .unwrap() | 
|  | 75 | +            ) | 
|  | 76 | +        ); | 
|  | 77 | +        assert_eq!( | 
|  | 78 | +            iter.next().unwrap(), | 
|  | 79 | +            ( | 
|  | 80 | +                1u8, | 
|  | 81 | +                &Miniscript::<bitcoin::XOnlyPublicKey, Tap>::from_str(&format!( | 
|  | 82 | +                    "multi_a(2,{},{})", | 
|  | 83 | +                    pubkeys[0], pubkeys[1] | 
|  | 84 | +                )) | 
|  | 85 | +                .unwrap() | 
|  | 86 | +            ) | 
|  | 87 | +        ); | 
|  | 88 | +        assert_eq!(iter.next(), None); | 
|  | 89 | +    } | 
|  | 90 | + | 
|  | 91 | +    // Max Satisfaction Weight for compilation, corresponding to the script-path spend | 
|  | 92 | +    // `multi_a(2,PUBKEY_1,PUBKEY_2) at taptree depth 1, having | 
|  | 93 | +    // Max Witness Size = scriptSig len + control_block size + varint(script_size) + script_size + | 
|  | 94 | +    //                     varint(max satisfaction elements) + max satisfaction size | 
|  | 95 | +    //                  = 4 + 65 + 1 + 70 + 1 + 132 | 
|  | 96 | +    let max_sat_wt = private_desc.max_satisfaction_weight().unwrap(); | 
|  | 97 | +    assert_eq!(max_sat_wt, 273); | 
|  | 98 | + | 
|  | 99 | +    // Compute the bitcoin address and check if it matches | 
|  | 100 | +    let network = Network::Bitcoin; | 
|  | 101 | +    let priv_addr = private_desc.address(network).unwrap(); | 
|  | 102 | +    let expected_addr = bitcoin::Address::from_str( | 
|  | 103 | +        "bc1pcc8ku64slu3wu04a6g376d2s8ck9y5alw5sus4zddvn8xgpdqw2swrghwx", | 
|  | 104 | +    ) | 
|  | 105 | +    .unwrap(); | 
|  | 106 | +    assert_eq!(priv_addr, expected_addr); | 
|  | 107 | +} | 
|  | 108 | + | 
|  | 109 | +fn hardcoded_xonlypubkeys() -> Vec<bitcoin::XOnlyPublicKey> { | 
|  | 110 | +    let serialized_keys: [[u8; 32]; 4] = [ | 
|  | 111 | +        [ | 
|  | 112 | +            22, 37, 41, 4, 57, 254, 191, 38, 14, 184, 200, 133, 111, 226, 145, 183, 245, 112, 100, | 
|  | 113 | +            42, 69, 210, 146, 60, 179, 170, 174, 247, 231, 224, 221, 52, | 
|  | 114 | +        ], | 
|  | 115 | +        [ | 
|  | 116 | +            194, 16, 47, 19, 231, 1, 0, 143, 203, 11, 35, 148, 101, 75, 200, 15, 14, 54, 222, 208, | 
|  | 117 | +            31, 205, 191, 215, 80, 69, 214, 126, 10, 124, 107, 154, | 
|  | 118 | +        ], | 
|  | 119 | +        [ | 
|  | 120 | +            202, 56, 167, 245, 51, 10, 193, 145, 213, 151, 66, 122, 208, 43, 10, 17, 17, 153, 170, | 
|  | 121 | +            29, 89, 133, 223, 134, 220, 212, 166, 138, 2, 152, 122, 16, | 
|  | 122 | +        ], | 
|  | 123 | +        [ | 
|  | 124 | +            50, 23, 194, 4, 213, 55, 42, 210, 67, 101, 23, 3, 195, 228, 31, 70, 127, 79, 21, 188, | 
|  | 125 | +            168, 39, 134, 58, 19, 181, 3, 63, 235, 103, 155, 213, | 
|  | 126 | +        ], | 
|  | 127 | +    ]; | 
|  | 128 | +    let mut keys: Vec<bitcoin::XOnlyPublicKey> = vec![]; | 
|  | 129 | +    for idx in 0..4 { | 
|  | 130 | +        keys.push(bitcoin::XOnlyPublicKey::from_slice(&serialized_keys[idx][..]).unwrap()); | 
|  | 131 | +    } | 
|  | 132 | +    keys | 
|  | 133 | +} | 
0 commit comments