3

I'm trying to sign a segwit transaction in rust-bitcoin. To start with, my transaction setup looks as follows:

let previous_output = OutPoint::new(Txid::from_hex("991ab2b13f6bc6c13002d79d5e9775626a5e7328e14cd16837d50d1cc637dc6a").unwrap(), 0);
let tx_input = TxIn {
    previous_output,
    script_sig: Default::default(),
    sequence: 0xffffffff,
    witness: Default::default()
};

let tx_output = TxOut {
    value: 4999995000,
    script_pubkey: output_details.script_pubkey() // don't worry about this
};

let mut transaction = Transaction {
    version: 2,
    lock_time: 0,
    input: vec![tx_input],
    output: vec![tx_output]
};

In order to sign the transaction (using Schnorr, by the way), I extract the sighash as follows:

let signature_hash = transaction.signature_hash(0, &output_details.script_pubkey(), 0); // is that really the most idiomatic way of passing a sighash flag in rust-bitcoin?
let message = Message::from_slice(&signature_hash.to_vec()).unwrap();

When I print the hex-serialized message, I get the following value: fa42b9b0f54972b31712b2efbc86db1bf78aa833bc9c969ea7860ef38a25fbaf.

However, in order to sign a segwit transaction, the previous output's amount must also be committed to, and I'm not seeing a way to do that. As far as I can tell, the sighash I should be signing is in fact 61746ec9baa14bbe82586c7f149926b1492871c67dc7fe21e7cfe2f2260a1405.

Finally, though less relevantly, if things were to work fine, here's how my signature process would conlude:

let signature = private_key.sign_schnorr(message);
let signature_vec = signature.as_ref().to_vec();
transaction.input[0].witness = Witness::from_vec(vec![signature_vec]);

Thanks in advance for helping me figure this out!

0

1 Answer 1

4

There is a bitcoin::util::sighash module which has all of the sighashing stuff. I'm not certain that it is the correct way, but it is definitely a way to get the result that you want.

It looks like you have to create a SighashCache object, which you can then use to get all different kinds of sighashes for the transaction.

In your case, this code might make it work:

let previous_output_as_tx_out = TxOut {
    value: 50_0000_0000,
    script_pubkey: output_details.script_pubkey()
};
let prevouts = vec![previous_output_as_tx_out];

let mut sighash_cache = bitcoin::util::sighash::SighashCache::new(&transaction);
let input_index = 0;

// taproot_key_spend_signature_hash is the crucial part
let signature_hash = sighash_cache.taproot_key_spend_signature_hash(input_index, &Prevouts::All(&prevouts), SchnorrSighashType::Default).unwrap();
let message = Message::from_slice(&signature_hash.to_vec()).unwrap();
3
  • 2
    This is the correct way. I apologize for the misleading Transaction::signature_hash name, which should probably be renamed to Transaction::legacy_signature_hash and/or deprecated in favor of the SighashCache object. To arik, the reason for the weird SighashCache API is that from segwit v0 onward, the signature hashes for different inputs in a transaction share a significant amount of computational work, which saves time when saving a whole transaction. Jun 17, 2022 at 17:20
  • Thanks for the response! There doesn't appear to be much documentation or really anything in terms of examples on how to use the taproot_key_spend_signature_hash method, in particular its prevouts parameters. But I should hopefully figure it out within a couple minutes.
    – arik
    Jun 17, 2022 at 21:06
  • All right, got it. Feel free to update your answer with the code I used :)
    – arik
    Jun 17, 2022 at 21:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.