mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-08-13 22:23:15 +00:00

* Remove HF1 activation code * Remove test with an overflow * Fix "max sompi amount is never dust" * Remove estimatedHeaderUpperBound logic
263 lines
8.8 KiB
Go
263 lines
8.8 KiB
Go
package mergedepthmanager
|
|
|
|
import (
|
|
"github.com/kaspanet/kaspad/domain/consensus/model"
|
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
|
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type mergeDepthManager struct {
|
|
databaseContext model.DBReader
|
|
dagTopologyManager model.DAGTopologyManager
|
|
dagTraversalManager model.DAGTraversalManager
|
|
finalityManager model.FinalityManager
|
|
|
|
genesisHash *externalapi.DomainHash
|
|
mergeDepth uint64
|
|
|
|
ghostdagDataStore model.GHOSTDAGDataStore
|
|
mergeDepthRootStore model.MergeDepthRootStore
|
|
daaBlocksStore model.DAABlocksStore
|
|
pruningStore model.PruningStore
|
|
finalityStore model.FinalityStore
|
|
}
|
|
|
|
// New instantiates a new MergeDepthManager
|
|
func New(
|
|
databaseContext model.DBReader,
|
|
dagTopologyManager model.DAGTopologyManager,
|
|
dagTraversalManager model.DAGTraversalManager,
|
|
finalityManager model.FinalityManager,
|
|
|
|
genesisHash *externalapi.DomainHash,
|
|
mergeDepth uint64,
|
|
|
|
ghostdagDataStore model.GHOSTDAGDataStore,
|
|
mergeDepthRootStore model.MergeDepthRootStore,
|
|
daaBlocksStore model.DAABlocksStore,
|
|
pruningStore model.PruningStore,
|
|
finalityStore model.FinalityStore) model.MergeDepthManager {
|
|
|
|
return &mergeDepthManager{
|
|
databaseContext: databaseContext,
|
|
dagTopologyManager: dagTopologyManager,
|
|
dagTraversalManager: dagTraversalManager,
|
|
finalityManager: finalityManager,
|
|
genesisHash: genesisHash,
|
|
mergeDepth: mergeDepth,
|
|
ghostdagDataStore: ghostdagDataStore,
|
|
mergeDepthRootStore: mergeDepthRootStore,
|
|
daaBlocksStore: daaBlocksStore,
|
|
pruningStore: pruningStore,
|
|
finalityStore: finalityStore,
|
|
}
|
|
|
|
}
|
|
|
|
// CheckBoundedMergeDepth is used for validation, so must follow the HF1 DAA score for determining the correct depth to verify
|
|
func (mdm *mergeDepthManager) CheckBoundedMergeDepth(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash, isBlockWithTrustedData bool) error {
|
|
ghostdagData, err := mdm.ghostdagDataStore.Get(mdm.databaseContext, stagingArea, blockHash, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Return nil on genesis
|
|
if ghostdagData.SelectedParent() == nil {
|
|
return nil
|
|
}
|
|
|
|
mergeDepthRoot, err := mdm.MergeDepthRoot(stagingArea, blockHash, isBlockWithTrustedData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// We call FinalityPoint in order to save it to storage.
|
|
_, err = mdm.finalityManager.FinalityPoint(stagingArea, blockHash, isBlockWithTrustedData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nonBoundedMergeDepthViolatingBlues, err := mdm.NonBoundedMergeDepthViolatingBlues(stagingArea, blockHash, mergeDepthRoot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, red := range ghostdagData.MergeSetReds() {
|
|
doesRedHaveMergeRootInPast, err := mdm.dagTopologyManager.IsAncestorOf(stagingArea, mergeDepthRoot, red)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if doesRedHaveMergeRootInPast {
|
|
continue
|
|
}
|
|
|
|
isRedInPastOfAnyNonMergeDepthViolatingBlue, err :=
|
|
mdm.dagTopologyManager.IsAncestorOfAny(stagingArea, red, nonBoundedMergeDepthViolatingBlues)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !isRedInPastOfAnyNonMergeDepthViolatingBlue {
|
|
return errors.Wrapf(ruleerrors.ErrViolatingBoundedMergeDepth, "block is violating bounded merge depth")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (mdm *mergeDepthManager) NonBoundedMergeDepthViolatingBlues(
|
|
stagingArea *model.StagingArea, blockHash, mergeDepthRoot *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
|
|
|
ghostdagData, err := mdm.ghostdagDataStore.Get(mdm.databaseContext, stagingArea, blockHash, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nonBoundedMergeDepthViolatingBlues := make([]*externalapi.DomainHash, 0, len(ghostdagData.MergeSetBlues()))
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, blue := range ghostdagData.MergeSetBlues() {
|
|
isMergeDepthRootInSelectedChainOfBlue, err := mdm.dagTopologyManager.IsInSelectedParentChainOf(stagingArea, mergeDepthRoot, blue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if isMergeDepthRootInSelectedChainOfBlue {
|
|
nonBoundedMergeDepthViolatingBlues = append(nonBoundedMergeDepthViolatingBlues, blue)
|
|
}
|
|
}
|
|
|
|
return nonBoundedMergeDepthViolatingBlues, nil
|
|
}
|
|
|
|
func (mdm *mergeDepthManager) VirtualMergeDepthRoot(stagingArea *model.StagingArea) (*externalapi.DomainHash, error) {
|
|
log.Tracef("VirtualMergeDepthRoot start")
|
|
defer log.Tracef("VirtualMergeDepthRoot end")
|
|
|
|
virtualMergeDepthRoot, err := mdm.calculateMergeDepthRoot(stagingArea, model.VirtualBlockHash, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
log.Debugf("The current virtual merge depth root is: %s", virtualMergeDepthRoot)
|
|
|
|
return virtualMergeDepthRoot, nil
|
|
}
|
|
|
|
func (mdm *mergeDepthManager) MergeDepthRoot(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash, isBlockWithTrustedData bool) (*externalapi.DomainHash, error) {
|
|
log.Tracef("MergeDepthRoot start")
|
|
defer log.Tracef("MergeDepthRoot end")
|
|
if blockHash.Equal(model.VirtualBlockHash) {
|
|
return mdm.VirtualMergeDepthRoot(stagingArea)
|
|
}
|
|
root, err := mdm.mergeDepthRootStore.MergeDepthRoot(mdm.databaseContext, stagingArea, blockHash)
|
|
if err != nil {
|
|
log.Debugf("%s merge depth root not found in store - calculating", blockHash)
|
|
if errors.Is(err, database.ErrNotFound) {
|
|
return mdm.calculateAndStageMergeDepthRoot(stagingArea, blockHash, isBlockWithTrustedData)
|
|
}
|
|
return nil, err
|
|
}
|
|
return root, nil
|
|
}
|
|
|
|
func (mdm *mergeDepthManager) calculateAndStageMergeDepthRoot(
|
|
stagingArea *model.StagingArea, blockHash *externalapi.DomainHash, isBlockWithTrustedData bool) (*externalapi.DomainHash, error) {
|
|
|
|
root, err := mdm.calculateMergeDepthRoot(stagingArea, blockHash, isBlockWithTrustedData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
mdm.mergeDepthRootStore.StageMergeDepthRoot(stagingArea, blockHash, root)
|
|
return root, nil
|
|
}
|
|
|
|
func (mdm *mergeDepthManager) calculateMergeDepthRoot(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash, isBlockWithTrustedData bool) (
|
|
*externalapi.DomainHash, error) {
|
|
|
|
log.Tracef("calculateMergeDepthRoot start")
|
|
defer log.Tracef("calculateMergeDepthRoot end")
|
|
|
|
if isBlockWithTrustedData {
|
|
return model.VirtualGenesisBlockHash, nil
|
|
}
|
|
|
|
ghostdagData, err := mdm.ghostdagDataStore.Get(mdm.databaseContext, stagingArea, blockHash, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if ghostdagData.BlueScore() < mdm.mergeDepth {
|
|
log.Debugf("%s blue score lower then merge depth - returning genesis as merge depth root", blockHash)
|
|
return mdm.genesisHash, nil
|
|
}
|
|
|
|
pruningPoint, err := mdm.pruningStore.PruningPoint(mdm.databaseContext, stagingArea)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pruningPointGhostdagData, err := mdm.ghostdagDataStore.Get(mdm.databaseContext, stagingArea, pruningPoint, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ghostdagData.BlueScore() < pruningPointGhostdagData.BlueScore()+mdm.mergeDepth {
|
|
log.Debugf("%s blue score less than merge depth over pruning point - returning virtual genesis as merge depth root", blockHash)
|
|
return model.VirtualGenesisBlockHash, nil
|
|
}
|
|
isPruningPointOnChain, err := mdm.dagTopologyManager.IsInSelectedParentChainOf(stagingArea, pruningPoint, blockHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !isPruningPointOnChain {
|
|
log.Debugf("pruning point not in selected chain of %s - returning virtual genesis as merge depth root", blockHash)
|
|
return model.VirtualGenesisBlockHash, nil
|
|
}
|
|
|
|
selectedParent := ghostdagData.SelectedParent()
|
|
if selectedParent.Equal(mdm.genesisHash) {
|
|
return mdm.genesisHash, nil
|
|
}
|
|
|
|
current, err := mdm.mergeDepthRootStore.MergeDepthRoot(mdm.databaseContext, stagingArea, ghostdagData.SelectedParent())
|
|
if database.IsNotFoundError(err) {
|
|
// This should only occur for a few blocks following the upgrade
|
|
log.Debugf("merge point root not in store for %s, falling back to finality point", ghostdagData.SelectedParent())
|
|
current, err = mdm.finalityStore.FinalityPoint(mdm.databaseContext, stagingArea, ghostdagData.SelectedParent())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
// In this case we expect the pruning point or a block above it to be the merge depth root.
|
|
// Note that above we already verified the chain and distance conditions for this
|
|
if current.Equal(model.VirtualGenesisBlockHash) {
|
|
current = pruningPoint
|
|
}
|
|
|
|
requiredBlueScore := ghostdagData.BlueScore() - mdm.mergeDepth
|
|
log.Debugf("%s's merge depth root is the one having the highest blue score lower then %d", blockHash, requiredBlueScore)
|
|
|
|
var next *externalapi.DomainHash
|
|
for {
|
|
next, err = mdm.dagTopologyManager.ChildInSelectedParentChainOf(stagingArea, current, blockHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
nextGHOSTDAGData, err := mdm.ghostdagDataStore.Get(mdm.databaseContext, stagingArea, next, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if nextGHOSTDAGData.BlueScore() >= requiredBlueScore {
|
|
log.Debugf("%s's merge depth root is %s", blockHash, current)
|
|
return current, nil
|
|
}
|
|
|
|
current = next
|
|
}
|
|
}
|