mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-11-27 07:48:44 +00:00
Just some name changes, put in a stand in emission amount, and started copying the algo from Karlsen. Not release worthy yet. Therefore Dev branch exists now. Also, for now this is for research purposes only. I got no clue what to build on top of Kaspa yet. Help would be appreciated for ideas and implementations.
205 lines
6.3 KiB
Go
205 lines
6.3 KiB
Go
package flowcontext
|
|
|
|
import (
|
|
"github.com/zoomy-network/zoomyd/domain/consensus/model/externalapi"
|
|
"github.com/zoomy-network/zoomyd/domain/consensus/ruleerrors"
|
|
"github.com/zoomy-network/zoomyd/domain/consensus/utils/consensushashing"
|
|
"github.com/zoomy-network/zoomyd/domain/consensus/utils/hashset"
|
|
"github.com/zoomy-network/zoomyd/infrastructure/logger"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// maxOrphans is the maximum amount of orphans allowed in the
|
|
// orphans collection. This number is an approximation of how
|
|
// many orphans there can possibly be on average. It is based
|
|
// on: 2^orphanResolutionRange * PHANTOM K.
|
|
const maxOrphans = 600
|
|
|
|
// AddOrphan adds the block to the orphan set
|
|
func (f *FlowContext) AddOrphan(orphanBlock *externalapi.DomainBlock) {
|
|
f.orphansMutex.Lock()
|
|
defer f.orphansMutex.Unlock()
|
|
|
|
orphanHash := consensushashing.BlockHash(orphanBlock)
|
|
f.orphans[*orphanHash] = orphanBlock
|
|
|
|
if len(f.orphans) > maxOrphans {
|
|
log.Debugf("Orphan collection size exceeded. Evicting a random orphan")
|
|
f.evictRandomOrphan()
|
|
}
|
|
|
|
log.Infof("Received a block with missing parents, adding to orphan pool: %s", orphanHash)
|
|
}
|
|
|
|
func (f *FlowContext) evictRandomOrphan() {
|
|
var toEvict externalapi.DomainHash
|
|
for hash := range f.orphans {
|
|
toEvict = hash
|
|
break
|
|
}
|
|
delete(f.orphans, toEvict)
|
|
log.Debugf("Evicted %s from the orphan collection", toEvict)
|
|
}
|
|
|
|
// IsOrphan returns whether the given blockHash belongs to an orphan block
|
|
func (f *FlowContext) IsOrphan(blockHash *externalapi.DomainHash) bool {
|
|
f.orphansMutex.RLock()
|
|
defer f.orphansMutex.RUnlock()
|
|
|
|
_, ok := f.orphans[*blockHash]
|
|
return ok
|
|
}
|
|
|
|
// UnorphanBlocks removes the block from the orphan set, and remove all of the blocks that are not orphans anymore.
|
|
func (f *FlowContext) UnorphanBlocks(rootBlock *externalapi.DomainBlock) ([]*externalapi.DomainBlock, error) {
|
|
f.orphansMutex.Lock()
|
|
defer f.orphansMutex.Unlock()
|
|
|
|
// Find all the children of rootBlock among the orphans
|
|
// and add them to the process queue
|
|
rootBlockHash := consensushashing.BlockHash(rootBlock)
|
|
processQueue := f.addChildOrphansToProcessQueue(rootBlockHash, []externalapi.DomainHash{})
|
|
|
|
var unorphanedBlocks []*externalapi.DomainBlock
|
|
for len(processQueue) > 0 {
|
|
var orphanHash externalapi.DomainHash
|
|
orphanHash, processQueue = processQueue[0], processQueue[1:]
|
|
orphanBlock := f.orphans[orphanHash]
|
|
|
|
log.Debugf("Considering to unorphan block %s with parents %s",
|
|
orphanHash, orphanBlock.Header.DirectParents())
|
|
|
|
canBeUnorphaned := true
|
|
for _, orphanBlockParentHash := range orphanBlock.Header.DirectParents() {
|
|
orphanBlockParentInfo, err := f.domain.Consensus().GetBlockInfo(orphanBlockParentHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !orphanBlockParentInfo.Exists || orphanBlockParentInfo.BlockStatus == externalapi.StatusHeaderOnly {
|
|
log.Debugf("Cannot unorphan block %s. It's missing at "+
|
|
"least the following parent: %s", orphanHash, orphanBlockParentHash)
|
|
|
|
canBeUnorphaned = false
|
|
break
|
|
}
|
|
}
|
|
if canBeUnorphaned {
|
|
unorphaningSucceeded, err := f.unorphanBlock(orphanHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if unorphaningSucceeded {
|
|
unorphanedBlocks = append(unorphanedBlocks, orphanBlock)
|
|
processQueue = f.addChildOrphansToProcessQueue(&orphanHash, processQueue)
|
|
}
|
|
}
|
|
}
|
|
|
|
return unorphanedBlocks, nil
|
|
}
|
|
|
|
// addChildOrphansToProcessQueue finds all child orphans of `blockHash`
|
|
// and adds them to the given `processQueue` if they don't already exist
|
|
// inside of it
|
|
// Note that this method does not modify the given `processQueue`
|
|
func (f *FlowContext) addChildOrphansToProcessQueue(blockHash *externalapi.DomainHash,
|
|
processQueue []externalapi.DomainHash) []externalapi.DomainHash {
|
|
|
|
blockChildren := f.findChildOrphansOfBlock(blockHash)
|
|
for _, blockChild := range blockChildren {
|
|
exists := false
|
|
for _, queueOrphan := range processQueue {
|
|
if queueOrphan == blockChild {
|
|
exists = true
|
|
break
|
|
}
|
|
}
|
|
if !exists {
|
|
processQueue = append(processQueue, blockChild)
|
|
}
|
|
}
|
|
return processQueue
|
|
}
|
|
|
|
func (f *FlowContext) findChildOrphansOfBlock(blockHash *externalapi.DomainHash) []externalapi.DomainHash {
|
|
var childOrphans []externalapi.DomainHash
|
|
for orphanHash, orphanBlock := range f.orphans {
|
|
for _, orphanBlockParentHash := range orphanBlock.Header.DirectParents() {
|
|
if orphanBlockParentHash.Equal(blockHash) {
|
|
childOrphans = append(childOrphans, orphanHash)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return childOrphans
|
|
}
|
|
|
|
func (f *FlowContext) unorphanBlock(orphanHash externalapi.DomainHash) (bool, error) {
|
|
orphanBlock, ok := f.orphans[orphanHash]
|
|
if !ok {
|
|
return false, errors.Errorf("attempted to unorphan a non-orphan block %s", orphanHash)
|
|
}
|
|
delete(f.orphans, orphanHash)
|
|
|
|
err := f.domain.Consensus().ValidateAndInsertBlock(orphanBlock, true)
|
|
if err != nil {
|
|
if errors.As(err, &ruleerrors.RuleError{}) {
|
|
log.Warnf("Validation failed for orphan block %s: %s", orphanHash, err)
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
|
|
log.Infof("Unorphaned block %s", orphanHash)
|
|
return true, nil
|
|
}
|
|
|
|
// GetOrphanRoots returns the roots of the missing ancestors DAG of the given orphan
|
|
func (f *FlowContext) GetOrphanRoots(orphan *externalapi.DomainHash) ([]*externalapi.DomainHash, bool, error) {
|
|
onEnd := logger.LogAndMeasureExecutionTime(log, "GetOrphanRoots")
|
|
defer onEnd()
|
|
|
|
f.orphansMutex.RLock()
|
|
defer f.orphansMutex.RUnlock()
|
|
|
|
_, ok := f.orphans[*orphan]
|
|
if !ok {
|
|
return nil, false, nil
|
|
}
|
|
|
|
queue := []*externalapi.DomainHash{orphan}
|
|
addedToQueueSet := hashset.New()
|
|
addedToQueueSet.Add(orphan)
|
|
|
|
roots := []*externalapi.DomainHash{}
|
|
for len(queue) > 0 {
|
|
var current *externalapi.DomainHash
|
|
current, queue = queue[0], queue[1:]
|
|
|
|
block, ok := f.orphans[*current]
|
|
if !ok {
|
|
blockInfo, err := f.domain.Consensus().GetBlockInfo(current)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
if !blockInfo.Exists || blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
|
|
roots = append(roots, current)
|
|
} else {
|
|
log.Debugf("Block %s was skipped when checking for orphan roots: "+
|
|
"exists: %t, status: %s", current, blockInfo.Exists, blockInfo.BlockStatus)
|
|
}
|
|
continue
|
|
}
|
|
|
|
for _, parent := range block.Header.DirectParents() {
|
|
if !addedToQueueSet.Contains(parent) {
|
|
queue = append(queue, parent)
|
|
addedToQueueSet.Add(parent)
|
|
}
|
|
}
|
|
}
|
|
|
|
return roots, true, nil
|
|
}
|