mirror of
https://github.com/btcsuite/btcd.git
synced 2024-11-19 09:50:08 +01:00
e781b66e2f
In this commit, we implement the new BIP 341+342 taproot sighash digest computation. The digest is similar, but re-orders some fragments and also starts to commit to the input values of all the transactions in the SIGHASH_ALL case. A new implicit sighash flag, SIGHASH_DEFAULT has been added that allows signatures to always be 64-bytes for the common case. The hashcache has been updated as well to store both the v0 and v1 mid state hashes. The v0 hashes are a double-sha of the contents, while the v1 hash is a single sha. As a result, if a transaction spends both v0 and v1 inputs, then we 're able to re-use all the intermediate hashes. As the sighash computation needs the input values and scripts, we create an abstraction: the PrevOutFetcher to give the caller flexibility w.r.t how this is done. We also create a `CannedPrevOutputFetcher` that holds the information in a map for a single input. A series of function options are also added to allow re-use of the same base sig hash calculation for both BIP 341 and 342.
215 lines
6.9 KiB
Go
215 lines
6.9 KiB
Go
// Copyright (c) 2014-2016 The btcsuite developers
|
|
// Copyright (c) 2015-2019 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package txscript_test
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
"github.com/btcsuite/btcd/txscript"
|
|
"github.com/btcsuite/btcd/wire"
|
|
)
|
|
|
|
// This example demonstrates creating a script which pays to a bitcoin address.
|
|
// It also prints the created script hex and uses the DisasmString function to
|
|
// display the disassembled script.
|
|
func ExamplePayToAddrScript() {
|
|
// Parse the address to send the coins to into a btcutil.Address
|
|
// which is useful to ensure the accuracy of the address and determine
|
|
// the address type. It is also required for the upcoming call to
|
|
// PayToAddrScript.
|
|
addressStr := "12gpXQVcCL2qhTNQgyLVdCFG2Qs2px98nV"
|
|
address, err := btcutil.DecodeAddress(addressStr, &chaincfg.MainNetParams)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
// Create a public key script that pays to the address.
|
|
script, err := txscript.PayToAddrScript(address)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
fmt.Printf("Script Hex: %x\n", script)
|
|
|
|
disasm, err := txscript.DisasmString(script)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
fmt.Println("Script Disassembly:", disasm)
|
|
|
|
// Output:
|
|
// Script Hex: 76a914128004ff2fcaf13b2b91eb654b1dc2b674f7ec6188ac
|
|
// Script Disassembly: OP_DUP OP_HASH160 128004ff2fcaf13b2b91eb654b1dc2b674f7ec61 OP_EQUALVERIFY OP_CHECKSIG
|
|
}
|
|
|
|
// This example demonstrates extracting information from a standard public key
|
|
// script.
|
|
func ExampleExtractPkScriptAddrs() {
|
|
// Start with a standard pay-to-pubkey-hash script.
|
|
scriptHex := "76a914128004ff2fcaf13b2b91eb654b1dc2b674f7ec6188ac"
|
|
script, err := hex.DecodeString(scriptHex)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
// Extract and print details from the script.
|
|
scriptClass, addresses, reqSigs, err := txscript.ExtractPkScriptAddrs(
|
|
script, &chaincfg.MainNetParams)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
fmt.Println("Script Class:", scriptClass)
|
|
fmt.Println("Addresses:", addresses)
|
|
fmt.Println("Required Signatures:", reqSigs)
|
|
|
|
// Output:
|
|
// Script Class: pubkeyhash
|
|
// 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(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(wire.TxVersion)
|
|
prevOut := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0))
|
|
txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}, nil)
|
|
originTx.AddTxIn(txIn)
|
|
pkScript, err := txscript.PayToAddrScript(addr)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
txOut := wire.NewTxOut(100000000, pkScript)
|
|
originTx.AddTxOut(txOut)
|
|
originTxHash := originTx.TxHash()
|
|
|
|
// Create the transaction to redeem the fake transaction.
|
|
redeemTx := wire.NewMsgTx(wire.TxVersion)
|
|
|
|
// 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, 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.ScriptVerifyDERSignatures |
|
|
txscript.ScriptStrictMultiSig |
|
|
txscript.ScriptDiscourageUpgradableNops
|
|
vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0,
|
|
flags, nil, nil, -1, nil)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
if err := vm.Execute(); err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
fmt.Println("Transaction successfully signed")
|
|
|
|
// Output:
|
|
// Transaction successfully signed
|
|
}
|
|
|
|
// This example demonstrates creating a script tokenizer instance and using it
|
|
// to count the number of opcodes a script contains.
|
|
func ExampleScriptTokenizer() {
|
|
// Create a script to use in the example. Ordinarily this would come from
|
|
// some other source.
|
|
hash160 := btcutil.Hash160([]byte("example"))
|
|
script, err := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).
|
|
AddOp(txscript.OP_HASH160).AddData(hash160).
|
|
AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).Script()
|
|
if err != nil {
|
|
fmt.Printf("failed to build script: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// Create a tokenizer to iterate the script and count the number of opcodes.
|
|
const scriptVersion = 0
|
|
var numOpcodes int
|
|
tokenizer := txscript.MakeScriptTokenizer(scriptVersion, script)
|
|
for tokenizer.Next() {
|
|
numOpcodes++
|
|
}
|
|
if tokenizer.Err() != nil {
|
|
fmt.Printf("script failed to parse: %v\n", err)
|
|
} else {
|
|
fmt.Printf("script contains %d opcode(s)\n", numOpcodes)
|
|
}
|
|
|
|
// Output:
|
|
// script contains 5 opcode(s)
|
|
}
|