From f82f7b66634886bd1b0662359f32965508858575 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Mon, 9 Feb 2015 10:11:55 -0600 Subject: [PATCH] txscript: Add example for manully signing a txout. This commit adds a new example to the txscript package that demonstrates creating a new transaction which redeems funds and signing the referenced transaction output the SignTxOutput function. --- txscript/README.md | 4 ++ txscript/example_test.go | 109 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/txscript/README.md b/txscript/README.md index 96fce1444e..1cca8c4e55 100644 --- a/txscript/README.md +++ b/txscript/README.md @@ -49,6 +49,10 @@ $ go get github.com/btcsuite/btcd/txscript (http://godoc.org/github.com/btcsuite/btcd/txscript#example-ExtractPkScriptAddrs) Demonstrates extracting information from a standard public key script. +* [Manually Signing a Transaction Output] + (http://godoc.org/github.com/btcsuite/btcd/txscript#example-SignTxOutput) + Demonstrates manually creating and signing a redeem transaction. + ## GPG Verification Key All official release tags are signed by Conformal so users can ensure the code diff --git a/txscript/example_test.go b/txscript/example_test.go index 21d1cfe4c5..76a021f115 100644 --- a/txscript/example_test.go +++ b/txscript/example_test.go @@ -8,8 +8,10 @@ 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" ) @@ -75,3 +77,110 @@ func ExampleExtractPkScriptAddrs() { // Addresses: [12gpXQVcCL2qhTNQgyLVdCFG2Qs2px98nV] // Required Signatures: 1 } + +// This example demonstrates manually creating and signing a redeem transaction. +func ExampleSignTxOutput() { + // 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) + pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed()) + addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash, + &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 := wire.NewMsgTx() + prevOut := wire.NewOutPoint(&wire.ShaHash{}, ^uint32(0)) + txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}) + originTx.AddTxIn(txIn) + pkScript, err := txscript.PayToAddrScript(addr) + if err != nil { + fmt.Println(err) + return + } + txOut := wire.NewTxOut(100000000, pkScript) + originTx.AddTxOut(txOut) + + originTxHash, err := originTx.TxSha() + 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. + 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 + } + // Notice that the script database parameter is nil here since it isn't + // used. It must be specified when pay-to-script-hash transactions are + // being signed. + 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 + + // Prove that the transaction has been validly signed by executing the + // script pair. + flags := txscript.ScriptBip16 | txscript.ScriptCanonicalSignatures | + txscript.ScriptStrictMultiSig | + txscript.ScriptDiscourageUpgradableNops + s, err := txscript.NewScript(redeemTx.TxIn[0].SignatureScript, + originTx.TxOut[0].PkScript, 0, redeemTx, flags) + if err != nil { + fmt.Println(err) + return + } + if err := s.Execute(); err != nil { + fmt.Println(err) + return + } + fmt.Println("Transaction successfully signed") + + // Output: + // Transaction successfully signed +}