Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save farukterzioglu/ce43f6263d087b5cd2bc5f9d3569bcd3 to your computer and use it in GitHub Desktop.
Save farukterzioglu/ce43f6263d087b5cd2bc5f9d3569bcd3 to your computer and use it in GitHub Desktop.
Example of signing a transaction using txscript.SignTxOutput.
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.
lookupKey := func(a btcutil.Address) (*btcec.PrivateKey, bool, error) {
// Ordinarily this function would involve looking up the private
// key for the provided address, but since the only thing being
// signed in this example uses the address associated with the
// private key from above, simply return it with the compressed
// flag set since the address is using the associated compressed
// public key.
//
// NOTE: If you want to prove the code is actually signing the
// transaction properly, uncomment the following line which
// intentionally returns an invalid key to sign with, which in
// turn will result in a failure during the script execution
// when verifying the signature.
//
// privKey.D.SetInt64(12345)
//
return privKey, true, nil
}
sigScript, err := txscript.SignTxOutput(&chaincfg.MainNetParams,
redeemTx, 0, originTx.TxOut[0].PkScript, txscript.SigHashAll,
txscript.KeyClosure(lookupKey), nil, nil)
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.ScriptBip16 | txscript.ScriptVerifyDERSignatures |
txscript.ScriptStrictMultiSig |
txscript.ScriptDiscourageUpgradableNops
vm, err := txscript.NewEngine(originTx.TxOut[0].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!")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment