I am attempting to make a taproot HTLC utilizing rust-bitcoin
for an atomic swap protocol. My plan is that within the “pleased case” of my protocol, the cash are spent with the key-path, after which there’s a tapleaf for the “refund” case (after n
blocks, the unique proprietor can spend), and a tapleaf with a hashlock.
Assuming I’ve features to construct up the hashlock and timelock scripts, I am constructing some TaprootSpendInfo
and a obtain deal with as so:
let taproot_spend_info = TaprootBuilder::new()
.add_leaf(
1u8,
build_hashlock_script(alice_hashlock.as_slice(), &bob.hl_keypair.x_only_public_key().0)
)
.anticipate("could not add hashlock leaf")
.add_leaf(
1u8,
build_timelock_script(2, &alice.refund_keypair.x_only_public_key().0)
)
.anticipate("Could not add timelock leaf")
.finalize(
&secp,
alice.escrow_keypair.x_only_public_key().0
)
.anticipate("Couldn't finalize taproot spend information");
let alice2bob_addr = Handle::p2tr_tweaked(taproot_spend_info.output_key(), Community::Regtest);
I am going and fund that deal with. Then after I need to spend from that deal with with the key-path, I make a transaction and get the sighash as so:
let mut keypath_tx = Transaction {
model: 1,
lock_time: PackedLockTime::ZERO,
enter: vec![ TxIn {
previous_output: OutPoint {
txid: first_funding_txid,
vout: 0
},
script_sig: script::Builder::new().into_script(), // this might be wrong
sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
witness: Witness::new(),
}],
output: vec![ TxOut {
value: Amount::from_btc(0.99).unwrap().to_sat(),
script_pubkey: miner_address.script_pubkey()
}]
};
let sighash = keypath_tx.signature_hash(0, &script::Builder::new().into_script(), 0x00);
let hash_to_be_signed = sighash.as_hash();
After which I take that sighash and signal it with libsecp:
let message = secp256k1::Message::from_slice(&hash_to_be_signed).unwrap();
let signature = secp.sign_schnorr(&message, &alice.escrow_keypair.secret_key().add_tweak(&taproot_spend_info.tap_tweak().to_scalar()).unwrap().keypair(&secp));
keypath_tx.enter[0].witness.push(signature.as_ref());
When I attempt to broadcast this transaction, I get an error Invalid Schnorr signature
.
I believe I am including the signature to the witness appropriately: BIP341 says that if after eradicating the annex from the witness (if it is there), if there may be one merchandise left, it is interpreted as a key-path spend and the merchandise is interpreted as a signature. So I believe pushing the signature into the in any other case empty witness is the proper factor to do. I additionally checked that the signature is legitimate. That makes me suppose that I am both signing the incorrect factor or that I am not tweaking the personal key appropriately at signing time.
My solely taproot expertise to this point has been with descriptor wallets, so any recommendations on what I am doing incorrect can be enormously appreciated. Thanks!