Add a new behavior flag to provide a dry run.
This commit adds a new behavior flag, BFDryRun which allows the caller to indicate all checks should be performed against the block as normal except it will not modify any state. This is useful to test that a block is valid without actually modifying the current chain or memory state. This commit also adds a few additional checks which were elided before since they are implicitly handled by btcwire. However, with the ability to propose blocks which didn't necessarily come through the btcwire path, these checks need to be enforced in the chain code as well. As a part of adding the checks, three new error codes named ErrBlockTooBig, ErrTooManyTransactions, and ErrTxTooBig have been introduced. Closes #5.
This commit is contained in:
parent
ae51c3e6e0
commit
dbca1d59c3
7 changed files with 138 additions and 36 deletions
|
@ -16,8 +16,11 @@ import (
|
||||||
//
|
//
|
||||||
// The flags modify the behavior of this function as follows:
|
// The flags modify the behavior of this function as follows:
|
||||||
// - BFFastAdd: The somewhat expensive BIP0034 validation is not performed.
|
// - BFFastAdd: The somewhat expensive BIP0034 validation is not performed.
|
||||||
|
// - BFDryRun: The memory chain index will not be pruned and no accept
|
||||||
|
// notification will be sent since the block is not being accepted.
|
||||||
func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) error {
|
func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) error {
|
||||||
fastAdd := flags&BFFastAdd == BFFastAdd
|
fastAdd := flags&BFFastAdd == BFFastAdd
|
||||||
|
dryRun := flags&BFDryRun == BFDryRun
|
||||||
|
|
||||||
// Get a block node for the block previous to this one. Will be nil
|
// Get a block node for the block previous to this one. Will be nil
|
||||||
// if this is the genesis block.
|
// if this is the genesis block.
|
||||||
|
@ -105,7 +108,7 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags)
|
||||||
if !fastAdd {
|
if !fastAdd {
|
||||||
// Reject version 1 blocks once a majority of the network has
|
// Reject version 1 blocks once a majority of the network has
|
||||||
// upgraded. This is part of BIP0034.
|
// upgraded. This is part of BIP0034.
|
||||||
if blockHeader.Version == 1 {
|
if blockHeader.Version < 2 {
|
||||||
if b.isMajorityVersion(2, prevNode,
|
if b.isMajorityVersion(2, prevNode,
|
||||||
b.netParams.BlockV1RejectNumRequired,
|
b.netParams.BlockV1RejectNumRequired,
|
||||||
b.netParams.BlockV1RejectNumToCheck) {
|
b.netParams.BlockV1RejectNumToCheck) {
|
||||||
|
@ -143,10 +146,12 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags)
|
||||||
|
|
||||||
// Prune block nodes which are no longer needed before creating
|
// Prune block nodes which are no longer needed before creating
|
||||||
// a new node.
|
// a new node.
|
||||||
|
if !dryRun {
|
||||||
err = b.pruneBlockNodes()
|
err = b.pruneBlockNodes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create a new block node for the block and add it to the in-memory
|
// Create a new block node for the block and add it to the in-memory
|
||||||
// block chain (could be either a side chain or the main chain).
|
// block chain (could be either a side chain or the main chain).
|
||||||
|
@ -168,7 +173,9 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags)
|
||||||
// Notify the caller that the new block was accepted into the block
|
// Notify the caller that the new block was accepted into the block
|
||||||
// chain. The caller would typically want to react by relaying the
|
// chain. The caller would typically want to react by relaying the
|
||||||
// inventory to other peers.
|
// inventory to other peers.
|
||||||
|
if !dryRun {
|
||||||
b.sendNotification(NTBlockAccepted, block)
|
b.sendNotification(NTBlockAccepted, block)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
52
chain.go
52
chain.go
|
@ -69,7 +69,7 @@ type blockNode struct {
|
||||||
inMainChain bool
|
inMainChain bool
|
||||||
|
|
||||||
// Some fields from block headers to aid in best chain selection.
|
// Some fields from block headers to aid in best chain selection.
|
||||||
version uint32
|
version int32
|
||||||
bits uint32
|
bits uint32
|
||||||
timestamp time.Time
|
timestamp time.Time
|
||||||
}
|
}
|
||||||
|
@ -605,7 +605,7 @@ func (b *BlockChain) pruneBlockNodes() error {
|
||||||
|
|
||||||
// isMajorityVersion determines if a previous number of blocks in the chain
|
// isMajorityVersion determines if a previous number of blocks in the chain
|
||||||
// starting with startNode are at least the minimum passed version.
|
// starting with startNode are at least the minimum passed version.
|
||||||
func (b *BlockChain) isMajorityVersion(minVer uint32, startNode *blockNode, numRequired, numToCheck uint64) bool {
|
func (b *BlockChain) isMajorityVersion(minVer int32, startNode *blockNode, numRequired, numToCheck uint64) bool {
|
||||||
numFound := uint64(0)
|
numFound := uint64(0)
|
||||||
iterNode := startNode
|
iterNode := startNode
|
||||||
for i := uint64(0); i < numToCheck && iterNode != nil; i++ {
|
for i := uint64(0); i < numToCheck && iterNode != nil; i++ {
|
||||||
|
@ -811,7 +811,11 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block) erro
|
||||||
// disconnected must be in reverse order (think of popping them off
|
// disconnected must be in reverse order (think of popping them off
|
||||||
// the end of the chain) and nodes the are being attached must be in forwards
|
// the end of the chain) and nodes the are being attached must be in forwards
|
||||||
// order (think pushing them onto the end of the chain).
|
// order (think pushing them onto the end of the chain).
|
||||||
func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error {
|
//
|
||||||
|
// The flags modify the behavior of this function as follows:
|
||||||
|
// - BFDryRun: Only the checks which ensure the reorganize can be completed
|
||||||
|
// successfully are performed. The chain is not reorganized.
|
||||||
|
func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags BehaviorFlags) error {
|
||||||
// Ensure all of the needed side chain blocks are in the cache.
|
// Ensure all of the needed side chain blocks are in the cache.
|
||||||
for e := attachNodes.Front(); e != nil; e = e.Next() {
|
for e := attachNodes.Front(); e != nil; e = e.Next() {
|
||||||
n := e.Value.(*blockNode)
|
n := e.Value.(*blockNode)
|
||||||
|
@ -842,6 +846,12 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip disconnecting and connecting the blocks when running with the
|
||||||
|
// dry run flag set.
|
||||||
|
if flags&BFDryRun == BFDryRun {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Disconnect blocks from the main chain.
|
// Disconnect blocks from the main chain.
|
||||||
for e := detachNodes.Front(); e != nil; e = e.Next() {
|
for e := detachNodes.Front(); e != nil; e = e.Next() {
|
||||||
n := e.Value.(*blockNode)
|
n := e.Value.(*blockNode)
|
||||||
|
@ -892,8 +902,12 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||||
// The flags modify the behavior of this function as follows:
|
// The flags modify the behavior of this function as follows:
|
||||||
// - BFFastAdd: Avoids the call to checkConnectBlock which does several
|
// - BFFastAdd: Avoids the call to checkConnectBlock which does several
|
||||||
// expensive transaction validation operations.
|
// expensive transaction validation operations.
|
||||||
|
// - BFDryRun: Prevents the block from being connected and avoids modifying the
|
||||||
|
// state of the memory chain index. Also, any log messages related to
|
||||||
|
// modifying the state are avoided.
|
||||||
func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, flags BehaviorFlags) error {
|
func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, flags BehaviorFlags) error {
|
||||||
fastAdd := flags&BFFastAdd == BFFastAdd
|
fastAdd := flags&BFFastAdd == BFFastAdd
|
||||||
|
dryRun := flags&BFDryRun == BFDryRun
|
||||||
|
|
||||||
// We haven't selected a best chain yet or we are extending the main
|
// We haven't selected a best chain yet or we are extending the main
|
||||||
// (best) chain with a new block. This is the most common case.
|
// (best) chain with a new block. This is the most common case.
|
||||||
|
@ -910,6 +924,11 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't connect the block if performing a dry run.
|
||||||
|
if dryRun {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Connect the block to the main chain.
|
// Connect the block to the main chain.
|
||||||
err := b.connectBlock(node, block)
|
err := b.connectBlock(node, block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -932,7 +951,9 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
||||||
// become the main chain, but in either case we need the block stored
|
// become the main chain, but in either case we need the block stored
|
||||||
// for future processing, so add the block to the side chain holding
|
// for future processing, so add the block to the side chain holding
|
||||||
// cache.
|
// cache.
|
||||||
|
if !dryRun {
|
||||||
log.Debugf("Adding block %v to side chain cache", node.hash)
|
log.Debugf("Adding block %v to side chain cache", node.hash)
|
||||||
|
}
|
||||||
b.blockCache[*node.hash] = block
|
b.blockCache[*node.hash] = block
|
||||||
b.index[*node.hash] = node
|
b.index[*node.hash] = node
|
||||||
|
|
||||||
|
@ -940,9 +961,27 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
||||||
node.inMainChain = false
|
node.inMainChain = false
|
||||||
node.parent.children = append(node.parent.children, node)
|
node.parent.children = append(node.parent.children, node)
|
||||||
|
|
||||||
|
// Remove the block from the side chain cache and disconnect it from the
|
||||||
|
// parent node when the function returns when running in dry run mode.
|
||||||
|
if dryRun {
|
||||||
|
defer func() {
|
||||||
|
children := node.parent.children
|
||||||
|
children = removeChildNode(children, node)
|
||||||
|
node.parent.children = children
|
||||||
|
|
||||||
|
delete(b.index, *node.hash)
|
||||||
|
delete(b.blockCache, *node.hash)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
// We're extending (or creating) a side chain, but the cumulative
|
// We're extending (or creating) a side chain, but the cumulative
|
||||||
// work for this new side chain is not enough to make it the new chain.
|
// work for this new side chain is not enough to make it the new chain.
|
||||||
if node.workSum.Cmp(b.bestChain.workSum) <= 0 {
|
if node.workSum.Cmp(b.bestChain.workSum) <= 0 {
|
||||||
|
// Skip Logging info when the dry run flag is set.
|
||||||
|
if dryRun {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Find the fork point.
|
// Find the fork point.
|
||||||
fork := node
|
fork := node
|
||||||
for ; fork.parent != nil; fork = fork.parent {
|
for ; fork.parent != nil; fork = fork.parent {
|
||||||
|
@ -975,8 +1014,11 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
||||||
detachNodes, attachNodes := b.getReorganizeNodes(node)
|
detachNodes, attachNodes := b.getReorganizeNodes(node)
|
||||||
|
|
||||||
// Reorganize the chain.
|
// Reorganize the chain.
|
||||||
log.Infof("REORGANIZE: Block %v is causing a reorganize.", node.hash)
|
if !dryRun {
|
||||||
err := b.reorganizeChain(detachNodes, attachNodes)
|
log.Infof("REORGANIZE: Block %v is causing a reorganize.",
|
||||||
|
node.hash)
|
||||||
|
}
|
||||||
|
err := b.reorganizeChain(detachNodes, attachNodes, flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
15
error.go
15
error.go
|
@ -17,6 +17,10 @@ const (
|
||||||
// exists.
|
// exists.
|
||||||
ErrDuplicateBlock ErrorCode = iota
|
ErrDuplicateBlock ErrorCode = iota
|
||||||
|
|
||||||
|
// ErrBlockTooBig indicates the serialized block size exceeds the
|
||||||
|
// maximum allowed size.
|
||||||
|
ErrBlockTooBig
|
||||||
|
|
||||||
// ErrBlockVersionTooOld indicates the block version is too old and is
|
// ErrBlockVersionTooOld indicates the block version is too old and is
|
||||||
// no longer accepted since the majority of the network has upgraded
|
// no longer accepted since the majority of the network has upgraded
|
||||||
// to a newer version.
|
// to a newer version.
|
||||||
|
@ -67,6 +71,10 @@ const (
|
||||||
// transaction.
|
// transaction.
|
||||||
ErrNoTransactions
|
ErrNoTransactions
|
||||||
|
|
||||||
|
// ErrTooManyTransactions indicates the block has more transactions than
|
||||||
|
// are allowed.
|
||||||
|
ErrTooManyTransactions
|
||||||
|
|
||||||
// ErrNoTxInputs indicates a transaction does not have any inputs. A
|
// ErrNoTxInputs indicates a transaction does not have any inputs. A
|
||||||
// valid transaction must have at least one input.
|
// valid transaction must have at least one input.
|
||||||
ErrNoTxInputs
|
ErrNoTxInputs
|
||||||
|
@ -75,6 +83,10 @@ const (
|
||||||
// valid transaction must have at least one output.
|
// valid transaction must have at least one output.
|
||||||
ErrNoTxOutputs
|
ErrNoTxOutputs
|
||||||
|
|
||||||
|
// ErrTxTooBig indicates a transaction exceeds the maximum allowed size
|
||||||
|
// when serialized.
|
||||||
|
ErrTxTooBig
|
||||||
|
|
||||||
// ErrBadTxOutValue indicates an output value for a transaction is
|
// ErrBadTxOutValue indicates an output value for a transaction is
|
||||||
// invalid in some way such as being out of range.
|
// invalid in some way such as being out of range.
|
||||||
ErrBadTxOutValue
|
ErrBadTxOutValue
|
||||||
|
@ -167,6 +179,7 @@ const (
|
||||||
// Map of ErrorCode values back to their constant names for pretty printing.
|
// Map of ErrorCode values back to their constant names for pretty printing.
|
||||||
var errorCodeStrings = map[ErrorCode]string{
|
var errorCodeStrings = map[ErrorCode]string{
|
||||||
ErrDuplicateBlock: "ErrDuplicateBlock",
|
ErrDuplicateBlock: "ErrDuplicateBlock",
|
||||||
|
ErrBlockTooBig: "ErrBlockTooBig",
|
||||||
ErrBlockVersionTooOld: "ErrBlockVersionTooOld",
|
ErrBlockVersionTooOld: "ErrBlockVersionTooOld",
|
||||||
ErrInvalidTime: "ErrInvalidTime",
|
ErrInvalidTime: "ErrInvalidTime",
|
||||||
ErrTimeTooOld: "ErrTimeTooOld",
|
ErrTimeTooOld: "ErrTimeTooOld",
|
||||||
|
@ -178,8 +191,10 @@ var errorCodeStrings = map[ErrorCode]string{
|
||||||
ErrBadCheckpoint: "ErrBadCheckpoint",
|
ErrBadCheckpoint: "ErrBadCheckpoint",
|
||||||
ErrForkTooOld: "ErrForkTooOld",
|
ErrForkTooOld: "ErrForkTooOld",
|
||||||
ErrNoTransactions: "ErrNoTransactions",
|
ErrNoTransactions: "ErrNoTransactions",
|
||||||
|
ErrTooManyTransactions: "ErrTooManyTransactions",
|
||||||
ErrNoTxInputs: "ErrNoTxInputs",
|
ErrNoTxInputs: "ErrNoTxInputs",
|
||||||
ErrNoTxOutputs: "ErrNoTxOutputs",
|
ErrNoTxOutputs: "ErrNoTxOutputs",
|
||||||
|
ErrTxTooBig: "ErrTxTooBig",
|
||||||
ErrBadTxOutValue: "ErrBadTxOutValue",
|
ErrBadTxOutValue: "ErrBadTxOutValue",
|
||||||
ErrDuplicateTxInputs: "ErrDuplicateTxInputs",
|
ErrDuplicateTxInputs: "ErrDuplicateTxInputs",
|
||||||
ErrBadTxInput: "ErrBadTxInput",
|
ErrBadTxInput: "ErrBadTxInput",
|
||||||
|
|
|
@ -16,6 +16,7 @@ func TestErrorCodeStringer(t *testing.T) {
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{btcchain.ErrDuplicateBlock, "ErrDuplicateBlock"},
|
{btcchain.ErrDuplicateBlock, "ErrDuplicateBlock"},
|
||||||
|
{btcchain.ErrBlockTooBig, "ErrBlockTooBig"},
|
||||||
{btcchain.ErrBlockVersionTooOld, "ErrBlockVersionTooOld"},
|
{btcchain.ErrBlockVersionTooOld, "ErrBlockVersionTooOld"},
|
||||||
{btcchain.ErrInvalidTime, "ErrInvalidTime"},
|
{btcchain.ErrInvalidTime, "ErrInvalidTime"},
|
||||||
{btcchain.ErrTimeTooOld, "ErrTimeTooOld"},
|
{btcchain.ErrTimeTooOld, "ErrTimeTooOld"},
|
||||||
|
@ -27,8 +28,10 @@ func TestErrorCodeStringer(t *testing.T) {
|
||||||
{btcchain.ErrBadCheckpoint, "ErrBadCheckpoint"},
|
{btcchain.ErrBadCheckpoint, "ErrBadCheckpoint"},
|
||||||
{btcchain.ErrForkTooOld, "ErrForkTooOld"},
|
{btcchain.ErrForkTooOld, "ErrForkTooOld"},
|
||||||
{btcchain.ErrNoTransactions, "ErrNoTransactions"},
|
{btcchain.ErrNoTransactions, "ErrNoTransactions"},
|
||||||
|
{btcchain.ErrTooManyTransactions, "ErrTooManyTransactions"},
|
||||||
{btcchain.ErrNoTxInputs, "ErrNoTxInputs"},
|
{btcchain.ErrNoTxInputs, "ErrNoTxInputs"},
|
||||||
{btcchain.ErrNoTxOutputs, "ErrNoTxOutputs"},
|
{btcchain.ErrNoTxOutputs, "ErrNoTxOutputs"},
|
||||||
|
{btcchain.ErrTxTooBig, "ErrTxTooBig"},
|
||||||
{btcchain.ErrBadTxOutValue, "ErrBadTxOutValue"},
|
{btcchain.ErrBadTxOutValue, "ErrBadTxOutValue"},
|
||||||
{btcchain.ErrDuplicateTxInputs, "ErrDuplicateTxInputs"},
|
{btcchain.ErrDuplicateTxInputs, "ErrDuplicateTxInputs"},
|
||||||
{btcchain.ErrBadTxInput, "ErrBadTxInput"},
|
{btcchain.ErrBadTxInput, "ErrBadTxInput"},
|
||||||
|
|
24
process.go
24
process.go
|
@ -26,6 +26,11 @@ const (
|
||||||
// not be performed.
|
// not be performed.
|
||||||
BFNoPoWCheck
|
BFNoPoWCheck
|
||||||
|
|
||||||
|
// BFDryRun may be set to indicate the block should not modify the chain
|
||||||
|
// or memory chain index. This is useful to test that a block is valid
|
||||||
|
// without modifying the current state.
|
||||||
|
BFDryRun
|
||||||
|
|
||||||
// BFNone is a convenience value to specifically indicate no flags.
|
// BFNone is a convenience value to specifically indicate no flags.
|
||||||
BFNone BehaviorFlags = 0
|
BFNone BehaviorFlags = 0
|
||||||
)
|
)
|
||||||
|
@ -110,6 +115,7 @@ func (b *BlockChain) processOrphans(hash *btcwire.ShaHash, flags BehaviorFlags)
|
||||||
// when the error is nil.
|
// when the error is nil.
|
||||||
func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bool, error) {
|
func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bool, error) {
|
||||||
fastAdd := flags&BFFastAdd == BFFastAdd
|
fastAdd := flags&BFFastAdd == BFFastAdd
|
||||||
|
dryRun := flags&BFDryRun == BFDryRun
|
||||||
|
|
||||||
blockHash, err := block.Sha()
|
blockHash, err := block.Sha()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -179,9 +185,11 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo
|
||||||
// Handle orphan blocks.
|
// Handle orphan blocks.
|
||||||
prevHash := &blockHeader.PrevBlock
|
prevHash := &blockHeader.PrevBlock
|
||||||
if !prevHash.IsEqual(zeroHash) && !b.blockExists(prevHash) {
|
if !prevHash.IsEqual(zeroHash) && !b.blockExists(prevHash) {
|
||||||
log.Infof("Adding orphan block %v with parent %v", blockHash,
|
if !dryRun {
|
||||||
prevHash)
|
log.Infof("Adding orphan block %v with parent %v",
|
||||||
|
blockHash, prevHash)
|
||||||
b.addOrphanBlock(block)
|
b.addOrphanBlock(block)
|
||||||
|
}
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -193,14 +201,18 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accept any orphan blocks that depend on this block (they are no
|
// Don't process any orphans or log when the dry run flag is set.
|
||||||
// longer orphans) and repeat for those accepted blocks until there are
|
if !dryRun {
|
||||||
// no more.
|
// Accept any orphan blocks that depend on this block (they are
|
||||||
err = b.processOrphans(blockHash, flags)
|
// no longer orphans) and repeat for those accepted blocks until
|
||||||
|
// there are no more.
|
||||||
|
err := b.processOrphans(blockHash, flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Accepted block %v", blockHash)
|
log.Debugf("Accepted block %v", blockHash)
|
||||||
|
}
|
||||||
|
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,9 @@ func connectTransactions(txStore TxStore, block *btcutil.Block) error {
|
||||||
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 {
|
||||||
|
if originIndex > uint32(len(originTx.Spent)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
originTx.Spent[originIndex] = true
|
originTx.Spent[originIndex] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,6 +85,9 @@ func disconnectTransactions(txStore TxStore, block *btcutil.Block) error {
|
||||||
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 {
|
||||||
|
if originIndex > uint32(len(originTx.Spent)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
originTx.Spent[originIndex] = false
|
originTx.Spent[originIndex] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
47
validate.go
47
validate.go
|
@ -189,10 +189,14 @@ func CheckTransactionSanity(tx *btcutil.Tx) error {
|
||||||
return ruleError(ErrNoTxOutputs, "transaction has no outputs")
|
return ruleError(ErrNoTxOutputs, "transaction has no outputs")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: bitcoind does size limits checking here, but the size limits
|
// A transaction must not exceed the maximum allowed block payload when
|
||||||
// have already been checked by btcwire for incoming transactions.
|
// serialized.
|
||||||
// Also, btcwire checks the size limits on send too, so there is no need
|
serializedTxSize := tx.MsgTx().SerializeSize()
|
||||||
// to double check it here.
|
if serializedTxSize > btcwire.MaxBlockPayload {
|
||||||
|
str := fmt.Sprintf("serialized transaction is too big - got "+
|
||||||
|
"%d, max %d", serializedTxSize, btcwire.MaxBlockPayload)
|
||||||
|
return ruleError(ErrTxTooBig, str)
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure the transaction amounts are in range. Each transaction
|
// Ensure the transaction amounts are in range. Each transaction
|
||||||
// output must not be negative or more than the max allowed per
|
// output must not be negative or more than the max allowed per
|
||||||
|
@ -414,10 +418,29 @@ func CountP2SHSigOps(tx *btcutil.Tx, isCoinBaseTx bool, txStore TxStore) (int, e
|
||||||
// The flags do not modify the behavior of this function directly, however they
|
// The flags do not modify the behavior of this function directly, however they
|
||||||
// are needed to pass along to checkProofOfWork.
|
// are needed to pass along to checkProofOfWork.
|
||||||
func checkBlockSanity(block *btcutil.Block, powLimit *big.Int, flags BehaviorFlags) error {
|
func checkBlockSanity(block *btcutil.Block, powLimit *big.Int, flags BehaviorFlags) error {
|
||||||
// NOTE: bitcoind does size limits checking here, but the size limits
|
// A block must have at least one transaction.
|
||||||
// have already been checked by btcwire for incoming blocks. Also,
|
msgBlock := block.MsgBlock()
|
||||||
// btcwire checks the size limits on send too, so there is no need
|
numTx := len(msgBlock.Transactions)
|
||||||
// to double check it here.
|
if numTx == 0 {
|
||||||
|
return ruleError(ErrNoTransactions, "block does not contain "+
|
||||||
|
"any transactions")
|
||||||
|
}
|
||||||
|
|
||||||
|
// A block must not have more transactions than the max block payload.
|
||||||
|
if numTx > btcwire.MaxBlockPayload {
|
||||||
|
str := fmt.Sprintf("block contains too many transactions - "+
|
||||||
|
"got %d, max %d", numTx, btcwire.MaxBlockPayload)
|
||||||
|
return ruleError(ErrTooManyTransactions, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A block must not exceed the maximum allowed block payload when
|
||||||
|
// serialized.
|
||||||
|
serializedSize := msgBlock.SerializeSize()
|
||||||
|
if serializedSize > btcwire.MaxBlockPayload {
|
||||||
|
str := fmt.Sprintf("serialized block is too big - got %d, "+
|
||||||
|
"max %d", serializedSize, btcwire.MaxBlockPayload)
|
||||||
|
return ruleError(ErrBlockTooBig, str)
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure the proof of work bits in the block header is in min/max range
|
// Ensure the proof of work bits in the block header is in min/max range
|
||||||
// and the block hash is less than the target value described by the
|
// and the block hash is less than the target value described by the
|
||||||
|
@ -446,14 +469,8 @@ func checkBlockSanity(block *btcutil.Block, powLimit *big.Int, flags BehaviorFla
|
||||||
return ruleError(ErrTimeTooNew, str)
|
return ruleError(ErrTimeTooNew, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A block must have at least one transaction.
|
|
||||||
transactions := block.Transactions()
|
|
||||||
if len(transactions) == 0 {
|
|
||||||
return ruleError(ErrNoTransactions, "block does not contain "+
|
|
||||||
"any transactions")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The first transaction in a block must be a coinbase.
|
// The first transaction in a block must be a coinbase.
|
||||||
|
transactions := block.Transactions()
|
||||||
if !IsCoinBase(transactions[0]) {
|
if !IsCoinBase(transactions[0]) {
|
||||||
return ruleError(ErrFirstTxNotCoinbase, "first transaction in "+
|
return ruleError(ErrFirstTxNotCoinbase, "first transaction in "+
|
||||||
"block is not a coinbase")
|
"block is not a coinbase")
|
||||||
|
|
Loading…
Reference in a new issue