fc69776371
Several of the functions require a map of contextual transaction data to use as a source for referenced transactions. This commit exports the underlying TxData type and creates a new type TxStore, which is a map of points to the under TxData. In addition, this commit exposes a new function, FetchTransactionStore, which returns a transaction store (TxStore) containing all of the transactions referenced by the passed transaction, as well as the existing transaction if it already exists. This paves the way for subsequent commits which will expose some of the functions which depend on this transaction store.
136 lines
3.6 KiB
Go
136 lines
3.6 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
|
|
}
|
|
|
|
// validateAllTxIn validates the scripts for the passed transaction using
|
|
// multiple goroutines.
|
|
func validateAllTxIn(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
|
|
for i, tx := range block.MsgBlock().Transactions {
|
|
txHash, _ := block.TxSha(i)
|
|
err := validateAllTxIn(tx, txHash, timestamp, txStore)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|