[DEV-34] Modified blockindex.go to account for multiple previous hashes and changed accept.go to reflect those changes.

This commit is contained in:
Stas Boutenko 2018-06-21 13:25:40 +03:00
parent ab75fcf5fb
commit 4260d37158
2 changed files with 73 additions and 66 deletions

View File

@ -28,7 +28,7 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags)
numPrevHashes := prevHeader.NumPrevBlocks
prevHashes := prevHeader.PrevBlocks
var isMainChain bool
nodes := make([]blockNode, numPrevHashes)
for i := byte(0); i < numPrevHashes; i++ {
prevHash := prevHashes[i]
node := b.index.LookupNode(&prevHash)
@ -40,56 +40,55 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags)
return false, ruleError(ErrInvalidAncestorBlock, str)
}
blockHeight := node.height + 1
block.SetHeight(blockHeight)
nodes = append(nodes, *node)
}
// The block must pass all of the validation rules which depend on the
// position of the block within the block chain.
err := b.checkBlockContext(block, node, flags)
if err != nil {
return false, err
}
firstNode := nodes[0]
blockHeight := firstNode.height + 1
block.SetHeight(blockHeight)
// Insert the block into the database if it's not already there. Even
// though it is possible the block will ultimately fail to connect, it
// has already passed all proof-of-work and validity tests which means
// it would be prohibitively expensive for an attacker to fill up the
// disk with a bunch of blocks that fail to connect. This is necessary
// since it allows block download to be decoupled from the much more
// expensive connection logic. It also has some other nice properties
// such as making blocks that never become part of the main chain or
// blocks that fail to connect available for further analysis.
err = b.db.Update(func(dbTx database.Tx) error {
return dbStoreBlock(dbTx, block)
})
if err != nil {
return false, err
}
// The block must pass all of the validation rules which depend on the
// position of the block within the block chain.
err := b.checkBlockContext(block, &firstNode, flags)
if err != nil {
return false, err
}
// Create a new block node for the block and add it to the node index. Even
// if the block ultimately gets connected to the main chain, it starts out
// on a side chain.
blockHeader := &block.MsgBlock().Header
newNode := newBlockNode(blockHeader, node)
newNode.status = statusDataStored
// Insert the block into the database if it's not already there. Even
// though it is possible the block will ultimately fail to connect, it
// has already passed all proof-of-work and validity tests which means
// it would be prohibitively expensive for an attacker to fill up the
// disk with a bunch of blocks that fail to connect. This is necessary
// since it allows block download to be decoupled from the much more
// expensive connection logic. It also has some other nice properties
// such as making blocks that never become part of the main chain or
// blocks that fail to connect available for further analysis.
err = b.db.Update(func(dbTx database.Tx) error {
return dbStoreBlock(dbTx, block)
})
if err != nil {
return false, err
}
b.index.AddNode(newNode)
err = b.index.flushToDB()
if err != nil {
return false, err
}
// Create a new block node for the block and add it to the node index. Even
// if the block ultimately gets connected to the main chain, it starts out
// on a side chain.
blockHeader := &block.MsgBlock().Header
newNode := newBlockNode(blockHeader, nodes)
newNode.status = statusDataStored
// Connect the passed block to the chain while respecting proper chain
// selection according to the chain with the most proof of work. This
// also handles validation of the transaction scripts.
isMainChain, err := b.connectBestChain(newNode, block, flags)
if err != nil {
return false, err
}
b.index.AddNode(newNode)
err = b.index.flushToDB()
if err != nil {
return false, err
}
if !isMainChain {
break
}
// Connect the passed block to the chain while respecting proper chain
// selection according to the chain with the most proof of work. This
// also handles validation of the transaction scripts.
isMainChain, err := b.connectBestChain(newNode, block, flags)
if err != nil {
return false, err
}
// Notify the caller that the new block was accepted into the block

View File

@ -71,8 +71,11 @@ type blockNode struct {
// hundreds of thousands of these in memory, so a few extra bytes of
// padding adds up.
// parent is the parent block for this node.
parent *blockNode
// numParents is the amount of parent blocks for this node.
numParents byte
// parents is the parent blocks for this node.
parents []blockNode
// hash is the double sha 256 of the block.
hash daghash.Hash
@ -101,13 +104,16 @@ type blockNode struct {
status blockStatus
}
// initBlockNode initializes a block node from the given header and parent node,
// calculating the height and workSum from the respective fields on the parent.
// initBlockNode initializes a block node from the given header and parent nodes,
// calculating the height and workSum from the respective fields on the first parent.
// This function is NOT safe for concurrent access. It must only be called when
// initially creating a node.
func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parent *blockNode) {
func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parents []blockNode) {
numParents := byte(len(parents))
*node = blockNode{
hash: blockHeader.BlockHash(),
parents: parents,
numParents: numParents,
workSum: CalcWork(blockHeader.Bits),
version: blockHeader.Version,
bits: blockHeader.Bits,
@ -115,19 +121,19 @@ func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parent *block
timestamp: blockHeader.Timestamp.Unix(),
merkleRoot: blockHeader.MerkleRoot,
}
if parent != nil {
node.parent = parent
if numParents > 0 {
parent := parents[0]
node.height = parent.height + 1
node.workSum = node.workSum.Add(parent.workSum, node.workSum)
}
}
// newBlockNode returns a new block node for the given block header and parent
// node, calculating the height and workSum from the respective fields on the
// nodes, calculating the height and workSum from the respective fields on the
// parent. This function is NOT safe for concurrent access.
func newBlockNode(blockHeader *wire.BlockHeader, parent *blockNode) *blockNode {
func newBlockNode(blockHeader *wire.BlockHeader, parents []blockNode) *blockNode {
var node blockNode
initBlockNode(&node, blockHeader, parent)
initBlockNode(&node, blockHeader, parents)
return &node
}
@ -136,17 +142,19 @@ func newBlockNode(blockHeader *wire.BlockHeader, parent *blockNode) *blockNode {
// This function is safe for concurrent access.
func (node *blockNode) Header() wire.BlockHeader {
// No lock is needed because all accessed fields are immutable.
prevHash := &zeroHash
if node.parent != nil {
prevHash = &node.parent.hash
prevHashes := make([]daghash.Hash, node.numParents)
for _, parent := range node.parents {
prevHashes = append(prevHashes, parent.hash)
}
return wire.BlockHeader{
Version: node.version,
PrevBlock: *prevHash,
MerkleRoot: node.merkleRoot,
Timestamp: time.Unix(node.timestamp, 0),
Bits: node.bits,
Nonce: node.nonce,
Version: node.version,
NumPrevBlocks: node.numParents,
PrevBlocks: prevHashes,
MerkleRoot: node.merkleRoot,
Timestamp: time.Unix(node.timestamp, 0),
Bits: node.bits,
Nonce: node.nonce,
}
}
@ -162,7 +170,7 @@ func (node *blockNode) Ancestor(height int32) *blockNode {
}
n := node
for ; n != nil && n.height != height; n = n.parent {
for ; n != nil && n.height != height; n = &n.parents[0] {
// Intentionally left blank
}
@ -192,7 +200,7 @@ func (node *blockNode) CalcPastMedianTime() time.Time {
timestamps[i] = iterNode.timestamp
numNodes++
iterNode = iterNode.parent
iterNode = &iterNode.parents[0]
}
// Prune the slice to the actual number of available timestamps which