Skip to content

Instantly share code, notes, and snippets.

@jaoleal
Created February 13, 2025 14:04
Show Gist options
  • Select an option

  • Save jaoleal/f0282ca3a05885f53fd6433845657cbd to your computer and use it in GitHub Desktop.

Select an option

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".
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