Estoy intentando retirar fondos de una dirección generada con el siguiente descriptor:
tr(ff13a3311d3e14239bcbb9dfd5d304f4b57c32c0b35d313cb5255f93d7b2dc68,and_v(v:pk(b064bd37b71b7c39355f866e022287097757b05bb1790ecffc5de5fbf07cff69),sha256(a02e93b3dce9cb6031055905d94b04516819ef8fdae4c498777350469e6352dd)))#pygtzret
Configuré los fondos en una prueba de registro native usando nigiri para probar esta situación y estoy usando rust-bitcoin para construir la transacción. Hasta donde puedo decir, estoy construyendo correctamente el testigo para la entrada. Sin embargo, al verificar la transacción de salida usando testmempoolacceptcontinuamente recibo mandatory-script-verify-flag-failed (Invalid Schnorr signature). La clave que estoy usando parece ser correcta. Vi el comentario debajo esta respuesta sugiriendo que fue la serialización de mi firma aunque estoy usando la predeterminada. Sin embargo, usando lo que ahora parece estar en bitcoin::taproot::Signature no resuelve mi problema.
Estoy seguro de que estoy haciendo algo sutilmente mal aquí. Por lo tanto, cualquier empujón en la dirección correcta sería muy apreciado. Mi programa en su totalidad está a continuación:
use std::str::FromStr;
use anyhow::{Outcome, anyhow, bail};
use bitcoin::absolute::LockTime;
use bitcoin::consensus::Encodable;
use bitcoin::hashes::{Hash, sha256};
use bitcoin::hex::prelude::*;
use bitcoin::key::Keypair;
use bitcoin::secp256k1::{Message, Secp256k1};
use bitcoin::sighash::{Prevouts, SighashCache};
use bitcoin::transaction::Model;
use bitcoin::{
Deal with, Quantity, Community, OutPoint, TapSighashType, Transaction, TxIn, TxOut, Witness,
XOnlyPublicKey, taproot,
};
use miniscript::Descriptor;
const DESCRIPTOR: &str = "tr(ff13a3311d3e14239bcbb9dfd5d304f4b57c32c0b35d313cb5255f93d7b2dc68,and_v(v:pk(b064bd37b71b7c39355f866e022287097757b05bb1790ecffc5de5fbf07cff69),sha256(a02e93b3dce9cb6031055905d94b04516819ef8fdae4c498777350469e6352dd)))#pygtzret";
const PREIMAGE: &str = "366d2eff102fd290e7be0226f3dd746f4657bce85f08eeef55d0a7777d1a313f";
const PRIVATE_KEY: &str = "0301e0480b374b32851a9462db29dc19fe830a7f7d7a88b81612b9d42099c0ae";
fn foremost() -> Outcome<()> {
let Descriptor::Tr(taproot) = Descriptor::::from_str(DESCRIPTOR)? else {
bail!("not a taproot descriptor");
};
assert_eq!(
taproot.deal with(Community::Regtest).to_string(),
"bcrt1prg82xfnv265zvl0mt6q2rr39mpmskrf2nyum4hqnmdwen3kzsn3slts4sh"
);
let complete = Quantity::from_str("0.0025 BTC")?;
let previous_output =
OutPoint::from_str("44afabb1b631d2cd265910dc6befef7e3962df486ad4a4a12f83668c58c9c22c:0")?;
let txin = TxIn {
previous_output,
..Default::default()
};
let to_address = Deal with::from_str("bcrt1q9th3z6mknxp60avl9xq8vac05q4xgvygx7r7jz")?
.require_network(Community::Regtest)?;
let txout = TxOut {
worth: Quantity::ZERO,
script_pubkey: to_address.script_pubkey(),
};
let outputs = vec!(txout);
let mut tx = Transaction {
model: Model::TWO,
lock_time: LockTime::ZERO,
enter: vec!(txin),
output: outputs.clone(),
};
// Fundamental 5 sats/vbyte price
let price = Quantity::from_sat(tx.vsize() as u64 * 5);
tx.output(0).worth = complete - price;
let preimage: (u8; 32) = FromHex::from_hex(PREIMAGE)?;
let preimage_hash = sha256::Hash::hash(&preimage);
assert!(
preimage_hash.to_string()
== "a02e93b3dce9cb6031055905d94b04516819ef8fdae4c498777350469e6352dd"
);
let secp = Secp256k1::new();
let keypair = Keypair::from_seckey_str(&secp, PRIVATE_KEY)?;
let public_key = XOnlyPublicKey::from(keypair.public_key());
assert_eq!(
public_key.to_string(),
"b064bd37b71b7c39355f866e022287097757b05bb1790ecffc5de5fbf07cff69"
);
let spend_info = taproot.spend_info();
let leaf = spend_info
.leaves()
.subsequent()
.ok_or(anyhow!("taptree leaf lacking"))?;
let mut sighash_cache = SighashCache::new(&tx);
let prevouts = Prevouts::All(&outputs);
let sighash = sighash_cache.taproot_script_spend_signature_hash(
0,
&prevouts,
leaf.leaf_hash(),
TapSighashType::Default,
)?;
let message = Message::from_digest(*sighash.as_ref());
let signature = taproot::Signature {
signature: secp.sign_schnorr(&message, &keypair),
sighash_type: TapSighashType::Default,
};
let mut witness = Witness::new();
witness.push(preimage);
witness.push(signature.serialize());
witness.push(leaf.script());
witness.push(leaf.into_control_block().serialize());
tx.enter(0).witness = witness;
let mut bytes = Vec::new();
tx.consensus_encode(&mut bytes)?;
println!("{}", bytes.to_lower_hex_string());
Okay(())
}
Estoy intentando retirar fondos de una dirección generada con el siguiente descriptor:
tr(ff13a3311d3e14239bcbb9dfd5d304f4b57c32c0b35d313cb5255f93d7b2dc68,and_v(v:pk(b064bd37b71b7c39355f866e022287097757b05bb1790ecffc5de5fbf07cff69),sha256(a02e93b3dce9cb6031055905d94b04516819ef8fdae4c498777350469e6352dd)))#pygtzret
Configuré los fondos en una prueba de registro native usando nigiri para probar esta situación y estoy usando rust-bitcoin para construir la transacción. Hasta donde puedo decir, estoy construyendo correctamente el testigo para la entrada. Sin embargo, al verificar la transacción de salida usando testmempoolacceptcontinuamente recibo mandatory-script-verify-flag-failed (Invalid Schnorr signature). La clave que estoy usando parece ser correcta. Vi el comentario debajo esta respuesta sugiriendo que fue la serialización de mi firma aunque estoy usando la predeterminada. Sin embargo, usando lo que ahora parece estar en bitcoin::taproot::Signature no resuelve mi problema.
Estoy seguro de que estoy haciendo algo sutilmente mal aquí. Por lo tanto, cualquier empujón en la dirección correcta sería muy apreciado. Mi programa en su totalidad está a continuación:
use std::str::FromStr;
use anyhow::{Outcome, anyhow, bail};
use bitcoin::absolute::LockTime;
use bitcoin::consensus::Encodable;
use bitcoin::hashes::{Hash, sha256};
use bitcoin::hex::prelude::*;
use bitcoin::key::Keypair;
use bitcoin::secp256k1::{Message, Secp256k1};
use bitcoin::sighash::{Prevouts, SighashCache};
use bitcoin::transaction::Model;
use bitcoin::{
Deal with, Quantity, Community, OutPoint, TapSighashType, Transaction, TxIn, TxOut, Witness,
XOnlyPublicKey, taproot,
};
use miniscript::Descriptor;
const DESCRIPTOR: &str = "tr(ff13a3311d3e14239bcbb9dfd5d304f4b57c32c0b35d313cb5255f93d7b2dc68,and_v(v:pk(b064bd37b71b7c39355f866e022287097757b05bb1790ecffc5de5fbf07cff69),sha256(a02e93b3dce9cb6031055905d94b04516819ef8fdae4c498777350469e6352dd)))#pygtzret";
const PREIMAGE: &str = "366d2eff102fd290e7be0226f3dd746f4657bce85f08eeef55d0a7777d1a313f";
const PRIVATE_KEY: &str = "0301e0480b374b32851a9462db29dc19fe830a7f7d7a88b81612b9d42099c0ae";
fn foremost() -> Outcome<()> {
let Descriptor::Tr(taproot) = Descriptor::::from_str(DESCRIPTOR)? else {
bail!("not a taproot descriptor");
};
assert_eq!(
taproot.deal with(Community::Regtest).to_string(),
"bcrt1prg82xfnv265zvl0mt6q2rr39mpmskrf2nyum4hqnmdwen3kzsn3slts4sh"
);
let complete = Quantity::from_str("0.0025 BTC")?;
let previous_output =
OutPoint::from_str("44afabb1b631d2cd265910dc6befef7e3962df486ad4a4a12f83668c58c9c22c:0")?;
let txin = TxIn {
previous_output,
..Default::default()
};
let to_address = Deal with::from_str("bcrt1q9th3z6mknxp60avl9xq8vac05q4xgvygx7r7jz")?
.require_network(Community::Regtest)?;
let txout = TxOut {
worth: Quantity::ZERO,
script_pubkey: to_address.script_pubkey(),
};
let outputs = vec!(txout);
let mut tx = Transaction {
model: Model::TWO,
lock_time: LockTime::ZERO,
enter: vec!(txin),
output: outputs.clone(),
};
// Fundamental 5 sats/vbyte price
let price = Quantity::from_sat(tx.vsize() as u64 * 5);
tx.output(0).worth = complete - price;
let preimage: (u8; 32) = FromHex::from_hex(PREIMAGE)?;
let preimage_hash = sha256::Hash::hash(&preimage);
assert!(
preimage_hash.to_string()
== "a02e93b3dce9cb6031055905d94b04516819ef8fdae4c498777350469e6352dd"
);
let secp = Secp256k1::new();
let keypair = Keypair::from_seckey_str(&secp, PRIVATE_KEY)?;
let public_key = XOnlyPublicKey::from(keypair.public_key());
assert_eq!(
public_key.to_string(),
"b064bd37b71b7c39355f866e022287097757b05bb1790ecffc5de5fbf07cff69"
);
let spend_info = taproot.spend_info();
let leaf = spend_info
.leaves()
.subsequent()
.ok_or(anyhow!("taptree leaf lacking"))?;
let mut sighash_cache = SighashCache::new(&tx);
let prevouts = Prevouts::All(&outputs);
let sighash = sighash_cache.taproot_script_spend_signature_hash(
0,
&prevouts,
leaf.leaf_hash(),
TapSighashType::Default,
)?;
let message = Message::from_digest(*sighash.as_ref());
let signature = taproot::Signature {
signature: secp.sign_schnorr(&message, &keypair),
sighash_type: TapSighashType::Default,
};
let mut witness = Witness::new();
witness.push(preimage);
witness.push(signature.serialize());
witness.push(leaf.script());
witness.push(leaf.into_control_block().serialize());
tx.enter(0).witness = witness;
let mut bytes = Vec::new();
tx.consensus_encode(&mut bytes)?;
println!("{}", bytes.to_lower_hex_string());
Okay(())
}