github.com/btcsuite/btcd@v0.24.0/txscript/hashcache.go (about)

     1  // Copyright (c) 2016 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package txscript
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"math"
    11  	"sync"
    12  
    13  	"github.com/btcsuite/btcd/chaincfg/chainhash"
    14  	"github.com/btcsuite/btcd/wire"
    15  )
    16  
    17  // calcHashPrevOuts calculates a single hash of all the previous outputs
    18  // (txid:index) referenced within the passed transaction. This calculated hash
    19  // can be re-used when validating all inputs spending segwit outputs, with a
    20  // signature hash type of SigHashAll. This allows validation to re-use previous
    21  // hashing computation, reducing the complexity of validating SigHashAll inputs
    22  // from  O(N^2) to O(N).
    23  func calcHashPrevOuts(tx *wire.MsgTx) chainhash.Hash {
    24  	var b bytes.Buffer
    25  	for _, in := range tx.TxIn {
    26  		// First write out the 32-byte transaction ID one of whose
    27  		// outputs are being referenced by this input.
    28  		b.Write(in.PreviousOutPoint.Hash[:])
    29  
    30  		// Next, we'll encode the index of the referenced output as a
    31  		// little endian integer.
    32  		var buf [4]byte
    33  		binary.LittleEndian.PutUint32(buf[:], in.PreviousOutPoint.Index)
    34  		b.Write(buf[:])
    35  	}
    36  
    37  	return chainhash.HashH(b.Bytes())
    38  }
    39  
    40  // calcHashSequence computes an aggregated hash of each of the sequence numbers
    41  // within the inputs of the passed transaction. This single hash can be re-used
    42  // when validating all inputs spending segwit outputs, which include signatures
    43  // using the SigHashAll sighash type. This allows validation to re-use previous
    44  // hashing computation, reducing the complexity of validating SigHashAll inputs
    45  // from O(N^2) to O(N).
    46  func calcHashSequence(tx *wire.MsgTx) chainhash.Hash {
    47  	var b bytes.Buffer
    48  	for _, in := range tx.TxIn {
    49  		var buf [4]byte
    50  		binary.LittleEndian.PutUint32(buf[:], in.Sequence)
    51  		b.Write(buf[:])
    52  	}
    53  
    54  	return chainhash.HashH(b.Bytes())
    55  }
    56  
    57  // calcHashOutputs computes a hash digest of all outputs created by the
    58  // transaction encoded using the wire format. This single hash can be re-used
    59  // when validating all inputs spending witness programs, which include
    60  // signatures using the SigHashAll sighash type. This allows computation to be
    61  // cached, reducing the total hashing complexity from O(N^2) to O(N).
    62  func calcHashOutputs(tx *wire.MsgTx) chainhash.Hash {
    63  	var b bytes.Buffer
    64  	for _, out := range tx.TxOut {
    65  		wire.WriteTxOut(&b, 0, 0, out)
    66  	}
    67  
    68  	return chainhash.HashH(b.Bytes())
    69  }
    70  
    71  // PrevOutputFetcher is an interface used to supply the sighash cache with the
    72  // previous output information needed to calculate the pre-computed sighash
    73  // midstate for taproot transactions.
    74  type PrevOutputFetcher interface {
    75  	// FetchPrevOutput attempts to fetch the previous output referenced by
    76  	// the passed outpoint. A nil value will be returned if the passed
    77  	// outpoint doesn't exist.
    78  	FetchPrevOutput(wire.OutPoint) *wire.TxOut
    79  }
    80  
    81  // CannedPrevOutputFetcher is an implementation of PrevOutputFetcher that only
    82  // is able to return information for a single previous output.
    83  type CannedPrevOutputFetcher struct {
    84  	pkScript []byte
    85  	amt      int64
    86  }
    87  
    88  // NewCannedPrevOutputFetcher returns an instance of a CannedPrevOutputFetcher
    89  // that can only return the TxOut defined by the passed script and amount.
    90  func NewCannedPrevOutputFetcher(script []byte, amt int64) *CannedPrevOutputFetcher {
    91  	return &CannedPrevOutputFetcher{
    92  		pkScript: script,
    93  		amt:      amt,
    94  	}
    95  }
    96  
    97  // FetchPrevOutput attempts to fetch the previous output referenced by the
    98  // passed outpoint.
    99  //
   100  // NOTE: This is a part of the PrevOutputFetcher interface.
   101  func (c *CannedPrevOutputFetcher) FetchPrevOutput(wire.OutPoint) *wire.TxOut {
   102  	return &wire.TxOut{
   103  		PkScript: c.pkScript,
   104  		Value:    c.amt,
   105  	}
   106  }
   107  
   108  // A compile-time assertion to ensure that CannedPrevOutputFetcher matches the
   109  // PrevOutputFetcher interface.
   110  var _ PrevOutputFetcher = (*CannedPrevOutputFetcher)(nil)
   111  
   112  // MultiPrevOutFetcher is a custom implementation of the PrevOutputFetcher
   113  // backed by a key-value map of prevouts to outputs.
   114  type MultiPrevOutFetcher struct {
   115  	prevOuts map[wire.OutPoint]*wire.TxOut
   116  }
   117  
   118  // NewMultiPrevOutFetcher returns an instance of a PrevOutputFetcher that's
   119  // backed by an optional map which is used as an input source. The
   120  func NewMultiPrevOutFetcher(prevOuts map[wire.OutPoint]*wire.TxOut) *MultiPrevOutFetcher {
   121  	if prevOuts == nil {
   122  		prevOuts = make(map[wire.OutPoint]*wire.TxOut)
   123  	}
   124  
   125  	return &MultiPrevOutFetcher{
   126  		prevOuts: prevOuts,
   127  	}
   128  }
   129  
   130  // FetchPrevOutput attempts to fetch the previous output referenced by the
   131  // passed outpoint.
   132  //
   133  // NOTE: This is a part of the CannedPrevOutputFetcher interface.
   134  func (m *MultiPrevOutFetcher) FetchPrevOutput(op wire.OutPoint) *wire.TxOut {
   135  	return m.prevOuts[op]
   136  }
   137  
   138  // AddPrevOut adds a new prev out, tx out pair to the backing map.
   139  func (m *MultiPrevOutFetcher) AddPrevOut(op wire.OutPoint, txOut *wire.TxOut) {
   140  	m.prevOuts[op] = txOut
   141  }
   142  
   143  // Merge merges two instances of a MultiPrevOutFetcher into a single source.
   144  func (m *MultiPrevOutFetcher) Merge(other *MultiPrevOutFetcher) {
   145  	for k, v := range other.prevOuts {
   146  		m.prevOuts[k] = v
   147  	}
   148  }
   149  
   150  // A compile-time assertion to ensure that MultiPrevOutFetcher matches the
   151  // PrevOutputFetcher interface.
   152  var _ PrevOutputFetcher = (*MultiPrevOutFetcher)(nil)
   153  
   154  // calcHashInputAmounts computes a hash digest of the input amounts of all
   155  // inputs referenced in the passed transaction. This hash pre computation is only
   156  // used for validating taproot inputs.
   157  func calcHashInputAmounts(tx *wire.MsgTx, inputFetcher PrevOutputFetcher) chainhash.Hash {
   158  	var b bytes.Buffer
   159  	for _, txIn := range tx.TxIn {
   160  		prevOut := inputFetcher.FetchPrevOutput(txIn.PreviousOutPoint)
   161  
   162  		_ = binary.Write(&b, binary.LittleEndian, prevOut.Value)
   163  	}
   164  
   165  	return chainhash.HashH(b.Bytes())
   166  }
   167  
   168  // calcHashInputAmts computes the hash digest of all the previous input scripts
   169  // referenced by the passed transaction. This hash pre computation is only used
   170  // for validating taproot inputs.
   171  func calcHashInputScripts(tx *wire.MsgTx, inputFetcher PrevOutputFetcher) chainhash.Hash {
   172  	var b bytes.Buffer
   173  	for _, txIn := range tx.TxIn {
   174  		prevOut := inputFetcher.FetchPrevOutput(txIn.PreviousOutPoint)
   175  
   176  		_ = wire.WriteVarBytes(&b, 0, prevOut.PkScript)
   177  	}
   178  
   179  	return chainhash.HashH(b.Bytes())
   180  }
   181  
   182  // SegwitSigHashMidstate is the sighash midstate used in the base segwit
   183  // sighash calculation as defined in BIP 143.
   184  type SegwitSigHashMidstate struct {
   185  	HashPrevOutsV0 chainhash.Hash
   186  	HashSequenceV0 chainhash.Hash
   187  	HashOutputsV0  chainhash.Hash
   188  }
   189  
   190  // TaprootSigHashMidState is the sighash midstate used to compute taproot and
   191  // tapscript signatures as defined in BIP 341.
   192  type TaprootSigHashMidState struct {
   193  	HashPrevOutsV1     chainhash.Hash
   194  	HashSequenceV1     chainhash.Hash
   195  	HashOutputsV1      chainhash.Hash
   196  	HashInputScriptsV1 chainhash.Hash
   197  	HashInputAmountsV1 chainhash.Hash
   198  }
   199  
   200  // TxSigHashes houses the partial set of sighashes introduced within BIP0143.
   201  // This partial set of sighashes may be re-used within each input across a
   202  // transaction when validating all inputs. As a result, validation complexity
   203  // for SigHashAll can be reduced by a polynomial factor.
   204  type TxSigHashes struct {
   205  	SegwitSigHashMidstate
   206  
   207  	TaprootSigHashMidState
   208  }
   209  
   210  // NewTxSigHashes computes, and returns the cached sighashes of the given
   211  // transaction.
   212  func NewTxSigHashes(tx *wire.MsgTx,
   213  	inputFetcher PrevOutputFetcher) *TxSigHashes {
   214  
   215  	var (
   216  		sigHashes TxSigHashes
   217  		zeroHash  chainhash.Hash
   218  	)
   219  
   220  	// Base segwit (witness version v0), and taproot (witness version v1)
   221  	// differ in how the set of pre-computed cached sighash midstate is
   222  	// computed. For taproot, the prevouts, sequence, and outputs are
   223  	// computed as normal, but a single sha256 hash invocation is used. In
   224  	// addition, the hashes of all the previous input amounts and scripts
   225  	// are included as well.
   226  	//
   227  	// Based on the above distinction, we'll run through all the referenced
   228  	// inputs to determine what we need to compute.
   229  	var hasV0Inputs, hasV1Inputs bool
   230  	for _, txIn := range tx.TxIn {
   231  		// If this is a coinbase input, then we know that we only need
   232  		// the v0 midstate (though it won't be used) in this instance.
   233  		outpoint := txIn.PreviousOutPoint
   234  		if outpoint.Index == math.MaxUint32 && outpoint.Hash == zeroHash {
   235  			hasV0Inputs = true
   236  			continue
   237  		}
   238  
   239  		prevOut := inputFetcher.FetchPrevOutput(outpoint)
   240  
   241  		// If this is spending a script that looks like a taproot output,
   242  		// then we'll need to pre-compute the extra taproot data.
   243  		if IsPayToTaproot(prevOut.PkScript) {
   244  			hasV1Inputs = true
   245  		} else {
   246  			// Otherwise, we'll assume we need the v0 sighash midstate.
   247  			hasV0Inputs = true
   248  		}
   249  
   250  		// If the transaction has _both_ v0 and v1 inputs, then we can stop
   251  		// here.
   252  		if hasV0Inputs && hasV1Inputs {
   253  			break
   254  		}
   255  	}
   256  
   257  	// Now that we know which cached midstate we need to calculate, we can
   258  	// go ahead and do so.
   259  	//
   260  	// First, we can calculate the information that both segwit v0 and v1
   261  	// need: the prevout, sequence and output hashes. For v1 the only
   262  	// difference is that this is a single instead of a double hash.
   263  	//
   264  	// Both v0 and v1 share this base data computed using a sha256 single
   265  	// hash.
   266  	sigHashes.HashPrevOutsV1 = calcHashPrevOuts(tx)
   267  	sigHashes.HashSequenceV1 = calcHashSequence(tx)
   268  	sigHashes.HashOutputsV1 = calcHashOutputs(tx)
   269  
   270  	// The v0 data is the same as the v1 (newer data) but it uses a double
   271  	// hash instead.
   272  	if hasV0Inputs {
   273  		sigHashes.HashPrevOutsV0 = chainhash.HashH(
   274  			sigHashes.HashPrevOutsV1[:],
   275  		)
   276  		sigHashes.HashSequenceV0 = chainhash.HashH(
   277  			sigHashes.HashSequenceV1[:],
   278  		)
   279  		sigHashes.HashOutputsV0 = chainhash.HashH(
   280  			sigHashes.HashOutputsV1[:],
   281  		)
   282  	}
   283  
   284  	// Finally, we'll compute the taproot specific data if needed.
   285  	if hasV1Inputs {
   286  		sigHashes.HashInputAmountsV1 = calcHashInputAmounts(
   287  			tx, inputFetcher,
   288  		)
   289  		sigHashes.HashInputScriptsV1 = calcHashInputScripts(
   290  			tx, inputFetcher,
   291  		)
   292  	}
   293  
   294  	return &sigHashes
   295  }
   296  
   297  // HashCache houses a set of partial sighashes keyed by txid. The set of partial
   298  // sighashes are those introduced within BIP0143 by the new more efficient
   299  // sighash digest calculation algorithm. Using this threadsafe shared cache,
   300  // multiple goroutines can safely re-use the pre-computed partial sighashes
   301  // speeding up validation time amongst all inputs found within a block.
   302  type HashCache struct {
   303  	sigHashes map[chainhash.Hash]*TxSigHashes
   304  
   305  	sync.RWMutex
   306  }
   307  
   308  // NewHashCache returns a new instance of the HashCache given a maximum number
   309  // of entries which may exist within it at anytime.
   310  func NewHashCache(maxSize uint) *HashCache {
   311  	return &HashCache{
   312  		sigHashes: make(map[chainhash.Hash]*TxSigHashes, maxSize),
   313  	}
   314  }
   315  
   316  // AddSigHashes computes, then adds the partial sighashes for the passed
   317  // transaction.
   318  func (h *HashCache) AddSigHashes(tx *wire.MsgTx,
   319  	inputFetcher PrevOutputFetcher) {
   320  
   321  	h.Lock()
   322  	h.sigHashes[tx.TxHash()] = NewTxSigHashes(tx, inputFetcher)
   323  	h.Unlock()
   324  }
   325  
   326  // ContainsHashes returns true if the partial sighashes for the passed
   327  // transaction currently exist within the HashCache, and false otherwise.
   328  func (h *HashCache) ContainsHashes(txid *chainhash.Hash) bool {
   329  	h.RLock()
   330  	_, found := h.sigHashes[*txid]
   331  	h.RUnlock()
   332  
   333  	return found
   334  }
   335  
   336  // GetSigHashes possibly returns the previously cached partial sighashes for
   337  // the passed transaction. This function also returns an additional boolean
   338  // value indicating if the sighashes for the passed transaction were found to
   339  // be present within the HashCache.
   340  func (h *HashCache) GetSigHashes(txid *chainhash.Hash) (*TxSigHashes, bool) {
   341  	h.RLock()
   342  	item, found := h.sigHashes[*txid]
   343  	h.RUnlock()
   344  
   345  	return item, found
   346  }
   347  
   348  // PurgeSigHashes removes all partial sighashes from the HashCache belonging to
   349  // the passed transaction.
   350  func (h *HashCache) PurgeSigHashes(txid *chainhash.Hash) {
   351  	h.Lock()
   352  	delete(h.sigHashes, *txid)
   353  	h.Unlock()
   354  }