[NOD-1548] Re-add test difficulty + Make GHOSTDAGData immutable + don't clone in store (#1178)

* [NOD-1548] Readd TestDifficulty

* [NOD-1548] Make GHOSTDAGData immutable + don't clone in store

Co-authored-by: Ori Newman <orinewman1@gmail.com>
This commit is contained in:
Svarog 2020-12-06 12:35:14 +02:00 committed by GitHub
parent f97b8f7580
commit 33eaf9edac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 445 additions and 214 deletions

View File

@ -134,7 +134,7 @@ func (s *consensus) GetBlockInfo(blockHash *externalapi.DomainHash) (*externalap
return nil, err
}
blockInfo.BlueScore = ghostdagData.BlueScore
blockInfo.BlueScore = ghostdagData.BlueScore()
isBlockInHeaderPruningPointFuture, err := s.syncManager.IsBlockInHeaderPruningPointFuture(blockHash)
if err != nil {
@ -196,7 +196,7 @@ func (s *consensus) GetVirtualSelectedParent() (*externalapi.DomainBlock, error)
if err != nil {
return nil, err
}
return s.blockStore.Block(s.databaseContext, virtualGHOSTDAGData.SelectedParent)
return s.blockStore.Block(s.databaseContext, virtualGHOSTDAGData.SelectedParent())
}
func (s *consensus) CreateBlockLocator(lowHash, highHash *externalapi.DomainHash) (externalapi.BlockLocator, error) {

View File

@ -3,26 +3,27 @@ package serialization
import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager"
)
// BlockGHOSTDAGDataToDBBlockGHOSTDAGData converts BlockGHOSTDAGData to DbBlockGhostdagData
func BlockGHOSTDAGDataToDBBlockGHOSTDAGData(blockGHOSTDAGData *model.BlockGHOSTDAGData) *DbBlockGhostdagData {
func BlockGHOSTDAGDataToDBBlockGHOSTDAGData(blockGHOSTDAGData model.BlockGHOSTDAGData) *DbBlockGhostdagData {
var selectedParent *DbHash
if blockGHOSTDAGData.SelectedParent != nil {
selectedParent = DomainHashToDbHash(blockGHOSTDAGData.SelectedParent)
if blockGHOSTDAGData.SelectedParent() != nil {
selectedParent = DomainHashToDbHash(blockGHOSTDAGData.SelectedParent())
}
return &DbBlockGhostdagData{
BlueScore: blockGHOSTDAGData.BlueScore,
BlueScore: blockGHOSTDAGData.BlueScore(),
SelectedParent: selectedParent,
MergeSetBlues: DomainHashesToDbHashes(blockGHOSTDAGData.MergeSetBlues),
MergeSetReds: DomainHashesToDbHashes(blockGHOSTDAGData.MergeSetReds),
BluesAnticoneSizes: bluesAnticoneSizesToDBBluesAnticoneSizes(blockGHOSTDAGData.BluesAnticoneSizes),
MergeSetBlues: DomainHashesToDbHashes(blockGHOSTDAGData.MergeSetBlues()),
MergeSetReds: DomainHashesToDbHashes(blockGHOSTDAGData.MergeSetReds()),
BluesAnticoneSizes: bluesAnticoneSizesToDBBluesAnticoneSizes(blockGHOSTDAGData.BluesAnticoneSizes()),
}
}
// DBBlockGHOSTDAGDataToBlockGHOSTDAGData converts DbBlockGhostdagData to BlockGHOSTDAGData
func DBBlockGHOSTDAGDataToBlockGHOSTDAGData(dbBlockGHOSTDAGData *DbBlockGhostdagData) (*model.BlockGHOSTDAGData, error) {
func DBBlockGHOSTDAGDataToBlockGHOSTDAGData(dbBlockGHOSTDAGData *DbBlockGhostdagData) (model.BlockGHOSTDAGData, error) {
var selectedParent *externalapi.DomainHash
if dbBlockGHOSTDAGData.SelectedParent != nil {
var err error
@ -47,11 +48,11 @@ func DBBlockGHOSTDAGDataToBlockGHOSTDAGData(dbBlockGHOSTDAGData *DbBlockGhostdag
return nil, err
}
return &model.BlockGHOSTDAGData{
BlueScore: dbBlockGHOSTDAGData.BlueScore,
SelectedParent: selectedParent,
MergeSetBlues: mergetSetBlues,
MergeSetReds: mergetSetReds,
BluesAnticoneSizes: bluesAnticoneSizes,
}, nil
return ghostdagmanager.NewBlockGHOSTDAGData(
dbBlockGHOSTDAGData.BlueScore,
selectedParent,
mergetSetBlues,
mergetSetReds,
bluesAnticoneSizes,
), nil
}

View File

@ -13,21 +13,21 @@ var bucket = dbkeys.MakeBucket([]byte("block-ghostdag-data"))
// ghostdagDataStore represents a store of BlockGHOSTDAGData
type ghostdagDataStore struct {
staging map[externalapi.DomainHash]*model.BlockGHOSTDAGData
staging map[externalapi.DomainHash]model.BlockGHOSTDAGData
cache *lrucache.LRUCache
}
// New instantiates a new GHOSTDAGDataStore
func New(cacheSize int) model.GHOSTDAGDataStore {
return &ghostdagDataStore{
staging: make(map[externalapi.DomainHash]*model.BlockGHOSTDAGData),
staging: make(map[externalapi.DomainHash]model.BlockGHOSTDAGData),
cache: lrucache.New(cacheSize),
}
}
// Stage stages the given blockGHOSTDAGData for the given blockHash
func (gds *ghostdagDataStore) Stage(blockHash *externalapi.DomainHash, blockGHOSTDAGData *model.BlockGHOSTDAGData) {
gds.staging[*blockHash] = blockGHOSTDAGData.Clone()
func (gds *ghostdagDataStore) Stage(blockHash *externalapi.DomainHash, blockGHOSTDAGData model.BlockGHOSTDAGData) {
gds.staging[*blockHash] = blockGHOSTDAGData
}
func (gds *ghostdagDataStore) IsStaged() bool {
@ -35,7 +35,7 @@ func (gds *ghostdagDataStore) IsStaged() bool {
}
func (gds *ghostdagDataStore) Discard() {
gds.staging = make(map[externalapi.DomainHash]*model.BlockGHOSTDAGData)
gds.staging = make(map[externalapi.DomainHash]model.BlockGHOSTDAGData)
}
func (gds *ghostdagDataStore) Commit(dbTx model.DBTransaction) error {
@ -56,13 +56,13 @@ func (gds *ghostdagDataStore) Commit(dbTx model.DBTransaction) error {
}
// Get gets the blockGHOSTDAGData associated with the given blockHash
func (gds *ghostdagDataStore) Get(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*model.BlockGHOSTDAGData, error) {
func (gds *ghostdagDataStore) Get(dbContext model.DBReader, blockHash *externalapi.DomainHash) (model.BlockGHOSTDAGData, error) {
if blockGHOSTDAGData, ok := gds.staging[*blockHash]; ok {
return blockGHOSTDAGData.Clone(), nil
return blockGHOSTDAGData, nil
}
if blockGHOSTDAGData, ok := gds.cache.Get(blockHash); ok {
return blockGHOSTDAGData.(*model.BlockGHOSTDAGData).Clone(), nil
return blockGHOSTDAGData.(model.BlockGHOSTDAGData), nil
}
blockGHOSTDAGDataBytes, err := dbContext.Get(gds.hashAsKey(blockHash))
@ -75,18 +75,18 @@ func (gds *ghostdagDataStore) Get(dbContext model.DBReader, blockHash *externala
return nil, err
}
gds.cache.Add(blockHash, blockGHOSTDAGData)
return blockGHOSTDAGData.Clone(), nil
return blockGHOSTDAGData, nil
}
func (gds *ghostdagDataStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey {
return bucket.Key(hash[:])
}
func (gds *ghostdagDataStore) serializeBlockGHOSTDAGData(blockGHOSTDAGData *model.BlockGHOSTDAGData) ([]byte, error) {
func (gds *ghostdagDataStore) serializeBlockGHOSTDAGData(blockGHOSTDAGData model.BlockGHOSTDAGData) ([]byte, error) {
return proto.Marshal(serialization.BlockGHOSTDAGDataToDBBlockGHOSTDAGData(blockGHOSTDAGData))
}
func (gds *ghostdagDataStore) deserializeBlockGHOSTDAGData(blockGHOSTDAGDataBytes []byte) (*model.BlockGHOSTDAGData, error) {
func (gds *ghostdagDataStore) deserializeBlockGHOSTDAGData(blockGHOSTDAGDataBytes []byte) (model.BlockGHOSTDAGData, error) {
dbBlockGHOSTDAGData := &serialization.DbBlockGhostdagData{}
err := proto.Unmarshal(blockGHOSTDAGDataBytes, dbBlockGHOSTDAGData)
if err != nil {

View File

@ -63,24 +63,23 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
dbManager := consensusdatabase.New(db)
// Data Structures
storeCacheSize := 200
acceptanceDataStore := acceptancedatastore.New(storeCacheSize)
blockStore, err := blockstore.New(dbManager, storeCacheSize)
acceptanceDataStore := acceptancedatastore.New(200)
blockStore, err := blockstore.New(dbManager, 200)
if err != nil {
return nil, err
}
blockHeaderStore, err := blockheaderstore.New(dbManager, storeCacheSize)
blockHeaderStore, err := blockheaderstore.New(dbManager, 10_000)
if err != nil {
return nil, err
}
blockRelationStore := blockrelationstore.New(storeCacheSize)
blockStatusStore := blockstatusstore.New(storeCacheSize)
multisetStore := multisetstore.New(storeCacheSize)
blockRelationStore := blockrelationstore.New(200)
blockStatusStore := blockstatusstore.New(200)
multisetStore := multisetstore.New(200)
pruningStore := pruningstore.New()
reachabilityDataStore := reachabilitydatastore.New(storeCacheSize)
utxoDiffStore := utxodiffstore.New(storeCacheSize)
reachabilityDataStore := reachabilitydatastore.New(200)
utxoDiffStore := utxodiffstore.New(200)
consensusStateStore := consensusstatestore.New()
ghostdagDataStore := ghostdagdatastore.New(storeCacheSize)
ghostdagDataStore := ghostdagdatastore.New(10_000)
headerTipsStore := headertipsstore.New()
// Processes

View File

@ -155,7 +155,7 @@ func TestFinality(t *testing.T) {
t.Fatalf("TestFinality: Failed getting the ghost dag data of the sidechain tip: %v", err)
}
if selectedTipGhostDagData.BlueScore > sideChainTipGhostDagData.BlueScore {
if selectedTipGhostDagData.BlueScore() > sideChainTipGhostDagData.BlueScore() {
t.Fatalf("sideChainTip is not the bluest tip when it is expected to be")
}
@ -308,7 +308,7 @@ func TestBoundedMergeDepth(t *testing.T) {
}
// Make sure it's actually blue
found := false
for _, blue := range virtualGhotDagData.MergeSetBlues {
for _, blue := range virtualGhotDagData.MergeSetBlues() {
if *blue == *kosherizingBlockHash {
found = true
break
@ -356,7 +356,7 @@ func TestBoundedMergeDepth(t *testing.T) {
}
// Make sure it's actually blue
found = false
for _, blue := range virtualGhotDagData.MergeSetBlues {
for _, blue := range virtualGhotDagData.MergeSetBlues() {
if *blue == *kosherizingBlockHash {
found = true
break

View File

@ -3,32 +3,12 @@ package model
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
// BlockGHOSTDAGData represents GHOSTDAG data for some block
type BlockGHOSTDAGData struct {
BlueScore uint64
SelectedParent *externalapi.DomainHash
MergeSetBlues []*externalapi.DomainHash
MergeSetReds []*externalapi.DomainHash
BluesAnticoneSizes map[externalapi.DomainHash]KType
}
// Clone returns a clone of BlockGHOSTDAGData
func (bgd *BlockGHOSTDAGData) Clone() *BlockGHOSTDAGData {
if bgd == nil {
return nil
}
bluesAnticoneSizesClone := make(map[externalapi.DomainHash]KType, len(bgd.BluesAnticoneSizes))
for hash, size := range bgd.BluesAnticoneSizes {
bluesAnticoneSizesClone[hash] = size
}
return &BlockGHOSTDAGData{
BlueScore: bgd.BlueScore,
SelectedParent: bgd.SelectedParent.Clone(),
MergeSetBlues: externalapi.CloneHashes(bgd.MergeSetBlues),
MergeSetReds: externalapi.CloneHashes(bgd.MergeSetReds),
BluesAnticoneSizes: bluesAnticoneSizesClone,
}
type BlockGHOSTDAGData interface {
BlueScore() uint64
SelectedParent() *externalapi.DomainHash
MergeSetBlues() []*externalapi.DomainHash
MergeSetReds() []*externalapi.DomainHash
BluesAnticoneSizes() map[externalapi.DomainHash]KType
}
// KType defines the size of GHOSTDAG consensus algorithm K parameter.

View File

@ -5,7 +5,7 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
// GHOSTDAGDataStore represents a store of BlockGHOSTDAGData
type GHOSTDAGDataStore interface {
Store
Stage(blockHash *externalapi.DomainHash, blockGHOSTDAGData *BlockGHOSTDAGData)
Stage(blockHash *externalapi.DomainHash, blockGHOSTDAGData BlockGHOSTDAGData)
IsStaged() bool
Get(dbContext DBReader, blockHash *externalapi.DomainHash) (*BlockGHOSTDAGData, error)
Get(dbContext DBReader, blockHash *externalapi.DomainHash) (BlockGHOSTDAGData, error)
}

View File

@ -6,6 +6,6 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
type GHOSTDAGManager interface {
GHOSTDAG(blockHash *externalapi.DomainHash) error
ChooseSelectedParent(blockHashes ...*externalapi.DomainHash) (*externalapi.DomainHash, error)
Less(blockHashA *externalapi.DomainHash, ghostdagDataA *BlockGHOSTDAGData,
blockHashB *externalapi.DomainHash, ghostdagDataB *BlockGHOSTDAGData) bool
Less(blockHashA *externalapi.DomainHash, ghostdagDataA BlockGHOSTDAGData,
blockHashB *externalapi.DomainHash, ghostdagDataB BlockGHOSTDAGData) bool
}

View File

@ -96,13 +96,13 @@ func (bb *testBlockBuilder) buildBlockWithParents(parentHashes []*externalapi.Do
return nil, nil, err
}
selectedParentStatus, err := bb.testConsensus.ConsensusStateManager().ResolveBlockStatus(ghostdagData.SelectedParent)
selectedParentStatus, err := bb.testConsensus.ConsensusStateManager().ResolveBlockStatus(ghostdagData.SelectedParent())
if err != nil {
return nil, nil, err
}
if selectedParentStatus == externalapi.StatusDisqualifiedFromChain {
return nil, nil, errors.Errorf("Error building block with selectedParent %s with status DisqualifiedFromChain",
ghostdagData.SelectedParent)
ghostdagData.SelectedParent())
}
pastUTXO, acceptanceData, multiset, err := bb.consensusStateManager.CalculatePastUTXOAndAcceptanceData(tempBlockHash)

View File

@ -170,7 +170,7 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock)
return fmt.Sprintf("Failed to get sync info: %s", err)
}
return fmt.Sprintf("New virtual's blue score: %d. Sync state: %s. Block count: %d. Header count: %d",
virtualGhostDAGData.BlueScore, syncInfo.State, syncInfo.BlockCount, syncInfo.HeaderCount)
virtualGhostDAGData.BlueScore(), syncInfo.State, syncInfo.BlockCount, syncInfo.HeaderCount)
}))
if logClosureErr != nil {
return logClosureErr

View File

@ -39,7 +39,7 @@ func (v *blockValidator) checkBlockTransactionsFinalized(blockHash *externalapi.
// Ensure all transactions in the block are finalized.
for _, tx := range block.Transactions {
if !v.isFinalizedTransaction(tx, ghostdagData.BlueScore, blockTime) {
if !v.isFinalizedTransaction(tx, ghostdagData.BlueScore(), blockTime) {
txID := consensushashing.TransactionID(tx)
return errors.Wrapf(ruleerrors.ErrUnfinalizedTx, "block contains unfinalized "+
"transaction %s", txID)

View File

@ -115,7 +115,7 @@ func (v *blockValidator) checkMergeSizeLimit(hash *externalapi.DomainHash) error
return err
}
mergeSetSize := len(ghostdagData.MergeSetReds) + len(ghostdagData.MergeSetBlues)
mergeSetSize := len(ghostdagData.MergeSetReds()) + len(ghostdagData.MergeSetBlues())
if uint64(mergeSetSize) > v.mergeSetSizeLimit {
return errors.Wrapf(ruleerrors.ErrViolatingMergeLimit,

View File

@ -31,8 +31,8 @@ func (c coinbaseManager) ExpectedCoinbaseTransaction(blockHash *externalapi.Doma
return nil, err
}
txOuts := make([]*externalapi.DomainTransactionOutput, 0, len(ghostdagData.MergeSetBlues))
for i, blue := range ghostdagData.MergeSetBlues {
txOuts := make([]*externalapi.DomainTransactionOutput, 0, len(ghostdagData.MergeSetBlues()))
for i, blue := range ghostdagData.MergeSetBlues() {
txOut, hasReward, err := c.coinbaseOutputForBlueBlock(blue, acceptanceData[i])
if err != nil {
return nil, err
@ -43,7 +43,7 @@ func (c coinbaseManager) ExpectedCoinbaseTransaction(blockHash *externalapi.Doma
}
}
payload, err := c.serializeCoinbasePayload(ghostdagData.BlueScore, coinbaseData)
payload, err := c.serializeCoinbasePayload(ghostdagData.BlueScore(), coinbaseData)
if err != nil {
return nil, err
}
@ -120,7 +120,7 @@ func (c coinbaseManager) calcBlockSubsidy(blockHash *externalapi.DomainHash) (ui
}
// Equivalent to: baseSubsidy / 2^(blueScore/subsidyHalvingInterval)
return c.baseSubsidy >> uint(ghostdagData.BlueScore/c.subsidyReductionInterval), nil
return c.baseSubsidy >> uint(ghostdagData.BlueScore()/c.subsidyReductionInterval), nil
}
// New instantiates a new CoinbaseManager

View File

@ -80,8 +80,8 @@ func (csm *consensusStateManager) isNextVirtualSelectedParent(blockHash *externa
}
log.Tracef("Selecting the next selected parent between "+
"the block %s the current selected parent %s", blockHash, virtualGhostdagData.SelectedParent)
nextVirtualSelectedParent, err := csm.ghostdagManager.ChooseSelectedParent(virtualGhostdagData.SelectedParent, blockHash)
"the block %s the current selected parent %s", blockHash, virtualGhostdagData.SelectedParent())
nextVirtualSelectedParent, err := csm.ghostdagManager.ChooseSelectedParent(virtualGhostdagData.SelectedParent(), blockHash)
if err != nil {
return false, err
}

View File

@ -30,8 +30,8 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash *
}
log.Tracef("Restoring the past UTXO of block %s with selectedParent %s",
blockHash, blockGHOSTDAGData.SelectedParent)
selectedParentPastUTXO, err := csm.restorePastUTXO(blockGHOSTDAGData.SelectedParent)
blockHash, blockGHOSTDAGData.SelectedParent())
selectedParentPastUTXO, err := csm.restorePastUTXO(blockGHOSTDAGData.SelectedParent())
if err != nil {
return nil, nil, nil, err
}
@ -108,13 +108,13 @@ func (csm *consensusStateManager) restorePastUTXO(blockHash *externalapi.DomainH
}
func (csm *consensusStateManager) applyBlueBlocks(blockHash *externalapi.DomainHash,
selectedParentPastUTXODiff model.MutableUTXODiff, ghostdagData *model.BlockGHOSTDAGData) (
selectedParentPastUTXODiff model.MutableUTXODiff, ghostdagData model.BlockGHOSTDAGData) (
model.AcceptanceData, model.MutableUTXODiff, error) {
log.Tracef("applyBlueBlocks start for block %s", blockHash)
defer log.Tracef("applyBlueBlocks end for block %s", blockHash)
blueBlocks, err := csm.blockStore.Blocks(csm.databaseContext, ghostdagData.MergeSetBlues)
blueBlocks, err := csm.blockStore.Blocks(csm.databaseContext, ghostdagData.MergeSetBlues())
if err != nil {
return nil, nil, err
}
@ -146,7 +146,7 @@ func (csm *consensusStateManager) applyBlueBlocks(blockHash *externalapi.DomainH
transactionID, blueBlockHash)
isAccepted, accumulatedMass, err = csm.maybeAcceptTransaction(transaction, blockHash, isSelectedParent,
accumulatedUTXODiff, accumulatedMass, selectedParentMedianTime, ghostdagData.BlueScore)
accumulatedUTXODiff, accumulatedMass, selectedParentMedianTime, ghostdagData.BlueScore())
if err != nil {
return nil, nil, err
}

View File

@ -10,22 +10,22 @@ import (
)
func (csm *consensusStateManager) calculateMultiset(
acceptanceData model.AcceptanceData, blockGHOSTDAGData *model.BlockGHOSTDAGData) (model.Multiset, error) {
acceptanceData model.AcceptanceData, blockGHOSTDAGData model.BlockGHOSTDAGData) (model.Multiset, error) {
log.Tracef("calculateMultiset start for block with selected parent %s", blockGHOSTDAGData.SelectedParent)
defer log.Tracef("calculateMultiset end for block with selected parent %s", blockGHOSTDAGData.SelectedParent)
log.Tracef("calculateMultiset start for block with selected parent %s", blockGHOSTDAGData.SelectedParent())
defer log.Tracef("calculateMultiset end for block with selected parent %s", blockGHOSTDAGData.SelectedParent())
if blockGHOSTDAGData.SelectedParent == nil {
if blockGHOSTDAGData.SelectedParent() == nil {
log.Tracef("Selected parent is nil, which could only happen for the genesis. " +
"The genesis, by definition, has an empty multiset")
return multiset.New(), nil
}
ms, err := csm.multisetStore.Get(csm.databaseContext, blockGHOSTDAGData.SelectedParent)
ms, err := csm.multisetStore.Get(csm.databaseContext, blockGHOSTDAGData.SelectedParent())
if err != nil {
return nil, err
}
log.Tracef("The multiset for the selected parent %s is: %s", blockGHOSTDAGData.SelectedParent, ms.Hash())
log.Tracef("The multiset for the selected parent %s is: %s", blockGHOSTDAGData.SelectedParent(), ms.Hash())
for _, blockAcceptanceData := range acceptanceData {
for i, transactionAcceptanceData := range blockAcceptanceData.TransactionAcceptanceData {
@ -40,7 +40,7 @@ func (csm *consensusStateManager) calculateMultiset(
log.Tracef("Is transaction %s a coinbase transaction: %t", transactionID, isCoinbase)
var err error
err = addTransactionToMultiset(ms, transaction, blockGHOSTDAGData.BlueScore, isCoinbase)
err = addTransactionToMultiset(ms, transaction, blockGHOSTDAGData.BlueScore(), isCoinbase)
if err != nil {
return nil, err
}

View File

@ -214,7 +214,7 @@ func (csm *consensusStateManager) boundedMergeBreakingParents(
if err != nil {
return nil, err
}
for _, redBlock := range virtualGHOSTDAGData.MergeSetReds {
for _, redBlock := range virtualGHOSTDAGData.MergeSetReds() {
log.Tracef("Check whether red block %s is kosherized", redBlock)
isFinalityPointInPast, err := csm.dagTopologyManager.IsAncestorOf(virtualFinalityPoint, redBlock)
if err != nil {

View File

@ -79,7 +79,7 @@ func (csm *consensusStateManager) findSelectedParentStatus(unverifiedBlocks []*e
if err != nil {
return 0, err
}
return csm.blockStatusStore.Get(csm.databaseContext, lastUnverifiedBlockGHOSTDAGData.SelectedParent)
return csm.blockStatusStore.Get(csm.databaseContext, lastUnverifiedBlockGHOSTDAGData.SelectedParent())
}
func (csm *consensusStateManager) getUnverifiedChainBlocks(
@ -110,13 +110,13 @@ func (csm *consensusStateManager) getUnverifiedChainBlocks(
return nil, err
}
if currentBlockGHOSTDAGData.SelectedParent == nil {
if currentBlockGHOSTDAGData.SelectedParent() == nil {
log.Tracef("Genesis block reached. Returning all the "+
"unverified blocks prior to it: %s", unverifiedBlocks)
return unverifiedBlocks, nil
}
currentHash = currentBlockGHOSTDAGData.SelectedParent
currentHash = currentBlockGHOSTDAGData.SelectedParent()
}
}

View File

@ -9,7 +9,7 @@ import (
type blockHeapNode struct {
hash *externalapi.DomainHash
ghostdagData *model.BlockGHOSTDAGData
ghostdagData model.BlockGHOSTDAGData
}
// baseHeap is an implementation for heap.Interface that sorts blocks by their height

View File

@ -32,7 +32,7 @@ func (spi *selectedParentIterator) Next() bool {
if err != nil {
panic(fmt.Sprintf("ghostdagDataStore is missing ghostdagData for: %v. '%s' ", spi.current, err))
}
spi.current = ghostdagData.SelectedParent
spi.current = ghostdagData.SelectedParent()
return spi.current != nil
}
@ -75,17 +75,17 @@ func (dtm *dagTraversalManager) BlockAtDepth(highHash *externalapi.DomainHash, d
}
requiredBlueScore := uint64(0)
if highBlockGHOSTDAGData.BlueScore > depth {
requiredBlueScore = highBlockGHOSTDAGData.BlueScore - depth
if highBlockGHOSTDAGData.BlueScore() > depth {
requiredBlueScore = highBlockGHOSTDAGData.BlueScore() - depth
}
currentBlockGHOSTDAGData := highBlockGHOSTDAGData
// If we used `BlockIterator` we'd need to do more calls to `ghostdagDataStore` so we can get the blueScore
for currentBlockGHOSTDAGData.BlueScore >= requiredBlueScore {
if currentBlockGHOSTDAGData.SelectedParent == nil { // genesis
for currentBlockGHOSTDAGData.BlueScore() >= requiredBlueScore {
if currentBlockGHOSTDAGData.SelectedParent() == nil { // genesis
return currentBlockHash, nil
}
currentBlockHash = currentBlockGHOSTDAGData.SelectedParent
currentBlockHash = currentBlockGHOSTDAGData.SelectedParent()
currentBlockGHOSTDAGData, err = dtm.ghostdagDataStore.Get(dtm.databaseContext, currentBlockHash)
if err != nil {
return nil, err
@ -104,15 +104,15 @@ func (dtm *dagTraversalManager) LowestChainBlockAboveOrEqualToBlueScore(highHash
currentBlockGHOSTDAGData := highBlockGHOSTDAGData
iterator := dtm.SelectedParentIterator(highHash)
for iterator.Next() {
selectedParentBlockGHOSTDAGData, err := dtm.ghostdagDataStore.Get(dtm.databaseContext, currentBlockGHOSTDAGData.SelectedParent)
selectedParentBlockGHOSTDAGData, err := dtm.ghostdagDataStore.Get(dtm.databaseContext, currentBlockGHOSTDAGData.SelectedParent())
if err != nil {
return nil, err
}
if selectedParentBlockGHOSTDAGData.BlueScore < blueScore {
if selectedParentBlockGHOSTDAGData.BlueScore() < blueScore {
break
}
currentHash = selectedParentBlockGHOSTDAGData.SelectedParent
currentHash = selectedParentBlockGHOSTDAGData.SelectedParent()
currentBlockGHOSTDAGData = selectedParentBlockGHOSTDAGData
}

View File

@ -15,15 +15,15 @@ func (dtm *dagTraversalManager) BlueWindow(startingBlock *externalapi.DomainHash
return nil, err
}
for uint64(len(window)) < windowSize && currentGHOSTDAGData.SelectedParent != nil {
for _, blue := range currentGHOSTDAGData.MergeSetBlues {
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
currentHash = currentGHOSTDAGData.SelectedParent()
currentGHOSTDAGData, err = dtm.ghostdagDataStore.Get(dtm.databaseContext, currentHash)
if err != nil {
return nil, err

View File

@ -1,12 +1,13 @@
package difficultymanager
import (
"math/big"
"time"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/bigintpool"
"math/big"
"time"
)
// DifficultyManager provides a method to resolve the
@ -94,7 +95,7 @@ func (dm *difficultyManager) RequiredDifficulty(blockHash *externalapi.DomainHas
}
// Not enough blocks for building a difficulty window.
if bluestGhostDAG.BlueScore < dm.difficultyAdjustmentWindowSize+1 {
if bluestGhostDAG.BlueScore() < dm.difficultyAdjustmentWindowSize+1 {
return dm.genesisBits()
}

View File

@ -0,0 +1,203 @@
package difficultymanager_test
import (
"testing"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/util"
)
func TestDifficulty(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
params.K = 1
params.DifficultyAdjustmentWindowSize = 264
factory := consensus.NewFactory()
tc, teardown, err := factory.NewTestConsensus(params, "TestDifficulty")
if err != nil {
t.Fatalf("Error setting up consensus: %+v", err)
}
defer teardown()
addBlock := func(blockTime int64, parents ...*externalapi.DomainHash) (*externalapi.DomainBlock, *externalapi.DomainHash) {
bluestParent, err := tc.GHOSTDAGManager().ChooseSelectedParent(parents...)
if err != nil {
t.Fatalf("ChooseSelectedParent: %+v", err)
}
if blockTime == 0 {
header, err := tc.BlockHeaderStore().BlockHeader(tc.DatabaseContext(), bluestParent)
if err != nil {
t.Fatalf("BlockHeader: %+v", err)
}
blockTime = header.TimeInMilliseconds + params.TargetTimePerBlock.Milliseconds()
}
block, _, err := tc.BuildBlockWithParents(parents, nil, nil)
if err != nil {
t.Fatalf("BuildBlockWithParents: %+v", err)
}
block.Header.TimeInMilliseconds = blockTime
err = tc.ValidateAndInsertBlock(block)
if err != nil {
t.Fatalf("ValidateAndInsertBlock: %+v", err)
}
return block, consensushashing.BlockHash(block)
}
minimumTime := func(parents ...*externalapi.DomainHash) int64 {
var tempHash externalapi.DomainHash
tc.BlockRelationStore().StageBlockRelation(&tempHash, &model.BlockRelations{
Parents: parents,
Children: nil,
})
defer tc.BlockRelationStore().Discard()
err = tc.GHOSTDAGManager().GHOSTDAG(&tempHash)
if err != nil {
t.Fatalf("GHOSTDAG: %+v", err)
}
defer tc.GHOSTDAGDataStore().Discard()
pastMedianTime, err := tc.PastMedianTimeManager().PastMedianTime(&tempHash)
if err != nil {
t.Fatalf("PastMedianTime: %+v", err)
}
return pastMedianTime + 1
}
addBlockWithMinimumTime := func(parents ...*externalapi.DomainHash) (*externalapi.DomainBlock, *externalapi.DomainHash) {
minTime := minimumTime(parents...)
return addBlock(minTime, parents...)
}
tipHash := params.GenesisHash
tip := params.GenesisBlock
for i := uint64(0); i < params.DifficultyAdjustmentWindowSize; i++ {
tip, tipHash = addBlock(0, tipHash)
if tip.Header.Bits != params.GenesisBlock.Header.Bits {
t.Fatalf("As long as the bluest parent's blue score is less then the difficulty adjustment " +
"window size, the difficulty should be the same as genesis'")
}
}
for i := uint64(0); i < params.DifficultyAdjustmentWindowSize+100; i++ {
tip, tipHash = addBlock(0, tipHash)
if tip.Header.Bits != params.GenesisBlock.Header.Bits {
t.Fatalf("As long as the block rate remains the same, the difficulty shouldn't change")
}
}
blockInThePast, tipHash := addBlockWithMinimumTime(tipHash)
if blockInThePast.Header.Bits != tip.Header.Bits {
t.Fatalf("The difficulty should only change when blockInThePast is in the past of a block bluest parent")
}
tip = blockInThePast
tip, tipHash = addBlock(0, tipHash)
if tip.Header.Bits != blockInThePast.Header.Bits {
t.Fatalf("The difficulty should only change when blockInThePast is in the past of a block bluest parent")
}
tip, tipHash = addBlock(0, tipHash)
if compareBits(tip.Header.Bits, blockInThePast.Header.Bits) >= 0 {
t.Fatalf("tip.bits should be smaller than blockInThePast.bits because blockInThePast increased the " +
"block rate, so the difficulty should increase as well")
}
var expectedBits uint32
switch params.Name {
case "kaspa-testnet", "kaspa-devnet":
expectedBits = uint32(0x1e7f83df)
case "kaspa-mainnet", "kaspa-simnet":
expectedBits = uint32(0x207f83df)
}
if tip.Header.Bits != expectedBits {
t.Errorf("tip.bits was expected to be %x but got %x", expectedBits, tip.Header.Bits)
}
// Increase block rate to increase difficulty
for i := uint64(0); i < params.DifficultyAdjustmentWindowSize; i++ {
tip, tipHash = addBlockWithMinimumTime(tipHash)
tipGHOSTDAGData, err := tc.GHOSTDAGDataStore().Get(tc.DatabaseContext(), tipHash)
if err != nil {
t.Fatalf("GHOSTDAGDataStore: %+v", err)
}
selectedParentHeader, err := tc.BlockHeaderStore().BlockHeader(tc.DatabaseContext(),
tipGHOSTDAGData.SelectedParent())
if err != nil {
t.Fatalf("BlockHeader: %+v", err)
}
if compareBits(tip.Header.Bits, selectedParentHeader.Bits) > 0 {
t.Fatalf("Because we're increasing the block rate, the difficulty can't decrease")
}
}
// Add blocks until difficulty stabilizes
lastBits := tip.Header.Bits
sameBitsCount := uint64(0)
for sameBitsCount < params.DifficultyAdjustmentWindowSize+1 {
tip, tipHash = addBlock(0, tipHash)
if tip.Header.Bits == lastBits {
sameBitsCount++
} else {
lastBits = tip.Header.Bits
sameBitsCount = 0
}
}
slowBlockTime := tip.Header.TimeInMilliseconds + params.TargetTimePerBlock.Milliseconds() + 1000
slowBlock, tipHash := addBlock(slowBlockTime, tipHash)
if slowBlock.Header.Bits != tip.Header.Bits {
t.Fatalf("The difficulty should only change when slowBlock is in the past of a block bluest parent")
}
tip = slowBlock
tip, tipHash = addBlock(0, tipHash)
if tip.Header.Bits != slowBlock.Header.Bits {
t.Fatalf("The difficulty should only change when slowBlock is in the past of a block bluest parent")
}
tip, tipHash = addBlock(0, tipHash)
if compareBits(tip.Header.Bits, slowBlock.Header.Bits) <= 0 {
t.Fatalf("tip.bits should be smaller than slowBlock.bits because slowBlock decreased the block" +
" rate, so the difficulty should decrease as well")
}
_, tipHash = addBlock(0, tipHash)
splitBlockHash := tipHash
for i := 0; i < 100; i++ {
_, tipHash = addBlock(0, tipHash)
}
blueTipHash := tipHash
redChainTipHash := splitBlockHash
for i := 0; i < 10; i++ {
_, redChainTipHash = addBlockWithMinimumTime(redChainTipHash)
}
tipWithRedPast, _ := addBlock(0, redChainTipHash, blueTipHash)
tipWithoutRedPast, _ := addBlock(0, blueTipHash)
if tipWithoutRedPast.Header.Bits != tipWithRedPast.Header.Bits {
t.Fatalf("tipWithoutRedPast.bits should be the same as tipWithRedPast.bits because red blocks" +
" shouldn't affect the difficulty")
}
})
}
func compareBits(a uint32, b uint32) int {
aTarget := util.CompactToBig(a)
bTarget := util.CompactToBig(b)
return aTarget.Cmp(bTarget)
}

View File

@ -1,9 +1,12 @@
package ghostdag2
import (
"sort"
"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"sort"
)
type ghostdagHelper struct {
@ -44,7 +47,7 @@ func (gh *ghostdagHelper) GHOSTDAG(blockCandidate *externalapi.DomainHash) error
if err != nil {
return err
}
blockScore := blockData.BlueScore
blockScore := blockData.BlueScore()
if blockScore > maxNum {
selectedParent = parent
maxNum = blockScore
@ -91,13 +94,8 @@ func (gh *ghostdagHelper) GHOSTDAG(blockCandidate *externalapi.DomainHash) error
}
myScore += uint64(len(mergeSetBlues))
e := model.BlockGHOSTDAGData{
BlueScore: myScore,
SelectedParent: selectedParent,
MergeSetBlues: mergeSetBlues,
MergeSetReds: mergeSetReds,
}
gh.dataStore.Stage(blockCandidate, &e)
e := ghostdagmanager.NewBlockGHOSTDAGData(myScore, selectedParent, mergeSetBlues, mergeSetReds, nil)
gh.dataStore.Stage(blockCandidate, e)
return nil
}
@ -220,7 +218,7 @@ func (gh *ghostdagHelper) validateKCluster(chain *externalapi.DomainHash, checke
if err != nil {
return false, err
}
if mergeSetReds := dataStore.MergeSetReds; contains(checkedBlock, mergeSetReds) {
if mergeSetReds := dataStore.MergeSetReds(); contains(checkedBlock, mergeSetReds) {
return false, nil
}
} else {
@ -326,14 +324,14 @@ func (gh *ghostdagHelper) findBlueSet(blueSet *[]*externalapi.DomainHash, select
if err != nil {
return err
}
mergeSetBlue := blockData.MergeSetBlues
mergeSetBlue := blockData.MergeSetBlues()
for _, blue := range mergeSetBlue {
if contains(blue, *blueSet) {
continue
}
*blueSet = append(*blueSet, blue)
}
selectedParent = blockData.SelectedParent
selectedParent = blockData.SelectedParent()
}
return nil
}
@ -356,10 +354,10 @@ func (gh *ghostdagHelper) sortByBlueScore(arr []*externalapi.DomainHash) error {
return false
}
if blockLeft.BlueScore < blockRight.BlueScore {
if blockLeft.BlueScore() < blockRight.BlueScore() {
return true
}
if blockLeft.BlueScore == blockRight.BlueScore {
if blockLeft.BlueScore() == blockRight.BlueScore() {
return ismoreHash(arr[j], arr[i])
}
return false
@ -369,13 +367,13 @@ func (gh *ghostdagHelper) sortByBlueScore(arr []*externalapi.DomainHash) error {
/* --------------------------------------------- */
func (gh *ghostdagHelper) BlockData(blockHash *externalapi.DomainHash) (*model.BlockGHOSTDAGData, error) {
func (gh *ghostdagHelper) BlockData(blockHash *externalapi.DomainHash) (model.BlockGHOSTDAGData, error) {
return gh.dataStore.Get(gh.dbAccess, blockHash)
}
func (gh *ghostdagHelper) ChooseSelectedParent(blockHashes ...*externalapi.DomainHash) (*externalapi.DomainHash, error) {
panic("implement me")
}
func (gh *ghostdagHelper) Less(blockHashA *externalapi.DomainHash, ghostdagDataA *model.BlockGHOSTDAGData, blockHashB *externalapi.DomainHash, ghostdagDataB *model.BlockGHOSTDAGData) bool {
func (gh *ghostdagHelper) Less(blockHashA *externalapi.DomainHash, ghostdagDataA model.BlockGHOSTDAGData, blockHashB *externalapi.DomainHash, ghostdagDataB model.BlockGHOSTDAGData) bool {
panic("implement me")
}

View File

@ -53,11 +53,11 @@ func (gm *ghostdagManager) ChooseSelectedParent(blockHashes ...*externalapi.Doma
return selectedParent, nil
}
func (gm *ghostdagManager) Less(blockHashA *externalapi.DomainHash, ghostdagDataA *model.BlockGHOSTDAGData,
blockHashB *externalapi.DomainHash, ghostdagDataB *model.BlockGHOSTDAGData) bool {
func (gm *ghostdagManager) Less(blockHashA *externalapi.DomainHash, ghostdagDataA model.BlockGHOSTDAGData,
blockHashB *externalapi.DomainHash, ghostdagDataB model.BlockGHOSTDAGData) bool {
blockBlueScoreA := ghostdagDataA.BlueScore
blockBlueScoreB := ghostdagDataB.BlueScore
blockBlueScoreA := ghostdagDataA.BlueScore()
blockBlueScoreB := ghostdagDataB.BlueScore()
if blockBlueScoreA == blockBlueScoreB {
return hashes.Less(blockHashA, blockHashB)
}

View File

@ -25,10 +25,10 @@ import (
//
// For further details see the article https://eprint.iacr.org/2018/104.pdf
func (gm *ghostdagManager) GHOSTDAG(blockHash *externalapi.DomainHash) error {
newBlockData := &model.BlockGHOSTDAGData{
MergeSetBlues: make([]*externalapi.DomainHash, 0),
MergeSetReds: make([]*externalapi.DomainHash, 0),
BluesAnticoneSizes: make(map[externalapi.DomainHash]model.KType),
newBlockData := &blockGHOSTDAGData{
mergeSetBlues: make([]*externalapi.DomainHash, 0),
mergeSetReds: make([]*externalapi.DomainHash, 0),
bluesAnticoneSizes: make(map[externalapi.DomainHash]model.KType),
}
blockParents, err := gm.dagTopologyManager.Parents(blockHash)
@ -43,12 +43,12 @@ func (gm *ghostdagManager) GHOSTDAG(blockHash *externalapi.DomainHash) error {
return err
}
newBlockData.SelectedParent = selectedParent
newBlockData.MergeSetBlues = append(newBlockData.MergeSetBlues, selectedParent)
newBlockData.BluesAnticoneSizes[*selectedParent] = 0
newBlockData.selectedParent = selectedParent
newBlockData.mergeSetBlues = append(newBlockData.mergeSetBlues, selectedParent)
newBlockData.bluesAnticoneSizes[*selectedParent] = 0
}
mergeSetWithoutSelectedParent, err := gm.mergeSetWithoutSelectedParent(newBlockData.SelectedParent, blockParents)
mergeSetWithoutSelectedParent, err := gm.mergeSetWithoutSelectedParent(newBlockData.selectedParent, blockParents)
if err != nil {
return err
}
@ -61,25 +61,25 @@ func (gm *ghostdagManager) GHOSTDAG(blockHash *externalapi.DomainHash) error {
if isBlue {
// No k-cluster violation found, we can now set the candidate block as blue
newBlockData.MergeSetBlues = append(newBlockData.MergeSetBlues, blueCandidate)
newBlockData.BluesAnticoneSizes[*blueCandidate] = candidateAnticoneSize
newBlockData.mergeSetBlues = append(newBlockData.mergeSetBlues, blueCandidate)
newBlockData.bluesAnticoneSizes[*blueCandidate] = candidateAnticoneSize
for blue, blueAnticoneSize := range candidateBluesAnticoneSizes {
newBlockData.BluesAnticoneSizes[blue] = blueAnticoneSize + 1
newBlockData.bluesAnticoneSizes[blue] = blueAnticoneSize + 1
}
} else {
newBlockData.MergeSetReds = append(newBlockData.MergeSetReds, blueCandidate)
newBlockData.mergeSetReds = append(newBlockData.mergeSetReds, blueCandidate)
}
}
if !isGenesis {
selectedParentGHOSTDAGData, err := gm.ghostdagDataStore.Get(gm.databaseContext, newBlockData.SelectedParent)
selectedParentGHOSTDAGData, err := gm.ghostdagDataStore.Get(gm.databaseContext, newBlockData.selectedParent)
if err != nil {
return err
}
newBlockData.BlueScore = selectedParentGHOSTDAGData.BlueScore + uint64(len(newBlockData.MergeSetBlues))
newBlockData.blueScore = selectedParentGHOSTDAGData.BlueScore() + uint64(len(newBlockData.mergeSetBlues))
} else {
// Genesis's blue score is defined to be 0.
newBlockData.BlueScore = 0
newBlockData.blueScore = 0
}
gm.ghostdagDataStore.Stage(blockHash, newBlockData)
@ -89,15 +89,15 @@ func (gm *ghostdagManager) GHOSTDAG(blockHash *externalapi.DomainHash) error {
type chainBlockData struct {
hash *externalapi.DomainHash
blockData *model.BlockGHOSTDAGData
blockData model.BlockGHOSTDAGData
}
func (gm *ghostdagManager) checkBlueCandidate(newBlockData *model.BlockGHOSTDAGData, blueCandidate *externalapi.DomainHash) (
func (gm *ghostdagManager) checkBlueCandidate(newBlockData *blockGHOSTDAGData, blueCandidate *externalapi.DomainHash) (
isBlue bool, candidateAnticoneSize model.KType, candidateBluesAnticoneSizes map[externalapi.DomainHash]model.KType, err error) {
// The maximum length of node.blues can be K+1 because
// it contains the selected parent.
if model.KType(len(newBlockData.MergeSetBlues)) == gm.k+1 {
if model.KType(len(newBlockData.mergeSetBlues)) == gm.k+1 {
return false, 0, nil, nil
}
@ -126,12 +126,12 @@ func (gm *ghostdagManager) checkBlueCandidate(newBlockData *model.BlockGHOSTDAGD
return false, 0, nil, nil
}
selectedParentGHOSTDAGData, err := gm.ghostdagDataStore.Get(gm.databaseContext, chainBlock.blockData.SelectedParent)
selectedParentGHOSTDAGData, err := gm.ghostdagDataStore.Get(gm.databaseContext, chainBlock.blockData.SelectedParent())
if err != nil {
return false, 0, nil, err
}
chainBlock = chainBlockData{hash: chainBlock.blockData.SelectedParent,
chainBlock = chainBlockData{hash: chainBlock.blockData.SelectedParent(),
blockData: selectedParentGHOSTDAGData,
}
}
@ -139,7 +139,7 @@ func (gm *ghostdagManager) checkBlueCandidate(newBlockData *model.BlockGHOSTDAGD
return true, candidateAnticoneSize, candidateBluesAnticoneSizes, nil
}
func (gm *ghostdagManager) checkBlueCandidateWithChainBlock(newBlockData *model.BlockGHOSTDAGData,
func (gm *ghostdagManager) checkBlueCandidateWithChainBlock(newBlockData model.BlockGHOSTDAGData,
chainBlock chainBlockData, blueCandidate *externalapi.DomainHash,
candidateBluesAnticoneSizes map[externalapi.DomainHash]model.KType,
candidateAnticoneSize *model.KType) (isBlue, isRed bool, err error) {
@ -164,7 +164,7 @@ func (gm *ghostdagManager) checkBlueCandidateWithChainBlock(newBlockData *model.
}
}
for _, block := range chainBlock.blockData.MergeSetBlues {
for _, block := range chainBlock.blockData.MergeSetBlues() {
// Skip blocks that exist in the past of blueCandidate.
isAncestorOfBlueCandidate, err := gm.dagTopologyManager.IsAncestorOf(block, blueCandidate)
if err != nil {
@ -204,16 +204,16 @@ func (gm *ghostdagManager) checkBlueCandidateWithChainBlock(newBlockData *model.
// blueAnticoneSize returns the blue anticone size of 'block' from the worldview of 'context'.
// Expects 'block' to be in the blue set of 'context'
func (gm *ghostdagManager) blueAnticoneSize(block *externalapi.DomainHash, context *model.BlockGHOSTDAGData) (model.KType, error) {
func (gm *ghostdagManager) blueAnticoneSize(block *externalapi.DomainHash, context model.BlockGHOSTDAGData) (model.KType, error) {
for current := context; current != nil; {
if blueAnticoneSize, ok := current.BluesAnticoneSizes[*block]; ok {
if blueAnticoneSize, ok := current.BluesAnticoneSizes()[*block]; ok {
return blueAnticoneSize, nil
}
if current.SelectedParent == nil {
if current.SelectedParent() == nil {
break
}
var err error
current, err = gm.ghostdagDataStore.Get(gm.databaseContext, current.SelectedParent)
current, err = gm.ghostdagDataStore.Get(gm.databaseContext, current.SelectedParent())
if err != nil {
return 0, err
}

View File

@ -0,0 +1,51 @@
package ghostdagmanager
import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
type blockGHOSTDAGData struct {
blueScore uint64
selectedParent *externalapi.DomainHash
mergeSetBlues []*externalapi.DomainHash
mergeSetReds []*externalapi.DomainHash
bluesAnticoneSizes map[externalapi.DomainHash]model.KType
}
// NewBlockGHOSTDAGData creates a new instance of model.BlockGHOSTDAGData
func NewBlockGHOSTDAGData(
blueScore uint64,
selectedParent *externalapi.DomainHash,
mergeSetBlues []*externalapi.DomainHash,
mergeSetReds []*externalapi.DomainHash,
bluesAnticoneSizes map[externalapi.DomainHash]model.KType) model.BlockGHOSTDAGData {
return &blockGHOSTDAGData{
blueScore: blueScore,
selectedParent: selectedParent,
mergeSetBlues: mergeSetBlues,
mergeSetReds: mergeSetReds,
bluesAnticoneSizes: bluesAnticoneSizes,
}
}
func (bgd *blockGHOSTDAGData) BlueScore() uint64 {
return bgd.blueScore
}
func (bgd *blockGHOSTDAGData) SelectedParent() *externalapi.DomainHash {
return bgd.selectedParent
}
func (bgd *blockGHOSTDAGData) MergeSetBlues() []*externalapi.DomainHash {
return bgd.mergeSetBlues
}
func (bgd *blockGHOSTDAGData) MergeSetReds() []*externalapi.DomainHash {
return bgd.mergeSetReds
}
func (bgd *blockGHOSTDAGData) BluesAnticoneSizes() map[externalapi.DomainHash]model.KType {
return bgd.bluesAnticoneSizes
}

View File

@ -1,14 +1,17 @@
package ghostdagmanager
package ghostdagmanager_test
import (
"encoding/json"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdag2"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdag2"
)
// TestGHOSTDAG iterates over several dag simulations, and checks
@ -47,15 +50,9 @@ func TestGHOSTDAG(t *testing.T) {
}
ghostdagDataStore := &GHOSTDAGDataStoreImpl{
dagMap: make(map[externalapi.DomainHash]*model.BlockGHOSTDAGData),
}
var blockGHOSTDAGDataGenesis = &model.BlockGHOSTDAGData{
BlueScore: 0,
SelectedParent: nil,
MergeSetBlues: nil,
MergeSetReds: nil,
BluesAnticoneSizes: nil,
dagMap: make(map[externalapi.DomainHash]model.BlockGHOSTDAGData),
}
blockGHOSTDAGDataGenesis := ghostdagmanager.NewBlockGHOSTDAGData(0, nil, nil, nil, nil)
var testsCounter int
err := filepath.Walk("../../testdata/dags", func(path string, info os.FileInfo, err error) error {
@ -87,7 +84,7 @@ func TestGHOSTDAG(t *testing.T) {
//NOTE: FOR ADDING/REMOVING AN IMPLEMENTATION CHANGE BELOW:
implementationFactories := []implManager{
{New, "Original"},
{ghostdagmanager.New, "Original"},
{ghostdag2.New, "Tal's impl"},
}
@ -110,30 +107,30 @@ func TestGHOSTDAG(t *testing.T) {
factory.implName, info.Name(), testBlockData.ID, err)
}
if testBlockData.Score != (ghostdagData.BlueScore) {
if testBlockData.Score != (ghostdagData.BlueScore()) {
t.Fatalf("\nTEST FAILED:\n Impl: %s, FileName: %s \nBlock: %s, \nError: expected blue score %d but got %d.",
factory.implName, info.Name(), testBlockData.ID, testBlockData.Score, ghostdagData.BlueScore)
factory.implName, info.Name(), testBlockData.ID, testBlockData.Score, ghostdagData.BlueScore())
}
if *StringToByte(testBlockData.SelectedParent) != *ghostdagData.SelectedParent {
if *StringToByte(testBlockData.SelectedParent) != *ghostdagData.SelectedParent() {
t.Fatalf("\nTEST FAILED:\n Impl: %s, FileName: %s \nBlock: %s, \nError: expected selected parent %v but got %v.",
factory.implName, info.Name(), testBlockData.ID, testBlockData.SelectedParent, string(ghostdagData.SelectedParent[:]))
factory.implName, info.Name(), testBlockData.ID, testBlockData.SelectedParent, ghostdagData.SelectedParent())
}
if !reflect.DeepEqual(StringToByteArray(testBlockData.MergeSetBlues), ghostdagData.MergeSetBlues) {
if !reflect.DeepEqual(StringToByteArray(testBlockData.MergeSetBlues), ghostdagData.MergeSetBlues()) {
t.Fatalf("\nTEST FAILED:\n Impl: %s, FileName: %s \nBlock: %s, \nError: expected merge set blues %v but got %v.",
factory.implName, info.Name(), testBlockData.ID, testBlockData.MergeSetBlues, hashesToStrings(ghostdagData.MergeSetBlues))
factory.implName, info.Name(), testBlockData.ID, testBlockData.MergeSetBlues, hashesToStrings(ghostdagData.MergeSetBlues()))
}
if !reflect.DeepEqual(StringToByteArray(testBlockData.MergeSetReds), ghostdagData.MergeSetReds) {
if !reflect.DeepEqual(StringToByteArray(testBlockData.MergeSetReds), ghostdagData.MergeSetReds()) {
t.Fatalf("\nTEST FAILED:\n Impl: %s, FileName: %s \nBlock: %s, \nError: expected merge set reds %v but got %v.",
factory.implName, info.Name(), testBlockData.ID, testBlockData.MergeSetReds, hashesToStrings(ghostdagData.MergeSetReds))
factory.implName, info.Name(), testBlockData.ID, testBlockData.MergeSetReds, hashesToStrings(ghostdagData.MergeSetReds()))
}
}
dagTopology.parentsMap = make(map[externalapi.DomainHash][]*externalapi.DomainHash)
dagTopology.parentsMap[genesisHash] = nil
ghostdagDataStore.dagMap = make(map[externalapi.DomainHash]*model.BlockGHOSTDAGData)
ghostdagDataStore.dagMap = make(map[externalapi.DomainHash]model.BlockGHOSTDAGData)
ghostdagDataStore.dagMap[genesisHash] = blockGHOSTDAGDataGenesis
}
@ -172,10 +169,10 @@ func StringToByteArray(stringIDArr []string) []*externalapi.DomainHash {
/* ---------------------- */
type GHOSTDAGDataStoreImpl struct {
dagMap map[externalapi.DomainHash]*model.BlockGHOSTDAGData
dagMap map[externalapi.DomainHash]model.BlockGHOSTDAGData
}
func (ds *GHOSTDAGDataStoreImpl) Stage(blockHash *externalapi.DomainHash, blockGHOSTDAGData *model.BlockGHOSTDAGData) {
func (ds *GHOSTDAGDataStoreImpl) Stage(blockHash *externalapi.DomainHash, blockGHOSTDAGData model.BlockGHOSTDAGData) {
ds.dagMap[*blockHash] = blockGHOSTDAGData
}
@ -191,7 +188,7 @@ func (ds *GHOSTDAGDataStoreImpl) Commit(dbTx model.DBTransaction) error {
panic("implement me")
}
func (ds *GHOSTDAGDataStoreImpl) Get(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*model.BlockGHOSTDAGData, error) {
func (ds *GHOSTDAGDataStoreImpl) Get(dbContext model.DBReader, blockHash *externalapi.DomainHash) (model.BlockGHOSTDAGData, error) {
v, ok := ds.dagMap[*blockHash]
if ok {
return v, nil

View File

@ -46,7 +46,7 @@ func (mdm *mergeDepthManager) CheckBoundedMergeDepth(blockHash *externalapi.Doma
}
// Return nil on genesis
if ghostdagData.SelectedParent == nil {
if ghostdagData.SelectedParent() == nil {
return nil
}
@ -55,7 +55,7 @@ func (mdm *mergeDepthManager) CheckBoundedMergeDepth(blockHash *externalapi.Doma
return err
}
for _, red := range ghostdagData.MergeSetReds {
for _, red := range ghostdagData.MergeSetReds() {
doesRedHaveFinalityPointInPast, err := mdm.dagTopologyManager.IsAncestorOf(finalityPoint, red)
if err != nil {
return err
@ -85,9 +85,9 @@ func (mdm mergeDepthManager) NonBoundedMergeDepthViolatingBlues(blockHash *exter
return nil, err
}
nonBoundedMergeDepthViolatingBlues := make([]*externalapi.DomainHash, 0, len(ghostdagData.MergeSetBlues))
nonBoundedMergeDepthViolatingBlues := make([]*externalapi.DomainHash, 0, len(ghostdagData.MergeSetBlues()))
for _, blue := range ghostdagData.MergeSetBlues {
for _, blue := range ghostdagData.MergeSetBlues() {
notViolatingFinality, err := mdm.hasFinalityPointInOthersSelectedChain(blockHash, blue)
if err != nil {
return nil, err

View File

@ -1,10 +1,11 @@
package pastmediantimemanager
import (
"sort"
"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
@ -44,7 +45,7 @@ func (pmtm *pastMedianTimeManager) PastMedianTime(blockHash *externalapi.DomainH
if err != nil {
return 0, err
}
selectedParentHash := blockGHOSTDAGData.SelectedParent
selectedParentHash := blockGHOSTDAGData.SelectedParent()
// Genesis block
if selectedParentHash == nil {

View File

@ -99,14 +99,14 @@ func (pm *pruningManager) FindNextPruningPoint() error {
if err != nil {
return err
}
currentPBlueScore := currentPGhost.BlueScore
currentPBlueScore := currentPGhost.BlueScore()
// Because the pruning point changes only once per finality, then there's no need to even check for that if a finality interval hasn't passed.
if virtual.BlueScore <= currentPBlueScore+pm.finalityInterval {
if virtual.BlueScore() <= currentPBlueScore+pm.finalityInterval {
return nil
}
// This means the pruning point is still genesis.
if virtual.BlueScore <= pm.pruningDepth+pm.finalityInterval {
if virtual.BlueScore() <= pm.pruningDepth+pm.finalityInterval {
return nil
}
@ -121,7 +121,7 @@ func (pm *pruningManager) FindNextPruningPoint() error {
}
// Actually check if the pruning point changed
if (currentPBlueScore / pm.finalityInterval) < (candidatePGhost.BlueScore / pm.finalityInterval) {
if (currentPBlueScore / pm.finalityInterval) < (candidatePGhost.BlueScore() / pm.finalityInterval) {
err = pm.savePruningPoint(candidatePHash)
if err != nil {
return err

View File

@ -45,7 +45,7 @@ func (rt *reachabilityManager) AddBlock(blockHash *externalapi.DomainHash) error
}
// If this is the genesis node, simply initialize it and return
if ghostdagData.SelectedParent == nil {
if ghostdagData.SelectedParent() == nil {
rt.stageReindexRoot(blockHash)
return nil
}
@ -56,16 +56,16 @@ func (rt *reachabilityManager) AddBlock(blockHash *externalapi.DomainHash) error
}
// Insert the node into the selected parent's reachability tree
err = rt.addChild(ghostdagData.SelectedParent, blockHash, reindexRoot)
err = rt.addChild(ghostdagData.SelectedParent(), blockHash, reindexRoot)
if err != nil {
return err
}
// Add the block to the futureCoveringSets of all the blocks
// in the merget set
mergeSet := make([]*externalapi.DomainHash, len(ghostdagData.MergeSetBlues)+len(ghostdagData.MergeSetReds))
copy(mergeSet, ghostdagData.MergeSetBlues)
copy(mergeSet[len(ghostdagData.MergeSetBlues):], ghostdagData.MergeSetReds)
mergeSet := make([]*externalapi.DomainHash, len(ghostdagData.MergeSetBlues())+len(ghostdagData.MergeSetReds()))
copy(mergeSet, ghostdagData.MergeSetBlues())
copy(mergeSet[len(ghostdagData.MergeSetBlues()):], ghostdagData.MergeSetReds())
for _, current := range mergeSet {
err = rt.insertToFutureCoveringSet(current, blockHash)

View File

@ -814,7 +814,7 @@ func (rt *reachabilityManager) maybeMoveReindexRoot(reindexRoot, newTreeNode *ex
return nil, false, err
}
if newTreeNodeGHOSTDAGData.BlueScore-reindexRootChosenChildGHOSTDAGData.BlueScore < rt.reindexWindow {
if newTreeNodeGHOSTDAGData.BlueScore()-reindexRootChosenChildGHOSTDAGData.BlueScore() < rt.reindexWindow {
return nil, false, nil
}

View File

@ -16,12 +16,12 @@ func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.Doma
if err != nil {
return nil, err
}
lowBlockBlueScore := lowBlockGHOSTDAGData.BlueScore
lowBlockBlueScore := lowBlockGHOSTDAGData.BlueScore()
highBlockGHOSTDAGData, err := sm.ghostdagDataStore.Get(sm.databaseContext, highHash)
if err != nil {
return nil, err
}
highBlockBlueScore := highBlockGHOSTDAGData.BlueScore
highBlockBlueScore := highBlockGHOSTDAGData.BlueScore()
if lowBlockBlueScore >= highBlockBlueScore {
return nil, errors.Errorf("low hash blueScore >= high hash blueScore (%d >= %d)",
lowBlockBlueScore, highBlockBlueScore)
@ -37,7 +37,7 @@ func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.Doma
// fairly accurate because we presume that most DAG blocks are
// blue.
for highBlockBlueScore-lowBlockBlueScore+1 > maxHashesInAntiPastHashesBetween {
highHash = highBlockGHOSTDAGData.SelectedParent
highHash = highBlockGHOSTDAGData.SelectedParent()
}
// Collect every node in highHash's past (including itself) but

View File

@ -14,13 +14,13 @@ func (sm *syncManager) createBlockLocator(lowHash, highHash *externalapi.DomainH
if err != nil {
return nil, err
}
highHash = highBlockGHOSTDAGData.SelectedParent
highHash = highBlockGHOSTDAGData.SelectedParent()
lowBlockGHOSTDAGData, err := sm.ghostdagDataStore.Get(sm.databaseContext, lowHash)
if err != nil {
return nil, err
}
lowBlockBlueScore := lowBlockGHOSTDAGData.BlueScore
lowBlockBlueScore := lowBlockGHOSTDAGData.BlueScore()
currentHash := highHash
step := uint64(1)
@ -32,7 +32,7 @@ func (sm *syncManager) createBlockLocator(lowHash, highHash *externalapi.DomainH
if err != nil {
return nil, err
}
currentBlockBlueScore := currentBlockGHOSTDAGData.BlueScore
currentBlockBlueScore := currentBlockGHOSTDAGData.BlueScore()
// Nothing more to add once the low node has been added.
if currentBlockBlueScore <= lowBlockBlueScore {
@ -47,7 +47,7 @@ func (sm *syncManager) createBlockLocator(lowHash, highHash *externalapi.DomainH
// final node is lowNode.
nextBlueScore := currentBlockBlueScore - step
if currentBlockBlueScore < step {
nextBlueScore = lowBlockGHOSTDAGData.BlueScore
nextBlueScore = lowBlockGHOSTDAGData.BlueScore()
}
// Walk down currentHash's selected parent chain to the appropriate ancestor

View File

@ -91,7 +91,7 @@ func (sm *syncManager) virtualSelectedParentHash() (*externalapi.DomainHash, err
if err != nil {
return nil, err
}
return virtualGHOSTDAGData.SelectedParent, nil
return virtualGHOSTDAGData.SelectedParent(), nil
}
func (sm *syncManager) headerVirtualSelectedParentHash() (*externalapi.DomainHash, error) {

View File

@ -59,7 +59,7 @@ func (v *transactionValidator) checkTransactionCoinbaseMaturity(
return err
}
txBlueScore := ghostdagData.BlueScore
txBlueScore := ghostdagData.BlueScore()
var missingOutpoints []*externalapi.DomainOutpoint
for _, input := range tx.Inputs {
utxoEntry := input.UTXOEntry
@ -166,7 +166,7 @@ func (v *transactionValidator) checkTransactionSequenceLock(povBlockHash *extern
return err
}
if !v.sequenceLockActive(sequenceLock, ghostdagData.BlueScore, medianTime) {
if !v.sequenceLockActive(sequenceLock, ghostdagData.BlueScore(), medianTime) {
return errors.Wrapf(ruleerrors.ErrUnfinalizedTx, "block contains "+
"transaction whose input sequence "+
"locks are not met")
@ -270,16 +270,16 @@ func (v *transactionValidator) calcTxSequenceLockFromReferencedUTXOEntries(
for {
selectedParentGHOSTDAGData, err := v.ghostdagDataStore.Get(v.databaseContext,
baseGHOSTDAGData.SelectedParent)
baseGHOSTDAGData.SelectedParent())
if err != nil {
return nil, err
}
if selectedParentGHOSTDAGData.BlueScore <= inputBlueScore {
if selectedParentGHOSTDAGData.BlueScore() <= inputBlueScore {
break
}
baseHash = baseGHOSTDAGData.SelectedParent
baseHash = baseGHOSTDAGData.SelectedParent()
baseGHOSTDAGData = selectedParentGHOSTDAGData
}