From d7f2cf81c0a07a31d26e2cd32ce8b7ea08614e5e Mon Sep 17 00:00:00 2001 From: talelbaz <63008512+talelbaz@users.noreply.github.com> Date: Wed, 19 May 2021 14:40:55 +0300 Subject: [PATCH] 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 Co-authored-by: Ori Newman --- domain/consensus/model/ghostdag.go | 10 - .../interface_processes_ghostdagmanager.go | 1 + .../blockvalidator/block_header_in_context.go | 2 +- .../coinbasemanager/coinbasemanager.go | 19 +- .../calculate_past_utxo.go | 13 +- .../resolve_block_status_test.go | 248 ++++++++---------- .../difficultymanager/difficultymanager.go | 8 +- .../processes/ghostdag2/ghostdagimpl.go | 4 + .../processes/ghostdagmanager/mergeset.go | 44 ++++ .../processes/syncmanager/antipast.go | 48 +--- 10 files changed, 195 insertions(+), 202 deletions(-) diff --git a/domain/consensus/model/ghostdag.go b/domain/consensus/model/ghostdag.go index 5b77cd83d..4ebe4372e 100644 --- a/domain/consensus/model/ghostdag.go +++ b/domain/consensus/model/ghostdag.go @@ -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 -} diff --git a/domain/consensus/model/interface_processes_ghostdagmanager.go b/domain/consensus/model/interface_processes_ghostdagmanager.go index be831c488..95025696a 100644 --- a/domain/consensus/model/interface_processes_ghostdagmanager.go +++ b/domain/consensus/model/interface_processes_ghostdagmanager.go @@ -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) } diff --git a/domain/consensus/processes/blockvalidator/block_header_in_context.go b/domain/consensus/processes/blockvalidator/block_header_in_context.go index 71fe0be8b..957295c2c 100644 --- a/domain/consensus/processes/blockvalidator/block_header_in_context.go +++ b/domain/consensus/processes/blockvalidator/block_header_in_context.go @@ -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, diff --git a/domain/consensus/processes/coinbasemanager/coinbasemanager.go b/domain/consensus/processes/coinbasemanager/coinbasemanager.go index a19e739ec..61230429d 100644 --- a/domain/consensus/processes/coinbasemanager/coinbasemanager.go +++ b/domain/consensus/processes/coinbasemanager/coinbasemanager.go @@ -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 diff --git a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go index d1cb3bb49..e67ed39d0 100644 --- a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go +++ b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go @@ -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) diff --git a/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go b/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go index 97d7ab8b9..280b25e9a 100644 --- a/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go +++ b/domain/consensus/processes/consensusstatemanager/resolve_block_status_test.go @@ -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") } }) diff --git a/domain/consensus/processes/difficultymanager/difficultymanager.go b/domain/consensus/processes/difficultymanager/difficultymanager.go index 793368ccc..7dcf6ff36 100644 --- a/domain/consensus/processes/difficultymanager/difficultymanager.go +++ b/domain/consensus/processes/difficultymanager/difficultymanager.go @@ -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{}{} } diff --git a/domain/consensus/processes/ghostdag2/ghostdagimpl.go b/domain/consensus/processes/ghostdag2/ghostdagimpl.go index c48276e04..22003878a 100644 --- a/domain/consensus/processes/ghostdag2/ghostdagimpl.go +++ b/domain/consensus/processes/ghostdag2/ghostdagimpl.go @@ -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") +} diff --git a/domain/consensus/processes/ghostdagmanager/mergeset.go b/domain/consensus/processes/ghostdagmanager/mergeset.go index 8b2fd49c1..f6851947f 100644 --- a/domain/consensus/processes/ghostdagmanager/mergeset.go +++ b/domain/consensus/processes/ghostdagmanager/mergeset.go @@ -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 +} diff --git a/domain/consensus/processes/syncmanager/antipast.go b/domain/consensus/processes/syncmanager/antipast.go index 6909118f6..46b270cfa 100644 --- a/domain/consensus/processes/syncmanager/antipast.go +++ b/domain/consensus/processes/syncmanager/antipast.go @@ -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