mirror of
https://github.com/btcsuite/btcd.git
synced 2024-11-19 18:00:11 +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.
199 lines
5.0 KiB
Go
199 lines
5.0 KiB
Go
// Copyright (c) 2017 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package txscript
|
|
|
|
import (
|
|
"math/rand"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/davecgh/go-spew/spew"
|
|
)
|
|
|
|
func init() {
|
|
rand.Seed(time.Now().Unix())
|
|
}
|
|
|
|
// genTestTx creates a random transaction for uses within test cases.
|
|
func genTestTx() (*wire.MsgTx, *MultiPrevOutFetcher, error) {
|
|
tx := wire.NewMsgTx(2)
|
|
tx.Version = rand.Int31()
|
|
|
|
prevOuts := NewMultiPrevOutFetcher(nil)
|
|
|
|
numTxins := 1 + rand.Intn(11)
|
|
for i := 0; i < numTxins; i++ {
|
|
randTxIn := wire.TxIn{
|
|
PreviousOutPoint: wire.OutPoint{
|
|
Index: uint32(rand.Int31()),
|
|
},
|
|
Sequence: uint32(rand.Int31()),
|
|
}
|
|
_, err := rand.Read(randTxIn.PreviousOutPoint.Hash[:])
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
tx.TxIn = append(tx.TxIn, &randTxIn)
|
|
|
|
prevOuts.AddPrevOut(
|
|
randTxIn.PreviousOutPoint, &wire.TxOut{},
|
|
)
|
|
}
|
|
|
|
numTxouts := 1 + rand.Intn(11)
|
|
for i := 0; i < numTxouts; i++ {
|
|
randTxOut := wire.TxOut{
|
|
Value: rand.Int63(),
|
|
PkScript: make([]byte, rand.Intn(30)),
|
|
}
|
|
if _, err := rand.Read(randTxOut.PkScript); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
tx.TxOut = append(tx.TxOut, &randTxOut)
|
|
}
|
|
|
|
return tx, prevOuts, nil
|
|
}
|
|
|
|
// TestHashCacheAddContainsHashes tests that after items have been added to the
|
|
// hash cache, the ContainsHashes method returns true for all the items
|
|
// inserted. Conversely, ContainsHashes should return false for any items
|
|
// _not_ in the hash cache.
|
|
func TestHashCacheAddContainsHashes(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cache := NewHashCache(10)
|
|
|
|
var (
|
|
err error
|
|
randPrevOuts *MultiPrevOutFetcher
|
|
)
|
|
prevOuts := NewMultiPrevOutFetcher(nil)
|
|
|
|
// First, we'll generate 10 random transactions for use within our
|
|
// tests.
|
|
const numTxns = 10
|
|
txns := make([]*wire.MsgTx, numTxns)
|
|
for i := 0; i < numTxns; i++ {
|
|
txns[i], randPrevOuts, err = genTestTx()
|
|
if err != nil {
|
|
t.Fatalf("unable to generate test tx: %v", err)
|
|
}
|
|
|
|
prevOuts.Merge(randPrevOuts)
|
|
}
|
|
|
|
// With the transactions generated, we'll add each of them to the hash
|
|
// cache.
|
|
for _, tx := range txns {
|
|
cache.AddSigHashes(tx, prevOuts)
|
|
}
|
|
|
|
// Next, we'll ensure that each of the transactions inserted into the
|
|
// cache are properly located by the ContainsHashes method.
|
|
for _, tx := range txns {
|
|
txid := tx.TxHash()
|
|
if ok := cache.ContainsHashes(&txid); !ok {
|
|
t.Fatalf("txid %v not found in cache but should be: ",
|
|
txid)
|
|
}
|
|
}
|
|
|
|
randTx, _, err := genTestTx()
|
|
if err != nil {
|
|
t.Fatalf("unable to generate tx: %v", err)
|
|
}
|
|
|
|
// Finally, we'll assert that a transaction that wasn't added to the
|
|
// cache won't be reported as being present by the ContainsHashes
|
|
// method.
|
|
randTxid := randTx.TxHash()
|
|
if ok := cache.ContainsHashes(&randTxid); ok {
|
|
t.Fatalf("txid %v wasn't inserted into cache but was found",
|
|
randTxid)
|
|
}
|
|
}
|
|
|
|
// TestHashCacheAddGet tests that the sighashes for a particular transaction
|
|
// are properly retrieved by the GetSigHashes function.
|
|
func TestHashCacheAddGet(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cache := NewHashCache(10)
|
|
|
|
// To start, we'll generate a random transaction and compute the set of
|
|
// sighashes for the transaction.
|
|
randTx, prevOuts, err := genTestTx()
|
|
if err != nil {
|
|
t.Fatalf("unable to generate tx: %v", err)
|
|
}
|
|
sigHashes := NewTxSigHashes(randTx, prevOuts)
|
|
|
|
// Next, add the transaction to the hash cache.
|
|
cache.AddSigHashes(randTx, prevOuts)
|
|
|
|
// The transaction inserted into the cache above should be found.
|
|
txid := randTx.TxHash()
|
|
cacheHashes, ok := cache.GetSigHashes(&txid)
|
|
if !ok {
|
|
t.Fatalf("tx %v wasn't found in cache", txid)
|
|
}
|
|
|
|
// Finally, the sighashes retrieved should exactly match the sighash
|
|
// originally inserted into the cache.
|
|
if *sigHashes != *cacheHashes {
|
|
t.Fatalf("sighashes don't match: expected %v, got %v",
|
|
spew.Sdump(sigHashes), spew.Sdump(cacheHashes))
|
|
}
|
|
}
|
|
|
|
// TestHashCachePurge tests that items are able to be properly removed from the
|
|
// hash cache.
|
|
func TestHashCachePurge(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cache := NewHashCache(10)
|
|
|
|
var (
|
|
err error
|
|
randPrevOuts *MultiPrevOutFetcher
|
|
)
|
|
prevOuts := NewMultiPrevOutFetcher(nil)
|
|
|
|
// First we'll start by inserting numTxns transactions into the hash cache.
|
|
const numTxns = 10
|
|
txns := make([]*wire.MsgTx, numTxns)
|
|
for i := 0; i < numTxns; i++ {
|
|
txns[i], randPrevOuts, err = genTestTx()
|
|
if err != nil {
|
|
t.Fatalf("unable to generate test tx: %v", err)
|
|
}
|
|
|
|
prevOuts.Merge(randPrevOuts)
|
|
}
|
|
for _, tx := range txns {
|
|
cache.AddSigHashes(tx, prevOuts)
|
|
}
|
|
|
|
// Once all the transactions have been inserted, we'll purge them from
|
|
// the hash cache.
|
|
for _, tx := range txns {
|
|
txid := tx.TxHash()
|
|
cache.PurgeSigHashes(&txid)
|
|
}
|
|
|
|
// At this point, none of the transactions inserted into the hash cache
|
|
// should be found within the cache.
|
|
for _, tx := range txns {
|
|
txid := tx.TxHash()
|
|
if ok := cache.ContainsHashes(&txid); ok {
|
|
t.Fatalf("tx %v found in cache but should have "+
|
|
"been purged: ", txid)
|
|
}
|
|
}
|
|
}
|