lbcd/scriptval.go
Dale Rahn b680d3539f Revive old validate Tx in parallel code, faster...
requested by davec, cleanup/polish in-tree.
2013-10-11 18:31:54 -04:00

166 lines
4.3 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"
"time"
)
// 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, timestamp time.Time, originTx *btcwire.MsgTx) 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,
timestamp.After(btcscript.Bip16Activation))
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, timestamp time.Time, txStore TxStore) (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, timestamp,
originTx)
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 {
timestamp := block.MsgBlock().Header.Timestamp
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, timestamp, txStore)
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
}