blockchain: Don't store full header in block node.

This modifies the block node structure to include only the specifically
used fields, some of which in a more compact format, as opposed to
copying the entire header and updates all code and tests accordingly.

Not only is this a more efficient approach that helps pave the way for
future optimizations, it is also consistent with the upstream code which
helps minimize the differences to facilitate easier syncs due to less
merge conflicts.

In particular, since the merkle and stake roots, number of revocations,
size, nonce, and extradata fields aren't used currently, they are no
longer copied into the block node.  Also, the block node already had a
height field, which is also in the header, so this change also removes
that duplication.

Another change is that the block node now stores the timestamp as an
int64 unix-style timestamp which is only 8 bytes versus the old
timestamp that was in the header which is a time.Time and thus 24 bytes.

It should be noted that future optimizations will very likely end up
adding most of the omitted header fields back to the block node as
individual fields so the headers can be efficiently reconstructed from
memory, however, these changes are still beneficial due to the ability
to decouple the block node storage format from the header struct which
allows more compact representations and reording of the fields for
optimal struct packing.

Ultimately, the need for the parent hash can also be removed, which will
save an additional 32 bytes which would not be possible without this
decoupling.
This commit is contained in:
Dave Collins 2018-01-28 01:56:36 -06:00
parent b486cada60
commit 9d6cf805f4
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
15 changed files with 155 additions and 143 deletions

View File

@ -63,9 +63,15 @@ type blockNode struct {
// is when the best chain selection algorithm is used.
children []*blockNode
// hash is the double sha 256 of the block.
// hash is the hash of the block this node represents.
hash chainhash.Hash
// parentHash is the hash of the parent block of the block this node
// represents. This is kept here over simply relying on parent.hash
// directly since block nodes are sparse and the parent node might not be
// in memory when its hash is needed.
parentHash chainhash.Hash
// height is the position in the block chain.
height int64
@ -78,8 +84,18 @@ type blockNode struct {
// ancestor when switching chains.
inMainChain bool
// header is the full block header.
header wire.BlockHeader
// Some fields from block headers to aid in best chain selection and
// validation.
blockVersion int32
voteBits uint16
finalState [6]byte
voters uint16
freshStake uint8
poolSize uint32
bits uint32
sbits int64
timestamp int64
stakeVersion uint32
// stakeNode contains all the consensus information required for the
// staking system. The node also caches information required to add or
@ -118,9 +134,19 @@ func newBlockNode(blockHeader *wire.BlockHeader, spentTickets *stake.SpentTicket
// collected.
node := blockNode{
hash: blockHeader.BlockHash(),
parentHash: blockHeader.PrevBlock,
workSum: CalcWork(blockHeader.Bits),
height: int64(blockHeader.Height),
header: *blockHeader,
blockVersion: blockHeader.Version,
voteBits: blockHeader.VoteBits,
finalState: blockHeader.FinalState,
voters: blockHeader.Voters,
freshStake: blockHeader.FreshStake,
poolSize: blockHeader.PoolSize,
bits: blockHeader.Bits,
sbits: blockHeader.SBits,
timestamp: blockHeader.Timestamp.Unix(),
stakeVersion: blockHeader.StakeVersion,
lotteryIV: stake.CalcHash256PRNGIV(hB),
ticketsSpent: spentTickets.VotedTickets,
ticketsRevoked: spentTickets.RevokedTickets,
@ -186,7 +212,7 @@ func newBestState(node *blockNode, blockSize, numTxns, totalTxns uint64, medianT
return &BestState{
Hash: &node.hash,
Height: node.height,
Bits: node.header.Bits,
Bits: node.bits,
BlockSize: blockSize,
NumTxns: numTxns,
TotalTxns: totalTxns,
@ -349,8 +375,8 @@ func (b *BlockChain) GetStakeVersions(hash *chainhash.Hash, count int32) ([]Stak
sv := StakeVersions{
Hash: prevNode.hash,
Height: prevNode.height,
BlockVersion: prevNode.header.Version,
StakeVersion: prevNode.header.StakeVersion,
BlockVersion: prevNode.blockVersion,
StakeVersion: prevNode.stakeVersion,
Votes: prevNode.votes,
}
@ -666,7 +692,7 @@ func (b *BlockChain) loadBlockNode(dbTx database.Tx, hash *chainhash.Hash) (*blo
// further down the line in the blockchain to which the block
// could be attached, for example if the node had been pruned from
// the index.
foundParent, err := b.findNode(&node.header.PrevBlock, maxSearchDepth)
foundParent, err := b.findNode(&node.parentHash, maxSearchDepth)
if err == nil {
node.workSum = node.workSum.Add(foundParent.workSum, node.workSum)
foundParent.children = append(foundParent.children, node)
@ -711,7 +737,7 @@ func (b *BlockChain) findNode(nodeHash *chainhash.Hash, searchDepth int) (*block
break
}
last := foundPrev.header.PrevBlock
last := foundPrev.parentHash
foundPrev = foundPrev.parent
if foundPrev == nil {
parent, err := b.loadBlockNode(dbTx, &last)
@ -794,7 +820,7 @@ func (b *BlockChain) getPrevNodeFromNode(node *blockNode) (*blockNode, error) {
var prevBlockNode *blockNode
err := b.db.View(func(dbTx database.Tx) error {
var err error
prevBlockNode, err = b.loadBlockNode(dbTx, &node.header.PrevBlock)
prevBlockNode, err = b.loadBlockNode(dbTx, &node.parentHash)
return err
})
return prevBlockNode, err
@ -942,7 +968,7 @@ func (b *BlockChain) BestPrevHash() chainhash.Hash {
b.chainLock.Lock()
defer b.chainLock.Unlock()
return b.bestNode.header.PrevBlock
return b.bestNode.parentHash
}
// isMajorityVersion determines if a previous number of blocks in the chain
@ -955,7 +981,7 @@ func (b *BlockChain) isMajorityVersion(minVer int32, startNode *blockNode, numRe
for i := uint64(0); i < b.chainParams.BlockUpgradeNumToCheck &&
numFound < numRequired && iterNode != nil; i++ {
// This node has a version that is at least the minimum version.
if iterNode.header.Version >= minVer {
if iterNode.blockVersion >= minVer {
numFound++
}
@ -987,11 +1013,11 @@ func (b *BlockChain) calcPastMedianTime(startNode *blockNode) (time.Time, error)
// Create a slice of the previous few block timestamps used to calculate
// the median per the number defined by the constant medianTimeBlocks.
timestamps := make([]time.Time, medianTimeBlocks)
timestamps := make([]int64, medianTimeBlocks)
numNodes := 0
iterNode := startNode
for i := 0; i < medianTimeBlocks && iterNode != nil; i++ {
timestamps[i] = iterNode.header.Timestamp
timestamps[i] = iterNode.timestamp
numNodes++
// Get the previous block node. This function is used over
@ -1026,7 +1052,7 @@ func (b *BlockChain) calcPastMedianTime(startNode *blockNode) (time.Time, error)
// however, be aware that should the medianTimeBlocks constant ever be
// changed to an even number, this code will be wrong.
medianTimestamp := timestamps[numNodes/2]
return medianTimestamp, nil
return time.Unix(medianTimestamp, 0), nil
}
// getReorganizeNodes finds the fork point between the main chain and the passed
@ -1075,7 +1101,7 @@ func (b *BlockChain) getReorganizeNodes(node *blockNode) (*list.List, *list.List
if n.parent == nil {
var err error
n.parent, err = b.findNode(&n.header.PrevBlock, maxSearchDepth)
n.parent, err = b.findNode(&n.parentHash, maxSearchDepth)
if err != nil {
return nil, nil, err
}
@ -1564,7 +1590,7 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags
if err != nil {
return err
}
parent, err = b.fetchBlockFromHash(&n.header.PrevBlock)
parent, err = b.fetchBlockFromHash(&n.parentHash)
if err != nil {
return err
}
@ -1664,7 +1690,7 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags
for i, e := 0, detachNodes.Front(); e != nil; i, e = i+1, e.Next() {
n := e.Value.(*blockNode)
block := detachBlocks[i]
parent, err := b.fetchBlockFromHash(&n.header.PrevBlock)
parent, err := b.fetchBlockFromHash(&n.parentHash)
if err != nil {
return err
}
@ -1698,7 +1724,7 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags
block := b.blockCache[n.hash]
b.blockCacheLock.RUnlock()
parent, err := b.fetchBlockFromHash(&n.header.PrevBlock)
parent, err := b.fetchBlockFromHash(&n.parentHash)
if err != nil {
return err
}
@ -1778,7 +1804,7 @@ func (b *BlockChain) forceHeadReorganization(formerBest chainhash.Hash, newBest
// Check to make sure our forced-in node validates correctly.
view := NewUtxoViewpoint()
view.SetBestHash(&b.bestNode.header.PrevBlock)
view.SetBestHash(&b.bestNode.parentHash)
view.SetStakeViewpoint(ViewpointPrevValidInitial)
formerBestBlock, err := b.fetchBlockFromHash(&formerBest)
@ -1862,12 +1888,12 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *dcrutil.Block, fla
// We are extending the main (best) chain with a new block. This is the
// most common case.
if node.header.PrevBlock == b.bestNode.hash {
if node.parentHash == b.bestNode.hash {
// Perform several checks to verify the block can be connected
// to the main chain without violating any rules and without
// actually connecting the block.
view := NewUtxoViewpoint()
view.SetBestHash(&node.header.PrevBlock)
view.SetBestHash(&node.parentHash)
view.SetStakeViewpoint(ViewpointPrevValidInitial)
var stxos []spentTxOut
if !fastAdd {
@ -1887,7 +1913,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *dcrutil.Block, fla
// utxos, spend them, and add the new utxos being created by
// this block.
if fastAdd {
parent, err := b.fetchBlockFromHash(&node.header.PrevBlock)
parent, err := b.fetchBlockFromHash(&node.parentHash)
if err != nil {
return false, err
}
@ -1913,7 +1939,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *dcrutil.Block, fla
}
validateStr := "validating"
txTreeRegularValid := dcrutil.IsFlagSet16(node.header.VoteBits,
txTreeRegularValid := dcrutil.IsFlagSet16(node.voteBits,
dcrutil.BlockValid)
if !txTreeRegularValid {
validateStr = "invalidating"
@ -2048,8 +2074,8 @@ func (b *BlockChain) isCurrent() bool {
//
// The chain appears to be current if none of the checks reported
// otherwise.
minus24Hours := b.timeSource.AdjustedTime().Add(-24 * time.Hour)
return !b.bestNode.header.Timestamp.Before(minus24Hours)
minus24Hours := b.timeSource.AdjustedTime().Add(-24 * time.Hour).Unix()
return b.bestNode.timestamp >= minus24Hours
}
// IsCurrent returns whether or not the chain believes it is current. Several

View File

@ -1285,7 +1285,7 @@ func (b *BlockChain) createChainState() error {
numTxns := uint64(len(genesisBlock.MsgBlock().Transactions))
blockSize := uint64(genesisBlock.MsgBlock().SerializeSize())
b.stateSnapshot = newBestState(b.bestNode, blockSize, numTxns, numTxns,
b.bestNode.header.Timestamp, 0)
time.Unix(b.bestNode.timestamp, 0), 0)
// Create the initial the database chain state including creating the
// necessary index buckets and inserting the genesis block.
@ -1445,7 +1445,7 @@ func (b *BlockChain) initChainState() error {
// set.
if dbInfo.version >= 2 {
node.stakeNode, err = stake.LoadBestNode(dbTx, uint32(node.height),
node.hash, node.header, b.chainParams)
node.hash, *header, b.chainParams)
if err != nil {
return err
}
@ -1456,9 +1456,9 @@ func (b *BlockChain) initChainState() error {
b.bestNode = node
// Add the new node to the indices for faster lookups.
prevHash := node.header.PrevBlock
prevHash := &node.parentHash
b.index[node.hash] = node
b.depNodes[prevHash] = append(b.depNodes[prevHash], node)
b.depNodes[*prevHash] = append(b.depNodes[*prevHash], node)
// Calculate the median time for the block.
medianTime, err := b.calcPastMedianTime(node)

View File

@ -10,6 +10,7 @@ import (
"math/big"
"time"
"github.com/decred/dcrd/blockchain/stake"
"github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/wire"
@ -212,7 +213,7 @@ func (b *BlockChain) findPrevTestNetDifficulty(startNode *blockNode) (uint32, er
b.chainParams.WorkDiffWindows
iterNode := startNode
for iterNode != nil && iterNode.height%blocksPerRetarget != 0 &&
iterNode.header.Bits == b.chainParams.PowLimitBits {
iterNode.bits == b.chainParams.PowLimitBits {
// Get the previous block node. This function is used over
// simply accessing iterNode.parent directly as it will
@ -231,7 +232,7 @@ func (b *BlockChain) findPrevTestNetDifficulty(startNode *blockNode) (uint32, er
// appropriate block was found.
lastBits := b.chainParams.PowLimitBits
if iterNode != nil {
lastBits = iterNode.header.Bits
lastBits = iterNode.bits
}
return lastBits, nil
}
@ -251,8 +252,8 @@ func (b *BlockChain) calcNextRequiredDifficulty(curNode *blockNode, newBlockTime
// Get the old difficulty; if we aren't at a block height where it changes,
// just return this.
oldDiff := curNode.header.Bits
oldDiffBig := CompactToBig(curNode.header.Bits)
oldDiff := curNode.bits
oldDiffBig := CompactToBig(curNode.bits)
// We're not at a retarget point, return the oldDiff.
if (curNode.height+1)%b.chainParams.WorkDiffWindowSize != 0 {
@ -262,18 +263,20 @@ func (b *BlockChain) calcNextRequiredDifficulty(curNode *blockNode, newBlockTime
if b.chainParams.ReduceMinDifficulty {
// Return minimum difficulty when more than the desired
// amount of time has elapsed without mining a block.
reductionTime := b.chainParams.MinDiffReductionTime
allowMinTime := curNode.header.Timestamp.Add(reductionTime)
reductionTime := int64(b.chainParams.MinDiffReductionTime /
time.Second)
allowMinTime := curNode.timestamp + reductionTime
// For every extra target timespan that passes, we halve the
// difficulty.
if newBlockTime.After(allowMinTime) {
timePassed := newBlockTime.Sub(curNode.header.Timestamp)
timePassed -= b.chainParams.MinDiffReductionTime
shifts := uint((timePassed / b.chainParams.TargetTimePerBlock) + 1)
if newBlockTime.Unix() > allowMinTime {
timePassed := newBlockTime.Unix() - curNode.timestamp
timePassed -= reductionTime
shifts := uint((timePassed / int64(b.chainParams.TargetTimePerBlock/
time.Second)) + 1)
// Scale the difficulty with time passed.
oldTarget := CompactToBig(curNode.header.Bits)
oldTarget := CompactToBig(curNode.bits)
newTarget := new(big.Int)
if shifts < maxShift {
newTarget.Lsh(oldTarget, shifts)
@ -304,9 +307,9 @@ func (b *BlockChain) calcNextRequiredDifficulty(curNode *blockNode, newBlockTime
// Declare some useful variables.
RAFBig := big.NewInt(b.chainParams.RetargetAdjustmentFactor)
nextDiffBigMin := CompactToBig(curNode.header.Bits)
nextDiffBigMin := CompactToBig(curNode.bits)
nextDiffBigMin.Div(nextDiffBigMin, RAFBig)
nextDiffBigMax := CompactToBig(curNode.header.Bits)
nextDiffBigMax := CompactToBig(curNode.bits)
nextDiffBigMax.Mul(nextDiffBigMax, RAFBig)
alpha := b.chainParams.WorkDiffAlpha
@ -324,23 +327,25 @@ func (b *BlockChain) calcNextRequiredDifficulty(curNode *blockNode, newBlockTime
var olderTime, windowPeriod int64
var weights uint64
oldNode := curNode
recentTime := curNode.header.Timestamp.UnixNano()
recentTime := curNode.timestamp
for i := int64(0); ; i++ {
// Store and reset after reaching the end of every window period.
if i%b.chainParams.WorkDiffWindowSize == 0 && i != 0 {
olderTime = oldNode.header.Timestamp.UnixNano()
olderTime = oldNode.timestamp
timeDifference := recentTime - olderTime
// Just assume we're at the target (no change) if we've
// gone all the way back to the genesis block.
if oldNode.height == 0 {
timeDifference = int64(b.chainParams.TargetTimespan)
timeDifference = int64(b.chainParams.TargetTimespan /
time.Second)
}
timeDifBig := big.NewInt(timeDifference)
timeDifBig.Lsh(timeDifBig, 32) // Add padding
targetTemp := big.NewInt(int64(b.chainParams.TargetTimespan))
targetTemp := big.NewInt(int64(b.chainParams.TargetTimespan /
time.Second))
windowAdjusted := targetTemp.Div(timeDifBig, targetTemp)
@ -424,7 +429,7 @@ func (b *BlockChain) calcNextRequiredDifficulty(curNode *blockNode, newBlockTime
// precision.
nextDiffBits := BigToCompact(nextDiffBig)
log.Debugf("Difficulty retarget at block height %d", curNode.height+1)
log.Debugf("Old target %08x (%064x)", curNode.header.Bits, oldDiffBig)
log.Debugf("Old target %08x (%064x)", curNode.bits, oldDiffBig)
log.Debugf("New target %08x (%064x)", nextDiffBits, CompactToBig(nextDiffBits))
return nextDiffBits, nil
@ -516,7 +521,7 @@ func (b *BlockChain) calcNextRequiredStakeDifficultyV1(curNode *blockNode) (int6
// Get the old difficulty; if we aren't at a block height where it changes,
// just return this.
oldDiff := curNode.header.SBits
oldDiff := curNode.sbits
if (curNode.height+1)%b.chainParams.StakeDiffWindowSize != 0 {
return oldDiff, nil
}
@ -543,7 +548,7 @@ func (b *BlockChain) calcNextRequiredStakeDifficultyV1(curNode *blockNode) (int6
// First adjust based on ticketPoolSize. Skew the difference
// in ticketPoolSize by max adjustment factor to help
// weight ticket pool size versus tickets per block.
poolSizeSkew := (int64(oldNode.header.PoolSize)-
poolSizeSkew := (int64(oldNode.poolSize)-
targetForTicketPool)*TicketPoolWeight + targetForTicketPool
// Don't let this be negative or zero.
@ -636,7 +641,7 @@ func (b *BlockChain) calcNextRequiredStakeDifficultyV1(curNode *blockNode) (int6
for i := int64(0); ; i++ {
// Add the fresh stake into the store for this window period.
windowFreshStake += int64(oldNode.header.FreshStake)
windowFreshStake += int64(oldNode.freshStake)
// Store and reset after reaching the end of every window period.
if (i+1)%b.chainParams.StakeDiffWindowSize == 0 {
@ -790,7 +795,7 @@ func (b *BlockChain) sumPurchasedTickets(startNode *blockNode, numToSum int64) (
for node, numTraversed := startNode, int64(0); node != nil &&
numTraversed < numToSum; numTraversed++ {
numPurchased += int64(node.header.FreshStake)
numPurchased += int64(node.freshStake)
// Get the previous block node. This function is used over
// simply accessing iterNode.parent directly as it will
@ -902,7 +907,7 @@ func (b *BlockChain) calcNextRequiredStakeDifficultyV2(curNode *blockNode) (int6
// Return the previous block's difficulty requirements if the next block
// is not at a difficulty retarget interval.
intervalSize := b.chainParams.StakeDiffWindowSize
curDiff := curNode.header.SBits
curDiff := curNode.sbits
if nextHeight%intervalSize != 0 {
return curDiff, nil
}
@ -922,7 +927,7 @@ func (b *BlockChain) calcNextRequiredStakeDifficultyV2(curNode *blockNode) (int6
return 0, err
}
if prevRetargetNode != nil {
prevPoolSize = int64(prevRetargetNode.header.PoolSize)
prevPoolSize = int64(prevRetargetNode.poolSize)
}
ticketMaturity := int64(b.chainParams.TicketMaturity)
prevImmatureTickets, err := b.sumPurchasedTickets(prevRetargetNode,
@ -945,7 +950,7 @@ func (b *BlockChain) calcNextRequiredStakeDifficultyV2(curNode *blockNode) (int6
}
// Calculate and return the final next required difficulty.
curPoolSizeAll := int64(curNode.header.PoolSize) + immatureTickets
curPoolSizeAll := int64(curNode.poolSize) + immatureTickets
return calcNextStakeDiffV2(b.chainParams, nextHeight, curDiff,
prevPoolSizeAll, curPoolSizeAll), nil
}
@ -1033,7 +1038,7 @@ func (b *BlockChain) estimateNextStakeDifficultyV1(curNode *blockNode, ticketsIn
// Create a fake blockchain on top of the current best node with
// the number of freshly purchased tickets as indicated by the
// user.
oldDiff := curNode.header.SBits
oldDiff := curNode.sbits
topNode := curNode
if (curNode.height+1)%b.chainParams.StakeDiffWindowSize != 0 {
nextAdjHeight := ((curNode.height /
@ -1060,13 +1065,13 @@ func (b *BlockChain) estimateNextStakeDifficultyV1(curNode *blockNode, ticketsIn
// used to calculate the next difficulty below.
ticketsToInsert := ticketsInWindow
for i := curNode.height + 1; i < nextAdjHeight; i++ {
emptyHeader := new(wire.BlockHeader)
var emptyHeader wire.BlockHeader
emptyHeader.Height = uint32(i)
// User a constant pool size for estimate, since
// this has much less fluctuation than freshStake.
// TODO Use a better pool size estimate?
emptyHeader.PoolSize = curNode.header.PoolSize
emptyHeader.PoolSize = curNode.poolSize
// Insert the fake fresh stake into each block,
// decrementing the amount we need to use each
@ -1083,17 +1088,7 @@ func (b *BlockChain) estimateNextStakeDifficultyV1(curNode *blockNode, ticketsIn
// Connect the header.
emptyHeader.PrevBlock = topNode.hash
// Make up a node hash.
hB, err := emptyHeader.Bytes()
if err != nil {
return 0, err
}
emptyHeaderHash := chainhash.HashH(hB)
thisNode := new(blockNode)
thisNode.header = *emptyHeader
thisNode.hash = emptyHeaderHash
thisNode.height = i
thisNode := newBlockNode(&emptyHeader, &stake.SpentTicketsInBlock{})
thisNode.parent = topNode
topNode = thisNode
}
@ -1121,7 +1116,7 @@ func (b *BlockChain) estimateNextStakeDifficultyV1(curNode *blockNode, ticketsIn
// First adjust based on ticketPoolSize. Skew the difference
// in ticketPoolSize by max adjustment factor to help
// weight ticket pool size versus tickets per block.
poolSizeSkew := (int64(oldNode.header.PoolSize)-
poolSizeSkew := (int64(oldNode.poolSize)-
targetForTicketPool)*TicketPoolWeight + targetForTicketPool
// Don't let this be negative or zero.
@ -1214,7 +1209,7 @@ func (b *BlockChain) estimateNextStakeDifficultyV1(curNode *blockNode, ticketsIn
for i := int64(0); ; i++ {
// Add the fresh stake into the store for this window period.
windowFreshStake += int64(oldNode.header.FreshStake)
windowFreshStake += int64(oldNode.freshStake)
// Store and reset after reaching the end of every window period.
if (i+1)%b.chainParams.StakeDiffWindowSize == 0 {
@ -1390,7 +1385,7 @@ func (b *BlockChain) estimateNextStakeDifficultyV2(curNode *blockNode, newTicket
return 0, err
}
if prevRetargetNode != nil {
prevPoolSize = int64(prevRetargetNode.header.PoolSize)
prevPoolSize = int64(prevRetargetNode.poolSize)
}
prevImmatureTickets, err := b.sumPurchasedTickets(prevRetargetNode,
ticketMaturity)
@ -1400,7 +1395,7 @@ func (b *BlockChain) estimateNextStakeDifficultyV2(curNode *blockNode, newTicket
// Return the existing ticket price for the first few intervals to avoid
// division by zero and encourage initial pool population.
curDiff := curNode.header.SBits
curDiff := curNode.sbits
prevPoolSizeAll := prevPoolSize + prevImmatureTickets
if prevPoolSizeAll == 0 {
return curDiff, nil
@ -1447,7 +1442,7 @@ func (b *BlockChain) estimateNextStakeDifficultyV2(curNode *blockNode, newTicket
}
// Calculate what the pool size would be as of the next interval.
curPoolSize := int64(curNode.header.PoolSize)
curPoolSize := int64(curNode.poolSize)
estimatedPoolSize := curPoolSize + maturingTickets - pendingVotes
estimatedImmatureTickets := remainingImmatureTickets + newTickets
estimatedPoolSizeAll := estimatedPoolSize + estimatedImmatureTickets

View File

@ -1,5 +1,5 @@
// Copyright (c) 2014 The btcsuite developers
// Copyright (c) 2015-2017 The Decred developers
// Copyright (c) 2015-2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -421,7 +421,7 @@ nextTest:
for i := uint32(0); i < ticketInfo.numNodes; i++ {
// Make up a header.
nextHeight := bc.bestNode.header.Height + 1
nextHeight := uint32(bc.bestNode.height) + 1
header := &wire.BlockHeader{
Version: 4,
SBits: ticketInfo.stakeDiff,
@ -723,7 +723,7 @@ nextTest:
for i := uint32(0); i < ticketInfo.numNodes; i++ {
// Make up a header.
nextHeight := bc.bestNode.header.Height + 1
nextHeight := uint32(bc.bestNode.height) + 1
header := &wire.BlockHeader{
Version: 4,
SBits: ticketInfo.stakeDiff,

View File

@ -1,5 +1,5 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Copyright (c) 2015-2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -15,7 +15,6 @@ package blockchain
import (
"sort"
"time"
"github.com/decred/dcrd/blockchain/stake"
"github.com/decred/dcrd/wire"
@ -23,7 +22,7 @@ import (
// TstTimeSorter makes the internal timeSorter type available to the test
// package.
func TstTimeSorter(times []time.Time) sort.Interface {
func TstTimeSorter(times []int64) sort.Interface {
return timeSorter(times)
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017 The Decred developers
// Copyright (c) 2017-2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -37,7 +37,7 @@ func TestCalcSequenceLock(t *testing.T) {
params := &chaincfg.SimNetParams
bc := newFakeChain(params)
node := bc.bestNode
blockTime := node.header.Timestamp
blockTime := time.Unix(node.timestamp, 0)
for i := uint32(0); i < numBlocks; i++ {
blockTime = blockTime.Add(time.Second)
node = newFakeNode(node, 1, 1, 0, blockTime)

View File

@ -1,4 +1,4 @@
// Copyright (c) 2016 The Decred developers
// Copyright (c) 2016-2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -170,7 +170,7 @@ func (b *BlockChain) isStakeMajorityVersion(minVer uint32, prevNode *blockNode)
versionCount := int32(0)
iterNode := node
for i := int64(0); i < b.chainParams.StakeVersionInterval && iterNode != nil; i++ {
if iterNode.header.StakeVersion >= minVer {
if iterNode.stakeVersion >= minVer {
versionCount += 1
}
@ -219,7 +219,7 @@ func (b *BlockChain) calcPriorStakeVersion(prevNode *blockNode) (uint32, error)
versions := make(map[uint32]int32) // [version][count]
iterNode := node
for i := int64(0); i < b.chainParams.StakeVersionInterval && iterNode != nil; i++ {
versions[iterNode.header.StakeVersion]++
versions[iterNode.stakeVersion]++
var err error
iterNode, err = b.getPrevNodeFromNode(iterNode)

View File

@ -1,4 +1,4 @@
// Copyright (c) 2016 The Decred developers
// Copyright (c) 2016-2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -350,8 +350,8 @@ func TestCalcStakeVersionByNode(t *testing.T) {
set: func(node *blockNode) {
if int64(node.height) > svh {
appendFakeVotes(node, tpb, 3, 0)
node.header.StakeVersion = 2
node.header.Version = 3
node.stakeVersion = 2
node.blockVersion = 3
}
},
},
@ -362,8 +362,8 @@ func TestCalcStakeVersionByNode(t *testing.T) {
set: func(node *blockNode) {
if int64(node.height) > svh {
appendFakeVotes(node, tpb, 2, 0)
node.header.StakeVersion = 3
node.header.Version = 3
node.stakeVersion = 3
node.blockVersion = 3
}
},
},
@ -705,7 +705,7 @@ func TestIsStakeMajorityVersion(t *testing.T) {
// Create new BlockChain in order to blow away cache.
bc := newFakeChain(params)
node := bc.bestNode
node.header.StakeVersion = test.startStakeVersion
node.stakeVersion = test.startStakeVersion
ticketCount = 0
@ -778,7 +778,7 @@ func TestLarge(t *testing.T) {
// Create new BlockChain in order to blow away cache.
bc := newFakeChain(params)
node := bc.bestNode
node.header.StakeVersion = test.startStakeVersion
node.stakeVersion = test.startStakeVersion
for i := int64(1); i <= test.numNodes; i++ {
node = newFakeNode(node, test.blockVersion,

View File

@ -282,7 +282,7 @@ func BlockOneCoinbasePaysTokens(tx *dcrutil.Tx, params *chaincfg.Params) error {
// CoinbasePaysTax checks to see if a given block's coinbase correctly pays
// tax to the developer organization.
func CoinbasePaysTax(subsidyCache *SubsidyCache, tx *dcrutil.Tx, height uint32, voters uint16, params *chaincfg.Params) error {
func CoinbasePaysTax(subsidyCache *SubsidyCache, tx *dcrutil.Tx, height int64, voters uint16, params *chaincfg.Params) error {
// Taxes only apply from block 2 onwards.
if height <= 1 {
return nil
@ -311,7 +311,7 @@ func CoinbasePaysTax(subsidyCache *SubsidyCache, tx *dcrutil.Tx, height uint32,
// Get the amount of subsidy that should have been paid out to
// the organization, then check it.
orgSubsidy := CalcBlockTaxSubsidy(subsidyCache, int64(height), voters, params)
orgSubsidy := CalcBlockTaxSubsidy(subsidyCache, height, voters, params)
if orgSubsidy != taxOutput.Value {
errStr := fmt.Sprintf("amount in output 0 has non matching org "+
"calculated amount; got %v, want %v", taxOutput.Value,

View File

@ -1,17 +1,13 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Copyright (c) 2015-2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"time"
)
// timeSorter implements sort.Interface to allow a slice of timestamps to
// be sorted.
type timeSorter []time.Time
type timeSorter []int64
// Len returns the number of timestamps in the slice. It is part of the
// sort.Interface implementation.
@ -28,5 +24,5 @@ func (s timeSorter) Swap(i, j int) {
// Less returns whether the timstamp with index i should sort before the
// timestamp with index j. It is part of the sort.Interface implementation.
func (s timeSorter) Less(i, j int) bool {
return s[i].Before(s[j])
return s[i] < s[j]
}

View File

@ -1,5 +1,5 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Copyright (c) 2015-2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -9,7 +9,6 @@ import (
"reflect"
"sort"
"testing"
"time"
"github.com/decred/dcrd/blockchain"
)
@ -17,31 +16,29 @@ import (
// TestTimeSorter tests the timeSorter implementation.
func TestTimeSorter(t *testing.T) {
tests := []struct {
in []time.Time
want []time.Time
in []int64
want []int64
}{
{
in: []time.Time{
time.Unix(1351228575, 0), // Fri Oct 26 05:16:15 UTC 2012 (Block #205000)
time.Unix(1351228575, 1), // Fri Oct 26 05:16:15 UTC 2012 (+1 nanosecond)
time.Unix(1348310759, 0), // Sat Sep 22 10:45:59 UTC 2012 (Block #200000)
time.Unix(1305758502, 0), // Wed May 18 22:41:42 UTC 2011 (Block #125000)
time.Unix(1347777156, 0), // Sun Sep 16 06:32:36 UTC 2012 (Block #199000)
time.Unix(1349492104, 0), // Sat Oct 6 02:55:04 UTC 2012 (Block #202000)
in: []int64{
1351228575, // Fri Oct 26 05:16:15 UTC 2012 (Block #205000)
1348310759, // Sat Sep 22 10:45:59 UTC 2012 (Block #200000)
1305758502, // Wed May 18 22:41:42 UTC 2011 (Block #125000)
1347777156, // Sun Sep 16 06:32:36 UTC 2012 (Block #199000)
1349492104, // Sat Oct 6 02:55:04 UTC 2012 (Block #202000)
},
want: []time.Time{
time.Unix(1305758502, 0), // Wed May 18 22:41:42 UTC 2011 (Block #125000)
time.Unix(1347777156, 0), // Sun Sep 16 06:32:36 UTC 2012 (Block #199000)
time.Unix(1348310759, 0), // Sat Sep 22 10:45:59 UTC 2012 (Block #200000)
time.Unix(1349492104, 0), // Sat Oct 6 02:55:04 UTC 2012 (Block #202000)
time.Unix(1351228575, 0), // Fri Oct 26 05:16:15 UTC 2012 (Block #205000)
time.Unix(1351228575, 1), // Fri Oct 26 05:16:15 UTC 2012 (+1 nanosecond)
want: []int64{
1305758502, // Wed May 18 22:41:42 UTC 2011 (Block #125000)
1347777156, // Sun Sep 16 06:32:36 UTC 2012 (Block #199000)
1348310759, // Sat Sep 22 10:45:59 UTC 2012 (Block #200000)
1349492104, // Sat Oct 6 02:55:04 UTC 2012 (Block #202000)
1351228575, // Fri Oct 26 05:16:15 UTC 2012 (Block #205000)
},
},
}
for i, test := range tests {
result := make([]time.Time, len(test.in))
result := make([]int64, len(test.in))
copy(result, test.in)
sort.Sort(blockchain.TstTimeSorter(result))
if !reflect.DeepEqual(result, test.want) {

View File

@ -1093,7 +1093,7 @@ func (b *BlockChain) FetchUtxoView(tx *dcrutil.Tx, treeValid bool) (*UtxoViewpoi
if err != nil {
return nil, err
}
parent, err := b.fetchBlockFromHash(&b.bestNode.header.PrevBlock)
parent, err := b.fetchBlockFromHash(&b.bestNode.parentHash)
if err != nil {
return nil, err
}

View File

@ -1,5 +1,5 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2017 The Decred developers
// Copyright (c) 2015-2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -852,7 +852,7 @@ func (b *BlockChain) CheckBlockStakeSanity(stakeValidationHeight int64, node *bl
blockHash := block.Hash()
prevBlockHash := &msgBlock.Header.PrevBlock
poolSize := int(msgBlock.Header.PoolSize)
finalState := node.header.FinalState
finalState := msgBlock.Header.FinalState
ticketsPerBlock := int(b.chainParams.TicketsPerBlock)
@ -2226,7 +2226,7 @@ func (b *BlockChain) checkTransactionsAndConnect(subsidyCache *SubsidyCache, inp
if txTree { //TxTreeRegular
// Apply penalty to fees if we're at stake validation height.
if node.height >= b.chainParams.StakeValidationHeight {
totalFees *= int64(node.header.Voters)
totalFees *= int64(node.voters)
totalFees /= int64(b.chainParams.TicketsPerBlock)
}
@ -2241,9 +2241,9 @@ func (b *BlockChain) checkTransactionsAndConnect(subsidyCache *SubsidyCache, inp
expAtomOut = subsidyCache.CalcBlockSubsidy(node.height)
} else {
subsidyWork := CalcBlockWorkSubsidy(subsidyCache,
node.height, node.header.Voters, b.chainParams)
node.height, node.voters, b.chainParams)
subsidyTax := CalcBlockTaxSubsidy(subsidyCache,
node.height, node.header.Voters, b.chainParams)
node.height, node.voters, b.chainParams)
expAtomOut = subsidyWork + subsidyTax + totalFees
}
@ -2294,7 +2294,7 @@ func (b *BlockChain) checkTransactionsAndConnect(subsidyCache *SubsidyCache, inp
// with the height of the current block.
expAtomOut = CalcStakeVoteSubsidy(subsidyCache,
node.height-1, b.chainParams) *
int64(node.header.Voters)
int64(node.voters)
} else {
expAtomOut = totalFees
}
@ -2360,7 +2360,7 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *dcrutil.Block, ut
// allowed a block that is no longer valid. However, since the
// implementation only currently uses memory for the side chain blocks,
// it isn't currently necessary.
parentBlock, err := b.fetchBlockFromHash(&node.header.PrevBlock)
parentBlock, err := b.fetchBlockFromHash(&node.parentHash)
if err != nil {
return ruleError(ErrMissingParent, err.Error())
}
@ -2373,16 +2373,16 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *dcrutil.Block, ut
}
// Ensure the view is for the node being checked.
if !utxoView.BestHash().IsEqual(&node.header.PrevBlock) {
if !utxoView.BestHash().IsEqual(&node.parentHash) {
return AssertError(fmt.Sprintf("inconsistent view when "+
"checking block connection: best hash is %v instead "+
"of expected %v", utxoView.BestHash(),
node.header.PrevBlock))
node.parentHash))
}
// Check that the coinbase pays the tax, if applicable.
err = CoinbasePaysTax(b.subsidyCache, block.Transactions()[0],
node.header.Height, node.header.Voters, b.chainParams)
err = CoinbasePaysTax(b.subsidyCache, block.Transactions()[0], node.height,
node.voters, b.chainParams)
if err != nil {
return err
}
@ -2422,7 +2422,7 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *dcrutil.Block, ut
// signature operations in each of the input transaction public key
// scripts.
// Do this for all TxTrees.
regularTxTreeValid := dcrutil.IsFlagSet16(node.header.VoteBits,
regularTxTreeValid := dcrutil.IsFlagSet16(node.voteBits,
dcrutil.BlockValid)
thisNodeStakeViewpoint := ViewpointPrevInvalidStake
thisNodeRegularViewpoint := ViewpointPrevInvalidRegular
@ -2657,7 +2657,7 @@ func (b *BlockChain) CheckConnectBlock(block *dcrutil.Block) error {
return err
}
parent, err := b.fetchBlockFromHash(&n.header.PrevBlock)
parent, err := b.fetchBlockFromHash(&n.parentHash)
if err != nil {
return err
}
@ -2701,7 +2701,7 @@ func (b *BlockChain) CheckConnectBlock(block *dcrutil.Block) error {
n.hash)
}
parent, err := b.fetchBlockFromHash(&n.header.PrevBlock)
parent, err := b.fetchBlockFromHash(&n.parentHash)
if err != nil {
return err
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017 The decred developers
// Copyright (c) 2017-2018 The Decred developers
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017 The Decred developers
// Copyright (c) 2017-2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -161,7 +161,7 @@ func TestNoQuorum(t *testing.T) {
params := defaultParams(pedro)
bc := newFakeChain(&params)
node := bc.bestNode
node.header.StakeVersion = posVersion
node.stakeVersion = posVersion
// get to svi
curTimestamp := time.Now()
@ -317,7 +317,7 @@ func TestYesQuorum(t *testing.T) {
params := defaultParams(pedro)
bc := newFakeChain(&params)
node := bc.bestNode
node.header.StakeVersion = posVersion
node.stakeVersion = posVersion
// get to svi
curTimestamp := time.Now()
@ -1493,7 +1493,7 @@ func TestVoting(t *testing.T) {
// We have to reset the cache for every test.
bc := newFakeChain(&params)
node := bc.bestNode
node.header.StakeVersion = test.startStakeVersion
node.stakeVersion = test.startStakeVersion
t.Logf("running: %v", test.name)
@ -1526,8 +1526,7 @@ func TestVoting(t *testing.T) {
}
t.Logf("Height %v, Start time %v, curTime %v, delta %v",
node.height, params.Deployments[4][0].StartTime,
node.header.Timestamp.Unix(),
node.header.Timestamp.Unix()-
node.timestamp, node.timestamp-
int64(params.Deployments[4][0].StartTime))
ts, err := bc.ThresholdState(&node.hash, posVersion,
test.vote.Id)
@ -1738,7 +1737,7 @@ func TestParallelVoting(t *testing.T) {
// We have to reset the cache for every test.
bc := newFakeChain(&params)
node := bc.bestNode
node.header.StakeVersion = test.startStakeVersion
node.stakeVersion = test.startStakeVersion
curTimestamp := time.Now()
for k := range test.expectedState[0] {