diff --git a/domain/consensus/model/testapi/test_consensus.go b/domain/consensus/model/testapi/test_consensus.go index 8b3f9d1fd..c36a69a36 100644 --- a/domain/consensus/model/testapi/test_consensus.go +++ b/domain/consensus/model/testapi/test_consensus.go @@ -23,6 +23,9 @@ type TestConsensus interface { AddBlock(parentHashes []*externalapi.DomainHash, coinbaseData *externalapi.DomainCoinbaseData, transactions []*externalapi.DomainTransaction) (*externalapi.DomainHash, *externalapi.BlockInsertionResult, error) + AddHeader(parentHashes []*externalapi.DomainHash, coinbaseData *externalapi.DomainCoinbaseData, + transactions []*externalapi.DomainTransaction) (*externalapi.DomainHash, *externalapi.BlockInsertionResult, error) + DiscardAllStores() AcceptanceDataStore() model.AcceptanceDataStore diff --git a/domain/consensus/processes/blockprocessor/validateandinsertblock_test.go b/domain/consensus/processes/blockprocessor/validateandinsertblock_test.go new file mode 100644 index 000000000..8f59baffe --- /dev/null +++ b/domain/consensus/processes/blockprocessor/validateandinsertblock_test.go @@ -0,0 +1,106 @@ +package blockprocessor_test + +import ( + "github.com/kaspanet/kaspad/domain/consensus" + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "github.com/kaspanet/kaspad/domain/consensus/ruleerrors" + "github.com/kaspanet/kaspad/domain/consensus/utils/blockheader" + "github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing" + "github.com/kaspanet/kaspad/domain/consensus/utils/testutils" + "github.com/kaspanet/kaspad/domain/dagconfig" + "github.com/pkg/errors" + "testing" +) + +func TestBlockStatus(t *testing.T) { + testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) { + factory := consensus.NewFactory() + tc, teardown, err := factory.NewTestConsensus(params, "TestBlockStatus") + if err != nil { + t.Fatalf("Error setting up consensus: %+v", err) + } + defer teardown(false) + + checkStatus := func(hash *externalapi.DomainHash, expectedStatus externalapi.BlockStatus) { + blockStatus, err := tc.BlockStatusStore().Get(tc.DatabaseContext(), hash) + if err != nil { + t.Fatalf("BlockStatusStore().Get: %+v", err) + } + + if blockStatus != expectedStatus { + t.Fatalf("Expected to have status %s but got %s", expectedStatus, blockStatus) + } + } + + tipHash := params.GenesisHash + for i := 0; i < 2; i++ { + tipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } + + checkStatus(tipHash, externalapi.StatusUTXOValid) + } + + headerHash, _, err := tc.AddHeader([]*externalapi.DomainHash{tipHash}, nil, nil) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } + + checkStatus(headerHash, externalapi.StatusHeaderOnly) + + nonChainBlockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{params.GenesisHash}, nil, nil) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } + + checkStatus(nonChainBlockHash, externalapi.StatusUTXOPendingVerification) + + disqualifiedBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{tipHash}, nil, nil) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } + disqualifiedBlock.Header = blockheader.NewImmutableBlockHeader( + disqualifiedBlock.Header.Version(), + disqualifiedBlock.Header.ParentHashes(), + disqualifiedBlock.Header.HashMerkleRoot(), + externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{}), // This should disqualify the block + disqualifiedBlock.Header.UTXOCommitment(), + disqualifiedBlock.Header.TimeInMilliseconds(), + disqualifiedBlock.Header.Bits(), + disqualifiedBlock.Header.Nonce(), + ) + + _, err = tc.ValidateAndInsertBlock(disqualifiedBlock) + if err != nil { + t.Fatalf("ValidateAndInsertBlock: %+v", err) + } + + checkStatus(consensushashing.BlockHash(disqualifiedBlock), externalapi.StatusDisqualifiedFromChain) + + invalidBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{tipHash}, nil, nil) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } + invalidBlock.Header = blockheader.NewImmutableBlockHeader( + disqualifiedBlock.Header.Version(), + disqualifiedBlock.Header.ParentHashes(), + externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{}), // This should invalidate the block + disqualifiedBlock.Header.AcceptedIDMerkleRoot(), + disqualifiedBlock.Header.UTXOCommitment(), + disqualifiedBlock.Header.TimeInMilliseconds(), + disqualifiedBlock.Header.Bits(), + disqualifiedBlock.Header.Nonce(), + ) + + _, err = tc.ValidateAndInsertBlock(invalidBlock) + if err == nil { + t.Fatalf("block is expected to be invalid") + } + if !errors.As(err, &ruleerrors.RuleError{}) { + t.Fatalf("ValidateAndInsertBlock: %+v", err) + } + + checkStatus(consensushashing.BlockHash(invalidBlock), externalapi.StatusInvalid) + }) +} diff --git a/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go b/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go index fc1559fe2..53f47191f 100644 --- a/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go +++ b/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go @@ -21,7 +21,7 @@ func TestChainedTransactions(t *testing.T) { factory := consensus.NewFactory() - tc, teardown, err := factory.NewTestConsensus(params, "TestUTXOCommitment") + tc, teardown, err := factory.NewTestConsensus(params, "TestChainedTransactions") if err != nil { t.Fatalf("Error setting up consensus: %+v", err) } diff --git a/domain/consensus/test_consensus.go b/domain/consensus/test_consensus.go index c5b4856ec..31481a7d7 100644 --- a/domain/consensus/test_consensus.go +++ b/domain/consensus/test_consensus.go @@ -60,6 +60,28 @@ func (tc *testConsensus) AddBlock(parentHashes []*externalapi.DomainHash, coinba return consensushashing.BlockHash(block), blockInsertionResult, nil } +func (tc *testConsensus) AddHeader(parentHashes []*externalapi.DomainHash, coinbaseData *externalapi.DomainCoinbaseData, + transactions []*externalapi.DomainTransaction) (*externalapi.DomainHash, *externalapi.BlockInsertionResult, error) { + + // Require write lock because BuildBlockWithParents stages temporary data + tc.lock.Lock() + defer tc.lock.Unlock() + + block, _, err := tc.testBlockBuilder.BuildBlockWithParents(parentHashes, coinbaseData, transactions) + if err != nil { + return nil, nil, err + } + + block.Transactions = nil + + blockInsertionResult, err := tc.blockProcessor.ValidateAndInsertBlock(block) + if err != nil { + return nil, nil, err + } + + return consensushashing.BlockHash(block), blockInsertionResult, nil +} + func (tc *testConsensus) DiscardAllStores() { tc.AcceptanceDataStore().Discard() tc.BlockHeaderStore().Discard()