Change merge set order to topological order (#1654)

* Change mergeSet to be ordered topologically.

* Add special condition for genesis.

* Add check that the coinbase is validated.

* Change names of variables(old: chainHash, blueHash).

* Fix the DAG diagram in the comment above the function.

* Fix variables names.

Co-authored-by: tal <tal@daglabs.com>
Co-authored-by: Ori Newman <orinewman1@gmail.com>
This commit is contained in:
talelbaz 2021-05-19 14:40:55 +03:00 committed by GitHub
parent 4658f9d05c
commit d7f2cf81c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 195 additions and 202 deletions

View File

@ -67,13 +67,3 @@ func (bgd *BlockGHOSTDAGData) MergeSetReds() []*externalapi.DomainHash {
func (bgd *BlockGHOSTDAGData) BluesAnticoneSizes() map[externalapi.DomainHash]KType {
return bgd.bluesAnticoneSizes
}
// MergeSet returns the whole MergeSet of the block (equivalent to MergeSetBlues+MergeSetReds)
func (bgd *BlockGHOSTDAGData) MergeSet() []*externalapi.DomainHash {
mergeSet := make([]*externalapi.DomainHash, len(bgd.mergeSetBlues)+len(bgd.mergeSetReds))
copy(mergeSet, bgd.mergeSetBlues)
if len(bgd.mergeSetReds) > 0 {
copy(mergeSet[len(bgd.mergeSetBlues):], bgd.mergeSetReds)
}
return mergeSet
}

View File

@ -8,4 +8,5 @@ type GHOSTDAGManager interface {
ChooseSelectedParent(stagingArea *StagingArea, blockHashes ...*externalapi.DomainHash) (*externalapi.DomainHash, error)
Less(blockHashA *externalapi.DomainHash, ghostdagDataA *BlockGHOSTDAGData,
blockHashB *externalapi.DomainHash, ghostdagDataB *BlockGHOSTDAGData) bool
GetSortedMergeSet(stagingArea *StagingArea, current *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
}

View File

@ -147,7 +147,7 @@ func (v *blockValidator) checkMergeSizeLimit(stagingArea *model.StagingArea, has
return err
}
mergeSetSize := len(ghostdagData.MergeSet())
mergeSetSize := len(ghostdagData.MergeSetBlues()) + len(ghostdagData.MergeSetReds())
if uint64(mergeSetSize) > v.mergeSetSizeLimit {
return errors.Wrapf(ruleerrors.ErrViolatingMergeLimit,

View File

@ -39,8 +39,9 @@ func (c *coinbaseManager) ExpectedCoinbaseTransaction(stagingArea *model.Staging
}
txOuts := make([]*externalapi.DomainTransactionOutput, 0, len(ghostdagData.MergeSetBlues()))
for i, blue := range ghostdagData.MergeSetBlues() {
txOut, hasReward, err := c.coinbaseOutputForBlueBlock(stagingArea, blue, acceptanceData[i], daaAddedBlocksSet)
acceptanceDataMap := acceptanceDataFromArrayToMap(acceptanceData)
for _, blue := range ghostdagData.MergeSetBlues() {
txOut, hasReward, err := c.coinbaseOutputForBlueBlock(stagingArea, blue, acceptanceDataMap[*blue], daaAddedBlocksSet)
if err != nil {
return nil, err
}
@ -120,10 +121,10 @@ func (c *coinbaseManager) coinbaseOutputForRewardFromRedBlocks(stagingArea *mode
ghostdagData *model.BlockGHOSTDAGData, acceptanceData externalapi.AcceptanceData, daaAddedBlocksSet hashset.HashSet,
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransactionOutput, bool, error) {
acceptanceDataMap := acceptanceDataFromArrayToMap(acceptanceData)
totalReward := uint64(0)
mergeSetBluesCount := len(ghostdagData.MergeSetBlues())
for i, red := range ghostdagData.MergeSetReds() {
reward, err := c.calcMergedBlockReward(stagingArea, red, acceptanceData[mergeSetBluesCount+i], daaAddedBlocksSet)
for _, red := range ghostdagData.MergeSetReds() {
reward, err := c.calcMergedBlockReward(stagingArea, red, acceptanceDataMap[*red], daaAddedBlocksSet)
if err != nil {
return nil, false, err
}
@ -141,6 +142,14 @@ func (c *coinbaseManager) coinbaseOutputForRewardFromRedBlocks(stagingArea *mode
}, true, nil
}
func acceptanceDataFromArrayToMap(acceptanceData externalapi.AcceptanceData) map[externalapi.DomainHash]*externalapi.BlockAcceptanceData {
acceptanceDataMap := make(map[externalapi.DomainHash]*externalapi.BlockAcceptanceData, len(acceptanceData))
for _, blockAcceptanceData := range acceptanceData {
acceptanceDataMap[*blockAcceptanceData.BlockHash] = blockAcceptanceData
}
return acceptanceDataMap
}
// calcBlockSubsidy returns the subsidy amount a block at the provided blue score
// should have. This is mainly used for determining how much the coinbase for
// newly generated blocks awards as well as validating the coinbase for blocks

View File

@ -61,8 +61,7 @@ func (csm *consensusStateManager) calculatePastUTXOAndAcceptanceDataWithSelected
}
log.Debugf("Applying blue blocks to the selected parent past UTXO of block %s", blockHash)
acceptanceData, utxoDiff, err := csm.applyMergeSetBlocks(
stagingArea, blockHash, selectedParentPastUTXO, blockGHOSTDAGData, daaScore)
acceptanceData, utxoDiff, err := csm.applyMergeSetBlocks(stagingArea, blockHash, selectedParentPastUTXO, daaScore)
if err != nil {
return nil, nil, nil, err
}
@ -136,13 +135,16 @@ func (csm *consensusStateManager) restorePastUTXO(
}
func (csm *consensusStateManager) applyMergeSetBlocks(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
selectedParentPastUTXODiff externalapi.UTXODiff, ghostdagData *model.BlockGHOSTDAGData, daaScore uint64) (
selectedParentPastUTXODiff externalapi.UTXODiff, daaScore uint64) (
externalapi.AcceptanceData, externalapi.MutableUTXODiff, error) {
log.Debugf("applyMergeSetBlocks start for block %s", blockHash)
defer log.Debugf("applyMergeSetBlocks end for block %s", blockHash)
mergeSetHashes := ghostdagData.MergeSet()
mergeSetHashes, err := csm.ghostdagManager.GetSortedMergeSet(stagingArea, blockHash)
if err != nil {
return nil, nil, err
}
log.Debugf("Merge set for block %s is %v", blockHash, mergeSetHashes)
mergeSetBlocks, err := csm.blockStore.Blocks(csm.databaseContext, stagingArea, mergeSetHashes)
if err != nil {
@ -266,8 +268,7 @@ func (csm *consensusStateManager) maybeAcceptTransaction(stagingArea *model.Stag
return true, accumulatedMassAfter, nil
}
func (csm *consensusStateManager) checkTransactionMass(
transaction *externalapi.DomainTransaction, accumulatedMassBefore uint64) (
func (csm *consensusStateManager) checkTransactionMass(transaction *externalapi.DomainTransaction, accumulatedMassBefore uint64) (
isAccepted bool, accumulatedMassAfter uint64) {
transactionID := consensushashing.TransactionID(transaction)

View File

@ -2,11 +2,12 @@ package consensusstatemanager_test
import (
"errors"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
"testing"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
@ -157,14 +158,15 @@ func TestDoubleSpends(t *testing.T) {
})
}
// TestTransactionAcceptance checks that blue blocks transactions are favoured above
// red blocks transactions, and that the block reward is paid only for blue blocks.
// TestTransactionAcceptance checks that block transactions are accepted correctly when the merge set is sorted topologically.
// DAG diagram:
// genesis <- blockA <- blockB <- blockC <- ..(chain of k-blocks).. lastBlockInChain <- blockD <- blockE <- blockF
// ^ ^ |
// | redBlock <------------------------ blueChildOfRedBlock <--------------------
func TestTransactionAcceptance(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
stagingArea := model.NewStagingArea()
consensusConfig.BlockCoinbaseMaturity = 0
factory := consensus.NewFactory()
testConsensus, teardown, err := factory.NewTestConsensus(consensusConfig, "TestTransactionAcceptance")
if err != nil {
@ -172,221 +174,199 @@ func TestTransactionAcceptance(t *testing.T) {
}
defer teardown(false)
fundingBlock1Hash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
blockHashA, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
if err != nil {
t.Fatalf("Error creating fundingBlock1: %+v", err)
t.Fatalf("Error creating blockA: %+v", err)
}
fundingBlock2Hash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{fundingBlock1Hash}, nil, nil)
blockHashB, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockHashA}, nil, nil)
if err != nil {
t.Fatalf("Error creating fundingBlock2: %+v", err)
t.Fatalf("Error creating blockB: %+v", err)
}
// Generate fundingBlock3 to pay for fundingBlock2
fundingBlock3Hash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{fundingBlock2Hash}, nil, nil)
blockHashC, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockHashB}, nil, nil)
if err != nil {
t.Fatalf("Error creating fundingBlock3: %+v", err)
t.Fatalf("Error creating blockC: %+v", err)
}
// Add a chain of K blocks above fundingBlock3 so we'll
// Add a chain of K blocks above blockC so we'll
// be able to mine a red block on top of it.
tipHash := fundingBlock3Hash
chainTipHash := blockHashC
for i := model.KType(0); i < consensusConfig.K; i++ {
var err error
tipHash, _, err = testConsensus.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil)
chainTipHash, _, err = testConsensus.AddBlock([]*externalapi.DomainHash{chainTipHash}, nil, nil)
if err != nil {
t.Fatalf("Error creating fundingBlock1: %+v", err)
t.Fatalf("Error creating a block: %+v", err)
}
}
fundingBlock2, err := testConsensus.GetBlock(fundingBlock2Hash)
lastBlockInChain := chainTipHash
blockC, err := testConsensus.GetBlock(blockHashC)
if err != nil {
t.Fatalf("Error getting fundingBlock: %+v", err)
t.Fatalf("Error getting blockC: %+v", err)
}
fundingTransaction1 := fundingBlock2.Transactions[transactionhelper.CoinbaseTransactionIndex]
fundingBlock3, err := testConsensus.GetBlock(fundingBlock3Hash)
fees := uint64(1)
transactionFromBlockC := blockC.Transactions[transactionhelper.CoinbaseTransactionIndex]
// transactionFromRedBlock is spending TransactionFromBlockC.
transactionFromRedBlock, err := testutils.CreateTransaction(transactionFromBlockC, fees)
if err != nil {
t.Fatalf("Error getting fundingBlock: %+v", err)
t.Fatalf("Error creating a transactionFromRedBlock: %+v", err)
}
fundingTransaction2 := fundingBlock3.Transactions[transactionhelper.CoinbaseTransactionIndex]
spendingTransaction1, err := testutils.CreateTransaction(fundingTransaction1, 1)
transactionFromRedBlockInput0UTXOEntry, err := testConsensus.ConsensusStateStore().
UTXOByOutpoint(testConsensus.DatabaseContext(), stagingArea, &transactionFromRedBlock.Inputs[0].PreviousOutpoint)
if err != nil {
t.Fatalf("Error creating spendingTransaction1: %+v", err)
t.Fatalf("Error getting UTXOEntry for transactionFromRedBlockInput: %s", err)
}
spendingTransaction1UTXOEntry, err := testConsensus.ConsensusStateStore().
UTXOByOutpoint(testConsensus.DatabaseContext(), stagingArea, &spendingTransaction1.Inputs[0].PreviousOutpoint)
if err != nil {
t.Fatalf("Error getting UTXOEntry for spendingTransaction1: %s", err)
}
spendingTransaction2, err := testutils.CreateTransaction(fundingTransaction2, 1)
if err != nil {
t.Fatalf("Error creating spendingTransaction1: %+v", err)
}
spendingTransaction2UTXOEntry, err := testConsensus.ConsensusStateStore().
UTXOByOutpoint(testConsensus.DatabaseContext(), stagingArea, &spendingTransaction2.Inputs[0].PreviousOutpoint)
if err != nil {
t.Fatalf("Error getting UTXOEntry for spendingTransaction2: %s", err)
}
redHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{fundingBlock3Hash}, nil,
[]*externalapi.DomainTransaction{spendingTransaction1, spendingTransaction2})
redHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockHashC}, nil,
[]*externalapi.DomainTransaction{transactionFromRedBlock})
if err != nil {
t.Fatalf("Error creating redBlock: %+v", err)
}
blueScriptPublicKey := &externalapi.ScriptPublicKey{Script: []byte{1}, Version: 0}
blueHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{tipHash}, &externalapi.DomainCoinbaseData{
ScriptPublicKey: blueScriptPublicKey,
ExtraData: nil,
},
[]*externalapi.DomainTransaction{spendingTransaction1})
transactionFromBlueChildOfRedBlock, err := testutils.CreateTransaction(transactionFromRedBlock, fees)
if err != nil {
t.Fatalf("Error creating blue: %+v", err)
t.Fatalf("Error creating transactionFromBlueChildOfRedBlock: %+v", err)
}
// Mining two blocks so tipHash will definitely be the selected tip.
tipHash, _, err = testConsensus.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil)
transactionFromBlueChildOfRedBlockInput0UTXOEntry, err := testConsensus.ConsensusStateStore().
UTXOByOutpoint(testConsensus.DatabaseContext(), stagingArea, &transactionFromBlueChildOfRedBlock.Inputs[0].PreviousOutpoint)
if err != nil {
t.Fatalf("Error creating tip: %+v", err)
t.Fatalf("Error getting UTXOEntry for transactionFromBlueChildOfRedBlockInput: %s", err)
}
finalTipSelectedParentScriptPublicKey := &externalapi.ScriptPublicKey{Script: []byte{3}, Version: 0}
finalTipSelectedParentHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{tipHash},
blueChildOfRedBlockScriptPublicKey := &externalapi.ScriptPublicKey{Script: []byte{3}, Version: 0}
// The blueChildOfRedBlock contains a transaction that spent an output from the red block.
hashBlueChildOfRedBlock, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{lastBlockInChain, redHash},
&externalapi.DomainCoinbaseData{
ScriptPublicKey: finalTipSelectedParentScriptPublicKey,
ScriptPublicKey: blueChildOfRedBlockScriptPublicKey,
ExtraData: nil,
}, []*externalapi.DomainTransaction{transactionFromBlueChildOfRedBlock})
if err != nil {
t.Fatalf("Error creating blueChildOfRedBlock: %+v", err)
}
// K blocks minded between blockC and blockD.
blockHashD, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{lastBlockInChain}, nil, nil)
if err != nil {
t.Fatalf("Error creating blockD : %+v", err)
}
blockEScriptPublicKey := &externalapi.ScriptPublicKey{Script: []byte{4}, Version: 0}
blockHashE, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockHashD},
&externalapi.DomainCoinbaseData{
ScriptPublicKey: blockEScriptPublicKey,
ExtraData: nil,
}, nil)
if err != nil {
t.Fatalf("Error creating tip: %+v", err)
t.Fatalf("Error creating blockE: %+v", err)
}
finalTipScriptPublicKey := &externalapi.ScriptPublicKey{Script: []byte{4}, Version: 0}
finalTipHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{finalTipSelectedParentHash, redHash, blueHash},
blockFScriptPublicKey := &externalapi.ScriptPublicKey{Script: []byte{5}, Version: 0}
blockHashF, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockHashE, hashBlueChildOfRedBlock},
&externalapi.DomainCoinbaseData{
ScriptPublicKey: finalTipScriptPublicKey,
ScriptPublicKey: blockFScriptPublicKey,
ExtraData: nil,
},
nil)
}, nil)
if err != nil {
t.Fatalf("Error creating finalTip: %+v", err)
t.Fatalf("Error creating blockF: %+v", err)
}
acceptanceData, err := testConsensus.AcceptanceDataStore().Get(testConsensus.DatabaseContext(), stagingArea, finalTipHash)
acceptanceData, err := testConsensus.AcceptanceDataStore().Get(testConsensus.DatabaseContext(), stagingArea, blockHashF)
if err != nil {
t.Fatalf("Error getting acceptance data: %+v", err)
}
finalTipSelectedParent, err := testConsensus.GetBlock(finalTipSelectedParentHash)
blueChildOfRedBlock, err := testConsensus.GetBlock(hashBlueChildOfRedBlock)
if err != nil {
t.Fatalf("Error getting finalTipSelectedParent: %+v", err)
t.Fatalf("Error getting blueChildOfRedBlock: %+v", err)
}
blue, err := testConsensus.GetBlock(blueHash)
blockE, err := testConsensus.GetBlock(blockHashE)
if err != nil {
t.Fatalf("Error getting blue: %+v", err)
t.Fatalf("Error getting blockE: %+v", err)
}
red, err := testConsensus.GetBlock(redHash)
redBlock, err := testConsensus.GetBlock(redHash)
if err != nil {
t.Fatalf("Error getting red: %+v", err)
t.Fatalf("Error getting redBlock: %+v", err)
}
// We expect spendingTransaction1 to be accepted by the blue block and not by the red one, because
// blue blocks in the merge set should always be ordered before red blocks in the merge set.
// We also expect spendingTransaction2 to be accepted by the red because nothing conflicts it.
blockF, err := testConsensus.GetBlock(blockHashF)
if err != nil {
t.Fatalf("Error getting blockF: %+v", err)
}
updatedDAAScoreVirtualBlock := 25
//We expect the second transaction in the "blue block" (blueChildOfRedBlock) to be accepted because the merge set is ordered topologically
//and the red block is ordered topologically before the "blue block" so the input is known in the UTXOSet.
expectedAcceptanceData := externalapi.AcceptanceData{
{
BlockHash: finalTipSelectedParentHash,
BlockHash: blockHashE,
TransactionAcceptanceData: []*externalapi.TransactionAcceptanceData{
{
Transaction: finalTipSelectedParent.Transactions[0],
Transaction: blockE.Transactions[0],
Fee: 0,
IsAccepted: true,
TransactionInputUTXOEntries: []externalapi.UTXOEntry{},
},
},
},
{
BlockHash: blueHash,
TransactionAcceptanceData: []*externalapi.TransactionAcceptanceData{
{
Transaction: blue.Transactions[0],
Fee: 0,
IsAccepted: false,
TransactionInputUTXOEntries: []externalapi.UTXOEntry{},
},
{
Transaction: spendingTransaction1,
Fee: 1,
IsAccepted: true,
TransactionInputUTXOEntries: []externalapi.UTXOEntry{spendingTransaction1UTXOEntry},
},
},
},
{
BlockHash: redHash,
TransactionAcceptanceData: []*externalapi.TransactionAcceptanceData{
{
Transaction: red.Transactions[0],
{ //Coinbase transaction outputs are added to the UTXO-set only if they are in the selected parent chain,
// and this block isn't.
Transaction: redBlock.Transactions[0],
Fee: 0,
IsAccepted: false,
TransactionInputUTXOEntries: []externalapi.UTXOEntry{},
},
{
Transaction: spendingTransaction1,
Fee: 0,
IsAccepted: false,
TransactionInputUTXOEntries: []externalapi.UTXOEntry{},
},
{
Transaction: spendingTransaction2,
Fee: 1,
Transaction: redBlock.Transactions[1],
Fee: fees,
IsAccepted: true,
TransactionInputUTXOEntries: []externalapi.UTXOEntry{spendingTransaction2UTXOEntry},
TransactionInputUTXOEntries: []externalapi.UTXOEntry{transactionFromRedBlockInput0UTXOEntry},
},
},
},
{
BlockHash: hashBlueChildOfRedBlock,
TransactionAcceptanceData: []*externalapi.TransactionAcceptanceData{
{ //Coinbase transaction outputs are added to the UTXO-set only if they are in the selected parent chain,
// and this block isn't.
Transaction: blueChildOfRedBlock.Transactions[0],
Fee: 0,
IsAccepted: false,
TransactionInputUTXOEntries: []externalapi.UTXOEntry{},
},
{ // The DAAScore was calculated by the virtual block pov. The DAAScore has changed since more blocks were added to the DAG.
// So we will change the DAAScore in the UTXOEntryInput to the updated virtual DAAScore.
Transaction: blueChildOfRedBlock.Transactions[1],
Fee: fees,
IsAccepted: true,
TransactionInputUTXOEntries: []externalapi.UTXOEntry{
utxo.NewUTXOEntry(transactionFromBlueChildOfRedBlockInput0UTXOEntry.Amount(),
transactionFromBlueChildOfRedBlockInput0UTXOEntry.ScriptPublicKey(),
transactionFromBlueChildOfRedBlockInput0UTXOEntry.IsCoinbase(), uint64(updatedDAAScoreVirtualBlock))},
},
},
},
}
if !acceptanceData.Equal(expectedAcceptanceData) {
t.Fatalf("The acceptance data is not the expected acceptance data")
}
finalTip, err := testConsensus.GetBlock(finalTipHash)
if err != nil {
t.Fatalf("Error getting finalTip: %+v", err)
}
// We expect the coinbase transaction to pay reward for the selected parent, the
// blue block, and bestow the red block reward to the merging block.
// We expect the coinbase transaction to pay reward for the selected parent(block E), the
// blueChildOfRedBlock, and bestow the red block reward to the merging block.
expectedCoinbase := &externalapi.DomainTransaction{
Version: constants.MaxTransactionVersion,
Inputs: nil,
Outputs: []*externalapi.DomainTransactionOutput{
{
Value: 50 * constants.SompiPerKaspa,
ScriptPublicKey: finalTipSelectedParentScriptPublicKey,
ScriptPublicKey: blockEScriptPublicKey,
},
{
Value: 50*constants.SompiPerKaspa + 1, // testutils.CreateTransaction pays a fee of 1 sompi
ScriptPublicKey: blueScriptPublicKey,
Value: 50*constants.SompiPerKaspa + fees, // testutils.CreateTransaction pays fees
ScriptPublicKey: blueChildOfRedBlockScriptPublicKey,
},
{
Value: 50*constants.SompiPerKaspa + 1,
ScriptPublicKey: finalTipScriptPublicKey,
Value: 50*constants.SompiPerKaspa + fees, // testutils.CreateTransaction pays fees
ScriptPublicKey: blockFScriptPublicKey,
},
},
LockTime: 0,
SubnetworkID: subnetworks.SubnetworkIDCoinbase,
Gas: 0,
Payload: finalTip.Transactions[0].Payload,
Payload: blockF.Transactions[0].Payload,
}
if !finalTip.Transactions[transactionhelper.CoinbaseTransactionIndex].Equal(expectedCoinbase) {
if !blockF.Transactions[transactionhelper.CoinbaseTransactionIndex].Equal(expectedCoinbase) {
t.Fatalf("Unexpected coinbase transaction")
}
})

View File

@ -167,9 +167,13 @@ func (dm *difficultyManager) calculateDaaScoreAndAddedBlocks(stagingArea *model.
if err != nil {
return 0, nil, err
}
mergeSetLength := len(ghostdagData.MergeSetBlues()) + len(ghostdagData.MergeSetReds())
mergeSet := make(map[externalapi.DomainHash]struct{}, mergeSetLength)
for _, hash := range ghostdagData.MergeSetBlues() {
mergeSet[*hash] = struct{}{}
}
mergeSet := make(map[externalapi.DomainHash]struct{}, len(ghostdagData.MergeSet()))
for _, hash := range ghostdagData.MergeSet() {
for _, hash := range ghostdagData.MergeSetReds() {
mergeSet[*hash] = struct{}{}
}

View File

@ -409,3 +409,7 @@ func (gh *ghostdagHelper) ChooseSelectedParent(stagingArea *model.StagingArea, b
func (gh *ghostdagHelper) Less(blockHashA *externalapi.DomainHash, ghostdagDataA *model.BlockGHOSTDAGData, blockHashB *externalapi.DomainHash, ghostdagDataB *model.BlockGHOSTDAGData) bool {
panic("implement me")
}
func (gh *ghostdagHelper) GetSortedMergeSet(*model.StagingArea, *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
panic("implement me")
}

View File

@ -82,3 +82,47 @@ func (gm *ghostdagManager) sortMergeSet(stagingArea *model.StagingArea, mergeSet
})
return err
}
// GetSortedMergeSet return the merge set sorted in a toplogical order.
func (gm *ghostdagManager) GetSortedMergeSet(stagingArea *model.StagingArea,
current *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
currentGhostdagData, err := gm.ghostdagDataStore.Get(gm.databaseContext, stagingArea, current)
if err != nil {
return nil, err
}
blueMergeSet := currentGhostdagData.MergeSetBlues()
redMergeSet := currentGhostdagData.MergeSetReds()
sortedMergeSet := make([]*externalapi.DomainHash, 0, len(blueMergeSet)+len(redMergeSet))
// If the current block is the genesis block:
if len(blueMergeSet) == 0 {
return sortedMergeSet, nil
}
selectedParent, blueMergeSet := blueMergeSet[0], blueMergeSet[1:]
sortedMergeSet = append(sortedMergeSet, selectedParent)
i, j := 0, 0
for i < len(blueMergeSet) && j < len(redMergeSet) {
currentBlue := blueMergeSet[i]
currentBlueGhostdagData, err := gm.ghostdagDataStore.Get(gm.databaseContext, stagingArea, currentBlue)
if err != nil {
return nil, err
}
currentRed := redMergeSet[j]
currentRedGhostdagData, err := gm.ghostdagDataStore.Get(gm.databaseContext, stagingArea, currentRed)
if err != nil {
return nil, err
}
if gm.Less(currentBlue, currentBlueGhostdagData, currentRed, currentRedGhostdagData) {
sortedMergeSet = append(sortedMergeSet, currentBlue)
i++
} else {
sortedMergeSet = append(sortedMergeSet, currentRed)
j++
}
}
sortedMergeSet = append(sortedMergeSet, blueMergeSet[i:]...)
sortedMergeSet = append(sortedMergeSet, redMergeSet[j:]...)
return sortedMergeSet, nil
}

View File

@ -71,7 +71,7 @@ func (sm *syncManager) antiPastHashesBetween(stagingArea *model.StagingArea, low
// Since the rest of the merge set is in the anticone of selectedParent, it's position in the list does not
// matter, even though it's blue score is the highest, we can arbitrarily decide it comes first.
// Therefore we first append the selectedParent, then the rest of blocks in ghostdag order.
sortedMergeSet, err := sm.getSortedMergeSet(stagingArea, current)
sortedMergeSet, err := sm.ghostdagManager.GetSortedMergeSet(stagingArea, current)
if err != nil {
return nil, nil, err
}
@ -97,49 +97,9 @@ func (sm *syncManager) antiPastHashesBetween(stagingArea *model.StagingArea, low
return blockHashes, highHash, nil
}
func (sm *syncManager) getSortedMergeSet(stagingArea *model.StagingArea, current *externalapi.DomainHash) (
[]*externalapi.DomainHash, error) {
currentGhostdagData, err := sm.ghostdagDataStore.Get(sm.databaseContext, stagingArea, current)
if err != nil {
return nil, err
}
blueMergeSet := currentGhostdagData.MergeSetBlues()
redMergeSet := currentGhostdagData.MergeSetReds()
sortedMergeSet := make([]*externalapi.DomainHash, 0, len(blueMergeSet)+len(redMergeSet))
selectedParent, blueMergeSet := blueMergeSet[0], blueMergeSet[1:]
sortedMergeSet = append(sortedMergeSet, selectedParent)
i, j := 0, 0
for i < len(blueMergeSet) && j < len(redMergeSet) {
currentBlue := blueMergeSet[i]
currentBlueGhostdagData, err := sm.ghostdagDataStore.Get(sm.databaseContext, stagingArea, currentBlue)
if err != nil {
return nil, err
}
currentRed := redMergeSet[j]
currentRedGhostdagData, err := sm.ghostdagDataStore.Get(sm.databaseContext, stagingArea, currentRed)
if err != nil {
return nil, err
}
if sm.ghostdagManager.Less(currentBlue, currentBlueGhostdagData, currentRed, currentRedGhostdagData) {
sortedMergeSet = append(sortedMergeSet, currentBlue)
i++
} else {
sortedMergeSet = append(sortedMergeSet, currentRed)
j++
}
}
sortedMergeSet = append(sortedMergeSet, blueMergeSet[i:]...)
sortedMergeSet = append(sortedMergeSet, redMergeSet[j:]...)
return sortedMergeSet, nil
}
func (sm *syncManager) findHighHashAccordingToMaxBlueScoreDifference(stagingArea *model.StagingArea,
lowHash *externalapi.DomainHash, highHash *externalapi.DomainHash, maxBlueScoreDifference uint64,
highBlockGHOSTDAGData *model.BlockGHOSTDAGData, lowBlockGHOSTDAGData *model.BlockGHOSTDAGData) (
*externalapi.DomainHash, error) {
func (sm *syncManager) findHighHashAccordingToMaxBlueScoreDifference(stagingArea *model.StagingArea, lowHash *externalapi.DomainHash,
highHash *externalapi.DomainHash, maxBlueScoreDifference uint64, highBlockGHOSTDAGData *model.BlockGHOSTDAGData,
lowBlockGHOSTDAGData *model.BlockGHOSTDAGData) (*externalapi.DomainHash, error) {
if highBlockGHOSTDAGData.BlueScore()-lowBlockGHOSTDAGData.BlueScore() <= maxBlueScoreDifference {
return highHash, nil