dcrd/blockchain/votebits.go
Dave Collins 9d6cf805f4
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.
2018-01-29 11:35:06 -06:00

135 lines
4.4 KiB
Go

// 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.
package blockchain
import (
"github.com/decred/dcrd/chaincfg"
)
// deploymentChecker provides a thresholdConditionChecker which can be used to
// test a specific deployment rule. This is required for properly detecting
// and activating consensus rule changes.
type deploymentChecker struct {
deployment *chaincfg.ConsensusDeployment
chain *BlockChain
}
// Ensure the deploymentChecker type implements the thresholdConditionChecker
// interface.
var _ thresholdConditionChecker = deploymentChecker{}
// BeginTime returns the unix timestamp for the median block time after which
// voting on a rule change starts (at the next window).
//
// This implementation returns the value defined by the specific deployment the
// checker is associated with.
//
// This is part of the thresholdConditionChecker interface implementation.
func (c deploymentChecker) BeginTime() uint64 {
return c.deployment.StartTime
}
// EndTime returns the unix timestamp for the median block time after which an
// attempted rule change fails if it has not already been locked in or
// activated.
//
// This implementation returns the value defined by the specific deployment the
// checker is associated with.
//
// This is part of the thresholdConditionChecker interface implementation.
func (c deploymentChecker) EndTime() uint64 {
return c.deployment.ExpireTime
}
// RuleChangeActivationQuorum is the minimum votes required to reach quorum.
//
// This implementation returns the value defined by the chain params the checker
// is associated with.
//
// This is part of the thresholdConditionChecker interface implementation.
func (c deploymentChecker) RuleChangeActivationQuorum() uint32 {
return c.chain.chainParams.RuleChangeActivationQuorum
}
// RuleChangeActivationThreshold is the number of votes required to reach the
// threshold as defined by chain params.
//
// This implementation returns the value defined by the chain params the checker
// is associated with.
//
// This is part of the thresholdConditionChecker interface implementation.
func (c deploymentChecker) RuleChangeActivationThreshold(totalVotes uint32) uint32 {
return totalVotes * c.chain.chainParams.RuleChangeActivationMultiplier /
c.chain.chainParams.RuleChangeActivationDivisor
}
// RuleChangeActivationInterval is the number of blocks in each threshold state
// retarget window.
//
// This implementation returns the value defined by the chain params the checker
// is associated with.
//
// This is part of the thresholdConditionChecker interface implementation.
func (c deploymentChecker) RuleChangeActivationInterval() uint32 {
return c.chain.chainParams.RuleChangeActivationInterval
}
// StakeValidationHeight is the minimum height required before votes start
// counting.
//
// This implementation returns the value defined by the chain params the checker
// is associated with.
//
// This is part of the thresholdConditionChecker interface implementation.
func (c deploymentChecker) StakeValidationHeight() int64 {
return c.chain.chainParams.StakeValidationHeight
}
// Condition returns true when the specific bit defined by the deployment
// associated with the checker is set.
//
// This is part of the thresholdConditionChecker interface implementation.
func (c deploymentChecker) Condition(node *blockNode, version uint32) ([]thresholdConditionTally, error) {
if c.deployment.Vote.Mask == 0 {
return []thresholdConditionTally{}, AssertError("invalid mask")
}
// Calculate shift in order to make a zero based index later.
var shift uint16
mask := c.deployment.Vote.Mask
for {
if mask&0x0001 == 0x0001 {
break
}
shift++
mask >>= 1
}
// Setup tally array and iterate over Choices to assemble the vote
// information into the thresholdConditionTally array.
tally := make([]thresholdConditionTally, len(c.deployment.Vote.Choices))
for t, choice := range c.deployment.Vote.Choices {
tally[t].isAbstain = choice.IsAbstain
tally[t].isNo = choice.IsNo
}
for _, vote := range node.votes {
if version != vote.Version {
// Wrong version, ignore.
continue
}
idx := c.deployment.Vote.Mask & vote.Bits >> shift
if int(idx) > len(c.deployment.Vote.Choices)-1 {
// Invalid choice.
continue
}
tally[idx].count += 1
}
return tally, nil
}