From c57c18c8e2e9c43b5cc81b04e5f84b23ccf95661 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sat, 2 Apr 2016 01:51:16 -0500 Subject: [PATCH] blockchain: Add median time to state snapshot. This adds a new field to the best chain state snapshot for the calculated past median time as returned by the calcPastMedianTime function. This is useful since it provides fast access to it without having to acquire the chain lock which is needed to recalculate it. This will ultimately allow the associated exported function to be removed since it only exists to be able to calculate this exact value, however this commit only introduces the new field in order to keep the changes minimal. --- blockchain/chain.go | 48 ++++++++++++++++++++++++++++--------------- blockchain/chainio.go | 14 ++++++++++--- 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/blockchain/chain.go b/blockchain/chain.go index 6b485302f9..dad7fd99e3 100644 --- a/blockchain/chain.go +++ b/blockchain/chain.go @@ -128,23 +128,25 @@ func removeChildNode(children []*blockNode, node *blockNode) []*blockNode { // However, the returned snapshot must be treated as immutable since it is // shared by all callers. type BestState struct { - Hash *chainhash.Hash // The hash of the block. - Height int32 // The height of the block. - Bits uint32 // The difficulty bits of the block. - BlockSize uint64 // The size of the block. - NumTxns uint64 // The number of txns in the block. - TotalTxns uint64 // The total number of txns in the chain. + Hash *chainhash.Hash // The hash of the block. + Height int32 // The height of the block. + Bits uint32 // The difficulty bits of the block. + BlockSize uint64 // The size of the block. + NumTxns uint64 // The number of txns in the block. + TotalTxns uint64 // The total number of txns in the chain. + MedianTime time.Time // Median time as per CalcPastMedianTime. } // newBestState returns a new best stats instance for the given parameters. -func newBestState(node *blockNode, blockSize, numTxns, totalTxns uint64) *BestState { +func newBestState(node *blockNode, blockSize, numTxns, totalTxns uint64, medianTime time.Time) *BestState { return &BestState{ - Hash: node.hash, - Height: node.height, - Bits: node.bits, - BlockSize: blockSize, - NumTxns: numTxns, - TotalTxns: totalTxns, + Hash: node.hash, + Height: node.height, + Bits: node.bits, + BlockSize: blockSize, + NumTxns: numTxns, + TotalTxns: totalTxns, + MedianTime: medianTime, } } @@ -779,6 +781,12 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *U "spent transaction out information") } + // Calculate the median time for the block. + medianTime, err := b.calcPastMedianTime(node) + if err != nil { + return err + } + // Generate a new best state snapshot that will be used to update the // database and later memory if all database updates are successful. b.stateLock.RLock() @@ -786,10 +794,11 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *U b.stateLock.RUnlock() numTxns := uint64(len(block.MsgBlock().Transactions)) blockSize := uint64(block.MsgBlock().SerializeSize()) - state := newBestState(node, blockSize, numTxns, curTotalTxns+numTxns) + state := newBestState(node, blockSize, numTxns, curTotalTxns+numTxns, + medianTime) // Atomically insert info into the database. - err := b.db.Update(func(dbTx database.Tx) error { + err = b.db.Update(func(dbTx database.Tx) error { // Update best block state. err := dbPutBestState(dbTx, state, node.workSum) if err != nil { @@ -892,6 +901,12 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block, view return err } + // Calculate the median time for the previous block. + medianTime, err := b.calcPastMedianTime(prevNode) + if err != nil { + return err + } + // Load the previous block since some details for it are needed below. var prevBlock *btcutil.Block err = b.db.View(func(dbTx database.Tx) error { @@ -911,7 +926,8 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block, view numTxns := uint64(len(prevBlock.MsgBlock().Transactions)) blockSize := uint64(prevBlock.MsgBlock().SerializeSize()) newTotalTxns := curTotalTxns - uint64(len(block.MsgBlock().Transactions)) - state := newBestState(prevNode, blockSize, numTxns, newTotalTxns) + state := newBestState(prevNode, blockSize, numTxns, newTotalTxns, + medianTime) err = b.db.Update(func(dbTx database.Tx) error { // Update best block state. diff --git a/blockchain/chainio.go b/blockchain/chainio.go index fd383ec83b..cd318dd602 100644 --- a/blockchain/chainio.go +++ b/blockchain/chainio.go @@ -1058,10 +1058,12 @@ func (b *BlockChain) createChainState() error { // Add the new node to the index which is used for faster lookups. b.index[*node.hash] = node - // Initialize the state related to the best block. + // Initialize the state related to the best block. Since it is the + // genesis block, use its timestamp for the median time. numTxns := uint64(len(genesisBlock.MsgBlock().Transactions)) blockSize := uint64(genesisBlock.MsgBlock().SerializeSize()) - b.stateSnapshot = newBestState(b.bestNode, blockSize, numTxns, numTxns) + b.stateSnapshot = newBestState(b.bestNode, blockSize, numTxns, numTxns, + b.bestNode.timestamp) // Create the initial the database chain state including creating the // necessary index buckets and inserting the genesis block. @@ -1159,11 +1161,17 @@ func (b *BlockChain) initChainState() error { b.index[*node.hash] = node b.depNodes[*prevHash] = append(b.depNodes[*prevHash], node) + // Calculate the median time for the block. + medianTime, err := b.calcPastMedianTime(node) + if err != nil { + return err + } + // Initialize the state related to the best block. blockSize := uint64(len(blockBytes)) numTxns := uint64(len(block.Transactions)) b.stateSnapshot = newBestState(b.bestNode, blockSize, numTxns, - state.totalTxns) + state.totalTxns, medianTime) isStateInitialized = true return nil