mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-25 16:26:48 +00:00
170 lines
6.7 KiB
Go
170 lines
6.7 KiB
Go
package consensus
|
|
|
|
import (
|
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
|
|
|
"testing"
|
|
)
|
|
|
|
func TestFinality(t *testing.T) {
|
|
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
|
// Set finalityInterval to 50 blocks, so that test runs quickly
|
|
params.FinalityDuration = 50 * params.TargetTimePerBlock
|
|
|
|
factory := NewFactory()
|
|
consensus, teardown, err := factory.NewTestConsensus(params, "TestFinality")
|
|
if err != nil {
|
|
t.Fatalf("Error setting up consensus: %+v", err)
|
|
}
|
|
defer teardown()
|
|
|
|
buildAndInsertBlock := func(parentHashes []*externalapi.DomainHash) (*externalapi.DomainBlock, error) {
|
|
block, err := consensus.BuildBlockWithParents(parentHashes, nil, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = consensus.ValidateAndInsertBlock(block)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return block, nil
|
|
}
|
|
|
|
// Build a chain of `finalityInterval - 1` blocks
|
|
finalityInterval := params.FinalityDepth()
|
|
var mainChainTip *externalapi.DomainBlock
|
|
mainChainTipHash := params.GenesisHash
|
|
|
|
for i := uint64(0); i < finalityInterval-1; i++ {
|
|
mainChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{mainChainTipHash})
|
|
if err != nil {
|
|
t.Fatalf("TestFinality: Failed to process Block #%d: %v", i, err)
|
|
}
|
|
mainChainTipHash = consensusserialization.BlockHash(mainChainTip)
|
|
|
|
blockInfo, err := consensus.GetBlockInfo(mainChainTipHash)
|
|
if err != nil {
|
|
t.Fatalf("TestFinality: Block #%d failed to get info: %v", i, err)
|
|
}
|
|
if blockInfo.BlockStatus != externalapi.StatusValid {
|
|
t.Fatalf("Block #%d in main chain expected to have status '%s', but got '%s'",
|
|
i, externalapi.StatusValid, blockInfo.BlockStatus)
|
|
}
|
|
}
|
|
|
|
// Mine another chain of `finality-Interval - 2` blocks
|
|
var sideChainTip *externalapi.DomainBlock
|
|
sideChainTipHash := params.GenesisHash
|
|
for i := uint64(0); i < finalityInterval-2; i++ {
|
|
sideChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{sideChainTipHash})
|
|
if err != nil {
|
|
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %v", i, err)
|
|
}
|
|
sideChainTipHash = consensusserialization.BlockHash(sideChainTip)
|
|
|
|
blockInfo, err := consensus.GetBlockInfo(sideChainTipHash)
|
|
if err != nil {
|
|
t.Fatalf("TestFinality: Block #%d failed to get info: %v", i, err)
|
|
} else if !blockInfo.Exists {
|
|
t.Fatalf("TestFinality: Failed getting block info, doesn't exists")
|
|
}
|
|
if blockInfo.BlockStatus != externalapi.StatusUTXOPendingVerification {
|
|
t.Fatalf("Block #%d in side chain expected to have status '%s', but got '%s'",
|
|
i, externalapi.StatusUTXOPendingVerification, blockInfo.BlockStatus)
|
|
}
|
|
}
|
|
|
|
// Add two more blocks in the side-chain until it becomes the selected chain
|
|
for i := uint64(0); i < 2; i++ {
|
|
sideChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{sideChainTipHash})
|
|
if err != nil {
|
|
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %v", i, err)
|
|
}
|
|
sideChainTipHash = consensusserialization.BlockHash(sideChainTip)
|
|
}
|
|
|
|
// Make sure that now the sideChainTip is valid and selectedTip
|
|
blockInfo, err := consensus.GetBlockInfo(sideChainTipHash)
|
|
if err != nil {
|
|
t.Fatalf("TestFinality: Failed to get block info: %v", err)
|
|
} else if !blockInfo.Exists {
|
|
t.Fatalf("TestFinality: Failed getting block info, doesn't exists")
|
|
}
|
|
if blockInfo.BlockStatus != externalapi.StatusValid {
|
|
t.Fatalf("TestFinality: Overtaking block in side-chain expected to have status '%s', but got '%s'",
|
|
externalapi.StatusValid, blockInfo.BlockStatus)
|
|
}
|
|
selectedTip, err := consensus.GetVirtualSelectedParent()
|
|
if err != nil {
|
|
t.Fatalf("TestFinality: Failed getting virtual selectedParent: %v", err)
|
|
}
|
|
if *consensusserialization.BlockHash(selectedTip) != *sideChainTipHash {
|
|
t.Fatalf("Overtaking block in side-chain is not selectedTip")
|
|
}
|
|
|
|
// Add two more blocks to main chain, to move finality point to first non-genesis block in mainChain
|
|
for i := uint64(0); i < 2; i++ {
|
|
mainChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{mainChainTipHash})
|
|
if err != nil {
|
|
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %v", i, err)
|
|
}
|
|
mainChainTipHash = consensusserialization.BlockHash(mainChainTip)
|
|
}
|
|
|
|
virtualFinality, err := consensus.ConsensusStateManager().VirtualFinalityPoint()
|
|
if err != nil {
|
|
t.Fatalf("TestFinality: Failed getting the virtual's finality point: %v", err)
|
|
}
|
|
|
|
if *virtualFinality == *params.GenesisHash {
|
|
t.Fatalf("virtual's finalityPoint is still genesis after adding finalityInterval + 1 blocks to the main chain")
|
|
}
|
|
|
|
// TODO: Make sure that a finality conflict notification is sent
|
|
// Add two more blocks to the side chain, so that it violates finality and gets status UTXOPendingVerification even
|
|
// though it is the block with the highest blue score.
|
|
for i := uint64(0); i < 2; i++ {
|
|
sideChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{sideChainTipHash})
|
|
if err != nil {
|
|
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %v", i, err)
|
|
}
|
|
sideChainTipHash = consensusserialization.BlockHash(sideChainTip)
|
|
}
|
|
|
|
// Check that sideChainTip hash higher blue score than the selected parent
|
|
selectedTip, err = consensus.GetVirtualSelectedParent()
|
|
if err != nil {
|
|
t.Fatalf("TestFinality: Failed getting virtual selectedParent: %v", err)
|
|
}
|
|
selectedTipGhostDagData, err := consensus.GHOSTDAGDataStore().Get(consensus.DatabaseContext(), consensusserialization.BlockHash(selectedTip))
|
|
if err != nil {
|
|
t.Fatalf("TestFinality: Failed getting the ghost dag data of the selected tip: %v", err)
|
|
}
|
|
|
|
sideChainTipGhostDagData, err := consensus.GHOSTDAGDataStore().Get(consensus.DatabaseContext(), sideChainTipHash)
|
|
if err != nil {
|
|
t.Fatalf("TestFinality: Failed getting the ghost dag data of the sidechain tip: %v", err)
|
|
}
|
|
|
|
if selectedTipGhostDagData.BlueScore > sideChainTipGhostDagData.BlueScore {
|
|
t.Fatalf("sideChainTip is not the bluest tip when it is expected to be")
|
|
}
|
|
|
|
// Blocks violating finality should have a UTXOPendingVerification status
|
|
blockInfo, err = consensus.GetBlockInfo(sideChainTipHash)
|
|
if err != nil {
|
|
t.Fatalf("TestFinality: Failed to get block info: %v", err)
|
|
} else if !blockInfo.Exists {
|
|
t.Fatalf("TestFinality: Failed getting block info, doesn't exists")
|
|
}
|
|
if blockInfo.BlockStatus != externalapi.StatusUTXOPendingVerification {
|
|
t.Fatalf("TestFinality: Finality violating block expected to have status '%s', but got '%s'",
|
|
externalapi.StatusUTXOPendingVerification, blockInfo.BlockStatus)
|
|
}
|
|
})
|
|
}
|