Skip to content

Instantly share code, notes, and snippets.

@davecgh
Last active September 20, 2018 19:45
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save davecgh/a062259efbc093e530ea to your computer and use it in GitHub Desktop.
Save davecgh/a062259efbc093e530ea to your computer and use it in GitHub Desktop.
Example of signing a transaction using txscript.SignatureScript
package main
import (
"encoding/hex"
"fmt"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// createFakeOriginTx creates a fake coinbase transaction that is used in the
// example as a stand-in for what ordinarily be the real transaction that is
// being spent.
func createFakeOriginTx(addr btcutil.Address) (*wire.MsgTx, error) {
tx := wire.NewMsgTx()
prevOut := wire.NewOutPoint(&wire.ShaHash{}, ^uint32(0))
txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0})
tx.AddTxIn(txIn)
pkScript, err := txscript.PayToAddrScript(addr)
if err != nil {
return nil, err
}
txOut := wire.NewTxOut(100000000, pkScript)
tx.AddTxOut(txOut)
return tx, nil
}
func main() {
// Ordinarily the private key would come from whatever storage mechanism
// is being used, but for this example just hard code it.
privKeyBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2" +
"d4f8720ee63e502ee2869afab7de234b80c")
if err != nil {
fmt.Println(err)
return
}
privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes)
addr, err := btcutil.NewAddressPubKey(pubKey.SerializeCompressed(),
&chaincfg.MainNetParams)
if err != nil {
fmt.Println(err)
return
}
// For this example, create a fake transaction that represents what
// would ordinarily be the real transaction that is being spent. It
// contains a single output that pays to address in the amount of 1 BTC.
originTx, err := createFakeOriginTx(addr.AddressPubKeyHash())
if err != nil {
fmt.Println(err)
return
}
// Create the transaction to redeem the fake transaction.
redeemTx := wire.NewMsgTx()
// Add the input(s) the redeeming transaction will spend. There is no
// signature script at this point since it hasn't been created or signed
// yet, hence nil is provided for it.
originTxHash := originTx.TxSha()
prevOut := wire.NewOutPoint(&originTxHash, 0)
txIn := wire.NewTxIn(prevOut, nil)
redeemTx.AddTxIn(txIn)
// Ordinarily this would contain that actual destination of the funds,
// but for this example don't bother.
txOut := wire.NewTxOut(0, nil)
redeemTx.AddTxOut(txOut)
// Sign the redeeming transaction.
pkScript := originTx.TxOut[0].PkScript
sigScript, err := txscript.SignatureScript(redeemTx, 0, pkScript,
txscript.SigHashAll, privKey, true)
if err != nil {
fmt.Println(err)
return
}
redeemTx.TxIn[0].SignatureScript = sigScript
// Print the scripts involved for illustrative purposes.
pkScriptDisasm, _ := txscript.DisasmString(originTx.TxOut[0].PkScript)
sigScriptDisasm, _ := txscript.DisasmString(redeemTx.TxIn[0].SignatureScript)
fmt.Println("pkScript:", pkScriptDisasm)
fmt.Println("sigScript:", sigScriptDisasm)
// Prove that the transaction has been validly signed by executing the
// scripts.
flags := txscript.StandardVerifyFlags
vm, err := txscript.NewEngine(pkScript, redeemTx, 0, flags, nil)
if err != nil {
fmt.Println(err)
return
}
if err := vm.Execute(); err != nil {
fmt.Println(err)
return
}
fmt.Println("Successfully signed transaction!")
}
@kyluke
Copy link

kyluke commented Dec 6, 2016

Hi @davecgh, how would the signing work with two outputs? I've been trying to get that right but the signature validation fails.

@winteraz
Copy link

winteraz commented Dec 17, 2017

This is bad code. The params seem to be incorrect .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment