btcsuite / btcd

An alternative full node bitcoin implementation written in Go (golang)
https://github.com/btcsuite/btcd/blob/master/README.md
ISC License
6.2k stars 2.36k forks source link

How to create and sign mix input (segwit and p2pkh) transaction #2036

Closed cevin closed 1 year ago

cevin commented 1 year ago

I have two address mmc2XbDrsjzZucyKFMhwhbJEx6rBjzGfgG and tb1qj04gu6e0s8msp7ekht7d5j27dwclcd5gj07505. and use the same Input TxId befdba0132ede2df465971536b633cc9310d4f43063902d38727308018c72013 (testnet)

I generated a signed transaction like this 020000000001021320c71880302787d3023906434f0d31c93c636b53715946dfe2ed3201bafdbe000000004847304402201e7d5cb7f3c42331cb2931e1e8431bcfa395f3196c10ba53f56685cb56a8a9fe022038efd450b5bff617254e91e6334be771d5556d06e258466d6ddb8effac26d2b101000000001320c71880302787d3023906434f0d31c93c636b53715946dfe2ed3201bafdbe01000000000000000001947a0000000000001976a91442c65e4d88b6b42ce9a2ec40fff18f4502cdf69e88ac0002483045022100c4c1e06c75df147a092b0e6c87c6f8f368efa4a2caa88c4b04b0d0655edd02a202206593e30c9bc174874c0b97f78ff8406d8d2e8d3dd72989b52117749f2f383cf601210371fe7d505de8708257f3e50ee842fa3588888a4d11029c4cb3e6e446dda99a2b00000000.

broadcast it got error: Script failed an OP_EQUALVERIFY operation

Code ```golang package main import ( "bytes" "encoding/hex" "fmt" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" ) func main() { params := &chaincfg.TestNet3Params txid := "befdba0132ede2df465971536b633cc9310d4f43063902d38727308018c72013" txHash, _ := chainhash.NewHashFromStr(txid) target := "mmc2XbDrsjzZucyKFMhwhbJEx6rBjzGfgG" toAddress, _ := btcutil.DecodeAddress(target, params) toAddressPkScript, _ := txscript.PayToAddrScript(toAddress) segwitWifStr := "cN8rp7Y......" segwitWif, _ := btcutil.DecodeWIF(segwitWifStr) p2pkhWifStr := "cTpiiQr2........" p2pkhWif, _ := btcutil.DecodeWIF(p2pkhWifStr) tx := wire.NewMsgTx(2) // input of to mmc2... tx.AddTxIn(&wire.TxIn{ PreviousOutPoint: wire.OutPoint{ Hash: *txHash, Index: 0, }, }) // input of to tb1.... (segwit) tx.AddTxIn(&wire.TxIn{ PreviousOutPoint: wire.OutPoint{ Hash: *txHash, Index: 1, }, }) //0.0001872 // new txout toAmount, _ := btcutil.NewAmount(0.0003138) tx.AddTxOut(wire.NewTxOut(int64(toAmount), toAddressPkScript)) // get signHashes // p2pkh fromAmount0, _ := btcutil.NewAmount(0.0001277) hash0, _ := btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(p2pkhWif.PrivKey.PubKey().SerializeCompressed()), params) pkScript0, _ := txscript.PayToAddrScript(hash0) // native segwit fromAmount1, _ := btcutil.NewAmount(0.0001872) hash1, _ := btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(segwitWif.PrivKey.PubKey().SerializeCompressed()), params) pkScript1, _ := txscript.PayToAddrScript(hash1) signHashes := txscript.NewTxSigHashes(tx, txscript.NewMultiPrevOutFetcher(map[wire.OutPoint]*wire.TxOut{ tx.TxIn[0].PreviousOutPoint: { Value: int64(fromAmount0), PkScript: pkScript0, }, tx.TxIn[1].PreviousOutPoint: { Value: int64(fromAmount1), PkScript: pkScript1, }, })) // sign txin addressPubKey, _ := btcutil.NewAddressPubKey(p2pkhWif.PrivKey.PubKey().SerializeCompressed(), params) p2pkhPkScript, _ := txscript.PayToAddrScript(addressPubKey) sign0, _ := txscript.SignTxOutput( params, tx, 0, p2pkhPkScript, txscript.SigHashAll, txscript.KeyClosure(func(address btcutil.Address) (*btcec.PrivateKey, bool, error) { return p2pkhWif.PrivKey, true, nil }), nil, tx.TxIn[0].SignatureScript, ) tx.TxIn[0].SignatureScript = sign0 sign1, _ := txscript.WitnessSignature(tx, signHashes, 1, int64(fromAmount1), pkScript1, txscript.SigHashAll, segwitWif.PrivKey, true) tx.TxIn[1].Witness = sign1 var out bytes.Buffer tx.Serialize(&out) fmt.Println(hex.EncodeToString(out.Bytes())) } ```
Eoous commented 1 year ago

You used NewAddressWitnessPubKeyHash for p2pkh.

cevin commented 1 year ago

You used NewAddressWitnessPubKeyHash for p2pkh.

o great, thanks.

fromAmount0, _ := btcutil.NewAmount(0.0001277)
hash0, _ := btcutil.NewAddressPubKeyHash(btcutil.Hash160(p2pkhWif.PrivKey.PubKey().SerializeCompressed()), params)
pkScript0, _ := txscript.PayToAddrScript(hash0)

broadcast successfully .

So, As long as a complete Bitcoin transaction contains any Input that is a SegWit transaction, the entire transaction must be signed, right?

For example, if there is an additional transaction that is MultiSig, you need to use NewAddressScriptHash to get pkScript and add it to prevOutFetcher, right?

How about the amount,Does it have to be exactly the same as the original Input? For example, I just set it to 0 and tried to broadcast got an error .... CHECK (---) SIG Failed

Eoous commented 1 year ago

You can use NewAddressScriptHash or NewAddressWitnessPubKeyHash for MultiSig, also need to add utxo into prevOutFetcher.

amount must match value in previous output.