@@ -29,15 +29,16 @@ type txValidateItem struct {
2929// inputs. It provides several channels for communication and a processing
3030// function that is intended to be in run multiple goroutines.
3131type txValidator struct {
32- validateChan chan * txValidateItem
33- quitChan chan struct {}
34- resultChan chan error
35- utxoView * UtxoViewpoint
36- flags txscript.ScriptFlags
37- sigCache * txscript.SigCache
38- hashCache * txscript.HashCache
39- sigChecks uint32
40- maxSigChecks uint32
32+ validateChan chan * txValidateItem
33+ quitChan chan struct {}
34+ resultChan chan error
35+ utxoView * UtxoViewpoint
36+ flags txscript.ScriptFlags
37+ sigCache * txscript.SigCache
38+ hashCache * txscript.HashCache
39+ sigChecks uint32
40+ maxSigChecks uint32
41+ upgrade9ForkHeight int32
4142}
4243
4344// sendResult sends the result of a script pair validation on the internal
9899 utxoEntryCache .AddEntry (i , * wire .NewTxOut (u .amount , u .pkScript , u .tokenData ))
99100 }
100101
101- _ , err := wire .RunCashTokensValidityAlgorithm (utxoEntryCache , txVI .tx .MsgTx ())
102- if err != nil {
102+ isPATFO := IsPATFO (
103+ utxo .tokenData , utxo .pkScript ,
104+ utxo .blockHeight , v .upgrade9ForkHeight )
105+
106+ if isPATFO {
107+ // PATFOs are provably unspendable. The software ignores
108+ // other types of provably unspendable tokens so we use
109+ // the same behaviour here.
110+ str := fmt .Sprintf ("unable to find unspent " +
111+ "output %v referenced from " +
112+ "transaction %s:%d" ,
113+ txIn .PreviousOutPoint , txVI .tx .Hash (),
114+ txVI .txInIndex )
115+ err := ruleError (ErrMissingTxOut , str )
103116 v .sendResult (err )
117+ break out
118+ }
119+
120+ if v .flags .HasFlag (txscript .ScriptAllowCashTokens ) {
121+ _ , err := wire .RunCashTokensValidityAlgorithm (utxoEntryCache , txVI .tx .MsgTx ())
122+ if err != nil {
123+ v .sendResult (err )
124+ }
104125 }
105126
106127 vm , err := txscript .NewEngine (pkScript , txVI .tx .MsgTx (),
@@ -222,24 +243,25 @@ func (v *txValidator) Validate(items []*txValidateItem) error {
222243// newTxValidator returns a new instance of txValidator to be used for
223244// validating transaction scripts asynchronously.
224245func newTxValidator (utxoView * UtxoViewpoint , flags txscript.ScriptFlags ,
225- sigCache * txscript.SigCache , hashCache * txscript.HashCache , maxSigChecks uint32 ) * txValidator {
246+ sigCache * txscript.SigCache , hashCache * txscript.HashCache , maxSigChecks uint32 , upgrade9ForkHeight int32 ) * txValidator {
226247 return & txValidator {
227- validateChan : make (chan * txValidateItem ),
228- quitChan : make (chan struct {}),
229- resultChan : make (chan error ),
230- utxoView : utxoView ,
231- sigCache : sigCache ,
232- hashCache : hashCache ,
233- flags : flags ,
234- maxSigChecks : maxSigChecks ,
248+ validateChan : make (chan * txValidateItem ),
249+ quitChan : make (chan struct {}),
250+ resultChan : make (chan error ),
251+ utxoView : utxoView ,
252+ sigCache : sigCache ,
253+ hashCache : hashCache ,
254+ flags : flags ,
255+ maxSigChecks : maxSigChecks ,
256+ upgrade9ForkHeight : upgrade9ForkHeight ,
235257 }
236258}
237259
238260// ValidateTransactionScripts validates the scripts for the passed transaction
239261// using multiple goroutines. It returns the number of sigchecks in the transaction.
240262func ValidateTransactionScripts (tx * bchutil.Tx , utxoView * UtxoViewpoint ,
241263 flags txscript.ScriptFlags , sigCache * txscript.SigCache ,
242- hashCache * txscript.HashCache ) (uint32 , error ) {
264+ hashCache * txscript.HashCache , upgrade9ForkHeight int32 ) (uint32 , error ) {
243265
244266 // If the HashCache is present, and it doesn't yet contain the
245267 // partial sighashes for this transaction, then we add the
@@ -269,8 +291,9 @@ func ValidateTransactionScripts(tx *bchutil.Tx, utxoView *UtxoViewpoint,
269291 }
270292 utxoCache .AddEntry (i , * wire .NewTxOut (u .amount , u .pkScript , u .tokenData ))
271293 }
272-
273- cachedHashes .AddTxSigHashUtxoFromUtxoCache (tx .MsgTx (), utxoCache )
294+ if flags .HasFlag (txscript .ScriptAllowCashTokens ) {
295+ cachedHashes .AddTxSigHashUtxoFromUtxoCache (tx .MsgTx (), utxoCache )
296+ }
274297 }
275298
276299 // Collect all of the transaction inputs and required information for
@@ -295,18 +318,32 @@ func ValidateTransactionScripts(tx *bchutil.Tx, utxoView *UtxoViewpoint,
295318 }
296319
297320 // Validate all of the inputs.
298- validator := newTxValidator (utxoView , flags , sigCache , hashCache , 0 )
321+ validator := newTxValidator (utxoView , flags , sigCache , hashCache , 0 , upgrade9ForkHeight )
299322 if err := validator .Validate (txValItems ); err != nil {
300323 return 0 , err
301324 }
302325 return sigChecks , nil
303326}
304327
328+ // Checks if the input contains pre-activation token-forgery output.
329+ // PATFOs are provably unspendable so a better place to check for them might be inside
330+ // txscript.IsUnspendable() but since we need to check for block heights, to mimimize
331+ // changes in the codebase, we handle it here. It might be a good idea to change this later.
332+ func IsPATFO (tokenData wire.TokenData , pkScript []byte , utxoBlockHeight int32 , upgrade9ForkHeight int32 ) bool {
333+ isPATFO := false
334+ if ! tokenData .IsEmpty () || pkScript [0 ] == 0xef {
335+ if utxoBlockHeight < upgrade9ForkHeight {
336+ isPATFO = true
337+ }
338+ }
339+ return isPATFO
340+ }
341+
305342// checkBlockScripts executes and validates the scripts for all transactions in
306343// the passed block using multiple goroutines.
307344func checkBlockScripts (block * bchutil.Block , utxoView * UtxoViewpoint ,
308345 scriptFlags txscript.ScriptFlags , sigCache * txscript.SigCache ,
309- hashCache * txscript.HashCache , maxSigChecks uint32 ) error {
346+ hashCache * txscript.HashCache , maxSigChecks uint32 , upgrade9ForkHeight int32 ) error {
310347
311348 // Collect all of the transaction inputs and required information for
312349 // validation for all transactions in the block into a single slice.
@@ -346,8 +383,9 @@ func checkBlockScripts(block *bchutil.Block, utxoView *UtxoViewpoint,
346383 }
347384 utxoCache .AddEntry (i , * wire .NewTxOut (u .amount , u .pkScript , u .tokenData ))
348385 }
349-
350- cachedHashes .AddTxSigHashUtxoFromUtxoCache (tx .MsgTx (), utxoCache )
386+ if scriptFlags .HasFlag (txscript .ScriptAllowCashTokens ) {
387+ cachedHashes .AddTxSigHashUtxoFromUtxoCache (tx .MsgTx (), utxoCache )
388+ }
351389 }
352390
353391 for txInIdx , txIn := range tx .MsgTx ().TxIn {
@@ -368,7 +406,7 @@ func checkBlockScripts(block *bchutil.Block, utxoView *UtxoViewpoint,
368406 }
369407
370408 // Validate all of the inputs.
371- validator := newTxValidator (utxoView , scriptFlags , sigCache , hashCache , maxSigChecks )
409+ validator := newTxValidator (utxoView , scriptFlags , sigCache , hashCache , maxSigChecks , upgrade9ForkHeight )
372410 start := time .Now ()
373411 if err := validator .Validate (txValItems ); err != nil {
374412 return err
0 commit comments