2

While working on go-elements I've been trying to expand on pset_test.go by implementing functions that broadcast different kinds of PSET transaction. Unfortunately I can't find many technical informations about this subject.
I'm trying to implement a test that Broadcasts a transaction created from a PSET that issues an asset, with P2PKH outputs for the asset, l-btc (for the change) and the fee.
The code I wrote is returning the error pset_test.go:324: Signature does not correspond to this input which is pretty self-explanatory but I'm not sure where I got it wrong. I would appreciate some help and also indication on technical resources that could help me understand this subject a bit better. Thank you

func TestBroadcastUnblindedIssuanceTxP2PKH(t *testing.T) {
    privkey, err := btcec.NewPrivateKey(btcec.S256())
    if err != nil {
        t.Fatal(err)
    }
    pubkey := privkey.PubKey()
    p2pkh := payment.FromPublicKey(pubkey, &network.Regtest, nil)
    address, _ := p2pkh.PubKeyHash()

    // Fund sender address.
    _, err = faucet(address)
    if err != nil {
        t.Fatal(err)
    }

    // Retrieve sender utxos.
    utxos, err := unspents(address)
    if err != nil {
        t.Fatal(err)
    }

    // The transaction will have 1 input and 3 outputs.
    txInputHash, _ := hex.DecodeString(utxos[0]["txid"].(string))
    txInputHash = bufferutil.ReverseBytes(txInputHash)
    txInputIndex := uint32(utxos[0]["vout"].(float64))
    txInput := transaction.NewTxInput(txInputHash, txInputIndex)

    lbtc, _ := hex.DecodeString(
        "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225",
    )
    lbtc = append([]byte{0x01}, bufferutil.ReverseBytes(lbtc)...)

    changeScript := p2pkh.Script
    changeValue, _ := confidential.SatoshiToElementsValue(99999500)
    changeOutput := transaction.NewTxOutput(lbtc, changeValue[:], changeScript)

    feeScript := []byte{}
    feeValue, _ := confidential.SatoshiToElementsValue(500)
    feeOutput := transaction.NewTxOutput(lbtc, feeValue[:], feeScript)

    // Create a new pset.
    inputs := []*transaction.TxInput{txInput}
    outputs := []*transaction.TxOutput{changeOutput, feeOutput}
    p, err := New(inputs, outputs, 2, 0)
    if err != nil {
        t.Fatal(err)
    }

    updater, err := NewUpdater(p)
    if err != nil {
        t.Fatal(err)
    }

    arg := AddIssuanceArg{
        Precision: 0,
        Contract: &transaction.IssuanceContract{
            Name:      "Test",
            Ticker:    "TST",
            Version:   0,
            Precision: 0,
            Entity: transaction.IssuanceEntity{
                Domain: "test.io",
            },
        },
        AssetAmount:  1000,
        TokenAmount:  1,
        AssetAddress: address,
        TokenAddress: address,
        TokenFlag:    0,
        Net:          network.Regtest,
    }
    err = updater.AddIssuance(arg)
    if err != nil {
        t.Fatal(err)
    }

    err = updater.AddInSighashType(txscript.SigHashAll, 0)
    if err != nil {
        t.Fatal(err)
    }

    err = updater.AddInNonWitnessUtxo(updater.Data.UnsignedTx, 0)
    if err != nil {
        t.Fatal(err)
    }

    txHash := updater.Data.UnsignedTx.TxHash()
    sig, err := privkey.Sign(txHash[:])
    if err != nil {
        t.Fatal(err)
    }

    sigWithHashType := append(sig.Serialize(), byte(txscript.SigHashAll))

    // Update the pset adding the input signature script and the pubkey.
    _, err = updater.Sign(0, sigWithHashType, pubkey.SerializeCompressed(), nil, nil)
    if err != nil {
        t.Fatal(err)
    }

    valid, err := updater.Data.ValidateAllSignatures()
    if err != nil {
        t.Fatal(err)
    }
    if !valid {
        t.Fatal(errors.New("invalid signatures"))
    }

    // Finalize the partial transaction.
    p = updater.Data
    err = FinalizeAll(p)
    if err != nil {
        t.Fatal(err)
    }

    // Extract the final signed transaction from the Pset wrapper.
    finalTx, err := Extract(p)
    if err != nil {
        t.Fatal(err)
    }

    // Serialize the transaction and try to broadcast.
    txHex, err := finalTx.ToHex()
    if err != nil {
        t.Fatal(err)
    }
    _, err = broadcast(txHex)
    if err != nil {
        t.Fatal(err)
    }
}
Murch
  • 71,155
  • 33
  • 180
  • 600

1 Answers1

3

The error hints that you're not signing the transaction correctly. In fact, you're generating a signature with:

txHash := updater.Data.UnsignedTx.TxHash()
sig, err := privkey.Sign(txHash[:])

But the current transaction hash is not what you need to sign. Instead, you should use HashForSignature for legacy input types:

sigHash, err := updater.Data.UnsignedTx.HashForSignature(0, p2pkh.Script, txscript.SigHashAll)
if err != nil {
    t.Fatal(err)
}
sig, err := privkey.Sign(sigHash[:])

PS. Note that at the moment the Elements protocol does not allow you to use an input owned by a legacy scriptpubkey, therefore even if the transaction is well-formed it won't get accepted in mempool and included in the blockchain.