mirror of
https://github.com/kaspanet/kaspad.git
synced 2026-03-08 01:51:39 +00:00
* [DEV-74] Implemented and written tests for utxoIterator. * [DEV-74] Improved utxoIterator tests. * [DEV-74] Implemented utxoCollection tests. * [DEV-74] Implemented utxoDiff and its tests. * [DEV-74] Implemented utxoSet. * [DEV -74] Added tests for fullUTXOSet. * [DEV-74] Added some tests for diffUTXOSet. * [DEV-74] Wrote tests for diffUTXOSet iterator. * [DEV-74] Added a negative test for diffUTXOSet.withDiff. * [DEV-74] Wrote tests for addTx. * [DEV-74] Wrote a toRemove test for addTx. * [DEV-74] Changed blockNode.utxoDiff to be of type utxoDiff. * [DEV-74] Removed superfluous whitespace. * [DEV-74] Renamed confusing "previousHash" to "hash". * [DEV-74] Fixed bad test and corrected failing test. * [DEV-74] Moved confusing "negatives" test to be part of the general utxoCollection test. * [DEV-74] Removed utxoDiff.inverted. * [DEV-74] Renamed blockNode.utxoDiff to blockNode.diff. * [DEV-74] Renamed diff to diffFrom for clarity's sake. * [DEV-74] Converted the type of utxoCollection from map[daghash.Hash]map[uint32]*wire.TxOut to map[wire.OutPoint]*UtxoEntry. * [DEV-74] Corrected test names in utxoCollection_test. * [DEV-74] Removed superfluous utxoCollection iterator and moved utxoIterator into utxoset.go. * [DEV-74] Renamed variables in utxoset.go. * [DEV-74] Renamed verifyTx to areInputsInUTXO and removed a superfulous test. * [DEV-74] Fixed bad test logic in TestDiffUTXOSet_addTx. * [DEV-74] Added a few comments. Added reference-equals checks to clone functions. * [DEV-74] Moved utxoCollection and utxoDiff into utxoset.go. * [DEV-74] Wrote explanations for utxoCollection and utxoDiff tests. * [DEV-74] Wrote explanations for all utxoSet tests besides addTx. * [DEV-74] Wrote explanations for TestDiffUTXOSet_addTx. * [DEV-74] Moved the documentation for utxoDiff into utxoset.go. * [DEV-74] Wrote an explanation on utxoSet. * [DEV-75] Found a typo. * [DEV-75] Renamed dag -> virtual, dagView -> virtualBlock. * [DEV-75] Renamed newDAGView to newVirtualBlock. * [DEV-75] Moved queries for the genesis block from virtualBlock to BlockDAG. * [DEV-75] Got rid of chainView height and findFork. * [DEV-75] Renamed receivers from c to v. * [DEV-75] Updated initBlockNode to allow for virtual (headerless) nodes, updated dbDAGState to contain multiple tip hashes, implemented virtualBlock.setTips. * [DEV-75] Got rid of virtualBlock.equals, which was not used anywhere. * [DEV-75] Got rid of virtualBlock.tip(). * [DEV-75] Got rid of SetTip everywhere except for tests. * [DEV-75] Got rid of Next(). * [DEV-75] Got rid of Contains(). * [DEV-75] Got rid of HeightRange(), as no one was using it. * [DEV-75] Made verifyDAG in rpcserver.go not use block height for iteration. * [DEV-75] Got rid of the part of Manager.Init() that handled "catching up" for side chains, which allowed me to get rid of BlockDAG.BlockByHeight(). * [DEV-75] Dropped support for the RPC command getblockhash since it was getting blocks by their height. * [DEV-75] Dropped getnetworkhashps since it was reliant on height, fixed another couple of RPC commands to return nextHashes instead of a nextHash, and got rid of nodeByHeight in virtualBlock. * [DEV-75] Got rid of setTip(). * [DEV-75] Moved blockLocator() out of virtualBlock and into BlockDAG. Also removed TestLocateInventory(). * [DEV-75] Implemented addTip(). * [DEV-75] Cleaned up virtualblock.go a bit. * [DEV-75] Erased irrelevant tests in virtualblock_test.go. Moved dag-related tests into dag_test.go. * [DEV-75] Removed unnecessary nil check. * [DEV-75] Wrote tests for virtualBlock. * [DEV-75] Fixed bad test, added explanations to tests. * [DEV-89] Fixed a comment. * [DEV-89] Fixed another comment. * [DEV-89] Removed the section in Manager::Init that handled rolling back indexes to the main chain if their tip is an orphaned fork. This could only happen during reorg, which no longer exists. Also removed BlockDAG::MainChainHasBlock, which was no longer used by anyone. * [DEV-89] Removed the nil check inside initBlockNode() and amended the one place that called it with nil. * [DEV-89] Renamed the receiver param for BlockDAG from b to dag. * [DEV-89] Moved fastLog2Floor from dag.go to btcutil/btcmath.go. * [DEV-89] Renamed tstTip to testTip. * [DEV-89] Renamed phanom_test.go to phantom_test.go. * [DEV-89] Fixed comments, renamed mainChainHeight to dagHeight. * [DEV-89] Rewrote virtualBlock.addTip(). * [DEV-89] Fixed a comment. (chain -> DAG) * [DEV-89] Fixed another chain -> DAG comment.
302 lines
10 KiB
Go
302 lines
10 KiB
Go
// Copyright (c) 2016-2017 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package blockdag
|
|
|
|
import (
|
|
"math"
|
|
|
|
"github.com/daglabs/btcd/dagconfig"
|
|
)
|
|
|
|
const (
|
|
// vbLegacyBlockVersion is the highest legacy block version before the
|
|
// version bits scheme became active.
|
|
vbLegacyBlockVersion = 4
|
|
|
|
// vbTopBits defines the bits to set in the version to signal that the
|
|
// version bits scheme is being used.
|
|
vbTopBits = 0x20000000
|
|
|
|
// vbTopMask is the bitmask to use to determine whether or not the
|
|
// version bits scheme is in use.
|
|
vbTopMask = 0xe0000000
|
|
|
|
// vbNumBits is the total number of bits available for use with the
|
|
// version bits scheme.
|
|
vbNumBits = 29
|
|
|
|
// unknownVerNumToCheck is the number of previous blocks to consider
|
|
// when checking for a threshold of unknown block versions for the
|
|
// purposes of warning the user.
|
|
unknownVerNumToCheck = 100
|
|
|
|
// unknownVerWarnNum is the threshold of previous blocks that have an
|
|
// unknown version to use for the purposes of warning the user.
|
|
unknownVerWarnNum = unknownVerNumToCheck / 2
|
|
)
|
|
|
|
// bitConditionChecker provides a thresholdConditionChecker which can be used to
|
|
// test whether or not a specific bit is set when it's not supposed to be
|
|
// according to the expected version based on the known deployments and the
|
|
// current state of the chain. This is useful for detecting and warning about
|
|
// unknown rule activations.
|
|
type bitConditionChecker struct {
|
|
bit uint32
|
|
chain *BlockDAG
|
|
}
|
|
|
|
// Ensure the bitConditionChecker type implements the thresholdConditionChecker
|
|
// interface.
|
|
var _ thresholdConditionChecker = bitConditionChecker{}
|
|
|
|
// BeginTime returns the unix timestamp for the median block time after which
|
|
// voting on a rule change starts (at the next window).
|
|
//
|
|
// Since this implementation checks for unknown rules, it returns 0 so the rule
|
|
// is always treated as active.
|
|
//
|
|
// This is part of the thresholdConditionChecker interface implementation.
|
|
func (c bitConditionChecker) BeginTime() uint64 {
|
|
return 0
|
|
}
|
|
|
|
// 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.
|
|
//
|
|
// Since this implementation checks for unknown rules, it returns the maximum
|
|
// possible timestamp so the rule is always treated as active.
|
|
//
|
|
// This is part of the thresholdConditionChecker interface implementation.
|
|
func (c bitConditionChecker) EndTime() uint64 {
|
|
return math.MaxUint64
|
|
}
|
|
|
|
// RuleChangeActivationThreshold is the number of blocks for which the condition
|
|
// must be true in order to lock in a rule change.
|
|
//
|
|
// 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 bitConditionChecker) RuleChangeActivationThreshold() uint32 {
|
|
return c.chain.dagParams.RuleChangeActivationThreshold
|
|
}
|
|
|
|
// MinerConfirmationWindow 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 bitConditionChecker) MinerConfirmationWindow() uint32 {
|
|
return c.chain.dagParams.MinerConfirmationWindow
|
|
}
|
|
|
|
// Condition returns true when the specific bit associated with the checker is
|
|
// set and it's not supposed to be according to the expected version based on
|
|
// the known deployments and the current state of the chain.
|
|
//
|
|
// This function MUST be called with the chain state lock held (for writes).
|
|
//
|
|
// This is part of the thresholdConditionChecker interface implementation.
|
|
func (c bitConditionChecker) Condition(node *blockNode) (bool, error) {
|
|
conditionMask := uint32(1) << c.bit
|
|
version := uint32(node.version)
|
|
if version&vbTopMask != vbTopBits {
|
|
return false, nil
|
|
}
|
|
if version&conditionMask == 0 {
|
|
return false, nil
|
|
}
|
|
|
|
expectedVersion, err := c.chain.calcNextBlockVersion(node.selectedParent)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return uint32(expectedVersion)&conditionMask == 0, nil
|
|
}
|
|
|
|
// 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 *dagconfig.ConsensusDeployment
|
|
chain *BlockDAG
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// RuleChangeActivationThreshold is the number of blocks for which the condition
|
|
// must be true in order to lock in a rule change.
|
|
//
|
|
// 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() uint32 {
|
|
return c.chain.dagParams.RuleChangeActivationThreshold
|
|
}
|
|
|
|
// MinerConfirmationWindow 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) MinerConfirmationWindow() uint32 {
|
|
return c.chain.dagParams.MinerConfirmationWindow
|
|
}
|
|
|
|
// 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) (bool, error) {
|
|
conditionMask := uint32(1) << c.deployment.BitNumber
|
|
version := uint32(node.version)
|
|
return (version&vbTopMask == vbTopBits) && (version&conditionMask != 0),
|
|
nil
|
|
}
|
|
|
|
// calcNextBlockVersion calculates the expected version of the block after the
|
|
// passed previous block node based on the state of started and locked in
|
|
// rule change deployments.
|
|
//
|
|
// This function differs from the exported CalcNextBlockVersion in that the
|
|
// exported version uses the current best chain as the previous block node
|
|
// while this function accepts any block node.
|
|
//
|
|
// This function MUST be called with the chain state lock held (for writes).
|
|
func (dag *BlockDAG) calcNextBlockVersion(prevNode *blockNode) (int32, error) {
|
|
// Set the appropriate bits for each actively defined rule deployment
|
|
// that is either in the process of being voted on, or locked in for the
|
|
// activation at the next threshold window change.
|
|
expectedVersion := uint32(vbTopBits)
|
|
for id := 0; id < len(dag.dagParams.Deployments); id++ {
|
|
deployment := &dag.dagParams.Deployments[id]
|
|
cache := &dag.deploymentCaches[id]
|
|
checker := deploymentChecker{deployment: deployment, chain: dag}
|
|
state, err := dag.thresholdState(prevNode, checker, cache)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if state == ThresholdStarted || state == ThresholdLockedIn {
|
|
expectedVersion |= uint32(1) << deployment.BitNumber
|
|
}
|
|
}
|
|
return int32(expectedVersion), nil
|
|
}
|
|
|
|
// CalcNextBlockVersion calculates the expected version of the block after the
|
|
// end of the current best chain based on the state of started and locked in
|
|
// rule change deployments.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (dag *BlockDAG) CalcNextBlockVersion() (int32, error) {
|
|
dag.dagLock.Lock()
|
|
version, err := dag.calcNextBlockVersion(dag.virtual.SelectedTip())
|
|
dag.dagLock.Unlock()
|
|
return version, err
|
|
}
|
|
|
|
// warnUnknownRuleActivations displays a warning when any unknown new rules are
|
|
// either about to activate or have been activated. This will only happen once
|
|
// when new rules have been activated and every block for those about to be
|
|
// activated.
|
|
//
|
|
// This function MUST be called with the chain state lock held (for writes)
|
|
func (dag *BlockDAG) warnUnknownRuleActivations(node *blockNode) error {
|
|
// Warn if any unknown new rules are either about to activate or have
|
|
// already been activated.
|
|
for bit := uint32(0); bit < vbNumBits; bit++ {
|
|
checker := bitConditionChecker{bit: bit, chain: dag}
|
|
cache := &dag.warningCaches[bit]
|
|
state, err := dag.thresholdState(node.selectedParent, checker, cache)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch state {
|
|
case ThresholdActive:
|
|
if !dag.unknownRulesWarned {
|
|
log.Warnf("Unknown new rules activated (bit %d)",
|
|
bit)
|
|
dag.unknownRulesWarned = true
|
|
}
|
|
|
|
case ThresholdLockedIn:
|
|
window := int32(checker.MinerConfirmationWindow())
|
|
activationHeight := window - (node.height % window)
|
|
log.Warnf("Unknown new rules are about to activate in "+
|
|
"%d blocks (bit %d)", activationHeight, bit)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// warnUnknownVersions logs a warning if a high enough percentage of the last
|
|
// blocks have unexpected versions.
|
|
//
|
|
// This function MUST be called with the chain state lock held (for writes)
|
|
func (dag *BlockDAG) warnUnknownVersions(node *blockNode) error {
|
|
// Nothing to do if already warned.
|
|
if dag.unknownVersionsWarned {
|
|
return nil
|
|
}
|
|
|
|
// Warn if enough previous blocks have unexpected versions.
|
|
numUpgraded := uint32(0)
|
|
for i := uint32(0); i < unknownVerNumToCheck && node != nil; i++ {
|
|
expectedVersion, err := dag.calcNextBlockVersion(node.selectedParent)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if expectedVersion > vbLegacyBlockVersion &&
|
|
(node.version & ^expectedVersion) != 0 {
|
|
|
|
numUpgraded++
|
|
}
|
|
|
|
node = node.selectedParent
|
|
}
|
|
if numUpgraded > unknownVerWarnNum {
|
|
log.Warn("Unknown block versions are being mined, so new " +
|
|
"rules might be in effect. Are you running the " +
|
|
"latest version of the software?")
|
|
dag.unknownVersionsWarned = true
|
|
}
|
|
|
|
return nil
|
|
}
|