rust-bitcoin / rust-bitcoin

Rust Bitcoin library
Creative Commons Zero v1.0 Universal
2.13k stars 696 forks source link

how to create a raw transaction and then sign it ? #294

Closed calldata closed 5 years ago

stevenroose commented 5 years ago

Just pushed some code I had laying around to GitHub for you :)

Tx creation: https://github.com/stevenroose/rust-bitcoin-wallet/blob/dcc4fc49225a4fd7fea1181a26cf2da0e78c83a8/src/wallet.rs#L268

Signing code in another project: https://github.com/Blockstream/gdk_rpc/blob/9dec80995c170d7fcd5a2aa875609b2e118f9cbc/src/wallet.rs#L361

(only does p2wpkh, but p2pkh is also possible)

thomaseizinger commented 5 years ago

@jayphbee What is your concrete usecase?

calldata commented 5 years ago

@thomaseizinger i'd like to sign a transaction offline use Transaction object, but i found it is not convenient to accomplish. I have to construct ScriptSig myself and set it back to Transaction.TxIn.script, then serialize it.

thomaseizinger commented 5 years ago

@thomaseizinger i'd like to sign a transaction offline use Transaction object, but i found it is not convenient to accomplish. I have to construct ScriptSig myself and set it back to Transaction.TxIn.script, then serialize it.

@jayphbee Unfortunately, that is a very generic description and I can't help much with that :(

The reason I was asking was because we currently don't have any examples (examples/ folder) that explain how to do certain things with the library. I was gonna add some but without a concrete usecase, it is hard to guess what you are looking for. There is many different aspects when it comes to constructing a Transaction:

The current interface allows for all of this and hence, can't be really opinionated towards a single one (it sounds like you are asking for a specific usecase since you mentioned lack of convenience).

What you described sounds right. If you want to manually sign a transaction, you need to set the inputs + outputs, serialize it and add the correct signature (from the input you are spending).

calldata commented 5 years ago

This is my way to build a p2pkh transaction with 2 inputs and 2 outputs

let raw_tx = Transaction {
        version: 1,
        lock_time: 0,
        input: vec![TxIn {
            previous_output: OutPoint::from_str("964b06c7d65bc2966ffc089be06469cf3961fdae4253cb51fe158bf1696882a1:1").unwrap(),
            script_sig: Script::new(),
            sequence: 0,
            witness: vec![],
        }, TxIn {
            previous_output: OutPoint::from_str("6c1fd83338c12326e9160d57a95198937a228b6c4f55e882792be19fe2038da5:1").unwrap(),
            script_sig: Script::new(),
            sequence: 0,
            witness: vec![],
        }],
        output: vec![TxOut {
            value: 81243807,
            script_pubkey: Script::from(decode("76a9145477d7bfe9bdf17cea9f5b2ecacc7a2577723c7488ac").unwrap()),
        }, TxOut {
            value: 100000,
            script_pubkey: Script::from(decode("76a91402245e1265ca65f5ab6d70289f7bcfed6204810588ac").unwrap()),
        }],
    };

    let sig_hash = raw_tx.signature_hash(0, &Script::from(decode("76a9145477d7bfe9bdf17cea9f5b2ecacc7a2577723c7488ac").unwrap()), 1);

    let secp = Secp256k1::new();
    let msg = Message::from_slice(&sig_hash.into_inner()).unwrap();
    let sk = PrivateKey::from_wif("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").unwrap();
    let pk = sk.public_key(&secp);
    let mut sig = secp.sign(&msg, &sk.key).serialize_der();
    sig.push(1); // sign hash type

    let builder1 = Builder::new()
                .push_slice(&sig)
                .push_key(&pk);

    // set the first input signature
    raw_tx.input[0].script_sig = builder1.into_script();

    let sig_hash2 = raw_tx.signature_hash(1, &Script::from(decode("76a9145477d7bfe9bdf17cea9f5b2ecacc7a2577723c7488ac").unwrap()), 1);
    let msg2 = Message::from_slice(&sig_hash2.into_inner()).unwrap();
    let mut sig2 = secp.sign(&msg2, &sk.key).serialize_der();
    sig2.push(1); // sign hash type

    println!("sig2: {:?}", encode(&sig2));

    let builder2 = Builder::new()
                .push_slice(&sig2)
                .push_key(&pk);

    // set the second input signature
    raw_tx.input[1].script_sig = builder2.into_script();

    let serialized = raw_tx.serialize();

    println!("serialized: {:?}", serialized);
    println!("txHash: {:?}", raw_tx.txid());

Is there a more high level api to do this ?

stevenroose commented 5 years ago

I'm thinking it might be worth it to have signing logic on the PSBT types. Like a single method try_sign or something that takes a set of private keys (or hashmap of pubkey->privkey) and goes over all the psbt inputs signing if it can sign them.

stevenroose commented 5 years ago

But it's certainly worth it to wait for rust-miniscript to stabilize so that we can use miniscript for that. (@apoelstra)

apoelstra commented 5 years ago

Yeah, miniscript is the tool for this.

apoelstra commented 5 years ago

rust-miniscript will also have PSBT support

calldata commented 5 years ago

good to know that. Close this issue

casey commented 2 years ago

Yeah, miniscript is the tool for this.

I needed to sign some transactions to send to a regtest bitcoind node for testing, and found this issue. Is rust-miniscript the right approach for quick-and-dirty testing as well?

apoelstra commented 2 years ago

If you know the exact shape of your scriptSig (or more likely, your witnessScript) then you can just hack it together manually. The PSBT API in rust-bitcoin is much nicer now than when this issue was opened. In particular #957 will make your life easier.

But rust-miniscript is probably the easist thing to use in any case because it can produce generic witnesses, even for complex scripts, and will figure out the background "is this segwit or legacy or taproot or what" questions.