Created
February 13, 2025 14:04
-
-
Save jaoleal/f0282ca3a05885f53fd6433845657cbd to your computer and use it in GitHub Desktop.
This is the code used to solved the final part of the "As Relíquias de Schnorr".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use bip39::Mnemonic; | |
| use bitcoin::absolute::LockTime; | |
| use bitcoin::bip32::{DerivationPath, Xpriv}; | |
| use bitcoin::consensus::{deserialize, serialize}; | |
| use bitcoin::hashes::Hash; | |
| use bitcoin::hex::{Case, DisplayHex, FromHex}; | |
| use bitcoin::transaction::Version; | |
| use bitcoin::{secp256k1, Network, OutPoint, Psbt, Script, Sequence, Transaction, TxIn, TxOut}; | |
| use miniscript::psbt::PsbtExt; | |
| use miniscript::{DefiniteDescriptorKey, Descriptor, DescriptorPublicKey}; | |
| use std::str::FromStr; | |
| fn main() { | |
| let seed = Mnemonic::from_str( | |
| "zero fee child payment parent version three combine next version three again", | |
| ) | |
| .unwrap() | |
| .to_seed(""); | |
| let secp: &secp256k1::Secp256k1<secp256k1::All> = &secp256k1::Secp256k1::new(); | |
| let network = Network::Signet; | |
| let master_key = Xpriv::new_master(network, &seed).unwrap(); | |
| let descriptor_string = format!("tr(6cfb7f2729c16046b4cfc0aecf4918586d3d2814f358d2bf726d62de4b3e3149,and_v(v:pk([8B1D9395/86'/0'/0']xpub6CDJdaNaT8DEU9aC831riBgpgfqoLNJahrR57NxuaFJS5BNmMadiU9LXoCVBYcgor8ZkdjWNXmzQBvNTi6MoovRNqgktZZdB6gpZhD8fZes/0/0),sha256(1a6e9f9e750ce56a1a9cfd857eea01488c8d223fd794b0814cc4338894418e2b)))"); | |
| let descriptor = Descriptor::<DescriptorPublicKey>::from_str(&descriptor_string).unwrap(); | |
| let derived_descriptor = descriptor.derived_descriptor(&secp, 0).unwrap(); | |
| let address = derived_descriptor.address(network).unwrap(); | |
| assert!( | |
| address.to_string() == "bc1p9e9f0k98d6tfu4n9llhdjmrq364vx2z0h37gc79hcu25ehea80yq7zt3t8" | |
| ); | |
| println!("ADDRESS\t\t{}", address); | |
| // create the psbt | |
| let previous_tx_hex = "030000000001011b1fca509b7b1716d7fe2c07bc7c70975a2080dd61c556d83e98d4fba774c6810000000000fdffffff022b941e00000000002251202e4a97d8a76e969e5665ffeed96c608eaac3284fbc7c8c78b7c7154cdf3d3bc800000000000000000d6a0b2f73742e6d617274696e2f01402152236074fd6a8702ffa9f947e8c857ab08830c919d8221215ecc44dc1bca3d3742441fb5d10f87f3574f228a331adf8f489b2d618a5df4d382b0476649daa400000000"; | |
| let previous_tx: Transaction = deserialize(&Vec::from_hex(previous_tx_hex).unwrap()).unwrap(); | |
| let mut transaction = Transaction { | |
| version: Version::non_standard(3), | |
| lock_time: LockTime::ZERO, | |
| input: vec![], | |
| output: vec![], | |
| }; | |
| let (outpoint, _prevout) = get_vout(&previous_tx, &derived_descriptor.script_pubkey()); | |
| //A personal address | |
| let destination_address = | |
| bitcoin::Address::from_str("bc1qu96g4j5as4e9caaecq80s0tklr0nyfj0sjkahq") | |
| .unwrap() | |
| .assume_checked(); | |
| transaction.input.push(TxIn { | |
| previous_output: outpoint, | |
| sequence: Sequence::ENABLE_RBF_NO_LOCKTIME, | |
| ..Default::default() | |
| }); | |
| transaction.output.push(TxOut { | |
| script_pubkey: destination_address.script_pubkey(), | |
| value: bitcoin::Amount::from_sat(2_000_000), | |
| }); | |
| let unsigned_tx = transaction; | |
| let mut psbt = Psbt::from_unsigned_tx(unsigned_tx).unwrap(); | |
| let prepreimage = "Schnorr".as_bytes(); | |
| let preimage_256_hash = bitcoin::hashes::sha256::Hash::hash(prepreimage); | |
| let result256_final = bitcoin::hashes::sha256::Hash::hash(&preimage_256_hash.to_byte_array()); | |
| psbt.inputs[0] | |
| .sha256_preimages | |
| .insert(result256_final, preimage_256_hash.to_byte_array().to_vec()); | |
| let desc = | |
| Descriptor::<DefiniteDescriptorKey>::from_str(&derived_descriptor.to_string()).unwrap(); | |
| psbt.update_input_with_descriptor(0, &desc).unwrap(); | |
| let derived_key_to_sign = &master_key | |
| .derive_priv(&secp, &"86'/0'/0'/0/0".parse::<DerivationPath>().unwrap()) | |
| .unwrap(); | |
| psbt.sign(derived_key_to_sign, secp).unwrap(); | |
| psbt.finalize_mut(&secp).unwrap(); | |
| let signed_tx = psbt.extract_tx().unwrap(); | |
| let raw_tx = serialize(&signed_tx).to_hex_string(Case::Lower); | |
| println!("{}", raw_tx); | |
| } | |
| fn get_vout(tx: &Transaction, spk: &Script) -> (OutPoint, TxOut) { | |
| for (i, txout) in tx.clone().output.into_iter().enumerate() { | |
| if spk == &txout.script_pubkey { | |
| return (OutPoint::new(tx.compute_txid(), i as u32), txout); | |
| } | |
| } | |
| panic!("Only call get vout on functions which have the expected outpoint"); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment