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.
135 lines
4.4 KiB
Go
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
|
|
}
|