github.com/btcsuite/btcd@v0.24.0/blockchain/scriptval.go (about)

     1  // Copyright (c) 2013-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 blockchain
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"runtime"
    11  	"time"
    12  
    13  	"github.com/btcsuite/btcd/btcutil"
    14  	"github.com/btcsuite/btcd/txscript"
    15  	"github.com/btcsuite/btcd/wire"
    16  )
    17  
    18  // txValidateItem holds a transaction along with which input to validate.
    19  type txValidateItem struct {
    20  	txInIndex int
    21  	txIn      *wire.TxIn
    22  	tx        *btcutil.Tx
    23  	sigHashes *txscript.TxSigHashes
    24  }
    25  
    26  // txValidator provides a type which asynchronously validates transaction
    27  // inputs.  It provides several channels for communication and a processing
    28  // function that is intended to be in run multiple goroutines.
    29  type txValidator struct {
    30  	validateChan chan *txValidateItem
    31  	quitChan     chan struct{}
    32  	resultChan   chan error
    33  	utxoView     *UtxoViewpoint
    34  	flags        txscript.ScriptFlags
    35  	sigCache     *txscript.SigCache
    36  	hashCache    *txscript.HashCache
    37  }
    38  
    39  // sendResult sends the result of a script pair validation on the internal
    40  // result channel while respecting the quit channel.  This allows orderly
    41  // shutdown when the validation process is aborted early due to a validation
    42  // error in one of the other goroutines.
    43  func (v *txValidator) sendResult(result error) {
    44  	select {
    45  	case v.resultChan <- result:
    46  	case <-v.quitChan:
    47  	}
    48  }
    49  
    50  // validateHandler consumes items to validate from the internal validate channel
    51  // and returns the result of the validation on the internal result channel. It
    52  // must be run as a goroutine.
    53  func (v *txValidator) validateHandler() {
    54  out:
    55  	for {
    56  		select {
    57  		case txVI := <-v.validateChan:
    58  			// Ensure the referenced input utxo is available.
    59  			txIn := txVI.txIn
    60  			utxo := v.utxoView.LookupEntry(txIn.PreviousOutPoint)
    61  			if utxo == nil {
    62  				str := fmt.Sprintf("unable to find unspent "+
    63  					"output %v referenced from "+
    64  					"transaction %s:%d",
    65  					txIn.PreviousOutPoint, txVI.tx.Hash(),
    66  					txVI.txInIndex)
    67  				err := ruleError(ErrMissingTxOut, str)
    68  				v.sendResult(err)
    69  				break out
    70  			}
    71  
    72  			// Create a new script engine for the script pair.
    73  			sigScript := txIn.SignatureScript
    74  			witness := txIn.Witness
    75  			pkScript := utxo.PkScript()
    76  			inputAmount := utxo.Amount()
    77  			vm, err := txscript.NewEngine(
    78  				pkScript, txVI.tx.MsgTx(), txVI.txInIndex,
    79  				v.flags, v.sigCache, txVI.sigHashes,
    80  				inputAmount, v.utxoView,
    81  			)
    82  			if err != nil {
    83  				str := fmt.Sprintf("failed to parse input "+
    84  					"%s:%d which references output %v - "+
    85  					"%v (input witness %x, input script "+
    86  					"bytes %x, prev output script bytes %x)",
    87  					txVI.tx.Hash(), txVI.txInIndex,
    88  					txIn.PreviousOutPoint, err, witness,
    89  					sigScript, pkScript)
    90  				err := ruleError(ErrScriptMalformed, str)
    91  				v.sendResult(err)
    92  				break out
    93  			}
    94  
    95  			// Execute the script pair.
    96  			if err := vm.Execute(); err != nil {
    97  				str := fmt.Sprintf("failed to validate input "+
    98  					"%s:%d which references output %v - "+
    99  					"%v (input witness %x, input script "+
   100  					"bytes %x, prev output script bytes %x)",
   101  					txVI.tx.Hash(), txVI.txInIndex,
   102  					txIn.PreviousOutPoint, err, witness,
   103  					sigScript, pkScript)
   104  				err := ruleError(ErrScriptValidation, str)
   105  				v.sendResult(err)
   106  				break out
   107  			}
   108  
   109  			// Validation succeeded.
   110  			v.sendResult(nil)
   111  
   112  		case <-v.quitChan:
   113  			break out
   114  		}
   115  	}
   116  }
   117  
   118  // Validate validates the scripts for all of the passed transaction inputs using
   119  // multiple goroutines.
   120  func (v *txValidator) Validate(items []*txValidateItem) error {
   121  	if len(items) == 0 {
   122  		return nil
   123  	}
   124  
   125  	// Limit the number of goroutines to do script validation based on the
   126  	// number of processor cores.  This helps ensure the system stays
   127  	// reasonably responsive under heavy load.
   128  	maxGoRoutines := runtime.NumCPU() * 3
   129  	if maxGoRoutines <= 0 {
   130  		maxGoRoutines = 1
   131  	}
   132  	if maxGoRoutines > len(items) {
   133  		maxGoRoutines = len(items)
   134  	}
   135  
   136  	// Start up validation handlers that are used to asynchronously
   137  	// validate each transaction input.
   138  	for i := 0; i < maxGoRoutines; i++ {
   139  		go v.validateHandler()
   140  	}
   141  
   142  	// Validate each of the inputs.  The quit channel is closed when any
   143  	// errors occur so all processing goroutines exit regardless of which
   144  	// input had the validation error.
   145  	numInputs := len(items)
   146  	currentItem := 0
   147  	processedItems := 0
   148  	for processedItems < numInputs {
   149  		// Only send items while there are still items that need to
   150  		// be processed.  The select statement will never select a nil
   151  		// channel.
   152  		var validateChan chan *txValidateItem
   153  		var item *txValidateItem
   154  		if currentItem < numInputs {
   155  			validateChan = v.validateChan
   156  			item = items[currentItem]
   157  		}
   158  
   159  		select {
   160  		case validateChan <- item:
   161  			currentItem++
   162  
   163  		case err := <-v.resultChan:
   164  			processedItems++
   165  			if err != nil {
   166  				close(v.quitChan)
   167  				return err
   168  			}
   169  		}
   170  	}
   171  
   172  	close(v.quitChan)
   173  	return nil
   174  }
   175  
   176  // newTxValidator returns a new instance of txValidator to be used for
   177  // validating transaction scripts asynchronously.
   178  func newTxValidator(utxoView *UtxoViewpoint, flags txscript.ScriptFlags,
   179  	sigCache *txscript.SigCache, hashCache *txscript.HashCache) *txValidator {
   180  	return &txValidator{
   181  		validateChan: make(chan *txValidateItem),
   182  		quitChan:     make(chan struct{}),
   183  		resultChan:   make(chan error),
   184  		utxoView:     utxoView,
   185  		sigCache:     sigCache,
   186  		hashCache:    hashCache,
   187  		flags:        flags,
   188  	}
   189  }
   190  
   191  // ValidateTransactionScripts validates the scripts for the passed transaction
   192  // using multiple goroutines.
   193  func ValidateTransactionScripts(tx *btcutil.Tx, utxoView *UtxoViewpoint,
   194  	flags txscript.ScriptFlags, sigCache *txscript.SigCache,
   195  	hashCache *txscript.HashCache) error {
   196  
   197  	// First determine if segwit is active according to the scriptFlags. If
   198  	// it isn't then we don't need to interact with the HashCache.
   199  	segwitActive := flags&txscript.ScriptVerifyWitness == txscript.ScriptVerifyWitness
   200  
   201  	// If the hashcache doesn't yet has the sighash midstate for this
   202  	// transaction, then we'll compute them now so we can re-use them
   203  	// amongst all worker validation goroutines.
   204  	if segwitActive && tx.MsgTx().HasWitness() &&
   205  		!hashCache.ContainsHashes(tx.Hash()) {
   206  		hashCache.AddSigHashes(tx.MsgTx(), utxoView)
   207  	}
   208  
   209  	var cachedHashes *txscript.TxSigHashes
   210  	if segwitActive && tx.MsgTx().HasWitness() {
   211  		// The same pointer to the transaction's sighash midstate will
   212  		// be re-used amongst all validation goroutines. By
   213  		// pre-computing the sighash here instead of during validation,
   214  		// we ensure the sighashes
   215  		// are only computed once.
   216  		cachedHashes, _ = hashCache.GetSigHashes(tx.Hash())
   217  	}
   218  
   219  	// Collect all of the transaction inputs and required information for
   220  	// validation.
   221  	txIns := tx.MsgTx().TxIn
   222  	txValItems := make([]*txValidateItem, 0, len(txIns))
   223  	for txInIdx, txIn := range txIns {
   224  		// Skip coinbases.
   225  		if txIn.PreviousOutPoint.Index == math.MaxUint32 {
   226  			continue
   227  		}
   228  
   229  		txVI := &txValidateItem{
   230  			txInIndex: txInIdx,
   231  			txIn:      txIn,
   232  			tx:        tx,
   233  			sigHashes: cachedHashes,
   234  		}
   235  		txValItems = append(txValItems, txVI)
   236  	}
   237  
   238  	// Validate all of the inputs.
   239  	validator := newTxValidator(utxoView, flags, sigCache, hashCache)
   240  	return validator.Validate(txValItems)
   241  }
   242  
   243  // checkBlockScripts executes and validates the scripts for all transactions in
   244  // the passed block using multiple goroutines.
   245  func checkBlockScripts(block *btcutil.Block, utxoView *UtxoViewpoint,
   246  	scriptFlags txscript.ScriptFlags, sigCache *txscript.SigCache,
   247  	hashCache *txscript.HashCache) error {
   248  
   249  	// First determine if segwit is active according to the scriptFlags. If
   250  	// it isn't then we don't need to interact with the HashCache.
   251  	segwitActive := scriptFlags&txscript.ScriptVerifyWitness == txscript.ScriptVerifyWitness
   252  
   253  	// Collect all of the transaction inputs and required information for
   254  	// validation for all transactions in the block into a single slice.
   255  	numInputs := 0
   256  	for _, tx := range block.Transactions() {
   257  		numInputs += len(tx.MsgTx().TxIn)
   258  	}
   259  	txValItems := make([]*txValidateItem, 0, numInputs)
   260  	for _, tx := range block.Transactions() {
   261  		hash := tx.Hash()
   262  
   263  		// If the HashCache is present, and it doesn't yet contain the
   264  		// partial sighashes for this transaction, then we add the
   265  		// sighashes for the transaction. This allows us to take
   266  		// advantage of the potential speed savings due to the new
   267  		// digest algorithm (BIP0143).
   268  		if segwitActive && tx.HasWitness() && hashCache != nil &&
   269  			!hashCache.ContainsHashes(hash) {
   270  
   271  			hashCache.AddSigHashes(tx.MsgTx(), utxoView)
   272  		}
   273  
   274  		var cachedHashes *txscript.TxSigHashes
   275  		if segwitActive && tx.HasWitness() {
   276  			if hashCache != nil {
   277  				cachedHashes, _ = hashCache.GetSigHashes(hash)
   278  			} else {
   279  				cachedHashes = txscript.NewTxSigHashes(
   280  					tx.MsgTx(), utxoView,
   281  				)
   282  			}
   283  		}
   284  
   285  		for txInIdx, txIn := range tx.MsgTx().TxIn {
   286  			// Skip coinbases.
   287  			if txIn.PreviousOutPoint.Index == math.MaxUint32 {
   288  				continue
   289  			}
   290  
   291  			txVI := &txValidateItem{
   292  				txInIndex: txInIdx,
   293  				txIn:      txIn,
   294  				tx:        tx,
   295  				sigHashes: cachedHashes,
   296  			}
   297  			txValItems = append(txValItems, txVI)
   298  		}
   299  	}
   300  
   301  	// Validate all of the inputs.
   302  	validator := newTxValidator(utxoView, scriptFlags, sigCache, hashCache)
   303  	start := time.Now()
   304  	if err := validator.Validate(txValItems); err != nil {
   305  		return err
   306  	}
   307  	elapsed := time.Since(start)
   308  
   309  	log.Tracef("block %v took %v to verify", block.Hash(), elapsed)
   310  
   311  	// If the HashCache is present, once we have validated the block, we no
   312  	// longer need the cached hashes for these transactions, so we purge
   313  	// them from the cache.
   314  	if segwitActive && hashCache != nil {
   315  		for _, tx := range block.Transactions() {
   316  			if tx.MsgTx().HasWitness() {
   317  				hashCache.PurgeSigHashes(tx.Hash())
   318  			}
   319  		}
   320  	}
   321  
   322  	return nil
   323  }