Convert API to use new btcutil.Tx.
This is part of the ongoing transaction hash optimization effort noted in conformal/btcd#25.
This commit is contained in:
parent
b6e4ae4441
commit
6165e9b95b
8 changed files with 83 additions and 129 deletions
12
accept.go
12
accept.go
|
@ -60,16 +60,10 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure all transactions in the block are finalized.
|
// Ensure all transactions in the block are finalized.
|
||||||
for i, tx := range block.MsgBlock().Transactions {
|
for _, tx := range block.Transactions() {
|
||||||
if !IsFinalizedTransaction(tx, blockHeight, blockHeader.Timestamp) {
|
if !IsFinalizedTransaction(tx, blockHeight, blockHeader.Timestamp) {
|
||||||
// Use the TxSha function from the block rather
|
|
||||||
// than the transaction itself since the block version
|
|
||||||
// is cached. Also, it's safe to ignore the error here
|
|
||||||
// since the only reason TxSha can fail is if the index
|
|
||||||
// is out of range which is impossible here.
|
|
||||||
txSha, _ := block.TxSha(i)
|
|
||||||
str := fmt.Sprintf("block contains unfinalized "+
|
str := fmt.Sprintf("block contains unfinalized "+
|
||||||
"transaction %v", txSha)
|
"transaction %v", tx.Sha())
|
||||||
return RuleError(str)
|
return RuleError(str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,7 +123,7 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block) error {
|
||||||
if prevNode != nil {
|
if prevNode != nil {
|
||||||
expectedHeight = prevNode.height + 1
|
expectedHeight = prevNode.height + 1
|
||||||
}
|
}
|
||||||
coinbaseTx := block.MsgBlock().Transactions[0]
|
coinbaseTx := block.Transactions()[0]
|
||||||
err := checkSerializedHeight(coinbaseTx, expectedHeight)
|
err := checkSerializedHeight(coinbaseTx, expectedHeight)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -154,11 +154,11 @@ func (b *BlockChain) findLatestKnownCheckpoint() (*btcutil.Block, error) {
|
||||||
|
|
||||||
// isNonstandardTransaction determines whether a transaction contains any
|
// isNonstandardTransaction determines whether a transaction contains any
|
||||||
// scripts which are not one of the standard types.
|
// scripts which are not one of the standard types.
|
||||||
func isNonstandardTransaction(tx *btcwire.MsgTx) bool {
|
func isNonstandardTransaction(tx *btcutil.Tx) bool {
|
||||||
// TODO(davec): Should there be checks for the input signature scripts?
|
// TODO(davec): Should there be checks for the input signature scripts?
|
||||||
|
|
||||||
// Check all of the output public key scripts for non-standard scripts.
|
// Check all of the output public key scripts for non-standard scripts.
|
||||||
for _, txOut := range tx.TxOut {
|
for _, txOut := range tx.MsgTx().TxOut {
|
||||||
scriptClass := btcscript.GetScriptClass(txOut.PkScript)
|
scriptClass := btcscript.GetScriptClass(txOut.PkScript)
|
||||||
if scriptClass == btcscript.NonStandardTy {
|
if scriptClass == btcscript.NonStandardTy {
|
||||||
return true
|
return true
|
||||||
|
@ -235,7 +235,7 @@ func (b *BlockChain) IsCheckpointCandidate(block *btcutil.Block) (bool, error) {
|
||||||
|
|
||||||
// A checkpoint must have transactions that only contain standard
|
// A checkpoint must have transactions that only contain standard
|
||||||
// scripts.
|
// scripts.
|
||||||
for _, tx := range block.MsgBlock().Transactions {
|
for _, tx := range block.Transactions() {
|
||||||
if isNonstandardTransaction(tx) {
|
if isNonstandardTransaction(tx) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ package btcchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/conformal/btcutil"
|
"github.com/conformal/btcutil"
|
||||||
"github.com/conformal/btcwire"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,6 +36,6 @@ func TstTimeSorter(times []time.Time) timeSorter {
|
||||||
|
|
||||||
// TstCheckSerializedHeight makes the internal checkSerializedHeight function
|
// TstCheckSerializedHeight makes the internal checkSerializedHeight function
|
||||||
// available to the test package.
|
// available to the test package.
|
||||||
func TstCheckSerializedHeight(coinbaseTx *btcwire.MsgTx, wantHeight int64) error {
|
func TstCheckSerializedHeight(coinbaseTx *btcutil.Tx, wantHeight int64) error {
|
||||||
return checkSerializedHeight(coinbaseTx, wantHeight)
|
return checkSerializedHeight(coinbaseTx, wantHeight)
|
||||||
}
|
}
|
||||||
|
|
12
merkle.go
12
merkle.go
|
@ -69,21 +69,15 @@ func hashMerkleBranches(left *btcwire.ShaHash, right *btcwire.ShaHash) *btcwire.
|
||||||
// Since this function uses nodes that are pointers to the hashes, empty nodes
|
// Since this function uses nodes that are pointers to the hashes, empty nodes
|
||||||
// will be nil.
|
// will be nil.
|
||||||
func BuildMerkleTreeStore(block *btcutil.Block) []*btcwire.ShaHash {
|
func BuildMerkleTreeStore(block *btcutil.Block) []*btcwire.ShaHash {
|
||||||
numTransactions := len(block.MsgBlock().Transactions)
|
|
||||||
|
|
||||||
// Calculate how many entries are required to hold the binary merkle
|
// Calculate how many entries are required to hold the binary merkle
|
||||||
// tree as a linear array and create an array of that size.
|
// tree as a linear array and create an array of that size.
|
||||||
nextPoT := nextPowerOfTwo(numTransactions)
|
nextPoT := nextPowerOfTwo(len(block.Transactions()))
|
||||||
arraySize := nextPoT*2 - 1
|
arraySize := nextPoT*2 - 1
|
||||||
merkles := make([]*btcwire.ShaHash, arraySize)
|
merkles := make([]*btcwire.ShaHash, arraySize)
|
||||||
|
|
||||||
// Create the base transaction shas and populate the array with them.
|
// Create the base transaction shas and populate the array with them.
|
||||||
for i := 0; i < numTransactions; i++ {
|
for i, tx := range block.Transactions() {
|
||||||
// Ignore the error since the only reason TxSha can fail is
|
merkles[i] = tx.Sha()
|
||||||
// if the index is out of range which is impossible here due
|
|
||||||
// to using a loop over the existing transactions.
|
|
||||||
sha, _ := block.TxSha(i)
|
|
||||||
merkles[i] = sha
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the array offset after the last transaction and adjusted to the
|
// Start the array offset after the last transaction and adjusted to the
|
||||||
|
|
39
scriptval.go
39
scriptval.go
|
@ -19,16 +19,10 @@ type txValidate struct {
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// txProcessList
|
|
||||||
type txProcessList struct {
|
|
||||||
txsha btcwire.ShaHash
|
|
||||||
tx *btcwire.MsgTx
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateTxIn validates a the script pair for the passed spending transaction
|
// validateTxIn validates a the script pair for the passed spending transaction
|
||||||
// (along with the specific input index) and origin transaction (with the
|
// (along with the specific input index) and origin transaction (with the
|
||||||
// specific output index).
|
// specific output index).
|
||||||
func validateTxIn(txInIdx int, txin *btcwire.TxIn, txSha *btcwire.ShaHash, tx *btcwire.MsgTx, originTx *btcwire.MsgTx, flags btcscript.ScriptFlags) error {
|
func validateTxIn(txInIdx int, txin *btcwire.TxIn, tx *btcutil.Tx, originTx *btcutil.Tx, flags btcscript.ScriptFlags) error {
|
||||||
// If the input transaction has no previous input, there is nothing
|
// If the input transaction has no previous input, there is nothing
|
||||||
// to check.
|
// to check.
|
||||||
originTxIdx := txin.PreviousOutpoint.Index
|
originTxIdx := txin.PreviousOutpoint.Index
|
||||||
|
@ -36,17 +30,17 @@ func validateTxIn(txInIdx int, txin *btcwire.TxIn, txSha *btcwire.ShaHash, tx *b
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if originTxIdx >= uint32(len(originTx.TxOut)) {
|
if originTxIdx >= uint32(len(originTx.MsgTx().TxOut)) {
|
||||||
originTxSha := &txin.PreviousOutpoint.Hash
|
originTxSha := &txin.PreviousOutpoint.Hash
|
||||||
log.Warnf("unable to locate source tx %v spending tx %v",
|
log.Warnf("unable to locate source tx %v spending tx %v",
|
||||||
originTxSha, &txSha)
|
originTxSha, tx.Sha())
|
||||||
return fmt.Errorf("invalid index %x", originTxIdx)
|
return fmt.Errorf("invalid index %x", originTxIdx)
|
||||||
}
|
}
|
||||||
|
|
||||||
sigScript := txin.SignatureScript
|
sigScript := txin.SignatureScript
|
||||||
pkScript := originTx.TxOut[originTxIdx].PkScript
|
pkScript := originTx.MsgTx().TxOut[originTxIdx].PkScript
|
||||||
engine, err := btcscript.NewScript(sigScript, pkScript, txInIdx, tx,
|
engine, err := btcscript.NewScript(sigScript, pkScript, txInIdx,
|
||||||
flags)
|
tx.MsgTx(), flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -62,9 +56,9 @@ func validateTxIn(txInIdx int, txin *btcwire.TxIn, txSha *btcwire.ShaHash, tx *b
|
||||||
|
|
||||||
// ValidateTransactionScripts validates the scripts for the passed transaction
|
// ValidateTransactionScripts validates the scripts for the passed transaction
|
||||||
// using multiple goroutines.
|
// using multiple goroutines.
|
||||||
func ValidateTransactionScripts(tx *btcwire.MsgTx, txHash *btcwire.ShaHash, txStore TxStore, flags btcscript.ScriptFlags) (err error) {
|
func ValidateTransactionScripts(tx *btcutil.Tx, txStore TxStore, flags btcscript.ScriptFlags) (err error) {
|
||||||
c := make(chan txValidate)
|
c := make(chan txValidate)
|
||||||
job := tx.TxIn
|
job := tx.MsgTx().TxIn
|
||||||
resultErrors := make([]error, len(job))
|
resultErrors := make([]error, len(job))
|
||||||
|
|
||||||
var currentItem int
|
var currentItem int
|
||||||
|
@ -72,12 +66,12 @@ func ValidateTransactionScripts(tx *btcwire.MsgTx, txHash *btcwire.ShaHash, txSt
|
||||||
|
|
||||||
processFunc := func(txInIdx int) {
|
processFunc := func(txInIdx int) {
|
||||||
log.Tracef("validating tx %v input %v len %v",
|
log.Tracef("validating tx %v input %v len %v",
|
||||||
txHash, currentItem, len(job))
|
tx.Sha(), currentItem, len(job))
|
||||||
txin := job[txInIdx]
|
txin := job[txInIdx]
|
||||||
originTxSha := &txin.PreviousOutpoint.Hash
|
originTxSha := &txin.PreviousOutpoint.Hash
|
||||||
origintxidx := txin.PreviousOutpoint.Index
|
origintxidx := txin.PreviousOutpoint.Index
|
||||||
|
|
||||||
var originTx *btcwire.MsgTx
|
var originTx *btcutil.Tx
|
||||||
if origintxidx != math.MaxUint32 {
|
if origintxidx != math.MaxUint32 {
|
||||||
txInfo, ok := txStore[*originTxSha]
|
txInfo, ok := txStore[*originTxSha]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -87,8 +81,7 @@ func ValidateTransactionScripts(tx *btcwire.MsgTx, txHash *btcwire.ShaHash, txSt
|
||||||
}
|
}
|
||||||
originTx = txInfo.Tx
|
originTx = txInfo.Tx
|
||||||
}
|
}
|
||||||
err := validateTxIn(txInIdx, job[txInIdx], txHash, tx, originTx,
|
err := validateTxIn(txInIdx, txin, tx, originTx, flags)
|
||||||
flags)
|
|
||||||
r := txValidate{txInIdx, err}
|
r := txValidate{txInIdx, err}
|
||||||
c <- r
|
c <- r
|
||||||
}
|
}
|
||||||
|
@ -114,7 +107,8 @@ func ValidateTransactionScripts(tx *btcwire.MsgTx, txHash *btcwire.ShaHash, txSt
|
||||||
}
|
}
|
||||||
for i := 0; i < len(job); i++ {
|
for i := 0; i < len(job); i++ {
|
||||||
if resultErrors[i] != nil {
|
if resultErrors[i] != nil {
|
||||||
log.Warnf("tx %v failed input %v, err %v", txHash, i, resultErrors[i])
|
log.Warnf("tx %v failed input %v, err %v", tx.Sha(), i,
|
||||||
|
resultErrors[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -130,17 +124,14 @@ func checkBlockScripts(block *btcutil.Block, txStore TxStore) error {
|
||||||
flags |= btcscript.ScriptBip16
|
flags |= btcscript.ScriptBip16
|
||||||
}
|
}
|
||||||
|
|
||||||
txList := block.MsgBlock().Transactions
|
txList := block.Transactions()
|
||||||
c := make(chan txValidate)
|
c := make(chan txValidate)
|
||||||
resultErrors := make([]error, len(txList))
|
resultErrors := make([]error, len(txList))
|
||||||
|
|
||||||
var currentItem int
|
var currentItem int
|
||||||
var completedItems int
|
var completedItems int
|
||||||
processFunc := func(txIdx int) {
|
processFunc := func(txIdx int) {
|
||||||
tx := txList[txIdx]
|
err := ValidateTransactionScripts(txList[txIdx], txStore, flags)
|
||||||
txHash, _ := block.TxSha(txIdx)
|
|
||||||
|
|
||||||
err := ValidateTransactionScripts(tx, txHash, txStore, flags)
|
|
||||||
r := txValidate{txIdx, err}
|
r := txValidate{txIdx, err}
|
||||||
c <- r
|
c <- r
|
||||||
}
|
}
|
||||||
|
|
55
txlookup.go
55
txlookup.go
|
@ -14,7 +14,7 @@ import (
|
||||||
// TxData contains contextual information about transactions such as which block
|
// TxData contains contextual information about transactions such as which block
|
||||||
// they were found in and whether or not the outputs are spent.
|
// they were found in and whether or not the outputs are spent.
|
||||||
type TxData struct {
|
type TxData struct {
|
||||||
Tx *btcwire.MsgTx
|
Tx *btcutil.Tx
|
||||||
Hash *btcwire.ShaHash
|
Hash *btcwire.ShaHash
|
||||||
BlockHeight int64
|
BlockHeight int64
|
||||||
Spent []bool
|
Spent []bool
|
||||||
|
@ -33,23 +33,19 @@ type TxStore map[btcwire.ShaHash]*TxData
|
||||||
func connectTransactions(txStore TxStore, block *btcutil.Block) error {
|
func connectTransactions(txStore TxStore, block *btcutil.Block) error {
|
||||||
// Loop through all of the transactions in the block to see if any of
|
// Loop through all of the transactions in the block to see if any of
|
||||||
// them are ones we need to update and spend based on the results map.
|
// them are ones we need to update and spend based on the results map.
|
||||||
for i, tx := range block.MsgBlock().Transactions {
|
for _, tx := range block.Transactions() {
|
||||||
txHash, err := block.TxSha(i)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the transaction store with the transaction information
|
// Update the transaction store with the transaction information
|
||||||
// if it's one of the requested transactions.
|
// if it's one of the requested transactions.
|
||||||
if txD, exists := txStore[*txHash]; exists {
|
msgTx := tx.MsgTx()
|
||||||
|
if txD, exists := txStore[*tx.Sha()]; exists {
|
||||||
txD.Tx = tx
|
txD.Tx = tx
|
||||||
txD.BlockHeight = block.Height()
|
txD.BlockHeight = block.Height()
|
||||||
txD.Spent = make([]bool, len(tx.TxOut))
|
txD.Spent = make([]bool, len(msgTx.TxOut))
|
||||||
txD.Err = nil
|
txD.Err = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spend the origin transaction output.
|
// Spend the origin transaction output.
|
||||||
for _, txIn := range tx.TxIn {
|
for _, txIn := range msgTx.TxIn {
|
||||||
originHash := &txIn.PreviousOutpoint.Hash
|
originHash := &txIn.PreviousOutpoint.Hash
|
||||||
originIndex := txIn.PreviousOutpoint.Index
|
originIndex := txIn.PreviousOutpoint.Index
|
||||||
if originTx, exists := txStore[*originHash]; exists {
|
if originTx, exists := txStore[*originHash]; exists {
|
||||||
|
@ -67,18 +63,13 @@ func connectTransactions(txStore TxStore, block *btcutil.Block) error {
|
||||||
func disconnectTransactions(txStore TxStore, block *btcutil.Block) error {
|
func disconnectTransactions(txStore TxStore, block *btcutil.Block) error {
|
||||||
// Loop through all of the transactions in the block to see if any of
|
// Loop through all of the transactions in the block to see if any of
|
||||||
// them are ones that need to be undone based on the transaction store.
|
// them are ones that need to be undone based on the transaction store.
|
||||||
for i, tx := range block.MsgBlock().Transactions {
|
for _, tx := range block.Transactions() {
|
||||||
txHash, err := block.TxSha(i)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear this transaction from the transaction store if needed.
|
// Clear this transaction from the transaction store if needed.
|
||||||
// Only clear it rather than deleting it because the transaction
|
// Only clear it rather than deleting it because the transaction
|
||||||
// connect code relies on its presence to decide whether or not
|
// connect code relies on its presence to decide whether or not
|
||||||
// to update the store and any transactions which exist on both
|
// to update the store and any transactions which exist on both
|
||||||
// sides of a fork would otherwise not be updated.
|
// sides of a fork would otherwise not be updated.
|
||||||
if txD, exists := txStore[*txHash]; exists {
|
if txD, exists := txStore[*tx.Sha()]; exists {
|
||||||
txD.Tx = nil
|
txD.Tx = nil
|
||||||
txD.BlockHeight = 0
|
txD.BlockHeight = 0
|
||||||
txD.Spent = nil
|
txD.Spent = nil
|
||||||
|
@ -86,7 +77,7 @@ func disconnectTransactions(txStore TxStore, block *btcutil.Block) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unspend the origin transaction output.
|
// Unspend the origin transaction output.
|
||||||
for _, txIn := range tx.TxIn {
|
for _, txIn := range tx.MsgTx().TxIn {
|
||||||
originHash := &txIn.PreviousOutpoint.Hash
|
originHash := &txIn.PreviousOutpoint.Hash
|
||||||
originIndex := txIn.PreviousOutpoint.Index
|
originIndex := txIn.PreviousOutpoint.Index
|
||||||
originTx, exists := txStore[*originHash]
|
originTx, exists := txStore[*originHash]
|
||||||
|
@ -146,7 +137,7 @@ func fetchTxStoreMain(db btcdb.Db, txSet map[btcwire.ShaHash]bool, includeSpent
|
||||||
// cause subtle errors, so avoid the potential altogether.
|
// cause subtle errors, so avoid the potential altogether.
|
||||||
txD.Err = txReply.Err
|
txD.Err = txReply.Err
|
||||||
if txReply.Err == nil {
|
if txReply.Err == nil {
|
||||||
txD.Tx = txReply.Tx
|
txD.Tx = btcutil.NewTx(txReply.Tx)
|
||||||
txD.BlockHeight = txReply.Height
|
txD.BlockHeight = txReply.Height
|
||||||
txD.Spent = make([]bool, len(txReply.TxSpent))
|
txD.Spent = make([]bool, len(txReply.TxSpent))
|
||||||
copy(txD.Spent, txReply.TxSpent)
|
copy(txD.Spent, txReply.TxSpent)
|
||||||
|
@ -240,14 +231,9 @@ func (b *BlockChain) fetchInputTransactions(node *blockNode, block *btcutil.Bloc
|
||||||
// this block could be referencing other transactions earlier in this
|
// this block could be referencing other transactions earlier in this
|
||||||
// block which are not yet in the chain.
|
// block which are not yet in the chain.
|
||||||
txInFlight := map[btcwire.ShaHash]int{}
|
txInFlight := map[btcwire.ShaHash]int{}
|
||||||
transactions := block.MsgBlock().Transactions
|
transactions := block.Transactions()
|
||||||
for i := range transactions {
|
for i, tx := range transactions {
|
||||||
// Get transaction hash. It's safe to ignore the error since
|
txInFlight[*tx.Sha()] = i
|
||||||
// it's already cached in the nominal code path and the only
|
|
||||||
// way it can fail is if the index is out of range which is
|
|
||||||
// impossible here.
|
|
||||||
txHash, _ := block.TxSha(i)
|
|
||||||
txInFlight[*txHash] = i
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop through all of the transaction inputs (except for the coinbase
|
// Loop through all of the transaction inputs (except for the coinbase
|
||||||
|
@ -256,7 +242,7 @@ func (b *BlockChain) fetchInputTransactions(node *blockNode, block *btcutil.Bloc
|
||||||
txNeededSet := make(map[btcwire.ShaHash]bool)
|
txNeededSet := make(map[btcwire.ShaHash]bool)
|
||||||
txStore := make(TxStore)
|
txStore := make(TxStore)
|
||||||
for i, tx := range transactions[1:] {
|
for i, tx := range transactions[1:] {
|
||||||
for _, txIn := range tx.TxIn {
|
for _, txIn := range tx.MsgTx().TxIn {
|
||||||
// Add an entry to the transaction store for the needed
|
// Add an entry to the transaction store for the needed
|
||||||
// transaction with it set to missing by default.
|
// transaction with it set to missing by default.
|
||||||
originHash := &txIn.PreviousOutpoint.Hash
|
originHash := &txIn.PreviousOutpoint.Hash
|
||||||
|
@ -279,7 +265,7 @@ func (b *BlockChain) fetchInputTransactions(node *blockNode, block *btcutil.Bloc
|
||||||
originTx := transactions[inFlightIndex]
|
originTx := transactions[inFlightIndex]
|
||||||
txD.Tx = originTx
|
txD.Tx = originTx
|
||||||
txD.BlockHeight = node.height
|
txD.BlockHeight = node.height
|
||||||
txD.Spent = make([]bool, len(originTx.TxOut))
|
txD.Spent = make([]bool, len(originTx.MsgTx().TxOut))
|
||||||
txD.Err = nil
|
txD.Err = nil
|
||||||
} else {
|
} else {
|
||||||
txNeededSet[*originHash] = true
|
txNeededSet[*originHash] = true
|
||||||
|
@ -306,18 +292,13 @@ func (b *BlockChain) fetchInputTransactions(node *blockNode, block *btcutil.Bloc
|
||||||
// passed transaction from the point of view of the end of the main chain. It
|
// passed transaction from the point of view of the end of the main chain. It
|
||||||
// also attempts to fetch the transaction itself so the returned TxStore can be
|
// also attempts to fetch the transaction itself so the returned TxStore can be
|
||||||
// examined for duplicate transactions.
|
// examined for duplicate transactions.
|
||||||
func (b *BlockChain) FetchTransactionStore(tx *btcwire.MsgTx) (TxStore, error) {
|
func (b *BlockChain) FetchTransactionStore(tx *btcutil.Tx) (TxStore, error) {
|
||||||
txHash, err := tx.TxSha()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a set of needed transactions from the transactions referenced
|
// Create a set of needed transactions from the transactions referenced
|
||||||
// by the inputs of the passed transaction. Also, add the passed
|
// by the inputs of the passed transaction. Also, add the passed
|
||||||
// transaction itself as a way for the caller to detect duplicates.
|
// transaction itself as a way for the caller to detect duplicates.
|
||||||
txNeededSet := make(map[btcwire.ShaHash]bool)
|
txNeededSet := make(map[btcwire.ShaHash]bool)
|
||||||
txNeededSet[txHash] = true
|
txNeededSet[*tx.Sha()] = true
|
||||||
for _, txIn := range tx.TxIn {
|
for _, txIn := range tx.MsgTx().TxIn {
|
||||||
txNeededSet[txIn.PreviousOutpoint.Hash] = true
|
txNeededSet[txIn.PreviousOutpoint.Hash] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
80
validate.go
80
validate.go
|
@ -94,7 +94,9 @@ func isNullOutpoint(outpoint *btcwire.OutPoint) bool {
|
||||||
// represented in the block chain by a transaction with a single input that has
|
// represented in the block chain by a transaction with a single input that has
|
||||||
// a previous output transaction index set to the maximum value along with a
|
// a previous output transaction index set to the maximum value along with a
|
||||||
// zero hash.
|
// zero hash.
|
||||||
func IsCoinBase(msgTx *btcwire.MsgTx) bool {
|
func IsCoinBase(tx *btcutil.Tx) bool {
|
||||||
|
msgTx := tx.MsgTx()
|
||||||
|
|
||||||
// A coin base must only have one transaction input.
|
// A coin base must only have one transaction input.
|
||||||
if len(msgTx.TxIn) != 1 {
|
if len(msgTx.TxIn) != 1 {
|
||||||
return false
|
return false
|
||||||
|
@ -111,7 +113,9 @@ func IsCoinBase(msgTx *btcwire.MsgTx) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsFinalizedTransaction determines whether or not a transaction is finalized.
|
// IsFinalizedTransaction determines whether or not a transaction is finalized.
|
||||||
func IsFinalizedTransaction(msgTx *btcwire.MsgTx, blockHeight int64, blockTime time.Time) bool {
|
func IsFinalizedTransaction(tx *btcutil.Tx, blockHeight int64, blockTime time.Time) bool {
|
||||||
|
msgTx := tx.MsgTx()
|
||||||
|
|
||||||
// Lock time of zero means the transaction is finalized.
|
// Lock time of zero means the transaction is finalized.
|
||||||
lockTime := msgTx.LockTime
|
lockTime := msgTx.LockTime
|
||||||
if lockTime == 0 {
|
if lockTime == 0 {
|
||||||
|
@ -175,14 +179,15 @@ func calcBlockSubsidy(height int64) int64 {
|
||||||
|
|
||||||
// CheckTransactionSanity performs some preliminary checks on a transaction to
|
// CheckTransactionSanity performs some preliminary checks on a transaction to
|
||||||
// ensure it is sane. These checks are context free.
|
// ensure it is sane. These checks are context free.
|
||||||
func CheckTransactionSanity(tx *btcwire.MsgTx) error {
|
func CheckTransactionSanity(tx *btcutil.Tx) error {
|
||||||
// A transaction must have at least one input.
|
// A transaction must have at least one input.
|
||||||
if len(tx.TxIn) == 0 {
|
msgTx := tx.MsgTx()
|
||||||
|
if len(msgTx.TxIn) == 0 {
|
||||||
return RuleError("transaction has no inputs")
|
return RuleError("transaction has no inputs")
|
||||||
}
|
}
|
||||||
|
|
||||||
// A transaction must have at least one output.
|
// A transaction must have at least one output.
|
||||||
if len(tx.TxOut) == 0 {
|
if len(msgTx.TxOut) == 0 {
|
||||||
return RuleError("transaction has no outputs")
|
return RuleError("transaction has no outputs")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +203,7 @@ func CheckTransactionSanity(tx *btcwire.MsgTx) error {
|
||||||
// as a satoshi. One bitcoin is a quantity of satoshi as defined by the
|
// as a satoshi. One bitcoin is a quantity of satoshi as defined by the
|
||||||
// satoshiPerBitcoin constant.
|
// satoshiPerBitcoin constant.
|
||||||
var totalSatoshi int64
|
var totalSatoshi int64
|
||||||
for _, txOut := range tx.TxOut {
|
for _, txOut := range msgTx.TxOut {
|
||||||
satoshi := txOut.Value
|
satoshi := txOut.Value
|
||||||
if satoshi < 0 {
|
if satoshi < 0 {
|
||||||
str := fmt.Sprintf("transaction output has negative "+
|
str := fmt.Sprintf("transaction output has negative "+
|
||||||
|
@ -231,7 +236,7 @@ func CheckTransactionSanity(tx *btcwire.MsgTx) error {
|
||||||
|
|
||||||
// Check for duplicate transaction inputs.
|
// Check for duplicate transaction inputs.
|
||||||
existingTxOut := make(map[btcwire.OutPoint]bool)
|
existingTxOut := make(map[btcwire.OutPoint]bool)
|
||||||
for _, txIn := range tx.TxIn {
|
for _, txIn := range msgTx.TxIn {
|
||||||
if _, exists := existingTxOut[txIn.PreviousOutpoint]; exists {
|
if _, exists := existingTxOut[txIn.PreviousOutpoint]; exists {
|
||||||
return RuleError("transaction contains duplicate outpoint")
|
return RuleError("transaction contains duplicate outpoint")
|
||||||
}
|
}
|
||||||
|
@ -240,7 +245,7 @@ func CheckTransactionSanity(tx *btcwire.MsgTx) error {
|
||||||
|
|
||||||
// Coinbase script length must be between min and max length.
|
// Coinbase script length must be between min and max length.
|
||||||
if IsCoinBase(tx) {
|
if IsCoinBase(tx) {
|
||||||
slen := len(tx.TxIn[0].SignatureScript)
|
slen := len(msgTx.TxIn[0].SignatureScript)
|
||||||
if slen < minCoinbaseScriptLen || slen > maxCoinbaseScriptLen {
|
if slen < minCoinbaseScriptLen || slen > maxCoinbaseScriptLen {
|
||||||
str := fmt.Sprintf("coinbase transaction script length "+
|
str := fmt.Sprintf("coinbase transaction script length "+
|
||||||
"of %d is out of range (min: %d, max: %d)",
|
"of %d is out of range (min: %d, max: %d)",
|
||||||
|
@ -250,7 +255,7 @@ func CheckTransactionSanity(tx *btcwire.MsgTx) error {
|
||||||
} else {
|
} else {
|
||||||
// Previous transaction outputs referenced by the inputs to this
|
// Previous transaction outputs referenced by the inputs to this
|
||||||
// transaction must not be null.
|
// transaction must not be null.
|
||||||
for _, txIn := range tx.TxIn {
|
for _, txIn := range msgTx.TxIn {
|
||||||
prevOut := &txIn.PreviousOutpoint
|
prevOut := &txIn.PreviousOutpoint
|
||||||
if isNullOutpoint(prevOut) {
|
if isNullOutpoint(prevOut) {
|
||||||
return RuleError("transaction input refers to " +
|
return RuleError("transaction input refers to " +
|
||||||
|
@ -302,7 +307,9 @@ func (b *BlockChain) checkProofOfWork(block *btcutil.Block) error {
|
||||||
// input and output scripts in the provided transaction. This uses the
|
// input and output scripts in the provided transaction. This uses the
|
||||||
// quicker, but imprecise, signature operation counting mechanism from
|
// quicker, but imprecise, signature operation counting mechanism from
|
||||||
// btcscript.
|
// btcscript.
|
||||||
func countSigOps(msgTx *btcwire.MsgTx) int {
|
func countSigOps(tx *btcutil.Tx) int {
|
||||||
|
msgTx := tx.MsgTx()
|
||||||
|
|
||||||
// Accumulate the number of signature operations in all transaction
|
// Accumulate the number of signature operations in all transaction
|
||||||
// inputs.
|
// inputs.
|
||||||
totalSigOps := 0
|
totalSigOps := 0
|
||||||
|
@ -325,20 +332,15 @@ func countSigOps(msgTx *btcwire.MsgTx) int {
|
||||||
// transactions which are of the pay-to-script-hash type. This uses the
|
// transactions which are of the pay-to-script-hash type. This uses the
|
||||||
// precise, signature operation counting mechanism from btcscript which requires
|
// precise, signature operation counting mechanism from btcscript which requires
|
||||||
// access to the input transaction scripts.
|
// access to the input transaction scripts.
|
||||||
func countP2SHSigOps(msgTx *btcwire.MsgTx, isCoinBaseTx bool, txStore TxStore) (int, error) {
|
func countP2SHSigOps(tx *btcutil.Tx, isCoinBaseTx bool, txStore TxStore) (int, error) {
|
||||||
// Coinbase transactions have no interesting inputs.
|
// Coinbase transactions have no interesting inputs.
|
||||||
if isCoinBaseTx {
|
if isCoinBaseTx {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(davec): Need to pass the cached version in.
|
|
||||||
txHash, err := msgTx.TxSha()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accumulate the number of signature operations in all transaction
|
// Accumulate the number of signature operations in all transaction
|
||||||
// inputs.
|
// inputs.
|
||||||
|
msgTx := tx.MsgTx()
|
||||||
totalSigOps := 0
|
totalSigOps := 0
|
||||||
for _, txIn := range msgTx.TxIn {
|
for _, txIn := range msgTx.TxIn {
|
||||||
// Ensure the referenced input transaction is available.
|
// Ensure the referenced input transaction is available.
|
||||||
|
@ -347,23 +349,24 @@ func countP2SHSigOps(msgTx *btcwire.MsgTx, isCoinBaseTx bool, txStore TxStore) (
|
||||||
if !exists || originTx.Err != nil || originTx.Tx == nil {
|
if !exists || originTx.Err != nil || originTx.Tx == nil {
|
||||||
str := fmt.Sprintf("unable to find input transaction "+
|
str := fmt.Sprintf("unable to find input transaction "+
|
||||||
"%v referenced from transaction %v", txInHash,
|
"%v referenced from transaction %v", txInHash,
|
||||||
txHash)
|
tx.Sha())
|
||||||
return 0, RuleError(str)
|
return 0, RuleError(str)
|
||||||
}
|
}
|
||||||
|
originMsgTx := originTx.Tx.MsgTx()
|
||||||
|
|
||||||
// Ensure the output index in the referenced transaction is
|
// Ensure the output index in the referenced transaction is
|
||||||
// available.
|
// available.
|
||||||
originTxIndex := txIn.PreviousOutpoint.Index
|
originTxIndex := txIn.PreviousOutpoint.Index
|
||||||
if originTxIndex >= uint32(len(originTx.Tx.TxOut)) {
|
if originTxIndex >= uint32(len(originMsgTx.TxOut)) {
|
||||||
str := fmt.Sprintf("out of bounds input index %d in "+
|
str := fmt.Sprintf("out of bounds input index %d in "+
|
||||||
"transaction %v referenced from transaction %v",
|
"transaction %v referenced from transaction %v",
|
||||||
originTxIndex, txInHash, txHash)
|
originTxIndex, txInHash, tx.Sha())
|
||||||
return 0, RuleError(str)
|
return 0, RuleError(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're only interested in pay-to-script-hash types, so skip
|
// We're only interested in pay-to-script-hash types, so skip
|
||||||
// this input if it's not one.
|
// this input if it's not one.
|
||||||
pkScript := originTx.Tx.TxOut[originTxIndex].PkScript
|
pkScript := originMsgTx.TxOut[originTxIndex].PkScript
|
||||||
if !btcscript.IsPayToScriptHash(pkScript) {
|
if !btcscript.IsPayToScriptHash(pkScript) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -407,8 +410,7 @@ func (b *BlockChain) checkBlockSanity(block *btcutil.Block) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the block time is not more than 2 hours in the future.
|
// Ensure the block time is not more than 2 hours in the future.
|
||||||
msgBlock := block.MsgBlock()
|
header := &block.MsgBlock().Header
|
||||||
header := &msgBlock.Header
|
|
||||||
if header.Timestamp.After(time.Now().Add(time.Hour * 2)) {
|
if header.Timestamp.After(time.Now().Add(time.Hour * 2)) {
|
||||||
str := fmt.Sprintf("block timestamp of %v is too far in the "+
|
str := fmt.Sprintf("block timestamp of %v is too far in the "+
|
||||||
"future", header.Timestamp)
|
"future", header.Timestamp)
|
||||||
|
@ -416,7 +418,7 @@ func (b *BlockChain) checkBlockSanity(block *btcutil.Block) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A block must have at least one transaction.
|
// A block must have at least one transaction.
|
||||||
transactions := msgBlock.Transactions
|
transactions := block.Transactions()
|
||||||
if len(transactions) == 0 {
|
if len(transactions) == 0 {
|
||||||
return RuleError("block does not contain any transactions")
|
return RuleError("block does not contain any transactions")
|
||||||
}
|
}
|
||||||
|
@ -463,11 +465,8 @@ func (b *BlockChain) checkBlockSanity(block *btcutil.Block) error {
|
||||||
// since the transaction hashes are already cached due to building the
|
// since the transaction hashes are already cached due to building the
|
||||||
// merkle tree above.
|
// merkle tree above.
|
||||||
existingTxHashes := make(map[btcwire.ShaHash]bool)
|
existingTxHashes := make(map[btcwire.ShaHash]bool)
|
||||||
txShas, err := block.TxShas()
|
for _, tx := range transactions {
|
||||||
if err != nil {
|
hash := tx.Sha()
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, hash := range txShas {
|
|
||||||
if _, exists := existingTxHashes[*hash]; exists {
|
if _, exists := existingTxHashes[*hash]; exists {
|
||||||
str := fmt.Sprintf("block contains duplicate "+
|
str := fmt.Sprintf("block contains duplicate "+
|
||||||
"transaction %v", hash)
|
"transaction %v", hash)
|
||||||
|
@ -497,8 +496,8 @@ func (b *BlockChain) checkBlockSanity(block *btcutil.Block) error {
|
||||||
|
|
||||||
// checkSerializedHeight checks if the signature script in the passed
|
// checkSerializedHeight checks if the signature script in the passed
|
||||||
// transaction starts with the serialized block height of wantHeight.
|
// transaction starts with the serialized block height of wantHeight.
|
||||||
func checkSerializedHeight(coinbaseTx *btcwire.MsgTx, wantHeight int64) error {
|
func checkSerializedHeight(coinbaseTx *btcutil.Tx, wantHeight int64) error {
|
||||||
sigScript := coinbaseTx.TxIn[0].SignatureScript
|
sigScript := coinbaseTx.MsgTx().TxIn[0].SignatureScript
|
||||||
if len(sigScript) < 1 {
|
if len(sigScript) < 1 {
|
||||||
str := "the coinbase signature script for blocks of " +
|
str := "the coinbase signature script for blocks of " +
|
||||||
"version %d or greater must start with the " +
|
"version %d or greater must start with the " +
|
||||||
|
@ -601,20 +600,15 @@ func (b *BlockChain) checkBIP0030(node *blockNode, block *btcutil.Block) error {
|
||||||
// amount, and verifying the signatures to prove the spender was the owner of
|
// amount, and verifying the signatures to prove the spender was the owner of
|
||||||
// the bitcoins and therefore allowed to spend them. As it checks the inputs,
|
// the bitcoins and therefore allowed to spend them. As it checks the inputs,
|
||||||
// it also calculates the total fees for the transaction and returns that value.
|
// it also calculates the total fees for the transaction and returns that value.
|
||||||
func CheckTransactionInputs(tx *btcwire.MsgTx, txHeight int64, txStore TxStore) (int64, error) {
|
func CheckTransactionInputs(tx *btcutil.Tx, txHeight int64, txStore TxStore) (int64, error) {
|
||||||
// Coinbase transactions have no inputs.
|
// Coinbase transactions have no inputs.
|
||||||
if IsCoinBase(tx) {
|
if IsCoinBase(tx) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(davec): Need to pass the cached version in.
|
txHash := tx.Sha()
|
||||||
txHash, err := tx.TxSha()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var totalSatoshiIn int64
|
var totalSatoshiIn int64
|
||||||
for _, txIn := range tx.TxIn {
|
for _, txIn := range tx.MsgTx().TxIn {
|
||||||
// Ensure the input is available.
|
// Ensure the input is available.
|
||||||
txInHash := &txIn.PreviousOutpoint.Hash
|
txInHash := &txIn.PreviousOutpoint.Hash
|
||||||
originTx, exists := txStore[*txInHash]
|
originTx, exists := txStore[*txInHash]
|
||||||
|
@ -659,7 +653,7 @@ func CheckTransactionInputs(tx *btcwire.MsgTx, txHeight int64, txStore TxStore)
|
||||||
// a transaction are in a unit value known as a satoshi. One
|
// a transaction are in a unit value known as a satoshi. One
|
||||||
// bitcoin is a quantity of satoshi as defined by the
|
// bitcoin is a quantity of satoshi as defined by the
|
||||||
// satoshiPerBitcoin constant.
|
// satoshiPerBitcoin constant.
|
||||||
originTxSatoshi := originTx.Tx.TxOut[originTxIndex].Value
|
originTxSatoshi := originTx.Tx.MsgTx().TxOut[originTxIndex].Value
|
||||||
if originTxSatoshi < 0 {
|
if originTxSatoshi < 0 {
|
||||||
str := fmt.Sprintf("transaction output has negative "+
|
str := fmt.Sprintf("transaction output has negative "+
|
||||||
"value of %v", originTxSatoshi)
|
"value of %v", originTxSatoshi)
|
||||||
|
@ -693,7 +687,7 @@ func CheckTransactionInputs(tx *btcwire.MsgTx, txHeight int64, txStore TxStore)
|
||||||
// to ignore overflow and out of range errors here because those error
|
// to ignore overflow and out of range errors here because those error
|
||||||
// conditions would have already been caught by checkTransactionSanity.
|
// conditions would have already been caught by checkTransactionSanity.
|
||||||
var totalSatoshiOut int64
|
var totalSatoshiOut int64
|
||||||
for _, txOut := range tx.TxOut {
|
for _, txOut := range tx.MsgTx().TxOut {
|
||||||
totalSatoshiOut += txOut.Value
|
totalSatoshiOut += txOut.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -771,7 +765,7 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block) er
|
||||||
// expands the count to include a precise count of pay-to-script-hash
|
// expands the count to include a precise count of pay-to-script-hash
|
||||||
// signature operations in each of the input transaction public key
|
// signature operations in each of the input transaction public key
|
||||||
// scripts.
|
// scripts.
|
||||||
transactions := block.MsgBlock().Transactions
|
transactions := block.Transactions()
|
||||||
totalSigOps := 0
|
totalSigOps := 0
|
||||||
for i, tx := range transactions {
|
for i, tx := range transactions {
|
||||||
numsigOps := countSigOps(tx)
|
numsigOps := countSigOps(tx)
|
||||||
|
@ -832,7 +826,7 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block) er
|
||||||
// errors here because those error conditions would have already been
|
// errors here because those error conditions would have already been
|
||||||
// caught by checkTransactionSanity.
|
// caught by checkTransactionSanity.
|
||||||
var totalSatoshiOut int64
|
var totalSatoshiOut int64
|
||||||
for _, txOut := range transactions[0].TxOut {
|
for _, txOut := range transactions[0].MsgTx().TxOut {
|
||||||
totalSatoshiOut += txOut.Value
|
totalSatoshiOut += txOut.Value
|
||||||
}
|
}
|
||||||
expectedSatoshiOut := calcBlockSubsidy(node.height) + totalFees
|
expectedSatoshiOut := calcBlockSubsidy(node.height) + totalFees
|
||||||
|
|
|
@ -66,8 +66,9 @@ func TestCheckSerializedHeight(t *testing.T) {
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
t.Logf("Running %d tests", len(tests))
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
tx := coinbaseTx.Copy()
|
msgTx := coinbaseTx.Copy()
|
||||||
tx.TxIn[0].SignatureScript = test.sigScript
|
msgTx.TxIn[0].SignatureScript = test.sigScript
|
||||||
|
tx := btcutil.NewTx(msgTx)
|
||||||
|
|
||||||
err := btcchain.TstCheckSerializedHeight(tx, test.wantHeight)
|
err := btcchain.TstCheckSerializedHeight(tx, test.wantHeight)
|
||||||
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue