Closed Eoous closed 1 year ago
Look at the sighashes you're signing (you can insert print statements to see why they differ before the sig msg is hashed). If they're different, then that's why you get an invalid final signature. Also look at the ordering of the keys in the script as well: tapscript doesn't have a normal multi-sig op code, so you either need to use checksigadd or a normal checksig.
Look at the sighashes you're signing (you can insert print statements to see why they differ before the sig msg is hashed). If they're different, then that's why you get an invalid final signature. Also look at the ordering of the keys in the script as well: tapscript doesn't have a normal multi-sig op code, so you either need to use checksigadd or a normal checksig.
Printed sighashes, they differ. But they generated from different UnsignedTx and OutFetcher, they should be same?
sighashes1:
3f1b201fa7d5793579c08ffb394f76132f2879f9fc4a4057d5263cb0a1abea87
0efd7b36e8b7ac90f08be47960f7a9a626cf4fb17f47afb1c099b7c01b1395ad
f022effa4e64b6a83efb44363f1823e297addbeabe420dfb81f4517e8862905c
b429a131e02198a3c0d252c4e7a1ff05c35fd982180ee5b158b8c5fd477d89e7
161ce34235a6d9212f1aac85096bacb3de11fec85967833b996f48d37614b04f
sighashes2:
f5ba73ad25860cbbbfb25d036a22cb7437900b09baf4d36bee1cab48e189714c
caf0acbb5b59fb577e5ef02af8c429ec2d36330d65d078ee5dce615644aea312
0e325a2c5b8ed4a7eb0e6efdbf97229331acd35235e0fca20a893eded16e42e5
44fb6442528119a34933596c1b5ecb49b0908fb14f7a887dfdc159c28c14a04d
b2df0d6cb74a90c9f73c4bafa597605438385ce86113ee0c6e58b287b5d03546
For result, there're 2 input(first from psbt2, second from psbt1) and 2 output(first from psbt2, second from psbt1).
Alice wants to sell something and receive bitcoin(psbt1, use Single|AnyOneCanPay). Then Bob pays(psbt2, Default) and gets change. They're both p2tr addresses. I want to figure out how psbt2 can merge pre-signed psbt1.
Perhaps this's not multi-sig? I don't know what to call it.
All codes:
func TestModularTransfer(t *testing.T) {
value := 7083
c, err := rpcclient.New(&rpcclient.ConnConfig{
Host: "testnet",
User: "user",
Pass: "pass",
HTTPPostMode: true, // Bitcoin core only supports HTTP POST mode
DisableTLS: true,
}, nil)
if err != nil {
log.Fatalln(err)
}
network := &chaincfg.TestNet3Params
var sellerPrevOutputFetcher *txscript.MultiPrevOutFetcher
b := func() string {
var inputsTxInput []*TxInput
inputsTxInput = append(inputsTxInput, &TxInput{
TxId: "dee67d15626a3f651236da375686ae3b3e14e2b514c5876653c8dbe67b9e2c11",
VOut: uint32(0),
Amount: int64(435),
Address: "tb1pt753u3nvyeny3609suzl0wc8cqdqx978qmp7pcasr6kvr3kluqhqaqyq8n",
PrivateKey: "priv",
})
var outputsTxOutput []*TxOutput
outputsTxOutput = append(outputsTxOutput, &TxOutput{
Address: "tb1pt753u3nvyeny3609suzl0wc8cqdqx978qmp7pcasr6kvr3kluqhqaqyq8n",
Amount: int64(value - 1000),
})
var inputs []*wire.OutPoint
var nSequences []uint32
prevOuts := make(map[wire.OutPoint]*wire.TxOut)
for _, in := range inputsTxInput {
txHash, err := chainhash.NewHashFromStr(in.TxId)
if err != nil {
panic(err)
}
prevOut := wire.NewOutPoint(txHash, in.VOut)
inputs = append(inputs, prevOut)
prevPkScript, err := AddrToPkScript(in.Address, network)
if err != nil {
panic(err)
}
witnessUtxo := wire.NewTxOut(in.Amount, prevPkScript)
prevOuts[*prevOut] = witnessUtxo
nSequences = append(nSequences, wire.MaxTxInSequenceNum)
}
var outputs []*wire.TxOut
for _, out := range outputsTxOutput {
pkScript, err := AddrToPkScript(out.Address, network)
if err != nil {
panic(err)
}
outputs = append(outputs, wire.NewTxOut(out.Amount, pkScript))
}
bp, err := psbt.New(inputs, outputs, txVersion, nLockTime, nSequences)
if err != nil {
panic(err)
}
updater, err := psbt.NewUpdater(bp)
sellerPrevOutputFetcher = txscript.NewMultiPrevOutFetcher(prevOuts)
for i, in := range inputsTxInput {
if err = signInput(updater, i, in, sellerPrevOutputFetcher, txscript.SigHashSingle|txscript.SigHashAnyOneCanPay, network); err != nil {
panic(err)
}
}
var buf bytes.Buffer
err = bp.Serialize(&buf)
if err != nil {
panic(err)
}
buf.Reset()
b, err := bp.B64Encode()
fmt.Println("is completed: ", bp.IsComplete())
return b
}()
preSignedpsbt, err := psbt.NewFromRawBytes(strings.NewReader(b), true)
if err != nil {
panic(err)
}
var inputsTxInput []*TxInput
inputsTxInput = append(inputsTxInput, &TxInput{
TxId: "fa9b34b0b4bfd27910c2e337a31a54a1f8d7e91f0981b8a9cf0b9123753e441c",
VOut: uint32(1),
Amount: int64(value),
Address: "tb1pns05k7ts67gvnxmjvk6n4hkqx4ghpr7h6e7m0q6el8z89lny9tgs6z6dhx",
PrivateKey: "priv",
})
var outputsTxOutput []*TxOutput
outputsTxOutput = append(outputsTxOutput, &TxOutput{
Address: "tb1pns05k7ts67gvnxmjvk6n4hkqx4ghpr7h6e7m0q6el8z89lny9tgs6z6dhx",
Amount: int64(435),
})
var inputs []*wire.OutPoint
var nSequences []uint32
prevOuts := make(map[wire.OutPoint]*wire.TxOut)
for _, in := range inputsTxInput {
txHash, err := chainhash.NewHashFromStr(in.TxId)
if err != nil {
panic(err)
}
prevOut := wire.NewOutPoint(txHash, in.VOut)
inputs = append(inputs, prevOut)
prevPkScript, err := AddrToPkScript(in.Address, network)
if err != nil {
panic(err)
}
witnessUtxo := wire.NewTxOut(in.Amount, prevPkScript)
prevOuts[*prevOut] = witnessUtxo
nSequences = append(nSequences, wire.MaxTxInSequenceNum)
}
var outputs []*wire.TxOut
for _, out := range outputsTxOutput {
pkScript, err := AddrToPkScript(out.Address, network)
if err != nil {
panic(err)
}
outputs = append(outputs, wire.NewTxOut(out.Amount, pkScript))
}
bp, err := psbt.New(inputs, outputs, txVersion, nLockTime, nSequences)
bp.Inputs = append(bp.Inputs, preSignedpsbt.Inputs[0])
bp.UnsignedTx.TxIn = append(bp.UnsignedTx.TxIn, preSignedpsbt.UnsignedTx.TxIn[0])
bp.UnsignedTx.TxIn[1].SignatureScript = nil
bp.Outputs = append(bp.Outputs, preSignedpsbt.Outputs[0])
bp.UnsignedTx.TxOut = append(bp.UnsignedTx.TxOut, preSignedpsbt.UnsignedTx.TxOut[0])
updater, err := psbt.NewUpdater(bp)
if err != nil {
panic(err)
}
prevOutputFetcher := txscript.NewMultiPrevOutFetcher(prevOuts)
prevOutputFetcher.Merge(sellerPrevOutputFetcher)
for i, in := range inputsTxInput {
if err = signInput(updater, i, in, prevOutputFetcher, txscript.SigHashDefault, network); err != nil {
panic(err)
}
if err = psbt.Finalize(bp, i); err != nil {
panic(err)
}
if err = psbt.Finalize(bp, i+1); err != nil {
panic(err)
}
}
buyerSignedTx, err := psbt.Extract(bp)
if err != nil {
panic(err)
}
var buf bytes.Buffer
if err = buyerSignedTx.Serialize(&buf); err != nil {
panic(err)
}
fmt.Println("is completed: ", bp.IsComplete())
fmt.Println(hex.EncodeToString(buf.Bytes()))
hash, err := c.SendRawTransaction(buyerSignedTx, false)
if err != nil {
log.Fatalln(err)
}
fmt.Println(hash.String())
}
func signInput(updater *psbt.Updater, i int, in *TxInput, prevOutFetcher *txscript.MultiPrevOutFetcher, hashType txscript.SigHashType, network *chaincfg.Params) error {
wif, err := btcutil.DecodeWIF(in.PrivateKey)
if err != nil {
return err
}
privKey := wif.PrivKey
prevPkScript, err := AddrToPkScript(in.Address, network)
if err != nil {
return err
}
witnessUtxo := wire.NewTxOut(in.Amount, prevPkScript)
err = updater.AddInWitnessUtxo(witnessUtxo, i)
if err != nil {
return err
}
if err = updater.AddInSighashType(hashType, i); err != nil {
return err
}
internalPubKey := schnorr.SerializePubKey(privKey.PubKey())
updater.Upsbt.Inputs[i].TaprootInternalKey = internalPubKey
sigHashes := txscript.NewTxSigHashes(updater.Upsbt.UnsignedTx, prevOutFetcher)
if hashType == txscript.SigHashAll {
hashType = txscript.SigHashDefault
}
witness, err := txscript.TaprootWitnessSignature(updater.Upsbt.UnsignedTx, sigHashes,
i, in.Amount, prevPkScript, hashType, privKey)
if err != nil {
return err
}
updater.Upsbt.Inputs[i].TaprootKeySpendSig = witness[0]
return nil
}
func AddrToPkScript(addr string, network *chaincfg.Params) ([]byte, error) {
address, err := btcutil.DecodeAddress(addr, network)
if err != nil {
return nil, err
}
return txscript.PayToAddrScript(address)
}
Can you print the two extracted raw transactions please? Did you try if things work if you use txscript.SigHashDefault
for both PSBTs?
Can you print the two extracted raw transactions please? Did you try if things work if you use
txscript.SigHashDefault
for both PSBTs?
Only extracted second psbt:
020000000001021c443e7523910bcfa9b881091fe9d7f8a1541aa337e3c21079d2bfb4b0349bfa0100000000ffffffff112c9e7be6dbc8536687c514b5e2143e3bae865637da3612653f6a62157de6de0000000000ffffffff02b3010000000000002251209c1f4b7970d790c99b7265b53adec03551708fd7d67db78359f9c472fe642ad1d5000000000000002251205fa91e466c266648e9e58705f7bb07c01a0317c706c3e0e3b01eacc1c6dfe02e0140e013c9225b2b406cefc2ed981d88468173189c7b1f9710154e5e5a60fcfd48300cd52bdb24fcf241f0949ed0b4d302a21ca3716a97e5e6709f9a320c51e83752014051fc7ce9402fab8633f200bb37277f8363467dfa57f0006eeae388c6d76e35608745e3948c9ffadd545e99ba63498823c8669f2b1ab62a0ca9c1877680b25b7600000000
Got same error by using txscript.SigHashDefault
for both PSBTs.
For first psbt, I need to finalize and extract it?
If i finalize and extract first psbt(use txscript.SigHashDefault
) and boardcast it, got error: bad-txns-in-belowout, value in (0.00000435) < value out (0.00006083)
. That's right.
Ah, I see what you're attempting to do now. And looking at the TX you sent, it is missing the sighash flag at the end of the signature. So that gets lost somewhere when splicing together the two transactions.
Ah, I see what you're attempting to do now. And looking at the TX you sent, it is missing the sighash flag at the end of the signature. So that gets lost somewhere when splicing together the two transactions.
I tried to add sighash flag,but not working:
hashtype := txscript.SigHashSingle | txscript.SigHashAnyOneCanPay
updater.AddInSighashType(hashtype, 1)
I changed value of output and tried to add sighash flag, here is new extracted tx:
020000000001021c443e7523910bcfa9b881091fe9d7f8a1541aa337e3c21079d2bfb4b0349bfa0100000000ffffffff112c9e7be6dbc8536687c514b5e2143e3bae865637da3612653f6a62157de6de0000000000ffffffff02b3010000000000002251209c1f4b7970d790c99b7265b53adec03551708fd7d67db78359f9c472fe642ad1c3170000000000002251205fa91e466c266648e9e58705f7bb07c01a0317c706c3e0e3b01eacc1c6dfe02e0140cc92b428180daa19c892becbbd9ae691023f377c17795709daee820485609448e9d22cff2e25409faa6e8afaf6f977ca05d0fe3504ef982660fcd8c2ee2f57730140892b736b69f534d74268f2cf47ce193361e5b3dc5c64ff1a8f8a0d8a5fbb2adafa405f9d3ab2aab5e65d8e3199aec63d39140c4eeb6c212b2764024741061ab300000000
Looks that also there isn't a sighash flag at end of signature.
Try adding it to the witness manually. I think this might be a bug in the PSBT library that the sighash is ignored when finalizing.
EDIT: This here should take into account the sighash flag: https://github.com/btcsuite/btcd/blob/2dbc98bdf3dc3f72a749c246cd0171bdd7abafd2/btcutil/psbt/finalizer.go#L522
Try adding it to the witness manually. I think this might be a bug in the PSBT library that the sighash is ignored when finalizing.
EDIT: This here should take into account the sighash flag:
It worked, thank you very much!
I want to create 2 of 2 multisig tx with psbt. But got error:
non-mandatory-script-verify-flag (Invalid Schnorr signature)
Here are steps, psbt1 and psbt2 are different instances:
txscript.TaprootWitnessSignature
withtxscript.SigHashSingle|txscript.SigHashAnyOneCanPay
and use private_key1updater.Upsbt.Inputs[0].TaprootKeySpendSig = witness from 2
UnsignedTx
txscript.TaprootWitnessSignature
withtxscript.SigHashDefault
and use another private_keyupdater.Upsbt.Inputs[0].TaprootKeySpendSig = witness from 6
Then got error.
Here is codes of 5: