OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / btcsuite / btcd / blockchain / scriptval.go
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/txscript"
14         "github.com/btcsuite/btcd/wire"
15         "github.com/btcsuite/btcutil"
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 transaction is available.
59                         txIn := txVI.txIn
60                         originTxHash := &txIn.PreviousOutPoint.Hash
61                         originTxIndex := txIn.PreviousOutPoint.Index
62                         txEntry := v.utxoView.LookupEntry(originTxHash)
63                         if txEntry == nil {
64                                 str := fmt.Sprintf("unable to find input "+
65                                         "transaction %v referenced from "+
66                                         "transaction %v", originTxHash,
67                                         txVI.tx.Hash())
68                                 err := ruleError(ErrMissingTxOut, str)
69                                 v.sendResult(err)
70                                 break out
71                         }
72
73                         // Ensure the referenced input transaction public key
74                         // script is available.
75                         pkScript := txEntry.PkScriptByIndex(originTxIndex)
76                         if pkScript == nil {
77                                 str := fmt.Sprintf("unable to find unspent "+
78                                         "output %v script referenced from "+
79                                         "transaction %s:%d",
80                                         txIn.PreviousOutPoint, txVI.tx.Hash(),
81                                         txVI.txInIndex)
82                                 err := ruleError(ErrBadTxInput, str)
83                                 v.sendResult(err)
84                                 break out
85                         }
86
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,
93                                 inputAmount)
94                         if err != nil {
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,
101                                         pkScript)
102                                 err := ruleError(ErrScriptMalformed, str)
103                                 v.sendResult(err)
104                                 break out
105                         }
106
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)
117                                 v.sendResult(err)
118                                 break out
119                         }
120
121                         // Validation succeeded.
122                         v.sendResult(nil)
123
124                 case <-v.quitChan:
125                         break out
126                 }
127         }
128 }
129
130 // Validate validates the scripts for all of the passed transaction inputs using
131 // multiple goroutines.
132 func (v *txValidator) Validate(items []*txValidateItem) error {
133         if len(items) == 0 {
134                 return nil
135         }
136
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 {
142                 maxGoRoutines = 1
143         }
144         if maxGoRoutines > len(items) {
145                 maxGoRoutines = len(items)
146         }
147
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()
152         }
153
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)
158         currentItem := 0
159         processedItems := 0
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
163                 // channel.
164                 var validateChan chan *txValidateItem
165                 var item *txValidateItem
166                 if currentItem < numInputs {
167                         validateChan = v.validateChan
168                         item = items[currentItem]
169                 }
170
171                 select {
172                 case validateChan <- item:
173                         currentItem++
174
175                 case err := <-v.resultChan:
176                         processedItems++
177                         if err != nil {
178                                 close(v.quitChan)
179                                 return err
180                         }
181                 }
182         }
183
184         close(v.quitChan)
185         return nil
186 }
187
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 {
192         return &txValidator{
193                 validateChan: make(chan *txValidateItem),
194                 quitChan:     make(chan struct{}),
195                 resultChan:   make(chan error),
196                 utxoView:     utxoView,
197                 sigCache:     sigCache,
198                 hashCache:    hashCache,
199                 flags:        flags,
200         }
201 }
202
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 {
208
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
212
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())
219         }
220
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())
229         }
230
231         // Collect all of the transaction inputs and required information for
232         // validation.
233         txIns := tx.MsgTx().TxIn
234         txValItems := make([]*txValidateItem, 0, len(txIns))
235         for txInIdx, txIn := range txIns {
236                 // Skip coinbases.
237                 if txIn.PreviousOutPoint.Index == math.MaxUint32 {
238                         continue
239                 }
240
241                 txVI := &txValidateItem{
242                         txInIndex: txInIdx,
243                         txIn:      txIn,
244                         tx:        tx,
245                         sigHashes: cachedHashes,
246                 }
247                 txValItems = append(txValItems, txVI)
248         }
249
250         // Validate all of the inputs.
251         validator := newTxValidator(utxoView, flags, sigCache, hashCache)
252         return validator.Validate(txValItems)
253 }
254
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 {
260
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
264
265         // Collect all of the transaction inputs and required information for
266         // validation for all transactions in the block into a single slice.
267         numInputs := 0
268         for _, tx := range block.Transactions() {
269                 numInputs += len(tx.MsgTx().TxIn)
270         }
271         txValItems := make([]*txValidateItem, 0, numInputs)
272         for _, tx := range block.Transactions() {
273                 hash := tx.Hash()
274
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) {
282
283                         hashCache.AddSigHashes(tx.MsgTx())
284                 }
285
286                 var cachedHashes *txscript.TxSigHashes
287                 if segwitActive && tx.HasWitness() {
288                         if hashCache != nil {
289                                 cachedHashes, _ = hashCache.GetSigHashes(hash)
290                         } else {
291                                 cachedHashes = txscript.NewTxSigHashes(tx.MsgTx())
292                         }
293                 }
294
295                 for txInIdx, txIn := range tx.MsgTx().TxIn {
296                         // Skip coinbases.
297                         if txIn.PreviousOutPoint.Index == math.MaxUint32 {
298                                 continue
299                         }
300
301                         txVI := &txValidateItem{
302                                 txInIndex: txInIdx,
303                                 txIn:      txIn,
304                                 tx:        tx,
305                                 sigHashes: cachedHashes,
306                         }
307                         txValItems = append(txValItems, txVI)
308                 }
309         }
310
311         // Validate all of the inputs.
312         validator := newTxValidator(utxoView, scriptFlags, sigCache, hashCache)
313         start := time.Now()
314         if err := validator.Validate(txValItems); err != nil {
315                 return err
316         }
317         elapsed := time.Since(start)
318
319         log.Tracef("block %v took %v to verify", block.Hash(), elapsed)
320
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())
328                         }
329                 }
330         }
331
332         return nil
333 }