mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-10-14 00:59:33 +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 {
|
func (bgd *BlockGHOSTDAGData) BluesAnticoneSizes() map[externalapi.DomainHash]KType {
|
||||||
return bgd.bluesAnticoneSizes
|
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)
|
ChooseSelectedParent(stagingArea *StagingArea, blockHashes ...*externalapi.DomainHash) (*externalapi.DomainHash, error)
|
||||||
Less(blockHashA *externalapi.DomainHash, ghostdagDataA *BlockGHOSTDAGData,
|
Less(blockHashA *externalapi.DomainHash, ghostdagDataA *BlockGHOSTDAGData,
|
||||||
blockHashB *externalapi.DomainHash, ghostdagDataB *BlockGHOSTDAGData) bool
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
mergeSetSize := len(ghostdagData.MergeSet())
|
mergeSetSize := len(ghostdagData.MergeSetBlues()) + len(ghostdagData.MergeSetReds())
|
||||||
|
|
||||||
if uint64(mergeSetSize) > v.mergeSetSizeLimit {
|
if uint64(mergeSetSize) > v.mergeSetSizeLimit {
|
||||||
return errors.Wrapf(ruleerrors.ErrViolatingMergeLimit,
|
return errors.Wrapf(ruleerrors.ErrViolatingMergeLimit,
|
||||||
|
@ -39,8 +39,9 @@ func (c *coinbaseManager) ExpectedCoinbaseTransaction(stagingArea *model.Staging
|
|||||||
}
|
}
|
||||||
|
|
||||||
txOuts := make([]*externalapi.DomainTransactionOutput, 0, len(ghostdagData.MergeSetBlues()))
|
txOuts := make([]*externalapi.DomainTransactionOutput, 0, len(ghostdagData.MergeSetBlues()))
|
||||||
for i, blue := range ghostdagData.MergeSetBlues() {
|
acceptanceDataMap := acceptanceDataFromArrayToMap(acceptanceData)
|
||||||
txOut, hasReward, err := c.coinbaseOutputForBlueBlock(stagingArea, blue, acceptanceData[i], daaAddedBlocksSet)
|
for _, blue := range ghostdagData.MergeSetBlues() {
|
||||||
|
txOut, hasReward, err := c.coinbaseOutputForBlueBlock(stagingArea, blue, acceptanceDataMap[*blue], daaAddedBlocksSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -120,10 +121,10 @@ func (c *coinbaseManager) coinbaseOutputForRewardFromRedBlocks(stagingArea *mode
|
|||||||
ghostdagData *model.BlockGHOSTDAGData, acceptanceData externalapi.AcceptanceData, daaAddedBlocksSet hashset.HashSet,
|
ghostdagData *model.BlockGHOSTDAGData, acceptanceData externalapi.AcceptanceData, daaAddedBlocksSet hashset.HashSet,
|
||||||
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransactionOutput, bool, error) {
|
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransactionOutput, bool, error) {
|
||||||
|
|
||||||
|
acceptanceDataMap := acceptanceDataFromArrayToMap(acceptanceData)
|
||||||
totalReward := uint64(0)
|
totalReward := uint64(0)
|
||||||
mergeSetBluesCount := len(ghostdagData.MergeSetBlues())
|
for _, red := range ghostdagData.MergeSetReds() {
|
||||||
for i, red := range ghostdagData.MergeSetReds() {
|
reward, err := c.calcMergedBlockReward(stagingArea, red, acceptanceDataMap[*red], daaAddedBlocksSet)
|
||||||
reward, err := c.calcMergedBlockReward(stagingArea, red, acceptanceData[mergeSetBluesCount+i], daaAddedBlocksSet)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
@ -141,6 +142,14 @@ func (c *coinbaseManager) coinbaseOutputForRewardFromRedBlocks(stagingArea *mode
|
|||||||
}, true, nil
|
}, 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
|
// 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
|
// 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
|
// 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)
|
log.Debugf("Applying blue blocks to the selected parent past UTXO of block %s", blockHash)
|
||||||
acceptanceData, utxoDiff, err := csm.applyMergeSetBlocks(
|
acceptanceData, utxoDiff, err := csm.applyMergeSetBlocks(stagingArea, blockHash, selectedParentPastUTXO, daaScore)
|
||||||
stagingArea, blockHash, selectedParentPastUTXO, blockGHOSTDAGData, daaScore)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
@ -136,13 +135,16 @@ func (csm *consensusStateManager) restorePastUTXO(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (csm *consensusStateManager) applyMergeSetBlocks(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
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) {
|
externalapi.AcceptanceData, externalapi.MutableUTXODiff, error) {
|
||||||
|
|
||||||
log.Debugf("applyMergeSetBlocks start for block %s", blockHash)
|
log.Debugf("applyMergeSetBlocks start for block %s", blockHash)
|
||||||
defer log.Debugf("applyMergeSetBlocks end 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)
|
log.Debugf("Merge set for block %s is %v", blockHash, mergeSetHashes)
|
||||||
mergeSetBlocks, err := csm.blockStore.Blocks(csm.databaseContext, stagingArea, mergeSetHashes)
|
mergeSetBlocks, err := csm.blockStore.Blocks(csm.databaseContext, stagingArea, mergeSetHashes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -266,8 +268,7 @@ func (csm *consensusStateManager) maybeAcceptTransaction(stagingArea *model.Stag
|
|||||||
return true, accumulatedMassAfter, nil
|
return true, accumulatedMassAfter, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (csm *consensusStateManager) checkTransactionMass(
|
func (csm *consensusStateManager) checkTransactionMass(transaction *externalapi.DomainTransaction, accumulatedMassBefore uint64) (
|
||||||
transaction *externalapi.DomainTransaction, accumulatedMassBefore uint64) (
|
|
||||||
isAccepted bool, accumulatedMassAfter uint64) {
|
isAccepted bool, accumulatedMassAfter uint64) {
|
||||||
|
|
||||||
transactionID := consensushashing.TransactionID(transaction)
|
transactionID := consensushashing.TransactionID(transaction)
|
||||||
|
@ -2,11 +2,12 @@ package consensusstatemanager_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"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"
|
"testing"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
"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"
|
"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
|
// TestTransactionAcceptance checks that block transactions are accepted correctly when the merge set is sorted topologically.
|
||||||
// red blocks transactions, and that the block reward is paid only for blue blocks.
|
// DAG diagram:
|
||||||
|
// genesis <- blockA <- blockB <- blockC <- ..(chain of k-blocks).. lastBlockInChain <- blockD <- blockE <- blockF
|
||||||
|
// ^ ^ |
|
||||||
|
// | redBlock <------------------------ blueChildOfRedBlock <--------------------
|
||||||
func TestTransactionAcceptance(t *testing.T) {
|
func TestTransactionAcceptance(t *testing.T) {
|
||||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||||
stagingArea := model.NewStagingArea()
|
stagingArea := model.NewStagingArea()
|
||||||
|
|
||||||
consensusConfig.BlockCoinbaseMaturity = 0
|
consensusConfig.BlockCoinbaseMaturity = 0
|
||||||
|
|
||||||
factory := consensus.NewFactory()
|
factory := consensus.NewFactory()
|
||||||
testConsensus, teardown, err := factory.NewTestConsensus(consensusConfig, "TestTransactionAcceptance")
|
testConsensus, teardown, err := factory.NewTestConsensus(consensusConfig, "TestTransactionAcceptance")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -172,221 +174,199 @@ func TestTransactionAcceptance(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer teardown(false)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("Error creating fundingBlock1: %+v", err)
|
t.Fatalf("Error creating blockA: %+v", err)
|
||||||
}
|
}
|
||||||
|
blockHashB, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockHashA}, nil, nil)
|
||||||
fundingBlock2Hash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{fundingBlock1Hash}, nil, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error creating fundingBlock2: %+v", err)
|
t.Fatalf("Error creating blockB: %+v", err)
|
||||||
}
|
}
|
||||||
|
blockHashC, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockHashB}, nil, nil)
|
||||||
// Generate fundingBlock3 to pay for fundingBlock2
|
|
||||||
fundingBlock3Hash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{fundingBlock2Hash}, nil, nil)
|
|
||||||
if err != 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 blockC so we'll
|
||||||
// Add a chain of K blocks above fundingBlock3 so we'll
|
|
||||||
// be able to mine a red block on top of it.
|
// be able to mine a red block on top of it.
|
||||||
tipHash := fundingBlock3Hash
|
chainTipHash := blockHashC
|
||||||
for i := model.KType(0); i < consensusConfig.K; i++ {
|
for i := model.KType(0); i < consensusConfig.K; i++ {
|
||||||
var err error
|
var err error
|
||||||
tipHash, _, err = testConsensus.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil)
|
chainTipHash, _, err = testConsensus.AddBlock([]*externalapi.DomainHash{chainTipHash}, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error creating fundingBlock1: %+v", err)
|
t.Fatalf("Error creating a block: %+v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
lastBlockInChain := chainTipHash
|
||||||
fundingBlock2, err := testConsensus.GetBlock(fundingBlock2Hash)
|
blockC, err := testConsensus.GetBlock(blockHashC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error getting fundingBlock: %+v", err)
|
t.Fatalf("Error getting blockC: %+v", err)
|
||||||
}
|
}
|
||||||
|
fees := uint64(1)
|
||||||
fundingTransaction1 := fundingBlock2.Transactions[transactionhelper.CoinbaseTransactionIndex]
|
transactionFromBlockC := blockC.Transactions[transactionhelper.CoinbaseTransactionIndex]
|
||||||
|
// transactionFromRedBlock is spending TransactionFromBlockC.
|
||||||
fundingBlock3, err := testConsensus.GetBlock(fundingBlock3Hash)
|
transactionFromRedBlock, err := testutils.CreateTransaction(transactionFromBlockC, fees)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error getting fundingBlock: %+v", err)
|
t.Fatalf("Error creating a transactionFromRedBlock: %+v", err)
|
||||||
}
|
}
|
||||||
|
transactionFromRedBlockInput0UTXOEntry, err := testConsensus.ConsensusStateStore().
|
||||||
fundingTransaction2 := fundingBlock3.Transactions[transactionhelper.CoinbaseTransactionIndex]
|
UTXOByOutpoint(testConsensus.DatabaseContext(), stagingArea, &transactionFromRedBlock.Inputs[0].PreviousOutpoint)
|
||||||
|
|
||||||
spendingTransaction1, err := testutils.CreateTransaction(fundingTransaction1, 1)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error creating spendingTransaction1: %+v", err)
|
t.Fatalf("Error getting UTXOEntry for transactionFromRedBlockInput: %s", err)
|
||||||
}
|
}
|
||||||
spendingTransaction1UTXOEntry, err := testConsensus.ConsensusStateStore().
|
redHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockHashC}, nil,
|
||||||
UTXOByOutpoint(testConsensus.DatabaseContext(), stagingArea, &spendingTransaction1.Inputs[0].PreviousOutpoint)
|
[]*externalapi.DomainTransaction{transactionFromRedBlock})
|
||||||
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})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error creating redBlock: %+v", err)
|
t.Fatalf("Error creating redBlock: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
blueScriptPublicKey := &externalapi.ScriptPublicKey{Script: []byte{1}, Version: 0}
|
transactionFromBlueChildOfRedBlock, err := testutils.CreateTransaction(transactionFromRedBlock, fees)
|
||||||
blueHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{tipHash}, &externalapi.DomainCoinbaseData{
|
|
||||||
ScriptPublicKey: blueScriptPublicKey,
|
|
||||||
ExtraData: nil,
|
|
||||||
},
|
|
||||||
[]*externalapi.DomainTransaction{spendingTransaction1})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error creating blue: %+v", err)
|
t.Fatalf("Error creating transactionFromBlueChildOfRedBlock: %+v", err)
|
||||||
}
|
}
|
||||||
|
transactionFromBlueChildOfRedBlockInput0UTXOEntry, err := testConsensus.ConsensusStateStore().
|
||||||
// Mining two blocks so tipHash will definitely be the selected tip.
|
UTXOByOutpoint(testConsensus.DatabaseContext(), stagingArea, &transactionFromBlueChildOfRedBlock.Inputs[0].PreviousOutpoint)
|
||||||
tipHash, _, err = testConsensus.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error creating tip: %+v", err)
|
t.Fatalf("Error getting UTXOEntry for transactionFromBlueChildOfRedBlockInput: %s", err)
|
||||||
}
|
}
|
||||||
|
blueChildOfRedBlockScriptPublicKey := &externalapi.ScriptPublicKey{Script: []byte{3}, Version: 0}
|
||||||
finalTipSelectedParentScriptPublicKey := &externalapi.ScriptPublicKey{Script: []byte{3}, Version: 0}
|
// The blueChildOfRedBlock contains a transaction that spent an output from the red block.
|
||||||
finalTipSelectedParentHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{tipHash},
|
hashBlueChildOfRedBlock, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{lastBlockInChain, redHash},
|
||||||
&externalapi.DomainCoinbaseData{
|
&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,
|
ExtraData: nil,
|
||||||
}, nil)
|
}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error creating tip: %+v", err)
|
t.Fatalf("Error creating blockE: %+v", err)
|
||||||
}
|
}
|
||||||
|
blockFScriptPublicKey := &externalapi.ScriptPublicKey{Script: []byte{5}, Version: 0}
|
||||||
finalTipScriptPublicKey := &externalapi.ScriptPublicKey{Script: []byte{4}, Version: 0}
|
blockHashF, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockHashE, hashBlueChildOfRedBlock},
|
||||||
finalTipHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{finalTipSelectedParentHash, redHash, blueHash},
|
|
||||||
&externalapi.DomainCoinbaseData{
|
&externalapi.DomainCoinbaseData{
|
||||||
ScriptPublicKey: finalTipScriptPublicKey,
|
ScriptPublicKey: blockFScriptPublicKey,
|
||||||
ExtraData: nil,
|
ExtraData: nil,
|
||||||
},
|
}, nil)
|
||||||
nil)
|
|
||||||
if err != 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 {
|
if err != nil {
|
||||||
t.Fatalf("Error getting acceptance data: %+v", err)
|
t.Fatalf("Error getting acceptance data: %+v", err)
|
||||||
}
|
}
|
||||||
|
blueChildOfRedBlock, err := testConsensus.GetBlock(hashBlueChildOfRedBlock)
|
||||||
finalTipSelectedParent, err := testConsensus.GetBlock(finalTipSelectedParentHash)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error getting finalTipSelectedParent: %+v", err)
|
t.Fatalf("Error getting blueChildOfRedBlock: %+v", err)
|
||||||
}
|
}
|
||||||
|
blockE, err := testConsensus.GetBlock(blockHashE)
|
||||||
blue, err := testConsensus.GetBlock(blueHash)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error getting blue: %+v", err)
|
t.Fatalf("Error getting blockE: %+v", err)
|
||||||
}
|
}
|
||||||
|
redBlock, err := testConsensus.GetBlock(redHash)
|
||||||
red, err := testConsensus.GetBlock(redHash)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error getting red: %+v", err)
|
t.Fatalf("Error getting redBlock: %+v", err)
|
||||||
}
|
}
|
||||||
|
blockF, err := testConsensus.GetBlock(blockHashF)
|
||||||
// We expect spendingTransaction1 to be accepted by the blue block and not by the red one, because
|
if err != nil {
|
||||||
// blue blocks in the merge set should always be ordered before red blocks in the merge set.
|
t.Fatalf("Error getting blockF: %+v", err)
|
||||||
// We also expect spendingTransaction2 to be accepted by the red because nothing conflicts it.
|
}
|
||||||
|
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{
|
expectedAcceptanceData := externalapi.AcceptanceData{
|
||||||
{
|
{
|
||||||
BlockHash: finalTipSelectedParentHash,
|
BlockHash: blockHashE,
|
||||||
TransactionAcceptanceData: []*externalapi.TransactionAcceptanceData{
|
TransactionAcceptanceData: []*externalapi.TransactionAcceptanceData{
|
||||||
{
|
{
|
||||||
Transaction: finalTipSelectedParent.Transactions[0],
|
Transaction: blockE.Transactions[0],
|
||||||
Fee: 0,
|
Fee: 0,
|
||||||
IsAccepted: true,
|
IsAccepted: true,
|
||||||
TransactionInputUTXOEntries: []externalapi.UTXOEntry{},
|
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,
|
BlockHash: redHash,
|
||||||
TransactionAcceptanceData: []*externalapi.TransactionAcceptanceData{
|
TransactionAcceptanceData: []*externalapi.TransactionAcceptanceData{
|
||||||
{
|
{ //Coinbase transaction outputs are added to the UTXO-set only if they are in the selected parent chain,
|
||||||
Transaction: red.Transactions[0],
|
// and this block isn't.
|
||||||
|
Transaction: redBlock.Transactions[0],
|
||||||
Fee: 0,
|
Fee: 0,
|
||||||
IsAccepted: false,
|
IsAccepted: false,
|
||||||
TransactionInputUTXOEntries: []externalapi.UTXOEntry{},
|
TransactionInputUTXOEntries: []externalapi.UTXOEntry{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Transaction: spendingTransaction1,
|
Transaction: redBlock.Transactions[1],
|
||||||
Fee: 0,
|
Fee: fees,
|
||||||
IsAccepted: false,
|
|
||||||
TransactionInputUTXOEntries: []externalapi.UTXOEntry{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Transaction: spendingTransaction2,
|
|
||||||
Fee: 1,
|
|
||||||
IsAccepted: true,
|
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) {
|
if !acceptanceData.Equal(expectedAcceptanceData) {
|
||||||
t.Fatalf("The acceptance data is not the expected acceptance data")
|
t.Fatalf("The acceptance data is not the expected acceptance data")
|
||||||
}
|
}
|
||||||
|
// We expect the coinbase transaction to pay reward for the selected parent(block E), the
|
||||||
finalTip, err := testConsensus.GetBlock(finalTipHash)
|
// blueChildOfRedBlock, and bestow the red block reward to the merging block.
|
||||||
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.
|
|
||||||
expectedCoinbase := &externalapi.DomainTransaction{
|
expectedCoinbase := &externalapi.DomainTransaction{
|
||||||
Version: constants.MaxTransactionVersion,
|
Version: constants.MaxTransactionVersion,
|
||||||
Inputs: nil,
|
Inputs: nil,
|
||||||
Outputs: []*externalapi.DomainTransactionOutput{
|
Outputs: []*externalapi.DomainTransactionOutput{
|
||||||
{
|
{
|
||||||
Value: 50 * constants.SompiPerKaspa,
|
Value: 50 * constants.SompiPerKaspa,
|
||||||
ScriptPublicKey: finalTipSelectedParentScriptPublicKey,
|
ScriptPublicKey: blockEScriptPublicKey,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Value: 50*constants.SompiPerKaspa + 1, // testutils.CreateTransaction pays a fee of 1 sompi
|
Value: 50*constants.SompiPerKaspa + fees, // testutils.CreateTransaction pays fees
|
||||||
ScriptPublicKey: blueScriptPublicKey,
|
ScriptPublicKey: blueChildOfRedBlockScriptPublicKey,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Value: 50*constants.SompiPerKaspa + 1,
|
Value: 50*constants.SompiPerKaspa + fees, // testutils.CreateTransaction pays fees
|
||||||
ScriptPublicKey: finalTipScriptPublicKey,
|
ScriptPublicKey: blockFScriptPublicKey,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
LockTime: 0,
|
LockTime: 0,
|
||||||
SubnetworkID: subnetworks.SubnetworkIDCoinbase,
|
SubnetworkID: subnetworks.SubnetworkIDCoinbase,
|
||||||
Gas: 0,
|
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")
|
t.Fatalf("Unexpected coinbase transaction")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -167,9 +167,13 @@ func (dm *difficultyManager) calculateDaaScoreAndAddedBlocks(stagingArea *model.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
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.MergeSetReds() {
|
||||||
for _, hash := range ghostdagData.MergeSet() {
|
|
||||||
mergeSet[*hash] = struct{}{}
|
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 {
|
func (gh *ghostdagHelper) Less(blockHashA *externalapi.DomainHash, ghostdagDataA *model.BlockGHOSTDAGData, blockHashB *externalapi.DomainHash, ghostdagDataB *model.BlockGHOSTDAGData) bool {
|
||||||
panic("implement me")
|
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
|
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
|
// 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.
|
// 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.
|
// 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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -97,49 +97,9 @@ func (sm *syncManager) antiPastHashesBetween(stagingArea *model.StagingArea, low
|
|||||||
return blockHashes, highHash, nil
|
return blockHashes, highHash, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *syncManager) getSortedMergeSet(stagingArea *model.StagingArea, current *externalapi.DomainHash) (
|
func (sm *syncManager) findHighHashAccordingToMaxBlueScoreDifference(stagingArea *model.StagingArea, lowHash *externalapi.DomainHash,
|
||||||
[]*externalapi.DomainHash, error) {
|
highHash *externalapi.DomainHash, maxBlueScoreDifference uint64, highBlockGHOSTDAGData *model.BlockGHOSTDAGData,
|
||||||
|
lowBlockGHOSTDAGData *model.BlockGHOSTDAGData) (*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) {
|
|
||||||
|
|
||||||
if highBlockGHOSTDAGData.BlueScore()-lowBlockGHOSTDAGData.BlueScore() <= maxBlueScoreDifference {
|
if highBlockGHOSTDAGData.BlueScore()-lowBlockGHOSTDAGData.BlueScore() <= maxBlueScoreDifference {
|
||||||
return highHash, nil
|
return highHash, nil
|
||||||
|
Loading…
x
Reference in New Issue
Block a user