Link to home
Start Free TrialLog in
Avatar of wesley too
wesley too

asked on

How to correctly create and sign a Bitcoin raw transaction using Btcutil library?

hi there,

Currently I'm trying to create, sign & broadcast bitcoin transaction using btcsuite. For start, I've already had testnet3 address & its associated privatekey for testing. However, hunting through the post and articles like below:-

https://www.thepolyglotdeveloper.com/2018/03/create-sign-bitcoin-transactions-golang/
https://github.com/prettymuchbryce/hellobitcoin/blob/master/transaction.go
https://github.com/btcsuite/btcd/issues/1164

The solution proposed above its not complete, for first one, it only covers until signing (i knew the author claimed its not broadcastable unless you provide the utxo which i did if i'm right) but when tried to braodcast it failed with message

"Error validating transaction: Transaction be9b294695bfb201a5cff32af074a4bf72b073e3c9dad1969111165bf118a622 orphaned, missing reference f0c0d3b3eecf911ede996a74ceadc9366068791450c9e6bacee9ae202f3690d1."

I have no idea what's going on and I suspect its script is incompatible.

So, the bottom line is I just want a workable example in bitcoin testnet3 that shows "from 1 address transfer some bitcoin to other address" by showing the process of creating raw transaction, sign it with private key, turn it to raw transaction in hex format & broadcast it using something like https://live.blockcypher.com/btc/pushtx/ (BTC testnet)

currently my code is as follow:-

package main
import (
    "fmt"
    "encoding/hex"
    "bytes"
    "github.com/btcsuite/btcutil"
    btcchain "github.com/btcsuite/btcd/chaincfg"
    "github.com/btcsuite/btcd/chaincfg/chainhash"
    "github.com/btcsuite/btcd/txscript"
    "github.com/btcsuite/btcd/wire"
)
func txToHex(tx *wire.MsgTx) string {
    buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
    tx.Serialize(buf)
    return hex.EncodeToString(buf.Bytes())
}

func stkbtc(){
  pvkey := "cNjXNxcfawzyfGUxaG94rKqayAL2n7QWioKhCkHbQsBRT7SbDyGu"
  txHash := "e028b5bf030a24986a03b03b89dec037e8462ae32bc93679cb49d7c779685987"
  destination := "n2kRiAkW1xr5DVy7QKVGaYiZbwpS7j23jJ"
  var amount int64 = 100000000
  txFee := int64(500000)

  //try send btc
  decodedwif,err := btcutil.DecodeWIF(pvkey)
  if err != nil {
    fmt.Printf("decodedwif error: %v\n",err)
  }
  fmt.Printf("decodedwif       : %v\n",decodedwif)

  addresspubkey, _ := btcutil.NewAddressPubKey(decodedwif.PrivKey.PubKey().SerializeUncompressed(), &btcchain.TestNet3Params)
  sourceTx := wire.NewMsgTx(wire.TxVersion)
  sourceUtxoHash, _ := chainhash.NewHashFromStr(txHash)

  sourceUtxo := wire.NewOutPoint(sourceUtxoHash, 0)

  sourceTxIn := wire.NewTxIn(sourceUtxo, nil, nil)
  destinationAddress, _ := btcutil.DecodeAddress(destination, &btcchain.TestNet3Params)

  sourceAddress, err := btcutil.DecodeAddress(addresspubkey.EncodeAddress(), &btcchain.TestNet3Params)
  if err != nil {
    fmt.Printf("sourceAddress err: %v\n",err)
  }

  destinationPkScript, _ := txscript.PayToAddrScript(destinationAddress)

  sourcePkScript, _ := txscript.PayToAddrScript(sourceAddress)
  sourceTxOut := wire.NewTxOut(amount, sourcePkScript)

  sourceTx.AddTxIn(sourceTxIn)
  sourceTx.AddTxOut(sourceTxOut)
  sourceTxHash := sourceTx.TxHash()

  redeemTx := wire.NewMsgTx(wire.TxVersion)
  prevOut := wire.NewOutPoint(&sourceTxHash, 0)
  redeemTxIn := wire.NewTxIn(prevOut, nil, nil)
  redeemTx.AddTxIn(redeemTxIn)
  redeemTxOut := wire.NewTxOut((amount - txFee), destinationPkScript)
  redeemTx.AddTxOut(redeemTxOut)

  sigScript, err := txscript.SignatureScript(redeemTx, 0, sourceTx.TxOut[0].PkScript, txscript.SigHashAll, decodedwif.PrivKey, false)
  if err != nil {
    fmt.Printf("sigScript err: %v\n",err)
  }
  redeemTx.TxIn[0].SignatureScript = sigScript
  fmt.Printf("sigScript: %v\n",hex.EncodeToString(sigScript))


  //Validate signature
  flags := txscript.StandardVerifyFlags
  vm, err := txscript.NewEngine(sourceTx.TxOut[0].PkScript, redeemTx, 0, flags, nil, nil, amount)
  if err != nil {
    fmt.Printf("err != nil: %v\n",err)
  }
  if err := vm.Execute(); err != nil {
    fmt.Printf("vm.Execute > err != nil: %v\n",err)
  }

  fmt.Printf("redeemTx: %v\n",txToHex(redeemTx))
}

func main(){
    stkbtc()
}

Open in new window


the txhash was from previous transaction where i got the testnet bitcoin from faucet & nothing else..

Sender address: mpYGA8wRCArpGTs6aJMmZRWyUuPoZmeLJv
Sender public key: 02217deb8b3782236fa8214da94bd5a9338f2eeb3299c39c057fb68ada63d93df7
Sender private key: cNjXNxcfawzyfGUxaG94rKqayAL2n7QWioKhCkHbQsBRT7SbDyGu

Target address: n2kRiAkW1xr5DVy7QKVGaYiZbwpS7j23jJ

Please advice what is wrong with the code above, really appreciate it if someone can point it out.
This question needs an answer!
Become an EE member today
7 DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform.
View membership options
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.