diff --git a/blockdag/accept.go b/blockdag/accept.go index 9ed1d610c..f956aae43 100644 --- a/blockdag/accept.go +++ b/blockdag/accept.go @@ -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 diff --git a/blockdag/blockindex.go b/blockdag/blockindex.go index 5d3cb12d8..bce7f4281 100644 --- a/blockdag/blockindex.go +++ b/blockdag/blockindex.go @@ -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