multi signature – How “addDummySigs” in golang when ship satoshi from p2tr multisig script?


I’ve a 2-of-3 multisig taproot script,create like bitcoinjs-lib:https://github.com/bitcoinjs/bitcoinjs-lib/blob/8d9775c20da03ab40ccbb1971251e71d117bcd8b/take a look at/integration/taproot.spec.ts#L531-L593:

builder := txscript.NewScriptBuilder()
for i, pk := vary leafPubkeys {
    if i == 0 {
        builder.AddData(pk)
        builder.AddOp(byte(txscript.OP_CHECKSIG))
    } else {
        builder.AddData(pk)
        builder.AddOp(byte(txscript.OP_CHECKSIGADD))
    }
}
builder.AddOp(byte(txscript.OP_1 - 1 + 2))
builder.AddOp(byte(txscript.OP_GREATERTHANOREQUAL))

then create a tx(ship satoshi from the script),and two keys signal it.however bought “invalid schnorr signatures” when despatched to bitcoind.
my golang code:

func TestGenTapscript(t *testing.T) {
    signpks := []string{
...
    }
    pks := []string{...}
    tapscript := NewMultisigTaprootScript(pks, 2, 3, &chaincfg.TestNet3Params)

    // construct tx
    const utxoHash = "..."
    const utxoVout = 0
    const utxoAmount = 1000000
    const sendAmount = utxoAmount - 1000

    var (
        inputs     []*wire.OutPoint
        nSequences []uint32
        outputs    []*wire.TxOut
    )
    { // output
        ...
        outputs = append(outputs, redeemTxOut)
    }
    { // enter
        ...
    }

    // Construct PSBT
    packet, err := psbt.New(inputs, outputs, 2, 0, nSequences)
    if err != nil {
        t.Deadly(err)
    }
    packet.Inputs[0].TaprootLeafScript = []*psbt.TaprootTapLeafScript{
        &psbt.TaprootTapLeafScript{
            ControlBlock: tapscript.controlBlock,
            Script:       tapscript.leafScript,
            LeafVersion:  0xc0,
        },
    }

    // replace
    updater, err := psbt.NewUpdater(packet)
    if err != nil {
        t.Deadly(err)
    }
    if err := updater.AddInWitnessUtxo(wire.NewTxOut(utxoAmount, tapscript.output), 0); err != nil {
        t.Deadly(err)
    }

    var serializedTx bytes.Buffer
    if err := packet.Serialize(&serializedTx); err != nil {
        t.Deadly(err)
    }
    psbtHex := hex.EncodeToString(serializedTx.Bytes())
    t.Log("psbtHex earlier than signal:", psbtHex)

    // signit
    cu := cuabs.New()
    signedPsbtHex, err := cu.PsbtSign(context.Background(), signpks[0], psbtHex)
    if err != nil {
        t.Deadly(err)
    }
    signedPsbtHex, err = cu.PsbtSign(context.Background(), signpks[2], signedPsbtHex)
    if err != nil {
        t.Deadly(err)
    }
    t.Log("psbtHex after signal:", signedPsbtHex)

    // addDummySigs like bitcoinjs-lib
    psbtBz, _ := hex.DecodeString(signedPsbtHex)
    packet, err = psbt.NewFromRawBytes(bytes.NewReader(psbtBz), false)
    if err != nil {
        t.Deadly(err)
    }

    for index, enter := vary packet.Inputs {
        for _, pk := vary pks {
            pkbz, _ := hex.DecodeString(pk)
            var signed bool
            for _, x := vary enter.TaprootScriptSpendSig {
                if bytes.Equal(pkbz, x.XOnlyPubKey) {
                    signed = true
                    break
                }
            }
            if !signed {
                packet.Inputs[index].TaprootScriptSpendSig = append(packet.Inputs[index].TaprootScriptSpendSig, &psbt.TaprootScriptSpendSig{
                    XOnlyPubKey: pkbz,
                    LeafHash:    tapscript.tapleafHash,
                    Signature:   nil,
                    SigHash:     0,
                })
            }
        }

        // sigs := packet.Inputs[index].TaprootScriptSpendSig

        // type.Slice(sigs, func(i, j int) bool {
        //  return bytes.Examine(sigs[i].XOnlyPubKey, sigs[j].XOnlyPubKey) < 0
        // })
        // packet.Inputs[index].TaprootScriptSpendSig = sigs
    }

    {
        var serializedTx bytes.Buffer
        if err := packet.Serialize(&serializedTx); err != nil {
            t.Deadly(err)
        }
        t.Log("psbtHex earlier than finalize", hex.EncodeToString(serializedTx.Bytes()))
    }

    if err = psbt.MaybeFinalizeAll(packet); err != nil {
        t.Deadly(err)
    }
    {
        var serializedTx bytes.Buffer
        if err := packet.Serialize(&serializedTx); err != nil {
            t.Deadly(err)
        }
        t.Log("psbtHex after finalize", hex.EncodeToString(serializedTx.Bytes()))
        
    }
    //extract()
    //broadcast()
}

Use js code,it work. “tapscript.tapleafHash” in golang is equal “leafHash” in js,and “pubkey” is equal.
After comparability, it was discovered that the “psbtHex earlier than finalize” and “psbtHex after finalize” have been totally different.
my js code:

const psbt = bitcoin.Psbt.fromHex(signedPsbtHex)
{
  const leafHash = tapleafHash({
    output: leafScript,
    model: LEAF_VERSION_TAPSCRIPT,
  })
  console.log(leafHash.toString('hex'))
  for (const enter of psbt.information.inputs) {
    if (!enter.tapScriptSig) proceed
    const signedPubkeys = enter.tapScriptSig.filter((ts) => ts.leafHash.equals(leafHash)).map((ts) => ts.pubkey)
    for (const pubkey of leafPubkeys) {
      if (signedPubkeys.some((sPub) => sPub.equals(pubkey))) proceed
      enter.tapScriptSig.push({
        // This may be reused for every dummy signature
        leafHash,
        // That is the pubkey that did not signal
        pubkey,
        // This should be an empty Buffer.
        signature: Buffer.from([]),
      })
    }
  }
} // this code from bitcoinjs-lib

psbt.finalizeAllInputs()
const tx = psbt.extractTransaction()
broadcast(...)

Leave a Reply

Your email address will not be published. Required fields are marked *