mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-05 13:46:42 +00:00
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:
parent
4658f9d05c
commit
d7f2cf81c0
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
}
|
||||
})
|
||||
|
@ -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{}{}
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user