Categories
Asia Australia Bitcoin Blockchain btcd Canada Comeback Crash Crypto Europe Launch Markets Merge Mining Minting Rates Security Spain Trading United States

How to sign bitcoin P2SH transactions in go (using btcd package)

I have code, which forms and signs transaction with 1 input and 1 output.

func CreateTransaction() (string, error) {
    // privKey isn't valid for the article 
    privKey, destination, amount :=
        "cPU4pnqJqR38ebnsvKvHSz8LidB63s7QeQmkdZGsmXM5SmiZaJnH",
        "2N1qk9szETpDxTqcANa3mvcQKtbT4ihyg7C",
        int64(68500)
    wif, err := btcutil.DecodeWIF(privKey)
    if err != nil {
        return "", err
    }

    // use TestNet3Params for interacting with bitcoin testnet
    // if we want to interact with main net should use MainNetParams
    addrPubKey, err := btcutil.NewAddressPubKey(wif.PrivKey.PubKey().SerializeUncompressed(), &chaincfg.TestNet3Params)
    if err != nil {
        return "", err
    }

    _ = addrPubKey

    txid, balance, pkScript, err := "0478c4f80ae03c4f6cf0b3fa9eaa50521474011a3e681d59e5190a3c531e8edf", int64(70000), "a914ca24ad3bd254295840b27f3745f42461fb805da687", nil
    //sourcePkgScript :=
    //txid, balance, pkScript, err := GetUTXO(addrPubKey.EncodeAddress())
    if err != nil {
        return "", err
    }

    /*
     * 1 or unit-amount in Bitcoin is equal to 1 satoshi and 1 Bitcoin = 100000000 satoshi
     */

    // checking for sufficiency of account
    if balance < amount {
        return "", fmt.Errorf("the balance of the account is not sufficient")
    }

    // extracting destination address as []byte from function argument (destination string)
    destinationAddr, err := btcutil.DecodeAddress(destination, &chaincfg.TestNet3Params)
    if err != nil {
        return "", err
    }

    destinationAddrByte, err := txscript.PayToAddrScript(destinationAddr)
    if err != nil {
        return "", err
    }

    // creating a new bitcoin transaction, different sections of the tx, including
    // input list (contain UTXOs) and outputlist (contain destination address and usually our address)
    // in next steps, sections will be field and pass to sign
    redeemTx, err := NewTx()
    if err != nil {
        return "", err
    }

    utxoHash, err := chainhash.NewHashFromStr(txid)
    if err != nil {
        return "", err
    }

    // the second argument is vout or Tx-index, which is the index
    // of spending UTXO in the transaction that Txid referred to
    outPoint := wire.NewOutPoint(utxoHash, 0)

    // making the input, and adding it to transaction
    txIn := wire.NewTxIn(outPoint, nil, nil)
    redeemTx.AddTxIn(txIn)

    // adding the destination address and the amount to
    // the transaction as output
    redeemTxOut := wire.NewTxOut(amount, destinationAddrByte)
    redeemTx.AddTxOut(redeemTxOut)

    // now sign the transaction
    finalRawTx, redeemTx, err := SignTx(privKey, pkScript, redeemTx)

    //sourcePKScript, _ := hex.DecodeString(pkScript)
    //e, _ := txscript.NewEngine(sourcePKScript, redeemTx, txscript.ScriptBip16)
    //e.Execute()
    return finalRawTx, nil
}

func SignTx(privKey string, pkScript string, redeemTx *wire.MsgTx) (string, *wire.MsgTx, error) {

    wif, err := btcutil.DecodeWIF(privKey)
    if err != nil {
        return "", nil, err
    }

    sourcePKScript, err := hex.DecodeString(pkScript)
    if err != nil {
        return "", nil, nil
    }

    // since there is only one input in our transaction
    // we use 0 as second argument, if the transaction
    // has more args, should pass related index
    signature, err := txscript.SignatureScript(redeemTx, 0, sourcePKScript, txscript.SigHashAll, wif.PrivKey, false)
    if err != nil {
        return "", nil, nil
    }

    // since there is only one input, and want to add
    // signature to it use 0 as index
    redeemTx.TxIn[0].SignatureScript = signature

    sigHex := hex.EncodeToString(signature)
    log.Println(sigHex)

    var serializedSignedTx bytes.Buffer
    err = redeemTx.Serialize(&serializedSignedTx)
    if err != nil {
        return "", nil, err
    }
    hexSignedTx := hex.EncodeToString(serializedSignedTx.Bytes())
    vm, err := txscript.NewEngine(sourcePKScript, redeemTx, 0, txscript.StandardVerifyFlags, nil, nil, 68500, nil)
    if err != nil {
        log.Fatal(err)
    }

    err = vm.Execute()
    if err != nil {
        log.Fatal(err)
    }

    return hexSignedTx, redeemTx, nil
}


Problem is – after forming and signing transaction the transaction isn’t valid, and code fails with error “false stack entry at end of script execution”:

    hexSignedTx := hex.EncodeToString(serializedSignedTx.Bytes())
    vm, err := txscript.NewEngine(sourcePKScript, redeemTx, 0, txscript.StandardVerifyFlags, nil, nil, 68500, nil)
    if err != nil {
        log.Fatal(err)
    }

    err = vm.Execute()
    if err != nil {
        log.Fatal(err)
    }

I have this great article at hand https://hlongvu.com/post/t0xx5dejn3-Understanding-btcd-Part-4-Create-and-Sign-a-Bitcoin-transaction-with-btcd

I suppose that problem is with P2SH address, and may be there are guides how to forms and sign transactions for P2SH addresses?