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.
13 "github.com/btcsuite/btcd/txscript"
14 "github.com/btcsuite/btcd/wire"
15 "github.com/btcsuite/btcutil"
18 // txValidateItem holds a transaction along with which input to validate.
19 type txValidateItem struct {
23 sigHashes *txscript.TxSigHashes
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{}
33 utxoView *UtxoViewpoint
34 flags txscript.ScriptFlags
35 sigCache *txscript.SigCache
36 hashCache *txscript.HashCache
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) {
45 case v.resultChan <- result:
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() {
57 case txVI := <-v.validateChan:
58 // Ensure the referenced input transaction is available.
60 originTxHash := &txIn.PreviousOutPoint.Hash
61 originTxIndex := txIn.PreviousOutPoint.Index
62 txEntry := v.utxoView.LookupEntry(originTxHash)
64 str := fmt.Sprintf("unable to find input "+
65 "transaction %v referenced from "+
66 "transaction %v", originTxHash,
68 err := ruleError(ErrMissingTxOut, str)
73 // Ensure the referenced input transaction public key
74 // script is available.
75 pkScript := txEntry.PkScriptByIndex(originTxIndex)
77 str := fmt.Sprintf("unable to find unspent "+
78 "output %v script referenced from "+
80 txIn.PreviousOutPoint, txVI.tx.Hash(),
82 err := ruleError(ErrBadTxInput, str)
87 // Create a new script engine for the script pair.
88 sigScript := txIn.SignatureScript
89 witness := txIn.Witness
90 inputAmount := txEntry.AmountByIndex(originTxIndex)
91 vm, err := txscript.NewEngine(pkScript, txVI.tx.MsgTx(),
92 txVI.txInIndex, v.flags, v.sigCache, txVI.sigHashes,
95 str := fmt.Sprintf("failed to parse input "+
96 "%s:%d which references output %s:%d - "+
97 "%v (input witness %x, input script "+
98 "bytes %x, prev output script bytes %x)",
99 txVI.tx.Hash(), txVI.txInIndex, originTxHash,
100 originTxIndex, err, witness, sigScript,
102 err := ruleError(ErrScriptMalformed, str)
107 // Execute the script pair.
108 if err := vm.Execute(); err != nil {
109 str := fmt.Sprintf("failed to validate input "+
110 "%s:%d which references output %s:%d - "+
111 "%v (input witness %x, input script "+
112 "bytes %x, prev output script bytes %x)",
113 txVI.tx.Hash(), txVI.txInIndex,
114 originTxHash, originTxIndex, err,
115 witness, sigScript, pkScript)
116 err := ruleError(ErrScriptValidation, str)
121 // Validation succeeded.
130 // Validate validates the scripts for all of the passed transaction inputs using
131 // multiple goroutines.
132 func (v *txValidator) Validate(items []*txValidateItem) error {
137 // Limit the number of goroutines to do script validation based on the
138 // number of processor cores. This helps ensure the system stays
139 // reasonably responsive under heavy load.
140 maxGoRoutines := runtime.NumCPU() * 3
141 if maxGoRoutines <= 0 {
144 if maxGoRoutines > len(items) {
145 maxGoRoutines = len(items)
148 // Start up validation handlers that are used to asynchronously
149 // validate each transaction input.
150 for i := 0; i < maxGoRoutines; i++ {
151 go v.validateHandler()
154 // Validate each of the inputs. The quit channel is closed when any
155 // errors occur so all processing goroutines exit regardless of which
156 // input had the validation error.
157 numInputs := len(items)
160 for processedItems < numInputs {
161 // Only send items while there are still items that need to
162 // be processed. The select statement will never select a nil
164 var validateChan chan *txValidateItem
165 var item *txValidateItem
166 if currentItem < numInputs {
167 validateChan = v.validateChan
168 item = items[currentItem]
172 case validateChan <- item:
175 case err := <-v.resultChan:
188 // newTxValidator returns a new instance of txValidator to be used for
189 // validating transaction scripts asynchronously.
190 func newTxValidator(utxoView *UtxoViewpoint, flags txscript.ScriptFlags,
191 sigCache *txscript.SigCache, hashCache *txscript.HashCache) *txValidator {
193 validateChan: make(chan *txValidateItem),
194 quitChan: make(chan struct{}),
195 resultChan: make(chan error),
198 hashCache: hashCache,
203 // ValidateTransactionScripts validates the scripts for the passed transaction
204 // using multiple goroutines.
205 func ValidateTransactionScripts(tx *btcutil.Tx, utxoView *UtxoViewpoint,
206 flags txscript.ScriptFlags, sigCache *txscript.SigCache,
207 hashCache *txscript.HashCache) error {
209 // First determine if segwit is active according to the scriptFlags. If
210 // it isn't then we don't need to interact with the HashCache.
211 segwitActive := flags&txscript.ScriptVerifyWitness == txscript.ScriptVerifyWitness
213 // If the hashcache doesn't yet has the sighash midstate for this
214 // transaction, then we'll compute them now so we can re-use them
215 // amongst all worker validation goroutines.
216 if segwitActive && tx.MsgTx().HasWitness() &&
217 !hashCache.ContainsHashes(tx.Hash()) {
218 hashCache.AddSigHashes(tx.MsgTx())
221 var cachedHashes *txscript.TxSigHashes
222 if segwitActive && tx.MsgTx().HasWitness() {
223 // The same pointer to the transaction's sighash midstate will
224 // be re-used amongst all validation goroutines. By
225 // pre-computing the sighash here instead of during validation,
226 // we ensure the sighashes
227 // are only computed once.
228 cachedHashes, _ = hashCache.GetSigHashes(tx.Hash())
231 // Collect all of the transaction inputs and required information for
233 txIns := tx.MsgTx().TxIn
234 txValItems := make([]*txValidateItem, 0, len(txIns))
235 for txInIdx, txIn := range txIns {
237 if txIn.PreviousOutPoint.Index == math.MaxUint32 {
241 txVI := &txValidateItem{
245 sigHashes: cachedHashes,
247 txValItems = append(txValItems, txVI)
250 // Validate all of the inputs.
251 validator := newTxValidator(utxoView, flags, sigCache, hashCache)
252 return validator.Validate(txValItems)
255 // checkBlockScripts executes and validates the scripts for all transactions in
256 // the passed block using multiple goroutines.
257 func checkBlockScripts(block *btcutil.Block, utxoView *UtxoViewpoint,
258 scriptFlags txscript.ScriptFlags, sigCache *txscript.SigCache,
259 hashCache *txscript.HashCache) error {
261 // First determine if segwit is active according to the scriptFlags. If
262 // it isn't then we don't need to interact with the HashCache.
263 segwitActive := scriptFlags&txscript.ScriptVerifyWitness == txscript.ScriptVerifyWitness
265 // Collect all of the transaction inputs and required information for
266 // validation for all transactions in the block into a single slice.
268 for _, tx := range block.Transactions() {
269 numInputs += len(tx.MsgTx().TxIn)
271 txValItems := make([]*txValidateItem, 0, numInputs)
272 for _, tx := range block.Transactions() {
275 // If the HashCache is present, and it doesn't yet contain the
276 // partial sighashes for this transaction, then we add the
277 // sighashes for the transaction. This allows us to take
278 // advantage of the potential speed savings due to the new
279 // digest algorithm (BIP0143).
280 if segwitActive && tx.HasWitness() && hashCache != nil &&
281 !hashCache.ContainsHashes(hash) {
283 hashCache.AddSigHashes(tx.MsgTx())
286 var cachedHashes *txscript.TxSigHashes
287 if segwitActive && tx.HasWitness() {
288 if hashCache != nil {
289 cachedHashes, _ = hashCache.GetSigHashes(hash)
291 cachedHashes = txscript.NewTxSigHashes(tx.MsgTx())
295 for txInIdx, txIn := range tx.MsgTx().TxIn {
297 if txIn.PreviousOutPoint.Index == math.MaxUint32 {
301 txVI := &txValidateItem{
305 sigHashes: cachedHashes,
307 txValItems = append(txValItems, txVI)
311 // Validate all of the inputs.
312 validator := newTxValidator(utxoView, scriptFlags, sigCache, hashCache)
314 if err := validator.Validate(txValItems); err != nil {
317 elapsed := time.Since(start)
319 log.Tracef("block %v took %v to verify", block.Hash(), elapsed)
321 // If the HashCache is present, once we have validated the block, we no
322 // longer need the cached hashes for these transactions, so we purge
323 // them from the cache.
324 if segwitActive && hashCache != nil {
325 for _, tx := range block.Transactions() {
326 if tx.MsgTx().HasWitness() {
327 hashCache.PurgeSigHashes(tx.Hash())