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)
    }
}

1 Answer 1

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.