[NOD-1469] Implement past median time (#968)

* [NOD-1469] Implement past median time

* [NOD-1469] Move BlueWindow to DAGTraversalManager
This commit is contained in:
Ori Newman 2020-10-27 08:45:47 -07:00 committed by GitHub
parent 97b5b0b875
commit 03790ad8a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 9 deletions

View File

@ -67,8 +67,9 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, databaseContext *dba
ghostdagDataStore,
model.KType(dagParams.K))
dagTraversalManager := dagtraversalmanager.New(
domainDBContext,
dagTopologyManager,
ghostdagManager)
ghostdagDataStore)
pruningManager := pruningmanager.New(
dagTraversalManager,
dagTopologyManager,
@ -92,7 +93,10 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, databaseContext *dba
difficultyManager := difficultymanager.New(
ghostdagManager)
pastMedianTimeManager := pastmediantimemanager.New(
ghostdagManager)
dagParams.TimestampDeviationTolerance,
domainDBContext,
dagTraversalManager,
blockStore)
transactionValidator := transactionvalidator.New(dagParams.BlockCoinbaseMaturity,
domainDBContext,
pastMedianTimeManager,

View File

@ -7,4 +7,5 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
type DAGTraversalManager interface {
HighestChainBlockBelowBlueScore(highHash *externalapi.DomainHash, blueScore uint64) (*externalapi.DomainHash, error)
SelectedParentIterator(highHash *externalapi.DomainHash) (SelectedParentIterator, error)
BlueWindow(highHash *externalapi.DomainHash, windowSize uint64) ([]*externalapi.DomainHash, error)
}

View File

@ -8,17 +8,21 @@ import (
// dagTraversalManager exposes methods for travering blocks
// in the DAG
type dagTraversalManager struct {
databaseContext model.DBContextProxy
dagTopologyManager model.DAGTopologyManager
ghostdagManager model.GHOSTDAGManager
ghostdagDataStore model.GHOSTDAGDataStore
}
// New instantiates a new DAGTraversalManager
func New(
databaseContext model.DBContextProxy,
dagTopologyManager model.DAGTopologyManager,
ghostdagManager model.GHOSTDAGManager) model.DAGTraversalManager {
ghostdagDataStore model.GHOSTDAGDataStore) model.DAGTraversalManager {
return &dagTraversalManager{
databaseContext: databaseContext,
dagTopologyManager: dagTopologyManager,
ghostdagManager: ghostdagManager,
ghostdagDataStore: ghostdagDataStore,
}
}

View File

@ -0,0 +1,41 @@
package dagtraversalmanager
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
// blueBlockWindow returns a blockWindow of the given size that contains the
// blues in the past of startindNode, sorted by GHOSTDAG order.
// If the number of blues in the past of startingNode is less then windowSize,
// the window will be padded by genesis blocks to achieve a size of windowSize.
func (dtm *dagTraversalManager) BlueWindow(startingBlock *externalapi.DomainHash, windowSize uint64) ([]*externalapi.DomainHash, error) {
window := make([]*externalapi.DomainHash, 0, windowSize)
currentHash := startingBlock
currentGHOSTDAGData, err := dtm.ghostdagDataStore.Get(dtm.databaseContext, currentHash)
if err != nil {
return nil, err
}
for uint64(len(window)) < windowSize && currentGHOSTDAGData.SelectedParent != nil {
for _, blue := range currentGHOSTDAGData.MergeSetBlues {
window = append(window, blue)
if uint64(len(window)) == windowSize {
break
}
}
currentHash = currentGHOSTDAGData.SelectedParent
currentGHOSTDAGData, err = dtm.ghostdagDataStore.Get(dtm.databaseContext, currentHash)
if err != nil {
return nil, err
}
}
if uint64(len(window)) < windowSize {
genesis := currentHash
for uint64(len(window)) < windowSize {
window = append(window, genesis)
}
}
return window, nil
}

View File

@ -3,22 +3,64 @@ package pastmediantimemanager
import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/pkg/errors"
"sort"
)
// pastMedianTimeManager provides a method to resolve the
// past median time of a block
type pastMedianTimeManager struct {
ghostdagManager model.GHOSTDAGManager
timestampDeviationTolerance uint64
databaseContext model.DBContextProxy
dagTraversalManager model.DAGTraversalManager
blockStore model.BlockStore
}
// New instantiates a new PastMedianTimeManager
func New(ghostdagManager model.GHOSTDAGManager) model.PastMedianTimeManager {
func New(timestampDeviationTolerance uint64,
databaseContext model.DBContextProxy,
dagTraversalManager model.DAGTraversalManager,
blockStore model.BlockStore) model.PastMedianTimeManager {
return &pastMedianTimeManager{
ghostdagManager: ghostdagManager,
timestampDeviationTolerance: timestampDeviationTolerance,
databaseContext: databaseContext,
dagTraversalManager: dagTraversalManager,
blockStore: blockStore,
}
}
// PastMedianTime returns the past median time for some block
func (pmtm *pastMedianTimeManager) PastMedianTime(blockHash *externalapi.DomainHash) (int64, error) {
return 0, nil
window, err := pmtm.dagTraversalManager.BlueWindow(blockHash, 2*pmtm.timestampDeviationTolerance-1)
if err != nil {
return 0, err
}
return pmtm.windowMedianTimestamp(window)
}
func (pmtm *pastMedianTimeManager) windowMedianTimestamp(window []*externalapi.DomainHash) (int64, error) {
if len(window) == 0 {
return 0, errors.New("Cannot calculate median timestamp for an empty block window")
}
timestamps := make([]int64, len(window))
for i, blockHash := range window {
block, err := pmtm.blockStore.Block(pmtm.databaseContext, blockHash)
if err != nil {
return 0, err
}
// TODO: Use headers store
timestamps[i] = block.Header.TimeInMilliseconds
}
sort.Slice(timestamps, func(i, j int) bool {
return timestamps[i] < timestamps[j]
})
return timestamps[len(timestamps)/2], nil
}