Elichai Turkel 971d50b684
[NOD-1418] Implement DAG Traversal (#953)
* Implement DAG Traversal

* Update the DAGTraversalManager interface
2020-10-29 16:48:41 +02:00

93 lines
2.9 KiB
Go

package dagtraversalmanager
import (
"fmt"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
// dagTraversalManager exposes methods for travering blocks
// in the DAG
type dagTraversalManager struct {
databaseContext model.DBReader
dagTopologyManager model.DAGTopologyManager
ghostdagDataStore model.GHOSTDAGDataStore
}
// selectedParentIterator implements the `model.SelectedParentIterator` API
type selectedParentIterator struct {
databaseContext model.DBReader
ghostdagDataStore model.GHOSTDAGDataStore
current *externalapi.DomainHash
}
func (spi *selectedParentIterator) Next() bool {
if spi.current == nil {
return false
}
ghostdagData, err := spi.ghostdagDataStore.Get(spi.databaseContext, spi.current)
if err != nil {
panic(fmt.Sprintf("ghostdagDataStore is missing ghostdagData for: %v. '%s' ", spi.current, err))
}
spi.current = ghostdagData.SelectedParent
return spi.current != nil
}
func (spi *selectedParentIterator) Get() *externalapi.DomainHash {
return spi.current
}
// New instantiates a new DAGTraversalManager
func New(
databaseContext model.DBReader,
dagTopologyManager model.DAGTopologyManager,
ghostdagDataStore model.GHOSTDAGDataStore) model.DAGTraversalManager {
return &dagTraversalManager{
databaseContext: databaseContext,
dagTopologyManager: dagTopologyManager,
ghostdagDataStore: ghostdagDataStore,
}
}
// SelectedParentIterator creates an iterator over the selected
// parent chain of the given highHash
func (dtm *dagTraversalManager) SelectedParentIterator(highHash *externalapi.DomainHash) model.SelectedParentIterator {
return &selectedParentIterator{
databaseContext: dtm.databaseContext,
ghostdagDataStore: dtm.ghostdagDataStore,
current: highHash,
}
}
// HighestChainBlockBelowBlueScore returns the hash of the
// highest block with a blue score lower than the given
// blueScore in the block with the given highHash's selected
// parent chain
func (dtm *dagTraversalManager) HighestChainBlockBelowBlueScore(highHash *externalapi.DomainHash, blueScore uint64) (*externalapi.DomainHash, error) {
blockHash := highHash
chainBlock, err := dtm.ghostdagDataStore.Get(dtm.databaseContext, highHash)
if err != nil {
return nil, err
}
if chainBlock.BlueScore < blueScore { // will practically return genesis.
blueScore = chainBlock.BlueScore
}
requiredBlueScore := chainBlock.BlueScore - blueScore
// If we used `SelectedParentIterator` we'd need to do more calls to `ghostdagDataStore` so we can get the blueScore
for chainBlock.BlueScore >= requiredBlueScore {
if chainBlock.SelectedParent == nil { // genesis
return blockHash, nil
}
blockHash = chainBlock.SelectedParent
chainBlock, err = dtm.ghostdagDataStore.Get(dtm.databaseContext, highHash)
if err != nil {
return nil, err
}
}
return blockHash, nil
}