Expose a transaction store and related functions.
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.
This commit is contained in:
parent
2f743b4821
commit
fc69776371
3 changed files with 140 additions and 85 deletions
21
scriptval.go
21
scriptval.go
|
@ -60,10 +60,11 @@ func validateTxIn(txInIdx int, txin *btcwire.TxIn, txSha *btcwire.ShaHash, tx *b
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateAllTxIn validates the scripts for all of the passed transaction
|
// validateAllTxIn validates the scripts for the passed transaction using
|
||||||
// inputs using multiple goroutines.
|
// multiple goroutines.
|
||||||
func validateAllTxIn(txsha *btcwire.ShaHash, txValidator *btcwire.MsgTx, timestamp time.Time, job []*btcwire.TxIn, txStore map[btcwire.ShaHash]*txData) (err error) {
|
func validateAllTxIn(tx *btcwire.MsgTx, txHash *btcwire.ShaHash, timestamp time.Time, txStore TxStore) (err error) {
|
||||||
c := make(chan txValidate)
|
c := make(chan txValidate)
|
||||||
|
job := tx.TxIn
|
||||||
resultErrors := make([]error, len(job))
|
resultErrors := make([]error, len(job))
|
||||||
|
|
||||||
var currentItem int
|
var currentItem int
|
||||||
|
@ -71,7 +72,7 @@ func validateAllTxIn(txsha *btcwire.ShaHash, txValidator *btcwire.MsgTx, timesta
|
||||||
|
|
||||||
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",
|
||||||
txsha, currentItem, len(job))
|
txHash, 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
|
||||||
|
@ -84,10 +85,10 @@ func validateAllTxIn(txsha *btcwire.ShaHash, txValidator *btcwire.MsgTx, timesta
|
||||||
fmt.Printf("obj not found in txStore %v",
|
fmt.Printf("obj not found in txStore %v",
|
||||||
originTxSha)
|
originTxSha)
|
||||||
}
|
}
|
||||||
originTx = txInfo.tx
|
originTx = txInfo.Tx
|
||||||
}
|
}
|
||||||
err := validateTxIn(txInIdx, job[txInIdx], txsha, txValidator,
|
err := validateTxIn(txInIdx, job[txInIdx], txHash, tx, timestamp,
|
||||||
timestamp, originTx)
|
originTx)
|
||||||
r := txValidate{txInIdx, err}
|
r := txValidate{txInIdx, err}
|
||||||
c <- r
|
c <- r
|
||||||
}
|
}
|
||||||
|
@ -113,7 +114,7 @@ func validateAllTxIn(txsha *btcwire.ShaHash, txValidator *btcwire.MsgTx, timesta
|
||||||
}
|
}
|
||||||
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", txsha, i, resultErrors[i])
|
log.Warnf("tx %v failed input %v, err %v", txHash, i, resultErrors[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -121,11 +122,11 @@ func validateAllTxIn(txsha *btcwire.ShaHash, txValidator *btcwire.MsgTx, timesta
|
||||||
|
|
||||||
// checkBlockScripts executes and validates the scripts for all transactions in
|
// checkBlockScripts executes and validates the scripts for all transactions in
|
||||||
// the passed block.
|
// the passed block.
|
||||||
func checkBlockScripts(block *btcutil.Block, txStore map[btcwire.ShaHash]*txData) error {
|
func checkBlockScripts(block *btcutil.Block, txStore TxStore) error {
|
||||||
timestamp := block.MsgBlock().Header.Timestamp
|
timestamp := block.MsgBlock().Header.Timestamp
|
||||||
for i, tx := range block.MsgBlock().Transactions {
|
for i, tx := range block.MsgBlock().Transactions {
|
||||||
txHash, _ := block.TxSha(i)
|
txHash, _ := block.TxSha(i)
|
||||||
err := validateAllTxIn(txHash, tx, timestamp, tx.TxIn, txStore)
|
err := validateAllTxIn(tx, txHash, timestamp, txStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
165
txlookup.go
165
txlookup.go
|
@ -11,20 +11,26 @@ import (
|
||||||
"github.com/conformal/btcwire"
|
"github.com/conformal/btcwire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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 *btcwire.MsgTx
|
||||||
hash *btcwire.ShaHash
|
Hash *btcwire.ShaHash
|
||||||
blockHeight int64
|
BlockHeight int64
|
||||||
spent []bool
|
Spent []bool
|
||||||
err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TxStore is used to store transactions needed by other transactions for things
|
||||||
|
// such as script validation and double spend prevention. This also allows the
|
||||||
|
// transaction data to be treated as a view since it can contain the information
|
||||||
|
// from the point-of-view of different points in the chain.
|
||||||
|
type TxStore map[btcwire.ShaHash]*TxData
|
||||||
|
|
||||||
// connectTransactions updates the passed map by applying transaction and
|
// connectTransactions updates the passed map by applying transaction and
|
||||||
// spend information for all the transactions in the passed block. Only
|
// spend information for all the transactions in the passed block. Only
|
||||||
// transactions in the passed map are updated.
|
// transactions in the passed map are updated.
|
||||||
func connectTransactions(txStore map[btcwire.ShaHash]*txData, 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 i, tx := range block.MsgBlock().Transactions {
|
||||||
|
@ -36,10 +42,10 @@ func connectTransactions(txStore map[btcwire.ShaHash]*txData, block *btcutil.Blo
|
||||||
// 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 {
|
if txD, exists := txStore[*txHash]; 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(tx.TxOut))
|
||||||
txD.err = nil
|
txD.Err = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spend the origin transaction output.
|
// Spend the origin transaction output.
|
||||||
|
@ -47,7 +53,7 @@ func connectTransactions(txStore map[btcwire.ShaHash]*txData, block *btcutil.Blo
|
||||||
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 {
|
||||||
originTx.spent[originIndex] = true
|
originTx.Spent[originIndex] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +64,7 @@ func connectTransactions(txStore map[btcwire.ShaHash]*txData, block *btcutil.Blo
|
||||||
// disconnectTransactions updates the passed map by undoing transaction and
|
// disconnectTransactions updates the passed map by undoing transaction and
|
||||||
// spend information for all transactions in the passed block. Only
|
// spend information for all transactions in the passed block. Only
|
||||||
// transactions in the passed map are updated.
|
// transactions in the passed map are updated.
|
||||||
func disconnectTransactions(txStore map[btcwire.ShaHash]*txData, 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 i, tx := range block.MsgBlock().Transactions {
|
||||||
|
@ -73,10 +79,10 @@ func disconnectTransactions(txStore map[btcwire.ShaHash]*txData, block *btcutil.
|
||||||
// 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[*txHash]; exists {
|
||||||
txD.tx = nil
|
txD.Tx = nil
|
||||||
txD.blockHeight = 0
|
txD.BlockHeight = 0
|
||||||
txD.spent = nil
|
txD.Spent = nil
|
||||||
txD.err = btcdb.TxShaMissing
|
txD.Err = btcdb.TxShaMissing
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unspend the origin transaction output.
|
// Unspend the origin transaction output.
|
||||||
|
@ -84,8 +90,8 @@ func disconnectTransactions(txStore map[btcwire.ShaHash]*txData, block *btcutil.
|
||||||
originHash := &txIn.PreviousOutpoint.Hash
|
originHash := &txIn.PreviousOutpoint.Hash
|
||||||
originIndex := txIn.PreviousOutpoint.Index
|
originIndex := txIn.PreviousOutpoint.Index
|
||||||
originTx, exists := txStore[*originHash]
|
originTx, exists := txStore[*originHash]
|
||||||
if exists && originTx.tx != nil && originTx.err == nil {
|
if exists && originTx.Tx != nil && originTx.Err == nil {
|
||||||
originTx.spent[originIndex] = false
|
originTx.Spent[originIndex] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,34 +100,20 @@ func disconnectTransactions(txStore map[btcwire.ShaHash]*txData, block *btcutil.
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchTxList fetches transaction data about the provided list of transactions
|
// fetchTxList fetches transaction data about the provided list of transactions
|
||||||
// from the point of view of the given node. For example, a given node might
|
// from the point of view of the end of the main chain.
|
||||||
// be down a side chain where a transaction hasn't been spent from its point of
|
func fetchTxListMain(db btcdb.Db, txList []*btcwire.ShaHash) TxStore {
|
||||||
// view even though it might have been spent in the main chain (or another side
|
|
||||||
// chain). Another scenario is where a transaction exists from the point of
|
|
||||||
// view of the main chain, but doesn't exist in a side chain that branches
|
|
||||||
// before the block that contains the transaction on the main chain.
|
|
||||||
func (b *BlockChain) fetchTxList(node *blockNode, txList []*btcwire.ShaHash) (map[btcwire.ShaHash]*txData, error) {
|
|
||||||
// Get the previous block node. This function is used over simply
|
|
||||||
// accessing node.parent directly as it will dynamically create previous
|
|
||||||
// block nodes as needed. This helps allow only the pieces of the chain
|
|
||||||
// that are needed to remain in memory.
|
|
||||||
prevNode, err := b.getPrevNodeFromNode(node)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// The transaction store map needs to have an entry for every requested
|
// The transaction store map needs to have an entry for every requested
|
||||||
// transaction. By default, all the transactions are marked as missing.
|
// transaction. By default, all the transactions are marked as missing.
|
||||||
// Each entry will be filled in with the appropriate data below.
|
// Each entry will be filled in with the appropriate data below.
|
||||||
txStore := make(map[btcwire.ShaHash]*txData)
|
txStore := make(TxStore)
|
||||||
for _, hash := range txList {
|
for _, hash := range txList {
|
||||||
txStore[*hash] = &txData{hash: hash, err: btcdb.TxShaMissing}
|
txStore[*hash] = &TxData{Hash: hash, Err: btcdb.TxShaMissing}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask the database (main chain) for the list of transactions. This
|
// Ask the database (main chain) for the list of transactions. This
|
||||||
// will return the information from the point of view of the end of the
|
// will return the information from the point of view of the end of the
|
||||||
// main chain.
|
// main chain.
|
||||||
txReplyList := b.db.FetchTxByShaList(txList)
|
txReplyList := db.FetchTxByShaList(txList)
|
||||||
for _, txReply := range txReplyList {
|
for _, txReply := range txReplyList {
|
||||||
// Lookup the existing results entry to modify. Skip
|
// Lookup the existing results entry to modify. Skip
|
||||||
// this reply if there is no corresponding entry in
|
// this reply if there is no corresponding entry in
|
||||||
|
@ -137,19 +129,42 @@ func (b *BlockChain) fetchTxList(node *blockNode, txList []*btcwire.ShaHash) (ma
|
||||||
// this code modifies the data. A bug caused by modifying the
|
// this code modifies the data. A bug caused by modifying the
|
||||||
// cached data would likely be difficult to track down and could
|
// cached data would likely be difficult to track down and could
|
||||||
// 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 = 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, we have the transaction data from the point of view
|
return txStore
|
||||||
// of the end of the main (best) chain. If we haven't selected a best
|
}
|
||||||
// chain yet or we are extending the main (best) chain with a new block,
|
|
||||||
// everything is accurate, so return the results now.
|
// fetchTxList fetches transaction data about the provided list of transactions
|
||||||
|
// from the point of view of the given node. For example, a given node might
|
||||||
|
// be down a side chain where a transaction hasn't been spent from its point of
|
||||||
|
// view even though it might have been spent in the main chain (or another side
|
||||||
|
// chain). Another scenario is where a transaction exists from the point of
|
||||||
|
// view of the main chain, but doesn't exist in a side chain that branches
|
||||||
|
// before the block that contains the transaction on the main chain.
|
||||||
|
func (b *BlockChain) fetchTxList(node *blockNode, txList []*btcwire.ShaHash) (TxStore, error) {
|
||||||
|
// Get the previous block node. This function is used over simply
|
||||||
|
// accessing node.parent directly as it will dynamically create previous
|
||||||
|
// block nodes as needed. This helps allow only the pieces of the chain
|
||||||
|
// that are needed to remain in memory.
|
||||||
|
prevNode, err := b.getPrevNodeFromNode(node)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the requested list from the point of view of the end of the
|
||||||
|
// main (best) chain.
|
||||||
|
txStore := fetchTxListMain(b.db, txList)
|
||||||
|
|
||||||
|
// If we haven't selected a best chain yet or we are extending the main
|
||||||
|
// (best) chain with a new block, everything is accurate, so return the
|
||||||
|
// results now.
|
||||||
if b.bestChain == nil || (prevNode != nil && prevNode.hash.IsEqual(b.bestChain.hash)) {
|
if b.bestChain == nil || (prevNode != nil && prevNode.hash.IsEqual(b.bestChain.hash)) {
|
||||||
return txStore, nil
|
return txStore, nil
|
||||||
}
|
}
|
||||||
|
@ -200,7 +215,7 @@ func (b *BlockChain) fetchTxList(node *blockNode, txList []*btcwire.ShaHash) (ma
|
||||||
// fetchInputTransactions fetches the input transactions referenced by the
|
// fetchInputTransactions fetches the input transactions referenced by the
|
||||||
// transactions in the given block from its point of view. See fetchTxList
|
// transactions in the given block from its point of view. See fetchTxList
|
||||||
// for more details on what the point of view entails.
|
// for more details on what the point of view entails.
|
||||||
func (b *BlockChain) fetchInputTransactions(node *blockNode, block *btcutil.Block) (map[btcwire.ShaHash]*txData, error) {
|
func (b *BlockChain) fetchInputTransactions(node *blockNode, block *btcutil.Block) (TxStore, error) {
|
||||||
// Build a map of in-flight transactions because some of the inputs in
|
// Build a map of in-flight transactions because some of the inputs in
|
||||||
// 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.
|
||||||
|
@ -229,13 +244,13 @@ func (b *BlockChain) fetchInputTransactions(node *blockNode, block *btcutil.Bloc
|
||||||
// Loop through all of the transaction inputs (except for the coinbase
|
// Loop through all of the transaction inputs (except for the coinbase
|
||||||
// which has no inputs) collecting them into lists of what is needed and
|
// which has no inputs) collecting them into lists of what is needed and
|
||||||
// what is already known (in-flight).
|
// what is already known (in-flight).
|
||||||
txStore := make(map[btcwire.ShaHash]*txData)
|
txStore := make(TxStore)
|
||||||
for i, tx := range transactions[1:] {
|
for i, tx := range transactions[1:] {
|
||||||
for _, txIn := range tx.TxIn {
|
for _, txIn := range tx.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
|
||||||
txD := &txData{hash: originHash, err: btcdb.TxShaMissing}
|
txD := &TxData{Hash: originHash, Err: btcdb.TxShaMissing}
|
||||||
txStore[*originHash] = txD
|
txStore[*originHash] = txD
|
||||||
|
|
||||||
// It is acceptable for a transaction input to reference
|
// It is acceptable for a transaction input to reference
|
||||||
|
@ -252,10 +267,10 @@ func (b *BlockChain) fetchInputTransactions(node *blockNode, block *btcutil.Bloc
|
||||||
i >= inFlightIndex {
|
i >= inFlightIndex {
|
||||||
|
|
||||||
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.TxOut))
|
||||||
txD.err = nil
|
txD.Err = nil
|
||||||
} else {
|
} else {
|
||||||
txNeededList = append(txNeededList, originHash)
|
txNeededList = append(txNeededList, originHash)
|
||||||
}
|
}
|
||||||
|
@ -271,7 +286,45 @@ func (b *BlockChain) fetchInputTransactions(node *blockNode, block *btcutil.Bloc
|
||||||
// Merge the results of the requested transactions and the in-flight
|
// Merge the results of the requested transactions and the in-flight
|
||||||
// transactions.
|
// transactions.
|
||||||
for _, txD := range txNeededStore {
|
for _, txD := range txNeededStore {
|
||||||
txStore[*txD.hash] = txD
|
txStore[*txD.Hash] = txD
|
||||||
|
}
|
||||||
|
|
||||||
|
return txStore, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchTransactionStore fetches the input transactions referenced by the
|
||||||
|
// 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
|
||||||
|
// examined for duplicate transactions.
|
||||||
|
func (b *BlockChain) FetchTransactionStore(tx *btcwire.MsgTx) (TxStore, error) {
|
||||||
|
txHash, err := tx.TxSha()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create list big
|
||||||
|
txNeededList := make([]*btcwire.ShaHash, 0, len(tx.TxIn)+1)
|
||||||
|
txNeededList = append(txNeededList, &txHash)
|
||||||
|
|
||||||
|
// Loop through all of the transaction inputs collecting them into lists of what is needed and
|
||||||
|
// what is already known (in-flight).
|
||||||
|
txStore := make(TxStore)
|
||||||
|
for _, txIn := range tx.TxIn {
|
||||||
|
// Add an entry to the transaction store for the needed
|
||||||
|
// transaction with it set to missing by default.
|
||||||
|
originHash := &txIn.PreviousOutpoint.Hash
|
||||||
|
txD := &TxData{Hash: originHash, Err: btcdb.TxShaMissing}
|
||||||
|
txStore[*originHash] = txD
|
||||||
|
txNeededList = append(txNeededList, originHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request the input transactions from the point of view of the node.
|
||||||
|
txNeededStore := fetchTxListMain(b.db, txNeededList)
|
||||||
|
|
||||||
|
// Merge the results of the requested transactions and the in-flight
|
||||||
|
// transactions.
|
||||||
|
for _, txD := range txNeededStore {
|
||||||
|
txStore[*txD.Hash] = txD
|
||||||
}
|
}
|
||||||
|
|
||||||
return txStore, nil
|
return txStore, nil
|
||||||
|
|
39
validate.go
39
validate.go
|
@ -327,7 +327,7 @@ 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 map[btcwire.ShaHash]*txData) (int, error) {
|
func countP2SHSigOps(msgTx *btcwire.MsgTx, 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
|
||||||
|
@ -346,7 +346,7 @@ func countP2SHSigOps(msgTx *btcwire.MsgTx, isCoinBaseTx bool, txStore map[btcwir
|
||||||
// Ensure the referenced input transaction is available.
|
// Ensure the referenced input transaction is available.
|
||||||
txInHash := &txIn.PreviousOutpoint.Hash
|
txInHash := &txIn.PreviousOutpoint.Hash
|
||||||
originTx, exists := txStore[*txInHash]
|
originTx, exists := txStore[*txInHash]
|
||||||
if !exists || originTx.err != nil || originTx.tx == nil {
|
if !exists || originTx.Err != nil || originTx.Tx == nil {
|
||||||
return 0, fmt.Errorf("unable to find input transaction "+
|
return 0, fmt.Errorf("unable to find input transaction "+
|
||||||
"%v referenced from transaction %v", txInHash,
|
"%v referenced from transaction %v", txInHash,
|
||||||
txHash)
|
txHash)
|
||||||
|
@ -355,7 +355,7 @@ func countP2SHSigOps(msgTx *btcwire.MsgTx, isCoinBaseTx bool, txStore map[btcwir
|
||||||
// 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(originTx.Tx.TxOut)) {
|
||||||
return 0, fmt.Errorf("out of bounds input index %d in "+
|
return 0, fmt.Errorf("out of bounds input index %d in "+
|
||||||
"transaction %v referenced from transaction %v",
|
"transaction %v referenced from transaction %v",
|
||||||
originTxIndex, txInHash, txHash)
|
originTxIndex, txInHash, txHash)
|
||||||
|
@ -363,7 +363,7 @@ func countP2SHSigOps(msgTx *btcwire.MsgTx, isCoinBaseTx bool, txStore map[btcwir
|
||||||
|
|
||||||
// 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 := originTx.Tx.TxOut[originTxIndex].PkScript
|
||||||
if !btcscript.IsPayToScriptHash(pkScript) {
|
if !btcscript.IsPayToScriptHash(pkScript) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -519,10 +519,11 @@ func checkSerializedHeight(coinbaseTx *btcwire.MsgTx, wantHeight int64) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isTransactionSpent returns whether or not the provided transaction is fully
|
// isTransactionSpent returns whether or not the provided transaction data
|
||||||
// spent. A fully spent transaction is one where all outputs have been spent.
|
// describes a fully spent transaction. A fully spent transaction is one where
|
||||||
func isTransactionSpent(tx *txData) bool {
|
// all outputs have been spent.
|
||||||
for _, isOutputSpent := range tx.spent {
|
func isTransactionSpent(txD *TxData) bool {
|
||||||
|
for _, isOutputSpent := range txD.Spent {
|
||||||
if !isOutputSpent {
|
if !isOutputSpent {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -552,7 +553,7 @@ func (b *BlockChain) checkBIP0030(node *blockNode, block *btcutil.Block) error {
|
||||||
|
|
||||||
// Examine the resulting data about the requested transactions.
|
// Examine the resulting data about the requested transactions.
|
||||||
for _, txD := range txResults {
|
for _, txD := range txResults {
|
||||||
switch txD.err {
|
switch txD.Err {
|
||||||
// A duplicate transaction was not found. This is the most
|
// A duplicate transaction was not found. This is the most
|
||||||
// common case.
|
// common case.
|
||||||
case btcdb.TxShaMissing:
|
case btcdb.TxShaMissing:
|
||||||
|
@ -564,14 +565,14 @@ func (b *BlockChain) checkBIP0030(node *blockNode, block *btcutil.Block) error {
|
||||||
if !isTransactionSpent(txD) {
|
if !isTransactionSpent(txD) {
|
||||||
str := fmt.Sprintf("tried to overwrite "+
|
str := fmt.Sprintf("tried to overwrite "+
|
||||||
"transaction %v at block height %d "+
|
"transaction %v at block height %d "+
|
||||||
"that is not fully spent", txD.hash,
|
"that is not fully spent", txD.Hash,
|
||||||
txD.blockHeight)
|
txD.BlockHeight)
|
||||||
return RuleError(str)
|
return RuleError(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some other unexpected error occurred. Return it now.
|
// Some other unexpected error occurred. Return it now.
|
||||||
default:
|
default:
|
||||||
return txD.err
|
return txD.Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,7 +587,7 @@ 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 map[btcwire.ShaHash]*txData) (int64, error) {
|
func checkTransactionInputs(tx *btcwire.MsgTx, 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
|
||||||
|
@ -611,8 +612,8 @@ func checkTransactionInputs(tx *btcwire.MsgTx, txHeight int64, txStore map[btcwi
|
||||||
|
|
||||||
// Ensure the transaction is not spending coins which have not
|
// Ensure the transaction is not spending coins which have not
|
||||||
// yet reached the required coinbase maturity.
|
// yet reached the required coinbase maturity.
|
||||||
if isCoinBase(originTx.tx) {
|
if isCoinBase(originTx.Tx) {
|
||||||
originHeight := originTx.blockHeight
|
originHeight := originTx.BlockHeight
|
||||||
blocksSincePrev := txHeight - originHeight
|
blocksSincePrev := txHeight - originHeight
|
||||||
if blocksSincePrev < coinbaseMaturity {
|
if blocksSincePrev < coinbaseMaturity {
|
||||||
str := fmt.Sprintf("tried to spend coinbase "+
|
str := fmt.Sprintf("tried to spend coinbase "+
|
||||||
|
@ -626,12 +627,12 @@ func checkTransactionInputs(tx *btcwire.MsgTx, txHeight int64, txStore map[btcwi
|
||||||
|
|
||||||
// Ensure the transaction is not double spending coins.
|
// Ensure the transaction is not double spending coins.
|
||||||
originTxIndex := txIn.PreviousOutpoint.Index
|
originTxIndex := txIn.PreviousOutpoint.Index
|
||||||
if originTxIndex >= uint32(len(originTx.spent)) {
|
if originTxIndex >= uint32(len(originTx.Spent)) {
|
||||||
return 0, fmt.Errorf("out of bounds input index %d in "+
|
return 0, fmt.Errorf("out of bounds input index %d in "+
|
||||||
"transaction %v referenced from transaction %v",
|
"transaction %v referenced from transaction %v",
|
||||||
originTxIndex, txInHash, txHash)
|
originTxIndex, txInHash, txHash)
|
||||||
}
|
}
|
||||||
if originTx.spent[originTxIndex] {
|
if originTx.Spent[originTxIndex] {
|
||||||
str := fmt.Sprintf("transaction %v tried to double "+
|
str := fmt.Sprintf("transaction %v tried to double "+
|
||||||
"spend coins from transaction %v", txHash,
|
"spend coins from transaction %v", txHash,
|
||||||
txInHash)
|
txInHash)
|
||||||
|
@ -644,7 +645,7 @@ func checkTransactionInputs(tx *btcwire.MsgTx, txHeight int64, txStore map[btcwi
|
||||||
// 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.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)
|
||||||
|
@ -671,7 +672,7 @@ func checkTransactionInputs(tx *btcwire.MsgTx, txHeight int64, txStore map[btcwi
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark the referenced output as spent.
|
// Mark the referenced output as spent.
|
||||||
originTx.spent[originTxIndex] = true
|
originTx.Spent[originTxIndex] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the total output amount for this transaction. It is safe
|
// Calculate the total output amount for this transaction. It is safe
|
||||||
|
|
Loading…
Reference in a new issue