lbcd/scriptval.go
Dave Collins 89d86d07ac ValidateTransactionScripts now accepts script flags.
This commit modifies the ValidateTransactionScripts API to accept the
recently added ScriptFlags from btcscript.  This provides flexibility to
the caller to choose validation behavior based on those new flags.
2013-10-25 10:49:06 -05:00

171 lines
4.5 KiB
Go

// Copyright (c) 2013 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package btcchain
import (
"fmt"
"github.com/conformal/btcscript"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
"math"
)
// txValidate is used to track results of validating scripts for each
// transaction input index.
type txValidate struct {
txIndex int
err error
}
// txProcessList
type txProcessList struct {
txsha btcwire.ShaHash
tx *btcwire.MsgTx
}
// validateTxIn validates a the script pair for the passed spending transaction
// (along with the specific input index) and origin transaction (with the
// specific output index).
func validateTxIn(txInIdx int, txin *btcwire.TxIn, txSha *btcwire.ShaHash, tx *btcwire.MsgTx, originTx *btcwire.MsgTx, flags btcscript.ScriptFlags) error {
// If the input transaction has no previous input, there is nothing
// to check.
originTxIdx := txin.PreviousOutpoint.Index
if originTxIdx == math.MaxUint32 {
return nil
}
if originTxIdx >= uint32(len(originTx.TxOut)) {
originTxSha := &txin.PreviousOutpoint.Hash
log.Warnf("unable to locate source tx %v spending tx %v",
originTxSha, &txSha)
return fmt.Errorf("invalid index %x", originTxIdx)
}
sigScript := txin.SignatureScript
pkScript := originTx.TxOut[originTxIdx].PkScript
engine, err := btcscript.NewScript(sigScript, pkScript, txInIdx, tx,
flags)
if err != nil {
return err
}
err = engine.Execute()
if err != nil {
log.Warnf("validate of input %v failed: %v", txInIdx, err)
return err
}
return nil
}
// ValidateTransactionScripts validates the scripts for the passed transaction
// using multiple goroutines.
func ValidateTransactionScripts(tx *btcwire.MsgTx, txHash *btcwire.ShaHash, txStore TxStore, flags btcscript.ScriptFlags) (err error) {
c := make(chan txValidate)
job := tx.TxIn
resultErrors := make([]error, len(job))
var currentItem int
var completedItems int
processFunc := func(txInIdx int) {
log.Tracef("validating tx %v input %v len %v",
txHash, currentItem, len(job))
txin := job[txInIdx]
originTxSha := &txin.PreviousOutpoint.Hash
origintxidx := txin.PreviousOutpoint.Index
var originTx *btcwire.MsgTx
if origintxidx != math.MaxUint32 {
txInfo, ok := txStore[*originTxSha]
if !ok {
//wtf?
fmt.Printf("obj not found in txStore %v",
originTxSha)
}
originTx = txInfo.Tx
}
err := validateTxIn(txInIdx, job[txInIdx], txHash, tx, originTx,
flags)
r := txValidate{txInIdx, err}
c <- r
}
for currentItem = 0; currentItem < len(job) && currentItem < 16; currentItem++ {
go processFunc(currentItem)
}
for completedItems < len(job) {
select {
case result := <-c:
completedItems++
resultErrors[result.txIndex] = result.err
// would be nice to determine if we could stop
// on early errors here instead of running more.
if err == nil {
err = result.err
}
if currentItem < len(job) {
go processFunc(currentItem)
currentItem++
}
}
}
for i := 0; i < len(job); i++ {
if resultErrors[i] != nil {
log.Warnf("tx %v failed input %v, err %v", txHash, i, resultErrors[i])
}
}
return
}
// checkBlockScripts executes and validates the scripts for all transactions in
// the passed block.
func checkBlockScripts(block *btcutil.Block, txStore TxStore) error {
// Setup the script validation flags. Blocks created after the BIP0016
// activation time need to have the pay-to-script-hash checks enabled.
var flags btcscript.ScriptFlags
if block.MsgBlock().Header.Timestamp.After(btcscript.Bip16Activation) {
flags |= btcscript.ScriptBip16
}
txList := block.MsgBlock().Transactions
c := make(chan txValidate)
resultErrors := make([]error, len(txList))
var currentItem int
var completedItems int
processFunc := func(txIdx int) {
tx := txList[txIdx]
txHash, _ := block.TxSha(txIdx)
err := ValidateTransactionScripts(tx, txHash, txStore, flags)
r := txValidate{txIdx, err}
c <- r
}
for currentItem = 0; currentItem < len(txList) && currentItem < 8; currentItem++ {
go processFunc(currentItem)
}
for completedItems < len(txList) {
select {
case result := <-c:
completedItems++
resultErrors[result.txIndex] = result.err
// would be nice to determine if we could stop
// on early errors here instead of running more.
if currentItem < len(txList) {
go processFunc(currentItem)
currentItem++
}
}
}
for i := 0; i < len(txList); i++ {
if resultErrors[i] != nil {
return resultErrors[i]
}
}
return nil
}