137 lines
3.7 KiB
Go
137 lines
3.7 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, pver uint32, 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,
|
||
|
pver, 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 all of the passed transaction
|
||
|
// inputs using multiple goroutines.
|
||
|
func validateAllTxIn(txsha *btcwire.ShaHash, txValidator *btcwire.MsgTx, pver uint32, timestamp time.Time, job []*btcwire.TxIn, txStore map[btcwire.ShaHash]*txData) (err error) {
|
||
|
c := make(chan txValidate)
|
||
|
resultErrors := make([]error, len(job))
|
||
|
|
||
|
var currentItem int
|
||
|
var completedItems int
|
||
|
|
||
|
processFunc := func(txInIdx int) {
|
||
|
log.Tracef("validating tx %v input %v len %v",
|
||
|
&txsha, 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], txsha, txValidator,
|
||
|
pver, 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", &txsha, i, resultErrors[i])
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// checkBlockScripts executes and validates the scripts for all transactions in
|
||
|
// the passed block.
|
||
|
func checkBlockScripts(block *btcutil.Block, txStore map[btcwire.ShaHash]*txData) error {
|
||
|
pver := block.ProtocolVersion()
|
||
|
timestamp := block.MsgBlock().Header.Timestamp
|
||
|
for i, tx := range block.MsgBlock().Transactions {
|
||
|
txHash, _ := block.TxSha(i)
|
||
|
err := validateAllTxIn(txHash, tx, pver, timestamp, tx.TxIn, txStore)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|