From acbc4c787d7c3a86ee8956b5c9670df1a570ddf1 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:33:51 +0200 Subject: [PATCH] test: Audit some integration tests, make minor changes, and add plaintext test list (#2089) * Add script to summarize integration tests * Add error messages for missing docstrings * Use better separator * Add docstring to test * Add some docstrings and adjust formatting * Add some more docstrings * Add makefile and git workflow * Add else case * Use temporary files instead of substitution * Add missing fi * Adding a docstring without regenerating the test docs * Give testing-docs workflow write permission * Specify branch to push to * Just fail the test instead of pushing automatically * Regenerate testing docs * Rename workflow * Unify type imports * Add newline around fenced codeblock * Fix repeated 'or' * Regenerate testing doc * Expand docstring for TestAllocateTokens * Adjust testing docs workflow * Improved some test descriptions * Improved misbehavior.go test descriptions * Improved normal_operations.go test descriptions * Improved provider_gov_hooks.go test descriptions * Improved query_providerinfo_test.go test descriptions * Removed warnings from setup.go * Improved slashing.go test descriptions * Improved stop_consumer.go test descriptions * Improved throttle_retry.go test descriptions * Improved throttle.go test descriptions * Fixed spelling mistake * Improved valset_update.go test descriptions * Improved test documentation * Improved testing-docs.yml * Testing git workflow for testing-docs * Testing git workflow for testing-docs * Testing git workflow for testing-docs * Testing git workflow for testing-docs * Testing git workflow for testing-docs * Testing git workflow for testing-docs * Update testing documentation * Testing git workflow for testing-docs * Testing git workflow for testing-docs * Update testing documentation * Testing git workflow for testing-docs * Update testing documentation * Improved some test descriptions * Update testing documentation * Fix minor typos * Finished testing-docs.yml * Fix minor typos * Rename test from testing-docs.yml * Cleaned up TODOs and updated test descriptions * Added check in misbehaviour.go * Fixed integration tests * Removed additional comments from key_assignment.go * Removed outdated file descriptions * Update testing documentation * Update .github/workflows/testing-docs.yml Co-authored-by: Marius Poke * Update tests/integration/README.md * Removed TestRelayAndApplyDoubleSignPacket test from slashing.go * Update testing documentation * Update .github/workflows/testing-docs.yml Co-authored-by: Marius Poke * Update scripts/test_doc/test_documentation.md Co-authored-by: Marius Poke * Update testing documentation * Integration test comments refactored * Update testing documentation * Update .github/workflows/testing-docs.yml Co-authored-by: Marius Poke * Changed inconsistent make command name * Testing testing-docs script * Update testing documentation * Testing testing-docs script * Update testing documentation * Changed inconsistent make command in TESTING.md * Updated testing-docs.yml * Updated testing-docs.yml * Testing testing-docs.yml * Update testing documentation * Testing testing-docs.yml * Update testing documentation * Removed failiure signal from testing-docs.yml * Removed TestValidatorDoubleSigning from slashing.go * Added TODOs in slashing.go * Added panics and a TODO for unexpected errors in key_assignment.go. * Implement security check for file path in extract_docstrings.go * Removed security check for file path in extract_docstrings.go --------- Co-authored-by: kirdatatjana Co-authored-by: github-actions Co-authored-by: kirdatatjana <116630536+kirdatatjana@users.noreply.github.com> Co-authored-by: Marius Poke --- .github/workflows/testing-docs.yml | 70 ++++++ Makefile | 3 + TESTING.md | 12 + scripts/test_doc/extract_docstrings.go | 139 +++++++++++ scripts/test_doc/test_documentation.md | 142 +++++++++++ tests/integration/README.md | 10 +- tests/integration/changeover.go | 9 + tests/integration/democracy.go | 25 +- tests/integration/distribution.go | 81 +++++- tests/integration/double_vote.go | 19 +- tests/integration/expired_client.go | 17 +- tests/integration/key_assignment.go | 39 ++- tests/integration/misbehaviour.go | 36 ++- tests/integration/normal_operations.go | 8 +- .../integration/partial_set_security_test.go | 10 +- tests/integration/query_providerinfo_test.go | 5 + tests/integration/setup.go | 2 +- tests/integration/slashing.go | 236 +++++------------- tests/integration/stop_consumer.go | 17 +- tests/integration/throttle.go | 67 ++++- tests/integration/throttle_retry.go | 13 + tests/integration/unbonding.go | 7 + tests/integration/valset_update.go | 23 +- testutil/keeper/unit_test_helpers.go | 2 +- 24 files changed, 766 insertions(+), 226 deletions(-) create mode 100644 .github/workflows/testing-docs.yml create mode 100644 scripts/test_doc/extract_docstrings.go create mode 100644 scripts/test_doc/test_documentation.md diff --git a/.github/workflows/testing-docs.yml b/.github/workflows/testing-docs.yml new file mode 100644 index 0000000000..886c66b6ad --- /dev/null +++ b/.github/workflows/testing-docs.yml @@ -0,0 +1,70 @@ +name: Generate testing docs + +on: + workflow_call: + pull_request: + merge_group: + push: + branches: + - main + - release/v* + - feat/* + +permissions: + contents: write + pull-requests: write # 'write' access to pull requests in order to update test documentation automatically + +jobs: + testing-docs: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: "1.22" + check-latest: true + cache: true + cache-dependency-path: go.sum + - uses: technote-space/get-diff-action@v6.1.2 + id: git_diff + with: + PATTERNS: | + tests/integration/**/*.go + **/Makefile + Makefile + + - name: Generate testing docs + if: env.GIT_DIFF + run: make build-testing-docs + + - name: Check for changes and update automatically + if: env.GIT_DIFF + id: check_changes_and_update + run: | + git show HEAD:scripts/test_doc/test_documentation.md > committed_file.md + cp scripts/test_doc/test_documentation.md generated_file.md + if ! diff -q generated_file.md committed_file.md; then + echo "Documentation for integration tests is out of date. Updating and pushing changes..." + cp generated_file.md scripts/test_doc/test_documentation.md + + if [ -n "$GITHUB_HEAD_REF" ]; then + branch=$GITHUB_HEAD_REF + else + branch=${GITHUB_REF#refs/heads/} + fi + + git fetch origin $branch + git checkout $branch + + cp generated_file.md scripts/test_doc/test_documentation.md + git config user.name "github-actions" + git config user.email "github-actions@github.com" + git add scripts/test_doc/test_documentation.md + git commit -m "Update testing documentation" + + git push origin "$branch" + + fi \ No newline at end of file diff --git a/Makefile b/Makefile index 26fb8116d8..c648654077 100644 --- a/Makefile +++ b/Makefile @@ -265,6 +265,9 @@ build-docs-deploy: build-docs-local: @cd docs && ./build_local.sh +build-testing-docs: + @cd scripts/test_doc && go run extract_docstrings.go + ############################################################################### ### Test Traces ### ############################################################################### diff --git a/TESTING.md b/TESTING.md index 4571e38fcc..4a50f6d3de 100644 --- a/TESTING.md +++ b/TESTING.md @@ -14,6 +14,18 @@ Unit tests are useful for simple standalone functionality, and CRUD operations. To run integration tests against your own consumer/provider implementations, use [instance_test.go](tests/integration/instance_test.go) as an example. All you'll need to do is make sure your applications implement the necessary interfaces defined in [interfaces.go](testutil/integration/interfaces.go), pattern match [specific_setup.go](testutil/ibc_testing/specific_setup.go), then pass in the appropriate types and parameters to the suite, as is done in `instance_test.go` for the dummy provider/consumer implementations. +A list of test scenarios covered by integration tests can be found in [scripts/test_doc/test_documentation.md](scripts/test_doc/test_documentation.md). +When adding an integration test, write a brief description as a docstring in the Golang code in this schema: + +```go +// This is a test that tests foo and bar. +// @Long Description@ +// Here is a detailed description +// that goes into more detail and describes the scenario. +``` + +Then, run `make build-testing-docs` to update the test documentation. + ## Model-Based Tests (MBT) [MBT](tests/mbt/) tests are similar to integration tests, but they compare the system state to an expected state generated from a formally verified specification written in Quint. diff --git a/scripts/test_doc/extract_docstrings.go b/scripts/test_doc/extract_docstrings.go new file mode 100644 index 0000000000..88c4498a79 --- /dev/null +++ b/scripts/test_doc/extract_docstrings.go @@ -0,0 +1,139 @@ +package main + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "log" + "os" + "path/filepath" + "strings" +) + +func main() { + // Define the directory to traverse and the output markdown file + dir := "../../tests/integration" + outputFile := "test_documentation.md" + + // Open the output file + out, err := os.Create(outputFile) + if err != nil { + log.Fatalf("Error creating output file: %v\n", err) + } + defer out.Close() + + // Write the header for the markdown file + fmt.Fprintf(out, "# Test Documentation\n\n") + + errorStatusCode := false + + // Walk through the directory + err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // Only process .go files + if !info.IsDir() && strings.HasSuffix(info.Name(), ".go") && !strings.HasSuffix(info.Name(), "_test.go") { + functionsMissingDocStrings := extractDocstrings(path, out) + if len(functionsMissingDocStrings) > 0 { + fmt.Printf("The following test functions in %s are missing docstrings:\n", path) + for _, fn := range functionsMissingDocStrings { + fmt.Printf("\t%s\n", fn) + } + errorStatusCode = true + } + } + return nil + }) + if err != nil { + log.Fatalf("Error walking the path %q: %v\n", dir, err) + } + + fmt.Printf("Documentation generated successfully in %s\n", outputFile) + + if errorStatusCode { + os.Exit(1) + } +} + +// extractDocstrings extracts the docstrings from the Go source file and writes them to the output file +// in a markdown table format. +// It returns a list of test functions that are missing docstrings. +func extractDocstrings(filePath string, out *os.File) []string { + // Read the Go source file + src, err := os.ReadFile(filePath) // #nosec G304 + if err != nil { + log.Fatalf("Error reading file %s: %v\n", filePath, err) + } + + // Create the AST for the source file + fset := token.NewFileSet() + node, err := parser.ParseFile(fset, filePath, src, parser.ParseComments) + if err != nil { + log.Fatalf("Error parsing file %s: %v\n", filePath, err) + } + + functionsMissingDocstrings := []string{} + + // Files that do not contain test functions are excluded from the documentation. + fileNameWritten := false + + // Traverse the AST + for _, f := range node.Decls { + if fn, isFn := f.(*ast.FuncDecl); isFn && strings.HasPrefix(fn.Name.Name, "Test") { + // Check if the function has a docstring + if fn.Doc != nil { + + if !fileNameWritten { + relativePath := strings.TrimPrefix(filePath, "../../tests/integration/") + doclink := fmt.Sprintf("[%s](%s)", relativePath, filePath) + fmt.Fprintf(out, "# %s \n", doclink) + fmt.Fprintf(out, "
Test Specifications \n\n") + + // Write table header + fmt.Fprintf(out, "| Function | Short Description |\n") + fmt.Fprintf(out, "|----------|-------------------|\n") + fileNameWritten = true + } + + doc := fn.Doc.Text() + link := fmt.Sprintf("[%s](%s#L%d)", fn.Name.Name, filePath, fset.Position(fn.Pos()).Line) + + // Split the docstring based on the separator "========" + parts := strings.Split(doc, "\n@Long Description@\n") + var shortDescription, longDescription string + if len(parts) > 1 { + shortDescription = strings.TrimSpace(parts[0]) + longDescription = strings.TrimSpace(parts[1]) + } else { + shortDescription = strings.TrimSpace(doc) + longDescription = "" + } + + // Format the description + + // avoid breaking the table format: newlines need to be replaced + // for the short description, use spaces + shortDescription = strings.ReplaceAll(shortDescription, "\n", " ") + // for the long description, use breaks + longDescription = strings.ReplaceAll(longDescription, "\n", "
") + + description := shortDescription + if longDescription != "" { + description += fmt.Sprintf("
Details%s
", longDescription) + } + + fmt.Fprintf(out, " %s | %s |\n", link, description) + } else { + functionsMissingDocstrings = append(functionsMissingDocstrings, fn.Name.Name) + + } + } + } + if fileNameWritten { + fmt.Fprintf(out, "
\n\n") + } + return functionsMissingDocstrings +} diff --git a/scripts/test_doc/test_documentation.md b/scripts/test_doc/test_documentation.md new file mode 100644 index 0000000000..9115809718 --- /dev/null +++ b/scripts/test_doc/test_documentation.md @@ -0,0 +1,142 @@ +# Test Documentation + +# [changeover.go](../../tests/integration/changeover.go) +
Test Specifications + +| Function | Short Description | +|----------|-------------------| + [TestRecycleTransferChannel](../../tests/integration/changeover.go#L17) | TestRecycleTransferChannel tests that an existing transfer channel can be reused when transitioning from a standalone to a consumer chain.
DetailsThe test case:
* sets up a provider chain and a standalone chain
* creates a connection between the two chains
* creates a transfer channel between the two chains
* transitions the standalone chain to a consumer chain
* confirms that no extra transfer channel is created, thus only one transfer channel and one CCV channel exist.
| +
+ +# [democracy.go](../../tests/integration/democracy.go) +
Test Specifications + +| Function | Short Description | +|----------|-------------------| + [TestDemocracyRewardsDistribution](../../tests/integration/democracy.go#L78) | TestDemocracyRewardsDistribution checks that rewards to democracy representatives, community pool, and provider redistribution account are done correctly.
Details* Set up a democracy consumer chain.
* Create a new block.
* Check that rewards to democracy representatives, community pool, and provider redistribution account are distributed in the right proportions.
| + [TestDemocracyGovernanceWhitelisting](../../tests/integration/democracy.go#L194) | TestDemocracyGovernanceWhitelisting checks that only whitelisted governance proposals can be executed on democracy consumer chains.
DetailsFor context, see the whitelist for proposals in app/consumer-democracy/proposals_whitelisting.go.
* Set up a democracy consumer chain.
* Submit a proposal containing changes to the auth and mint module parameters.
* Check that the proposal is not executed, since the change to the auth module is not whitelisted.
* Submit a proposal containing changes *only* to the mint module parameters.
* Check that the proposal is executed, since the change to the mint module is whitelisted.
* Submit a proposal containing changes *only* to the auth module parameters.
* Check that again, the proposal is not executed, since the change to the auth module is not whitelisted.
| + [TestDemocracyMsgUpdateParams](../../tests/integration/democracy.go#L294) | TestDemocracyMsgUpdateParams checks that the consumer parameters can be updated through a governance proposal.
Details* Set up a democracy consumer chain.
* Submit a proposal containing changes to the consumer module parameters.
* Check that the proposal is executed, and the parameters are updated.
| +
+ +# [distribution.go](../../tests/integration/distribution.go) +
Test Specifications + +| Function | Short Description | +|----------|-------------------| + [TestRewardsDistribution](../../tests/integration/distribution.go#L32) | TestRewardsDistribution tests the distribution of rewards from the consumer chain to the provider chain.
Details* Set up a provider and consumer chain and completes the channel initialization.
* Send tokens into the FeeCollector on the consumer chain,
and check that these tokens distributed correctly across the provider and consumer chain.
* Check that the tokens are distributed purely on the consumer chain,
then advance the block height to make the consumer chain send a packet with rewards to the provider chain.
* Don't whitelist the consumer denom, so that the tokens stay in the ConsumerRewardsPool on the provider chain.
| + [TestSendRewardsRetries](../../tests/integration/distribution.go#L190) | TestSendRewardsRetries tests that failed reward transmissions are retried every BlocksPerDistributionTransmission blocks
Details* Set up a provider and consumer chain and complete the channel initialization.
* Fill the fee pool on the consumer chain, then corrupt the transmission channel
and try to send rewards to the provider chain, which should fail.
* Advance the block height to trigger a retry of the reward transmission, and confirm that this time, the transmission is successful.
| + [TestEndBlockRD](../../tests/integration/distribution.go#L272) | TestEndBlockRD tests that the last transmission block height is correctly updated after the expected number of block have passed.
Details* Set up CCV and transmission channels between the provider and consumer chains.
* Fill the fee pool on the consumer chain, prepare the system for reward
distribution, and optionally corrupt the transmission channel to simulate failure scenarios.
* After advancing the block height, verify whether the LBTH is updated correctly
and if the escrow balance changes as expected.
* Check that the IBC transfer states are discarded if the reward distribution
to the provider has failed.

Note: this method is effectively a unit test for EndBLockRD(), but is written as an integration test to avoid excessive mocking.
| + [TestSendRewardsToProvider](../../tests/integration/distribution.go#L395) | TestSendRewardsToProvider is effectively a unit test for SendRewardsToProvider(), but is written as an integration test to avoid excessive mocking.
Details* Set up CCV and transmission channels between the provider and consumer chains.
* Verify the SendRewardsToProvider() function under various scenarios and checks if the
function handles each scenario correctly by ensuring the expected number of token transfers.
| + [TestIBCTransferMiddleware](../../tests/integration/distribution.go#L542) | TestIBCTransferMiddleware tests the logic of the IBC transfer OnRecvPacket callback.
Details* Set up IBC and transfer channels.
* Simulate various scenarios of token transfers from the provider chain to
the consumer chain, and evaluate how the middleware processes these transfers.
* Ensure that token transfers are handled correctly and rewards are allocated as expected.
| + [TestAllocateTokens](../../tests/integration/distribution.go#L736) | TestAllocateTokens is a happy-path test of the consumer rewards pool allocation to opted-in validators and the community pool.
Details* Set up a provider chain and multiple consumer chains, and initialize the channels between them.
* Fund the consumer rewards pools on the provider chain and allocate rewards to the consumer chains.
* Begin a new block to cause rewards to be distributed to the validators and the community pool,
and check that the rewards are allocated as expected.
| + [TestAllocateTokensToConsumerValidators](../../tests/integration/distribution.go#L883) | TestAllocateTokensToConsumerValidators tests the allocation of tokens to consumer validators.
Details* The test exclusively uses the provider chain.
* Set up a current set of consumer validators, then call the AllocateTokensToConsumerValidators
function to allocate a number of tokens to the validators.
* Check that the expected number of tokens were allocated to the validators.
* The test covers the following scenarios:
- The tokens to be allocated are empty
- The consumer validator set is empty
- The tokens are allocated to a single validator
- The tokens are allocated to multiple validators
| + [TestAllocateTokensToConsumerValidatorsWithDifferentValidatorHeights](../../tests/integration/distribution.go#L1028) | TestAllocateTokensToConsumerValidatorsWithDifferentValidatorHeights tests AllocateTokensToConsumerValidators test with consumer validators that have different heights.
Details* Set up a context where the consumer validators have different join heights and verify that rewards are
correctly allocated only to validators who have been active long enough.
* Ensure that rewards are evenly distributed among eligible validators, that validators
can withdraw their rewards correctly, and that no rewards are allocated to validators
who do not meet the required join height criteria.
* Confirm that validators that have been consumer validators for some time receive rewards,
while validators that recently became consumer validators do not receive rewards.
| + [TestMultiConsumerRewardsDistribution](../../tests/integration/distribution.go#L1148) | TestMultiConsumerRewardsDistribution tests the rewards distribution of multiple consumers chains.
Details* Set up multiple consumer and transfer channels and verify the distribution of rewards from
various consumer chains to the provider's reward pool.
* Ensure that the consumer reward pools are correctly populated
and that rewards are properly transferred to the provider.
* Checks that the provider's reward pool balance reflects the accumulated
rewards from all consumer chains after processing IBC transfer packets and relaying
committed packets.
| +
+ +# [double_vote.go](../../tests/integration/double_vote.go) +
Test Specifications + +| Function | Short Description | +|----------|-------------------| + [TestHandleConsumerDoubleVoting](../../tests/integration/double_vote.go#L23) | TestHandleConsumerDoubleVoting tests the handling of double voting evidence from the consumer chain.
Details* Set up a CCV channel.
* Create various double voting scenarios and submit those to the provider chain.
* Check if the provider chain correctly processes the evidence, jail and tombstone validators as needed, and apply the
correct slashing penalties.
* Verify that invalid evidence is properly rejected and does not result in incorrect penalties.
| + [TestHandleConsumerDoubleVotingSlashesUndelegationsAndRelegations](../../tests/integration/double_vote.go#L283) | TestHandleConsumerDoubleVotingSlashesUndelegationsAndRelegations tests the handling of double voting evidence from the consumer chain and checks if slashing, undelegations, and redelegations are correctly processed.
Details* Set up a CCV channel.
* Create various double voting scenarios and submit those to the provider chain.
* Verify that the evidence is processed correctly.
* Ensure that the provider chain slashes the validator appropriately, and that it handles undelegations and redelegations accurately.
* Confirm that the validator’s staking status reflects these actions.
* Check if the slashing penalties are applied correctly and update the validator’s balance and delegations as expected.
| +
+ +# [expired_client.go](../../tests/integration/expired_client.go) +
Test Specifications + +| Function | Short Description | +|----------|-------------------| + [TestVSCPacketSendExpiredClient](../../tests/integration/expired_client.go#L29) | TestVSCPacketSendExpiredClient tests queueing of VSCPackets when the consumer client is expired.
Details* Set up a CCV channel and expire the client on consumer chain.
* Bond tokens to provider, send CCV packet to consumer and check pending packets.
* While the consumer client is expired (or inactive for some reason) all packets will be queued.
* The packet sending and checks are then repeated.
* More tokens are bonded on provider to change validator powers.
* Upgrade expired client to the consumer and all packets are cleared once the consumer client is established.
| + [TestConsumerPacketSendExpiredClient](../../tests/integration/expired_client.go#L99) | TestConsumerPacketSendExpiredClient tests the consumer sending packets when the provider client is expired.
Details* Set up a CCV channel and bond tokens on provider.
* Send CCV packet to consumer and rebond tokens on provider.
* Check for pending VSC packets and relay all VSC packets to consumer.
* The provider client is then expired.
* Confirm that while the provider client is expired all packets will be queued and then cleared
once the provider client is upgraded.
| +
+ +# [key_assignment.go](../../tests/integration/key_assignment.go) +
Test Specifications + +| Function | Short Description | +|----------|-------------------| + [TestKeyAssignment](../../tests/integration/key_assignment.go#L35) | TestKeyAssignment tests key assignments relayed from the provider chain to the consumer chain at different times in the protocol lifecycle.
DetailsEach test scenario sets up a provider chain and then assigns a key for a validator.
However, the assignment comes at different times in the protocol lifecycle.
The test covers the following scenarios:
* successfully assign the key before the CCV channel initialization is complete, then check that a VSCPacket is indeed queued
* successfully assign the key after the CCV channel initialization is complete
* successfully assign the key during an same epoch where the validator power changes
* get an error when assigning the same key twice in the same block by different validators
* get an error when assigning the same key twice in the same block by the same validator
* successfully assign two different keys in the same block by one validator
* get an error when assigning the same key twice in different blocks by different validators
* get an error when assigning the same key twice in different blocks by the same validator
For each scenario where the key assignment does not produce an error,
the test also checks that VSCPackets are relayed to the consumer chain and that the clients on
the provider and consumer chain can be updated.
TODO: Remove panics when unexpected error occurs.
| +
+ +# [misbehaviour.go](../../tests/integration/misbehaviour.go) +
Test Specifications + +| Function | Short Description | +|----------|-------------------| + [TestHandleConsumerMisbehaviour](../../tests/integration/misbehaviour.go#L25) | TestHandleConsumerMisbehaviour tests the handling of consumer misbehavior.
Details* Set up a CCV channel and send an empty VSC packet to ensure that the consumer client revision height is greater than 0.
* Construct a Misbehaviour object with two conflicting headers and process the equivocation evidence.
* Verify that the provider chain correctly processes this misbehavior.
* Ensure that all involved validators are jailed, tombstoned, and slashed according to the expected outcomes.
* Assert that their tokens are adjusted based on the slashing fraction.
| + [TestGetByzantineValidators](../../tests/integration/misbehaviour.go#L101) | TestGetByzantineValidators checks the GetByzantineValidators function on various instances of misbehaviour.
Details* Set up a provider and consumer chain.
* Create a header with a subset of the validators on the consumer chain, then create a second header (in a variety of different ways),
and check which validators are considered Byzantine by calling the GetByzantineValidators function.
* The test scenarios are:
- when one of the headers is empty, the function should return an error
- when one of the headers has a corrupted validator set (e.g. by a validator having a different public key), the function should return an error
- when the signatures in one of the headers are corrupted, the function should return an error
- when the attack is an amnesia attack (i.e. the headers have different block IDs), no validator is considered byzantine
- for non-amnesia misbehaviour, all validators that signed both headers are considered byzantine
| + [TestCheckMisbehaviour](../../tests/integration/misbehaviour.go#L399) | TestCheckMisbehaviour tests that the CheckMisbehaviour function correctly checks for misbehaviour.
Details* Set up a provider and consumer chain.
* Create a valid client header and then create a misbehaviour by creating a second header in a variety of different ways.
* Check that the CheckMisbehaviour function correctly checks for misbehaviour by verifying that
it returns an error when the misbehaviour is invalid and no error when the misbehaviour is valid.
* The test scenarios are:
- both headers are identical (returns an error)
- the misbehaviour is not for the consumer chain (returns an error)
- passing an invalid client id (returns an error)
- passing a misbehaviour with different header height (returns an error)
- passing a misbehaviour older than the min equivocation evidence height (returns an error)
- one header of the misbehaviour has insufficient voting power (returns an error)
- passing a valid misbehaviour (no error)

* Test does not test actually submitting the misbehaviour to the chain or freezing the client.
| +
+ +# [normal_operations.go](../../tests/integration/normal_operations.go) +
Test Specifications + +| Function | Short Description | +|----------|-------------------| + [TestHistoricalInfo](../../tests/integration/normal_operations.go#L19) | TestHistoricalInfo tests the tracking of historical information in the context of new blocks being committed.
Details* Save the initial number of CC validators and current block height.
* Add a new validator and then advance the blockchain by one block, triggering the tracking of historical information.
* Create 2 validators and then call TrackHistoricalInfo with header block height.
* Verify that historical information is pruned correctly and that the validator set is updated as expected.
* Check if the historical information is correctly handled and pruned based on the block height.
| +
+ +# [slashing.go](../../tests/integration/slashing.go) +
Test Specifications + +| Function | Short Description | +|----------|-------------------| + [TestRelayAndApplyDowntimePacket](../../tests/integration/slashing.go#L50) | TestRelayAndApplyDowntimePacket tests that downtime slash packets can be properly relayed from consumer to provider, handled by provider, with a VSC and jailing eventually effective on consumer and provider.
Details* Set up CCV channels and retrieve consumer validators.
* Select a validator and create its consensus address.
* Retrieve the provider consensus address that corresponds to the consumer consensus address of the validator.
* The validator's current state is also retrieved, including its token balance,
* Set validator's signing information is to ensure it will be jailed for downtime.
* Create the slashing packet and send it from the consumer chain to the provider chain with a specified timeout.
* Receive the packet and verify that the validator was removed from the provider validator set.
* Relay VSC packets from the provider chain to each consumer chain and verify that the consumer chains correctly process these packets.
* Check the validator's balance and status on the provider chain to ensure it was jailed correctly but not slashed,
and its unjailing time is updated.
* Reset the outstanding downtime flag on the consumer chain, and ensure that the consumer
chain acknowledges receipt of the packet from the provider chain.

Note: This method does not test the actual slash packet sending logic for downtime
and double-signing, see TestValidatorDowntime and TestValidatorDoubleSigning for
those types of tests.
| + [TestSlashPacketAcknowledgement](../../tests/integration/slashing.go#L185) | TestSlashPacketAcknowledgement tests the handling of a slash packet acknowledgement.
Details* Set up a provider and consumer chain, with channel initialization between them performed.
* Send a slash packet with randomized fields from the consumer to the provider.
* The provider processes the packet
| + [TestHandleSlashPacketDowntime](../../tests/integration/slashing.go#L236) | TestHandleSlashPacketDowntime tests the handling of a downtime related slash packet, with integration tests.
Details* Retrieve a validator from provider chain's validators and checks if it's bonded.
* Set tThe signing information for the validator.
* The provider processes the downtime slashing packet from the consumer.
* Check that the validator has been jailed as a result of the downtime slashing packet being processed.
* Verify that the validator’s signing information is updated and that the jailing duration is set correctly.

Note that only downtime slash packets are processed by HandleSlashPacket.
| + [TestOnRecvSlashPacketErrors](../../tests/integration/slashing.go#L283) | TestOnRecvSlashPacketErrors tests errors for the OnRecvSlashPacket method in an integration testing setting.
Details* Set up all CCV channels and expect panic if the channel is not established via dest channel of packet.
* After the correct channelID is added to the packet, a panic shouldn't occur anymore.
* Create an instance of SlashPacketData and then verify correct processing and error handling
for slashing packets received by the provider chain.
TODO: Move to unit tests.
| + [TestValidatorDowntime](../../tests/integration/slashing.go#L401) | TestValidatorDowntime tests if a slash packet is sent and if the outstanding slashing flag is switched when a validator has downtime on the slashing module.
Details* Set up all CCV channel and send an empty VSC packet, then retrieve the address of a validator.
* Validator signs blocks for the duration of the signedBlocksWindow and a slash packet is constructed to be sent and committed.
* Simulate the validator missing blocks and then verify that the validator is jailed and the jailed time is correctly updated.
* Ensure that the missed block counters are reset.
* Check that there is a pending slash packet in the queue, and then send the pending packets.
* Check if slash record is created and verify that the consumer queue still contains the packet since no
acknowledgment has been received from the provider.
* Verify that the slash packet was sent and check that the outstanding slashing flag prevents the jailed validator to keep missing block.
| + [TestQueueAndSendSlashPacket](../../tests/integration/slashing.go#L522) | TestQueueAndSendSlashPacket tests the integration of QueueSlashPacket with SendPackets. In normal operation slash packets are queued in BeginBlock and sent in EndBlock.
Details* Set up all CCV channels and then queue slash packets for both downtime and double-signing infractions.
* Check that the correct number of slash requests are stored in the queue, including duplicates for downtime infractions.
* Prepare the CCV channel for sending actual slash packets.
* Send the slash packets and check that the outstanding downtime flags are correctly set for validators that were slashed
for downtime infractions.
* Ensure that the pending data packets queue is empty.
TODO: Move to unit tests.
| + [TestCISBeforeCCVEstablished](../../tests/integration/slashing.go#L607) | TestCISBeforeCCVEstablished tests that the consumer chain doesn't panic or have any undesired behavior when a slash packet is queued before the CCV channel is established. Then once the CCV channel is established, the slash packet should be sent soon after.
Details* Check that no pending packets exist and that there's no slash record found.
* Triggers a slashing event which queues a slash packet.
* The slash packet should be queued but not sent, and it should stay like that until the CCV channel is established and the packet is sent.
*Verify that a slashing record now exists, indicating that the slashing packet has been successfully sent.
| +
+ +# [stop_consumer.go](../../tests/integration/stop_consumer.go) +
Test Specifications + +| Function | Short Description | +|----------|-------------------| + [TestStopConsumerChain](../../tests/integration/stop_consumer.go#L26) | TestStopConsumerChain tests the functionality of stopping a consumer chain at a higher level than unit tests.
Details* Retrieve a validator from the provider chain's validators and then the delegator address.
* Set up test operations, populating the provider chain states using the following operations:
- Setup CCV channels; establishes the CCV channel and sets channelToChain, chainToChannel, and initHeight mapping for the consumer chain ID.
- Delegate the total bond amount to the chosen validator.
- Undelegate the shares in four consecutive blocks evenly; create UnbondingOp and UnbondingOpIndex entries for the consumer chain ID.
- Set SlashAck state for the consumer chain ID.

* After, the setup operations are executed, and the consumer chain is stopped.
* Check that the state associated with the consumer chain is properly cleaned up after it is stopped.
| + [TestStopConsumerOnChannelClosed](../../tests/integration/stop_consumer.go#L117) | TestStopConsumerOnChannelClosed tests stopping a consumer chain correctly.
Details* Set up CCV channel and transfer channel, and send empty VSC packet.
* Stop the consumer chain and verify that the provider chain's channel end is closed.

TODO Simon: implement OnChanCloseConfirm in IBC-GO testing to close the consumer chain's channel end
| +
+ +# [throttle.go](../../tests/integration/throttle.go) +
Test Specifications + +| Function | Short Description | +|----------|-------------------| + [TestBasicSlashPacketThrottling](../../tests/integration/throttle.go#L35) | TestBasicSlashPacketThrottling tests slash packet throttling with a single consumer, two slash packets, and no VSC matured packets. The most basic scenario.
Details* Set up various test cases, all CCV channels and validator powers.
* Retrieve the initial value of the slash meter, and the test verify it has the expected value.
* All validators are retrieved as well, and it's ensured that none of them are jailed from the start.
* Create a slash packet for the first validator and send it from the consumer to the provider.
* Asserts that validator 0 is jailed, has no power, and that the slash meter and allowance have the expected values.
* Then, create a second slash packet for a different validator, and check if the second validator is
not jailed after sending the second slash packet.
* Replenishes the slash meter until it is positive.
* Assert that validator 2 is jailed once the slash packet is retried and that it has no more voting power.
| + [TestMultiConsumerSlashPacketThrottling](../../tests/integration/throttle.go#L219) | TestMultiConsumerSlashPacketThrottling tests slash packet throttling in the context of multiple consumers sending slash packets to the provider, with VSC matured packets sprinkled around.
Details* Set up all CCV channels and validator powers.
* Choose three consumer bundles from the available bundles.
* Send the slash packets from each of the chosen consumer bundles to the provider chain. They will each slash a different validator.
* Confirm that the slash packet for the first consumer was handled first, and afterward, the slash packets for the second and
third consumers were bounced.
* Check the total power of validators in the provider chain to ensure it reflects the expected state after the first validator has been jailed.
* Replenish the slash meter and handle one of the two queued slash packet entries when both are retried.
* Verify again that the total power is updated.
* Replenish the slash meter one more time, and handle the final slash packet.
* Confirm that all validators are jailed.
| + [TestPacketSpam](../../tests/integration/throttle.go#L348) | TestPacketSpam confirms that the provider can handle a large number of incoming slash packets in a single block.
Details* Set up all CCV channels and validator powers.
* Set the parameters related to the handling of slash packets.
* Prepare the slash packets for the first three validators, and create 500 slash packets, alternating between
downtime and double-sign infractions.
* Simulate the reception of the 500 packets by the provider chain within the same block.
* Verify that the first three validators have been jailed as expected. This confirms that the
system correctly processed the slash packets and applied the penalties.
| + [TestDoubleSignDoesNotAffectThrottling](../../tests/integration/throttle.go#L420) | TestDoubleSignDoesNotAffectThrottling tests that a large number of double sign slash packets do not affect the throttling mechanism.
Details* Set up a scenario where 3 validators are slashed for double signing, and the 4th is not.
* Send 500 double sign slash packets from a consumer to the provider in a single block.
* Confirm that the slash meter is not affected by this, and that no validators are jailed.
| + [TestSlashingSmallValidators](../../tests/integration/throttle.go#L508) | TestSlashingSmallValidators tests that multiple slash packets from validators with small power can be handled by the provider chain in a non-throttled manner.
Details* Set up all CCV channels and delegate tokens to four validators, giving the first validator a larger amount of power.
* Initialize the slash meter, and verify that none of the validators are jailed before the slash packets are processed.
* Set up default signing information for the three smaller validators to prepare them for being jailed.
* The slash packets for the small validators are then constructed and sent.
* Verify validator powers after processing the slash packets.
* Confirm that the large validator remains unaffected and that the three smaller ones have been penalized and jailed.
| + [TestSlashMeterAllowanceChanges](../../tests/integration/throttle.go#L587) | TestSlashMeterAllowanceChanges tests scenarios where the slash meter allowance is expected to change.
Details* Set up all CCV channels, verify the initial slash meter allowance, and update the power of validators.
* Confirm that the value of the slash meter allowance is adjusted correctly after updating the validators' powers.
* Change the replenish fraction and assert the new expected allowance.

TODO: This should be a unit test, or replaced by TestTotalVotingPowerChanges.
| + [TestSlashAllValidators](../../tests/integration/throttle.go#L619) | TestSlashAllValidators is similar to TestSlashSameValidator, but 100% of validators' power is jailed in a single block.
Details* Set up all CCV channels and validator powers.
* Set the slash meter parameters.
* Create one slash packet for each validator, and then an additional five more for each validator
in order to test the system's ability to handle multiple slashing events in a single block.
* Receive and process each slashing packet in the provider chain and check that all validators are jailed as expected.

Note: This edge case should not occur in practice, but it is useful to validate that
the slash meter can allow any number of slash packets to be handled in a single block when
its allowance is set to "1.0".
| +
+ +# [throttle_retry.go](../../tests/integration/throttle_retry.go) +
Test Specifications + +| Function | Short Description | +|----------|-------------------| + [TestSlashRetries](../../tests/integration/throttle_retry.go#L27) | TestSlashRetries tests the throttling v2 retry logic at an integration level.
Details* Set up the CCV channels and the provider.
* Retrieve the validators and ensure that none are initially jailed.
* Select two validators and set up their signing information.
* Set up the consumer, and then construct and queue a slashing packet for the first validator.
* Verify that the packet is sent.
* Receive the packet on the provider side and handle it.
* Confirm that the first validator has been jailed and check the provider's slash meter to ensure it reflects the correct state.
* Acknowledge the packet on the consumer chain, and verify that the slash record has been deleted and no pending packets remain.
* Confirm that packet sending is now permitted.
* Queue a second slashing packet for the second validator and verify its pending status.
* Handle the second packet, check that the second validator is jailed, and confirm
the final state of the slash record and pending packets on the consumer chain.
| +
+ +# [unbonding.go](../../tests/integration/unbonding.go) +
Test Specifications + +| Function | Short Description | +|----------|-------------------| + [TestUndelegationCompletion](../../tests/integration/unbonding.go#L16) | TestUndelegationCompletion tests that undelegations complete after the unbonding period elapses on the provider, regardless of the consumer's state
Details* Set up CCV channel.
* Perform initial delegation of tokens followed by a partial undelegation (1/4 of the tokens).
* Verify that the staking unbonding operation is created as expected.
* Increment provider block height.
* Check that the unbonding operation has been completed.
* Verify that the token balances are correctly updated and the expected amount of tokens has been returned to the account.
| +
+ +# [valset_update.go](../../tests/integration/valset_update.go) +
Test Specifications + +| Function | Short Description | +|----------|-------------------| + [TestPacketRoundtrip](../../tests/integration/valset_update.go#L23) | TestPacketRoundtrip tests a CCV packet roundtrip when tokens are bonded on the provider.
Details* Set up CCV and transfer channels.
* Bond some tokens on the provider side in order to change validator power.
* Relay a packet from the provider chain to the consumer chain.
* Relays a matured packet from the consumer chain back to the provider chain.
| + [TestQueueAndSendVSCMaturedPackets](../../tests/integration/valset_update.go#L59) | TestQueueAndSendVSCMaturedPackets tests the behavior of EndBlock QueueVSCMaturedPackets call and its integration with SendPackets call.
Details* Set up CCV channel.
* Create and simulate the sending of three VSC packets from the provider chain to the consumer chain at different times.
* Send the first packet and validate its processing.
* Simulate the passage of one hour.
* Send the second packet and validate its processing.
* Simulate the passage of 24 more hours.
* Send the third packet and validate its processing.
* Retrieve all packet maturity times from the consumer, and use this to check the maturity status of the packets sent earlier.
* Advance the time so that the first two packets reach their unbonding period, while the third packet does not.
* Ensure first two packets are unbonded, their maturity times are deleted, and that VSCMatured packets are queued.
* The third packet is still in the store and has not yet been processed for unbonding.
* Checks that the packet commitments for the processed packets are correctly reflected in the consumer chain's state.
| +
+ diff --git a/tests/integration/README.md b/tests/integration/README.md index f826a9c2d7..bc22799e33 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -5,15 +5,19 @@ Integration tests are categorized into files as follows: - `setup.go` - setup for the integration tests - `common.go` - helper functions -- `channel_init.go` - integration tests for the _Channel Initialization_ sub-protocol - `valset_update.go` - integration tests for the _Validator Set Update_ sub-protocol - `unbonding.go` - integration tests for the _Completion of Unbonding Operations_ - `slashing.go` - integration tests for the _Consumer Initiated Slashing_ sub-protocol - `distribution.go` - integration tests for the _Reward Distribution_ sub-protocol - `stop_consumer.go` - integration tests for the _Consumer Chain Removal_ sub-protocol - `normal_operations.go` - integration tests for _normal operations_ of ICS enabled chains -- `expired_client.go` - integration tests for testing expired clients -- `key_assignment.go` - integration tests for testing key assignment +- `query_providerinfo_test.go` - integration tests for the `GetProviderInfo` method +- `changeover.go` - integration tests for reuse of existing transfer channels +- `double_vote.go` - integration tests for the handling of double voting +- `misbehavior.go` - integration tests for the handling of misbehaviors +- `partial_set_security_test.go` - integration tests for the partial set security +- `expired_client.go` - integration tests for expired clients +- `key_assignment.go` - integration tests for key assignment - `instance_test.go` - ties the integration test structure into golang's standard test mechanism, with appropriate definitions for concrete app types and setup callback To run the integration tests defined in this repo on any arbitrary consumer and provider implementation, copy the pattern exemplified in `instance_test.go` and `specific_setup.go` diff --git a/tests/integration/changeover.go b/tests/integration/changeover.go index 3c258dfc24..b18ea38f4b 100644 --- a/tests/integration/changeover.go +++ b/tests/integration/changeover.go @@ -5,6 +5,15 @@ import ( channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" ) +// TestRecycleTransferChannel tests that an existing transfer channel can be reused when transitioning from +// a standalone to a consumer chain. +// @Long Description@ +// The test case: +// * sets up a provider chain and a standalone chain +// * creates a connection between the two chains +// * creates a transfer channel between the two chains +// * transitions the standalone chain to a consumer chain +// * confirms that no extra transfer channel is created, thus only one transfer channel and one CCV channel exist. func (suite *CCVTestSuite) TestRecycleTransferChannel() { consumerKeeper := suite.consumerApp.GetConsumerKeeper() diff --git a/tests/integration/democracy.go b/tests/integration/democracy.go index 8f84f5ea32..1271bce12e 100644 --- a/tests/integration/democracy.go +++ b/tests/integration/democracy.go @@ -70,6 +70,11 @@ func (suite *ConsumerDemocracyTestSuite) SetupTest() { suite.consumerApp = suite.setupCallback(&suite.Suite) } +// TestDemocracyRewardsDistribution checks that rewards to democracy representatives, community pool, and provider redistribution account are done correctly. +// @Long Description@ +// * Set up a democracy consumer chain. +// * Create a new block. +// * Check that rewards to democracy representatives, community pool, and provider redistribution account are distributed in the right proportions. func (s *ConsumerDemocracyTestSuite) TestDemocracyRewardsDistribution() { s.consumerChain.NextBlock() stakingKeeper := s.consumerApp.GetTestStakingKeeper() @@ -153,7 +158,7 @@ func (s *ConsumerDemocracyTestSuite) TestDemocracyRewardsDistribution() { totalFees := nextConsumerRedistributeBalance.Add(providerDifference) s.Require().Equal(totalFees.Mul(consumerRedistributionFraction), nextConsumerRedistributeBalance) - // confirm begin blocker changes: democracy module distributes the fees from c onsumer redistribute address to representatives + // confirm begin blocker changes: democracy module distributes the fees from consumer redistribute address to representatives // and community fee pool // distribution module got tokens from previous consumer redistribute balance s.Require().Equal(distrModuleDifference, previousConsumerRedistributeBalance) @@ -175,6 +180,17 @@ func (s *ConsumerDemocracyTestSuite) TestDemocracyRewardsDistribution() { } } +// TestDemocracyGovernanceWhitelisting checks that only whitelisted governance proposals +// can be executed on democracy consumer chains. +// @Long Description@ +// For context, see the whitelist for proposals in app/consumer-democracy/proposals_whitelisting.go. +// * Set up a democracy consumer chain. +// * Submit a proposal containing changes to the auth and mint module parameters. +// * Check that the proposal is not executed, since the change to the auth module is not whitelisted. +// * Submit a proposal containing changes *only* to the mint module parameters. +// * Check that the proposal is executed, since the change to the mint module is whitelisted. +// * Submit a proposal containing changes *only* to the auth module parameters. +// * Check that again, the proposal is not executed, since the change to the auth module is not whitelisted. func (s *ConsumerDemocracyTestSuite) TestDemocracyGovernanceWhitelisting() { govKeeper := s.consumerApp.GetTestGovKeeper() params, err := govKeeper.Params.Get(s.consumerCtx()) @@ -270,6 +286,11 @@ func (s *ConsumerDemocracyTestSuite) TestDemocracyGovernanceWhitelisting() { s.Assert().Equal(votersOldBalances, getAccountsBalances(s.consumerCtx(), bankKeeper, bondDenom, votingAccounts)) } +// TestDemocracyMsgUpdateParams checks that the consumer parameters can be updated through a governance proposal. +// @Long Description@ +// * Set up a democracy consumer chain. +// * Submit a proposal containing changes to the consumer module parameters. +// * Check that the proposal is executed, and the parameters are updated. func (s *ConsumerDemocracyTestSuite) TestDemocracyMsgUpdateParams() { govKeeper := s.consumerApp.GetTestGovKeeper() params, err := govKeeper.Params.Get(s.consumerCtx()) @@ -280,11 +301,13 @@ func (s *ConsumerDemocracyTestSuite) TestDemocracyMsgUpdateParams() { votingAccounts := s.consumerChain.SenderAccounts bondDenom, err := stakingKeeper.BondDenom(s.consumerCtx()) s.Require().NoError(err) + depositAmount := params.MinDeposit duration := (3 * time.Second) params.VotingPeriod = &duration err = govKeeper.Params.Set(s.consumerCtx(), params) s.Assert().NoError(err) + proposer := s.consumerChain.SenderAccount s.consumerChain.NextBlock() votersOldBalances := getAccountsBalances(s.consumerCtx(), bankKeeper, bondDenom, votingAccounts) diff --git a/tests/integration/distribution.go b/tests/integration/distribution.go index 97cb5a5be6..afce8e6442 100644 --- a/tests/integration/distribution.go +++ b/tests/integration/distribution.go @@ -21,7 +21,14 @@ import ( ccv "github.com/cosmos/interchain-security/v6/x/ccv/types" ) -// This test is valid for minimal viable consumer chain +// TestRewardsDistribution tests the distribution of rewards from the consumer chain to the provider chain. +// @Long Description@ +// * Set up a provider and consumer chain and completes the channel initialization. +// * Send tokens into the FeeCollector on the consumer chain, +// and check that these tokens distributed correctly across the provider and consumer chain. +// * Check that the tokens are distributed purely on the consumer chain, +// then advance the block height to make the consumer chain send a packet with rewards to the provider chain. +// * Don't whitelist the consumer denom, so that the tokens stay in the ConsumerRewardsPool on the provider chain. func (s *CCVTestSuite) TestRewardsDistribution() { // set up channel and delegate some tokens in order for validator set update to be sent to the consumer chain s.SetupCCVChannel(s.path) @@ -175,6 +182,11 @@ func (s *CCVTestSuite) TestRewardsDistribution() { } // TestSendRewardsRetries tests that failed reward transmissions are retried every BlocksPerDistributionTransmission blocks +// @Long Description@ +// * Set up a provider and consumer chain and complete the channel initialization. +// * Fill the fee pool on the consumer chain, then corrupt the transmission channel +// and try to send rewards to the provider chain, which should fail. +// * Advance the block height to trigger a retry of the reward transmission, and confirm that this time, the transmission is successful. func (s *CCVTestSuite) TestSendRewardsRetries() { // TODO: this setup can be consolidated with other tests in the file @@ -246,9 +258,15 @@ func (s *CCVTestSuite) TestSendRewardsRetries() { "expected escrow balance to BE updated - OLD: %s, NEW: %s", oldEscBalance, newEscBalance) } -// TestEndBlockRD tests that the last transmission block height (LTBH) is correctly updated after the expected -// number of block have passed. It also checks that the IBC transfer states are discarded if -// the reward distribution to the provider has failed. +// TestEndBlockRD tests that the last transmission block height is correctly updated after the expected number of block have passed. +// @Long Description@ +// * Set up CCV and transmission channels between the provider and consumer chains. +// * Fill the fee pool on the consumer chain, prepare the system for reward +// distribution, and optionally corrupt the transmission channel to simulate failure scenarios. +// * After advancing the block height, verify whether the LBTH is updated correctly +// and if the escrow balance changes as expected. +// * Check that the IBC transfer states are discarded if the reward distribution +// to the provider has failed. // // Note: this method is effectively a unit test for EndBLockRD(), but is written as an integration test to avoid excessive mocking. func (s *CCVTestSuite) TestEndBlockRD() { @@ -369,8 +387,11 @@ func (s *CCVTestSuite) TestEndBlockRD() { } } -// TestSendRewardsToProvider is effectively a unit test for SendRewardsToProvider(), -// but is written as an integration test to avoid excessive mocking. +// TestSendRewardsToProvider is effectively a unit test for SendRewardsToProvider(), but is written as an integration test to avoid excessive mocking. +// @Long Description@ +// * Set up CCV and transmission channels between the provider and consumer chains. +// * Verify the SendRewardsToProvider() function under various scenarios and checks if the +// function handles each scenario correctly by ensuring the expected number of token transfers. func (s *CCVTestSuite) TestSendRewardsToProvider() { testCases := []struct { name string @@ -512,7 +533,12 @@ func (s *CCVTestSuite) TestSendRewardsToProvider() { } } -// TestIBCTransferMiddleware tests the logic of the IBC transfer OnRecvPacket callback +// TestIBCTransferMiddleware tests the logic of the IBC transfer OnRecvPacket callback. +// @Long Description@ +// * Set up IBC and transfer channels. +// * Simulate various scenarios of token transfers from the provider chain to +// the consumer chain, and evaluate how the middleware processes these transfers. +// * Ensure that token transfers are handled correctly and rewards are allocated as expected. func (s *CCVTestSuite) TestIBCTransferMiddleware() { var ( data transfertypes.FungibleTokenPacketData @@ -701,7 +727,12 @@ func (s *CCVTestSuite) TestIBCTransferMiddleware() { } // TestAllocateTokens is a happy-path test of the consumer rewards pool allocation -// to opted-in validators and the community pool +// to opted-in validators and the community pool. +// @Long Description@ +// * Set up a provider chain and multiple consumer chains, and initialize the channels between them. +// * Fund the consumer rewards pools on the provider chain and allocate rewards to the consumer chains. +// * Begin a new block to cause rewards to be distributed to the validators and the community pool, +// and check that the rewards are allocated as expected. func (s *CCVTestSuite) TestAllocateTokens() { // set up channel and delegate some tokens in order for validator set update to be sent to the consumer chain s.SetupAllCCVChannels() @@ -838,6 +869,17 @@ func (s *CCVTestSuite) prepareRewardDist() { s.coordinator.CommitNBlocks(s.consumerChain, uint64(blocksToGo)) } +// TestAllocateTokensToConsumerValidators tests the allocation of tokens to consumer validators. +// @Long Description@ +// * The test exclusively uses the provider chain. +// * Set up a current set of consumer validators, then call the AllocateTokensToConsumerValidators +// function to allocate a number of tokens to the validators. +// * Check that the expected number of tokens were allocated to the validators. +// * The test covers the following scenarios: +// - The tokens to be allocated are empty +// - The consumer validator set is empty +// - The tokens are allocated to a single validator +// - The tokens are allocated to multiple validators func (s *CCVTestSuite) TestAllocateTokensToConsumerValidators() { providerKeeper := s.providerApp.GetProviderKeeper() distributionKeeper := s.providerApp.GetTestDistributionKeeper() @@ -973,9 +1015,16 @@ func (s *CCVTestSuite) TestAllocateTokensToConsumerValidators() { } } -// TestAllocateTokensToConsumerValidatorsWithDifferentValidatorHeights tests `AllocateTokensToConsumerValidators` with -// consumer validators that have different heights. Specifically, test that validators that have been consumer validators -// for some time receive rewards, while validators that recently became consumer validators do not receive rewards. +// TestAllocateTokensToConsumerValidatorsWithDifferentValidatorHeights tests AllocateTokensToConsumerValidators test with +// consumer validators that have different heights. +// @Long Description@ +// * Set up a context where the consumer validators have different join heights and verify that rewards are +// correctly allocated only to validators who have been active long enough. +// * Ensure that rewards are evenly distributed among eligible validators, that validators +// can withdraw their rewards correctly, and that no rewards are allocated to validators +// who do not meet the required join height criteria. +// * Confirm that validators that have been consumer validators for some time receive rewards, +// while validators that recently became consumer validators do not receive rewards. func (s *CCVTestSuite) TestAllocateTokensToConsumerValidatorsWithDifferentValidatorHeights() { // Note this test is an adaptation of a `TestAllocateTokensToConsumerValidators` testcase. providerKeeper := s.providerApp.GetProviderKeeper() @@ -1087,7 +1136,15 @@ func (s *CCVTestSuite) TestAllocateTokensToConsumerValidatorsWithDifferentValida } } -// TestMultiConsumerRewardsDistribution tests the rewards distribution of multiple consumers chains +// TestMultiConsumerRewardsDistribution tests the rewards distribution of multiple consumers chains. +// @Long Description@ +// * Set up multiple consumer and transfer channels and verify the distribution of rewards from +// various consumer chains to the provider's reward pool. +// * Ensure that the consumer reward pools are correctly populated +// and that rewards are properly transferred to the provider. +// * Checks that the provider's reward pool balance reflects the accumulated +// rewards from all consumer chains after processing IBC transfer packets and relaying +// committed packets. func (s *CCVTestSuite) TestMultiConsumerRewardsDistribution() { s.SetupAllCCVChannels() s.SetupAllTransferChannels() diff --git a/tests/integration/double_vote.go b/tests/integration/double_vote.go index 100e82352d..e558b11d2b 100644 --- a/tests/integration/double_vote.go +++ b/tests/integration/double_vote.go @@ -13,8 +13,13 @@ import ( "github.com/cosmos/interchain-security/v6/x/ccv/provider/types" ) -// TestHandleConsumerDoubleVoting verifies that handling a double voting evidence -// of a consumer chain results in the expected tombstoning, jailing, and slashing of the misbehaved validator +// TestHandleConsumerDoubleVoting tests the handling of double voting evidence from the consumer chain. +// @Long Description@ +// * Set up a CCV channel. +// * Create various double voting scenarios and submit those to the provider chain. +// * Check if the provider chain correctly processes the evidence, jail and tombstone validators as needed, and apply the +// correct slashing penalties. +// * Verify that invalid evidence is properly rejected and does not result in incorrect penalties. func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() { s.SetupCCVChannel(s.path) // required to have the consumer client revision height greater than 0 @@ -267,8 +272,14 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() { } } -// TestHandleConsumerDoubleVotingSlashesUndelegationsAndRelegations verifies that handling a successful double voting -// evidence of a consumer chain results in the expected slashing of the misbehave validator undelegations +// TestHandleConsumerDoubleVotingSlashesUndelegationsAndRelegations tests the handling of double voting evidence from the consumer chain and checks if slashing, undelegations, and redelegations are correctly processed. +// @Long Description@ +// * Set up a CCV channel. +// * Create various double voting scenarios and submit those to the provider chain. +// * Verify that the evidence is processed correctly. +// * Ensure that the provider chain slashes the validator appropriately, and that it handles undelegations and redelegations accurately. +// * Confirm that the validator’s staking status reflects these actions. +// * Check if the slashing penalties are applied correctly and update the validator’s balance and delegations as expected. func (s *CCVTestSuite) TestHandleConsumerDoubleVotingSlashesUndelegationsAndRelegations() { s.SetupCCVChannel(s.path) // required to have the consumer client revision height greater than 0 diff --git a/tests/integration/expired_client.go b/tests/integration/expired_client.go index 34ad7e7eef..099fdd1613 100644 --- a/tests/integration/expired_client.go +++ b/tests/integration/expired_client.go @@ -19,8 +19,13 @@ import ( ) // TestVSCPacketSendExpiredClient tests queueing of VSCPackets when the consumer client is expired. -// While the consumer client is expired (or inactive for some reason) all packets will be queued and -// and cleared once the consumer client is established. +// @Long Description@ +// * Set up a CCV channel and expire the client on consumer chain. +// * Bond tokens to provider, send CCV packet to consumer and check pending packets. +// * While the consumer client is expired (or inactive for some reason) all packets will be queued. +// * The packet sending and checks are then repeated. +// * More tokens are bonded on provider to change validator powers. +// * Upgrade expired client to the consumer and all packets are cleared once the consumer client is established. func (s *CCVTestSuite) TestVSCPacketSendExpiredClient() { providerKeeper := s.providerApp.GetProviderKeeper() @@ -84,7 +89,13 @@ func (s *CCVTestSuite) TestVSCPacketSendExpiredClient() { } // TestConsumerPacketSendExpiredClient tests the consumer sending packets when the provider client is expired. -// While the provider client is expired all packets will be queued and and cleared once the provider client is upgraded. +// @Long Description@ +// * Set up a CCV channel and bond tokens on provider. +// * Send CCV packet to consumer and rebond tokens on provider. +// * Check for pending VSC packets and relay all VSC packets to consumer. +// * The provider client is then expired. +// * Confirm that while the provider client is expired all packets will be queued and then cleared +// once the provider client is upgraded. func (s *CCVTestSuite) TestConsumerPacketSendExpiredClient() { providerKeeper := s.providerApp.GetProviderKeeper() consumerKeeper := s.consumerApp.GetConsumerKeeper() diff --git a/tests/integration/key_assignment.go b/tests/integration/key_assignment.go index aabd96fdec..a7614a7063 100644 --- a/tests/integration/key_assignment.go +++ b/tests/integration/key_assignment.go @@ -11,9 +11,27 @@ import ( tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" providerkeeper "github.com/cosmos/interchain-security/v6/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v6/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v6/x/ccv/types" ) +// TestKeyAssignment tests key assignments relayed from the provider chain to the consumer chain at different times in the protocol lifecycle. +// @Long Description@ +// Each test scenario sets up a provider chain and then assigns a key for a validator. +// However, the assignment comes at different times in the protocol lifecycle. +// The test covers the following scenarios: +// * successfully assign the key before the CCV channel initialization is complete, then check that a VSCPacket is indeed queued +// * successfully assign the key after the CCV channel initialization is complete +// * successfully assign the key during an same epoch where the validator power changes +// * get an error when assigning the same key twice in the same block by different validators +// * get an error when assigning the same key twice in the same block by the same validator +// * successfully assign two different keys in the same block by one validator +// * get an error when assigning the same key twice in different blocks by different validators +// * get an error when assigning the same key twice in different blocks by the same validator +// For each scenario where the key assignment does not produce an error, +// the test also checks that VSCPackets are relayed to the consumer chain and that the clients on +// the provider and consumer chain can be updated. +// TODO: Remove panics when unexpected error occurs. func (s *CCVTestSuite) TestKeyAssignment() { testCases := []struct { name string @@ -52,6 +70,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { if err != nil { return err } + s.nextEpoch() return nil @@ -88,7 +107,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { validator, consumerKey := generateNewConsumerKey(s, 0) err := pk.AssignConsumerKey(s.providerCtx(), s.getFirstBundle().ConsumerId, validator, consumerKey) if err != nil { - return err + panic(err) } // same key assignment, but different validator @@ -111,7 +130,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { validator, consumerKey := generateNewConsumerKey(s, 0) err := pk.AssignConsumerKey(s.providerCtx(), s.getFirstBundle().ConsumerId, validator, consumerKey) if err != nil { - return err + panic(err) } // same key assignment, but different validator @@ -142,6 +161,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { if err != nil { return err } + s.nextEpoch() return nil @@ -156,8 +176,9 @@ func (s *CCVTestSuite) TestKeyAssignment() { validator, consumerKey := generateNewConsumerKey(s, 0) err := pk.AssignConsumerKey(s.providerCtx(), s.getFirstBundle().ConsumerId, validator, consumerKey) if err != nil { - return err + panic(err) } + s.nextEpoch() // same key assignment @@ -166,6 +187,15 @@ func (s *CCVTestSuite) TestKeyAssignment() { if err != nil { return err } + + // check that the key was not assigned to the second validator + valConsAddr2, getConsAddrErr := validator2.GetConsAddr() // make sure we don't override err, which we are saving for below + s.Require().NoError(getConsAddrErr) + actualConsumerKey2, found := pk.GetValidatorConsumerPubKey(s.providerCtx(), s.consumerChain.ChainID, types.NewProviderConsAddress(valConsAddr2)) + s.Require().True(found) + // the key for the second validator should *not* be the one we just assigned to the first validator + s.Require().NotEqual(consumerKey, actualConsumerKey2) + s.nextEpoch() return nil @@ -182,6 +212,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { if err != nil { return err } + s.nextEpoch() // same key assignment @@ -205,6 +236,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { if err != nil { return err } + s.nextEpoch() // same key assignment @@ -213,6 +245,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { if err != nil { return err } + s.nextEpoch() return nil diff --git a/tests/integration/misbehaviour.go b/tests/integration/misbehaviour.go index 14babf96e3..86752ee466 100644 --- a/tests/integration/misbehaviour.go +++ b/tests/integration/misbehaviour.go @@ -15,8 +15,13 @@ import ( "github.com/cosmos/interchain-security/v6/x/ccv/provider/types" ) -// TestHandleConsumerMisbehaviour tests that handling a valid misbehaviour, -// with conflicting headers forming an equivocation, results in the jailing of the validators +// TestHandleConsumerMisbehaviour tests the handling of consumer misbehavior. +// @Long Description@ +// * Set up a CCV channel and send an empty VSC packet to ensure that the consumer client revision height is greater than 0. +// * Construct a Misbehaviour object with two conflicting headers and process the equivocation evidence. +// * Verify that the provider chain correctly processes this misbehavior. +// * Ensure that all involved validators are jailed, tombstoned, and slashed according to the expected outcomes. +// * Assert that their tokens are adjusted based on the slashing fraction. func (s *CCVTestSuite) TestHandleConsumerMisbehaviour() { s.SetupCCVChannel(s.path) // required to have the consumer client revision height greater than 0 @@ -82,6 +87,17 @@ func (s *CCVTestSuite) TestHandleConsumerMisbehaviour() { } } +// TestGetByzantineValidators checks the GetByzantineValidators function on various instances of misbehaviour. +// @Long Description@ +// * Set up a provider and consumer chain. +// * Create a header with a subset of the validators on the consumer chain, then create a second header (in a variety of different ways), +// and check which validators are considered Byzantine by calling the GetByzantineValidators function. +// * The test scenarios are: +// - when one of the headers is empty, the function should return an error +// - when one of the headers has a corrupted validator set (e.g. by a validator having a different public key), the function should return an error +// - when the signatures in one of the headers are corrupted, the function should return an error +// - when the attack is an amnesia attack (i.e. the headers have different block IDs), no validator is considered byzantine +// - for non-amnesia misbehaviour, all validators that signed both headers are considered byzantine func (s *CCVTestSuite) TestGetByzantineValidators() { s.SetupCCVChannel(s.path) // required to have the consumer client revision height greater than 0 @@ -364,6 +380,22 @@ func (s *CCVTestSuite) TestGetByzantineValidators() { } } +// TestCheckMisbehaviour tests that the CheckMisbehaviour function correctly checks for misbehaviour. +// @Long Description@ +// * Set up a provider and consumer chain. +// * Create a valid client header and then create a misbehaviour by creating a second header in a variety of different ways. +// * Check that the CheckMisbehaviour function correctly checks for misbehaviour by verifying that +// it returns an error when the misbehaviour is invalid and no error when the misbehaviour is valid. +// * The test scenarios are: +// - both headers are identical (returns an error) +// - the misbehaviour is not for the consumer chain (returns an error) +// - passing an invalid client id (returns an error) +// - passing a misbehaviour with different header height (returns an error) +// - passing a misbehaviour older than the min equivocation evidence height (returns an error) +// - one header of the misbehaviour has insufficient voting power (returns an error) +// - passing a valid misbehaviour (no error) +// +// * Test does not test actually submitting the misbehaviour to the chain or freezing the client. func (s *CCVTestSuite) TestCheckMisbehaviour() { s.SetupCCVChannel(s.path) // required to have the consumer client revision height greater than 0 diff --git a/tests/integration/normal_operations.go b/tests/integration/normal_operations.go index bbd6b54856..2e6df8ced9 100644 --- a/tests/integration/normal_operations.go +++ b/tests/integration/normal_operations.go @@ -9,7 +9,13 @@ import ( ccvtypes "github.com/cosmos/interchain-security/v6/x/ccv/types" ) -// Tests the tracking of historical info in the context of new blocks being committed +// TestHistoricalInfo tests the tracking of historical information in the context of new blocks being committed. +// @Long Description@ +// * Save the initial number of CC validators and current block height. +// * Add a new validator and then advance the blockchain by one block, triggering the tracking of historical information. +// * Create 2 validators and then call TrackHistoricalInfo with header block height. +// * Verify that historical information is pruned correctly and that the validator set is updated as expected. +// * Check if the historical information is correctly handled and pruned based on the block height. func (k CCVTestSuite) TestHistoricalInfo() { //nolint:govet // this is a test so we can copy locks consumerKeeper := k.consumerApp.GetConsumerKeeper() cCtx := k.consumerChain.GetContext diff --git a/tests/integration/partial_set_security_test.go b/tests/integration/partial_set_security_test.go index 0519a7462d..c79bcd3fe2 100644 --- a/tests/integration/partial_set_security_test.go +++ b/tests/integration/partial_set_security_test.go @@ -23,11 +23,11 @@ import ( const stakeMultiplier = 1000000 // TestMinStake tests the min stake parameter. -// It starts a provider and single consumer chain, -// sets the initial powers according to the input, and then -// sets the min stake parameter according to the test case. -// Finally, it checks that the validator set on the consumer chain is as expected -// according to the min stake parameter. +// @Long Description@ +// * Start a provider and single consumer chain. +// * Set the initial powers according to the input. +// * Set the min stake parameter according to the test case. +// * Check that the validator set on the consumer chain is as expected according to the min stake parameter. func TestMinStake(t *testing.T) { testCases := []struct { name string diff --git a/tests/integration/query_providerinfo_test.go b/tests/integration/query_providerinfo_test.go index 8058248310..c3ca0c64e3 100644 --- a/tests/integration/query_providerinfo_test.go +++ b/tests/integration/query_providerinfo_test.go @@ -1,5 +1,10 @@ package integration +// TestQueryProviderInfo tests the results of GetProviderInfo method. +// @Long Description@ +// * Set up a CCV channel and send an empty VSC packet. +// * Verify that the result of GetProviderInfo method is correct and it +// provides expected information about the blockchain provider and consumer. func (s *CCVTestSuite) TestQueryProviderInfo() { s.SetupCCVChannel(s.path) s.SendEmptyVSCPacket() diff --git a/tests/integration/setup.go b/tests/integration/setup.go index 2d92867e8e..f1f4faebbd 100644 --- a/tests/integration/setup.go +++ b/tests/integration/setup.go @@ -390,7 +390,7 @@ func (suite *CCVTestSuite) SetupAllTransferChannels() { } } -func (s CCVTestSuite) validateEndpointsClientConfig(consumerBundle icstestingutils.ConsumerBundle) { //nolint:govet // this is a test so we can copy locks +func (s *CCVTestSuite) validateEndpointsClientConfig(consumerBundle icstestingutils.ConsumerBundle) { //nolint:govet // this is a test so we can copy locks consumerKeeper := consumerBundle.GetKeeper() providerStakingKeeper := s.providerApp.GetTestStakingKeeper() diff --git a/tests/integration/slashing.go b/tests/integration/slashing.go index 111303cbcf..ad3da52284 100644 --- a/tests/integration/slashing.go +++ b/tests/integration/slashing.go @@ -3,7 +3,6 @@ package integration import ( "context" "fmt" - "time" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" @@ -29,9 +28,21 @@ import ( ccv "github.com/cosmos/interchain-security/v6/x/ccv/types" ) -// TestRelayAndApplyDowntimePacket tests that downtime slash packets can be properly relayed -// from consumer to provider, handled by provider, with a VSC and jailing -// eventually effective on consumer and provider. +// TestRelayAndApplyDowntimePacket tests that downtime slash packets can be properly relayed from consumer to provider, +// handled by provider, with a VSC and jailing eventually effective on consumer and provider. +// @Long Description@ +// * Set up CCV channels and retrieve consumer validators. +// * Select a validator and create its consensus address. +// * Retrieve the provider consensus address that corresponds to the consumer consensus address of the validator. +// * The validator's current state is also retrieved, including its token balance, +// * Set validator's signing information is to ensure it will be jailed for downtime. +// * Create the slashing packet and send it from the consumer chain to the provider chain with a specified timeout. +// * Receive the packet and verify that the validator was removed from the provider validator set. +// * Relay VSC packets from the provider chain to each consumer chain and verify that the consumer chains correctly process these packets. +// * Check the validator's balance and status on the provider chain to ensure it was jailed correctly but not slashed, +// and its unjailing time is updated. +// * Reset the outstanding downtime flag on the consumer chain, and ensure that the consumer +// chain acknowledges receipt of the packet from the provider chain. // // Note: This method does not test the actual slash packet sending logic for downtime // and double-signing, see TestValidatorDowntime and TestValidatorDoubleSigning for @@ -166,89 +177,11 @@ func (s *CCVTestSuite) TestRelayAndApplyDowntimePacket() { s.Require().NoError(err) } -// Similar setup to TestRelayAndApplyDowntimePacket, but with a double sign slash packet. -// Note that double-sign slash packets should not affect the provider validator set. -func (s *CCVTestSuite) TestRelayAndApplyDoubleSignPacket() { - // Setup CCV channel for all instantiated consumers - s.SetupAllCCVChannels() - - providerStakingKeeper := s.providerApp.GetTestStakingKeeper() - providerKeeper := s.providerApp.GetProviderKeeper() - providerSlashingKeeper := s.providerApp.GetTestSlashingKeeper() - - validatorsPerChain := len(s.consumerChain.Vals.Validators) - - // pick first consumer validator - tmVal := s.consumerChain.Vals.Validators[0] - val, err := tmVal.ToProto() - s.Require().NoError(err) - pubkey, err := cryptocodec.FromCmtProtoPublicKey(val.GetPubKey()) - s.Require().Nil(err) - consumerConsAddr := providertypes.NewConsumerConsAddress(sdk.GetConsAddress(pubkey)) - // map consumer consensus address to provider consensus address - providerConsAddr, found := providerKeeper.GetValidatorByConsumerAddr( - s.providerCtx(), - s.getFirstBundle().ConsumerId, - consumerConsAddr) - s.Require().True(found) - - stakingVal, err := providerStakingKeeper.GetValidatorByConsAddr(s.providerCtx(), providerConsAddr.ToSdkConsAddr()) - s.Require().NoError(err) - valOldBalance := stakingVal.Tokens - - // Setup first val with mapped consensus address to be jailed on provider by setting signing info - // convert validator to TM type - pk, err := stakingVal.ConsPubKey() - s.Require().NoError(err) - tmPk, err := cryptocodec.ToCmtPubKeyInterface(pk) - s.Require().NoError(err) - s.setDefaultValSigningInfo(*tmtypes.NewValidator(tmPk, stakingVal.ConsensusPower(sdk.DefaultPowerReduction))) - - // Send slash packet from the first consumer chain - var ( - timeoutHeight = clienttypes.Height{} - timeoutTimestamp = uint64(s.getFirstBundle().GetCtx().BlockTime().Add(ccv.DefaultCCVTimeoutPeriod).UnixNano()) - ) - slashPacket := s.constructSlashPacketFromConsumer(s.getFirstBundle(), *tmVal, stakingtypes.Infraction_INFRACTION_DOUBLE_SIGN, 1) - packet := sendOnConsumerRecvOnProvider(s, s.getFirstBundle().Path, timeoutHeight, timeoutTimestamp, slashPacket.GetData()) - - // Advance a few more blocks to make sure any voting power changes would be reflected - s.providerChain.NextBlock() - s.providerChain.NextBlock() - s.providerChain.NextBlock() - - // Confirm validator was NOT removed from provider validator set - s.Require().Len(s.providerChain.Vals.Validators, validatorsPerChain) - - // Get staking keeper's validator obj after the relayed slash packet - stakingValAfter, err := providerStakingKeeper.GetValidatorByConsAddr(s.providerCtx(), providerConsAddr.ToSdkConsAddr()) - s.Require().NoError(err) - - // check that the validator's tokens were NOT slashed on provider - valNewBalance := stakingValAfter.GetTokens() - s.Require().Equal(valOldBalance, valNewBalance) - - // Get signing info for the validator - valSignInfo, err := providerSlashingKeeper.GetValidatorSigningInfo(s.providerCtx(), providerConsAddr.ToSdkConsAddr()) - s.Require().NoError(err) - - // check that the validator's unjailing time is NOT updated on provider - s.Require().Zero(valSignInfo.JailedUntil) - - // check that the validator is not jailed and still bonded on provider - s.Require().False(stakingValAfter.Jailed) - s.Require().Equal(stakingValAfter.Status, stakingtypes.Bonded) - - // check that validator was NOT tombstoned on provider - s.Require().False(valSignInfo.Tombstoned) - - // check that slashing packet gets acknowledged successfully, - // provider returns V1Result acks for double sign packets - ack := channeltypes.NewResultAcknowledgement(ccv.V1Result) - err = s.path.EndpointA.AcknowledgePacket(packet, ack.Acknowledgement()) - s.Require().NoError(err) -} - +// TestSlashPacketAcknowledgement tests the handling of a slash packet acknowledgement. +// @Long Description@ +// * Set up a provider and consumer chain, with channel initialization between them performed. +// * Send a slash packet with randomized fields from the consumer to the provider. +// * The provider processes the packet func (s *CCVTestSuite) TestSlashPacketAcknowledgement() { providerKeeper := s.providerApp.GetProviderKeeper() consumerKeeper := s.consumerApp.GetConsumerKeeper() @@ -292,6 +225,13 @@ func (s *CCVTestSuite) TestSlashPacketAcknowledgement() { } // TestHandleSlashPacketDowntime tests the handling of a downtime related slash packet, with integration tests. +// @Long Description@ +// * Retrieve a validator from provider chain's validators and checks if it's bonded. +// * Set tThe signing information for the validator. +// * The provider processes the downtime slashing packet from the consumer. +// * Check that the validator has been jailed as a result of the downtime slashing packet being processed. +// * Verify that the validator’s signing information is updated and that the jailing duration is set correctly. +// // Note that only downtime slash packets are processed by HandleSlashPacket. func (suite *CCVTestSuite) TestHandleSlashPacketDowntime() { providerKeeper := suite.providerApp.GetProviderKeeper() @@ -333,7 +273,13 @@ func (suite *CCVTestSuite) TestHandleSlashPacketDowntime() { suite.Require().Equal(suite.providerCtx().BlockTime().Add(jailDuration), signingInfo.JailedUntil) } -// TestOnRecvSlashPacketErrors tests errors for the OnRecvSlashPacket method in an integration testing setting +// TestOnRecvSlashPacketErrors tests errors for the OnRecvSlashPacket method in an integration testing setting. +// @Long Description@ +// * Set up all CCV channels and expect panic if the channel is not established via dest channel of packet. +// * After the correct channelID is added to the packet, a panic shouldn't occur anymore. +// * Create an instance of SlashPacketData and then verify correct processing and error handling +// for slashing packets received by the provider chain. +// TODO: Move to unit tests. func (suite *CCVTestSuite) TestOnRecvSlashPacketErrors() { providerKeeper := suite.providerApp.GetProviderKeeper() firstBundle := suite.getFirstBundle() @@ -441,9 +387,17 @@ func (suite *CCVTestSuite) TestOnRecvSlashPacketErrors() { suite.Require().Equal(ccv.SlashPacketHandledResult, ackResult, "expected successful ack") } -// TestValidatorDowntime tests if a slash packet is sent -// and if the outstanding slashing flag is switched -// when a validator has downtime on the slashing module +// TestValidatorDowntime tests if a slash packet is sent and if the outstanding slashing flag is switched +// when a validator has downtime on the slashing module. +// @Long Description@ +// * Set up all CCV channel and send an empty VSC packet, then retrieve the address of a validator. +// * Validator signs blocks for the duration of the signedBlocksWindow and a slash packet is constructed to be sent and committed. +// * Simulate the validator missing blocks and then verify that the validator is jailed and the jailed time is correctly updated. +// * Ensure that the missed block counters are reset. +// * Check that there is a pending slash packet in the queue, and then send the pending packets. +// * Check if slash record is created and verify that the consumer queue still contains the packet since no +// acknowledgment has been received from the provider. +// * Verify that the slash packet was sent and check that the outstanding slashing flag prevents the jailed validator to keep missing block. func (suite *CCVTestSuite) TestValidatorDowntime() { // initial setup suite.SetupCCVChannel(suite.path) @@ -555,95 +509,16 @@ func (suite *CCVTestSuite) TestValidatorDowntime() { }) } -// TestValidatorDoubleSigning tests if a slash packet is sent -// when a double-signing evidence is handled by the evidence module -func (suite *CCVTestSuite) TestValidatorDoubleSigning() { - // initial setup - suite.SetupCCVChannel(suite.path) - suite.SendEmptyVSCPacket() - - // sync suite context after CCV channel is established - ctx := suite.consumerCtx() - - channelID := suite.path.EndpointA.ChannelID - - // create a validator pubkey and address - // note that the validator won't necessarily be in valset to due the TM delay - pubkey := ed25519.GenPrivKey().PubKey() - consAddr := sdk.ConsAddress(pubkey.Address()) - - // set an arbitrary infraction height - infractionHeight := ctx.BlockHeight() - 1 - power := int64(100) - - // create evidence - e := &evidencetypes.Equivocation{ - Height: infractionHeight, - Power: power, - Time: time.Now().UTC(), - ConsensusAddress: consAddr.String(), - } - - // add validator signing-info to the store - suite.consumerApp.GetTestSlashingKeeper().SetValidatorSigningInfo(ctx, consAddr, slashingtypes.ValidatorSigningInfo{ - Address: consAddr.String(), - Tombstoned: false, - }) - - // save next sequence before sending a slash packet - seq, ok := suite.consumerApp.GetIBCKeeper().ChannelKeeper.GetNextSequenceSend(ctx, ccv.ConsumerPortID, channelID) - suite.Require().True(ok) - - // construct slash packet data and get the expected commit hash - packetData := ccv.NewSlashPacketData( - abci.Validator{Address: consAddr.Bytes(), Power: power}, - // get VSC ID mapping to the infraction height with the TM delay subtracted - suite.consumerApp.GetConsumerKeeper().GetHeightValsetUpdateID(ctx, uint64(infractionHeight-sdk.ValidatorUpdateDelay)), - stakingtypes.Infraction_INFRACTION_DOUBLE_SIGN, - ) - expCommit := suite.commitSlashPacket(ctx, *packetData) - - suite.consumerChain.NextBlock() - // // expect to send slash packet when handling double-sign evidence - // // NOTE: using IBCKeeper Authority as msg submitter (equal to gov module addr) - addr, err := sdk.AccAddressFromBech32(suite.consumerApp.GetIBCKeeper().GetAuthority()) - suite.Require().NoError(err) - evidenceMsg, err := evidencetypes.NewMsgSubmitEvidence(addr, e) - suite.Require().NoError(err) - suite.Require().NotEmpty(evidenceMsg) - - // this was previously done using suite.consumerApp.GetTestEvidenceKeeper().HandleEquivocationEvidence(ctx, e) - // HandleEquivocationEvidence is not exposed in the evidencekeeper interface starting cosmos-sdk v0.50.x - // suite.consumerApp.GetTestEvidenceKeeper().SubmitEvidence(ctx, e) - handleEquivocationEvidence(ctx, suite.consumerApp, e) - - // check slash packet is queued - pendingPackets := suite.consumerApp.GetConsumerKeeper().GetPendingPackets(ctx) - suite.Require().NotEmpty(pendingPackets, "pending packets empty") - suite.Require().Len(pendingPackets, 1, "pending packets len should be 1 is %d", len(pendingPackets)) - - // clear queue, commit packets - suite.consumerApp.GetConsumerKeeper().SendPackets(ctx) - - // Check slash record is created - slashRecord, found := suite.consumerApp.GetConsumerKeeper().GetSlashRecord(suite.consumerCtx()) - suite.Require().True(found, "slash record not found") - suite.Require().True(slashRecord.WaitingOnReply) - suite.Require().Equal(slashRecord.SendTime, suite.consumerCtx().BlockTime()) - - // check queue is not cleared, since no ack has been received from provider - pendingPackets = suite.consumerApp.GetConsumerKeeper().GetPendingPackets(ctx) - suite.Require().Len(pendingPackets, 1, "pending packets len should be 1 is %d", len(pendingPackets)) - - // check slash packet is sent - gotCommit := suite.consumerApp.GetIBCKeeper().ChannelKeeper.GetPacketCommitment(ctx, ccv.ConsumerPortID, channelID, seq) - suite.NotNil(gotCommit) - - suite.Require().EqualValues(expCommit, gotCommit) -} - // TestQueueAndSendSlashPacket tests the integration of QueueSlashPacket with SendPackets. // In normal operation slash packets are queued in BeginBlock and sent in EndBlock. +// @Long Description@ +// * Set up all CCV channels and then queue slash packets for both downtime and double-signing infractions. +// * Check that the correct number of slash requests are stored in the queue, including duplicates for downtime infractions. +// * Prepare the CCV channel for sending actual slash packets. +// * Send the slash packets and check that the outstanding downtime flags are correctly set for validators that were slashed +// for downtime infractions. +// * Ensure that the pending data packets queue is empty. +// TODO: Move to unit tests. func (suite *CCVTestSuite) TestQueueAndSendSlashPacket() { suite.SetupCCVChannel(suite.path) @@ -724,6 +599,11 @@ func (suite *CCVTestSuite) TestQueueAndSendSlashPacket() { // TestCISBeforeCCVEstablished tests that the consumer chain doesn't panic or // have any undesired behavior when a slash packet is queued before the CCV channel is established. // Then once the CCV channel is established, the slash packet should be sent soon after. +// @Long Description@ +// * Check that no pending packets exist and that there's no slash record found. +// * Triggers a slashing event which queues a slash packet. +// * The slash packet should be queued but not sent, and it should stay like that until the CCV channel is established and the packet is sent. +// *Verify that a slashing record now exists, indicating that the slashing packet has been successfully sent. func (suite *CCVTestSuite) TestCISBeforeCCVEstablished() { consumerKeeper := suite.consumerApp.GetConsumerKeeper() diff --git a/tests/integration/stop_consumer.go b/tests/integration/stop_consumer.go index 2f7c97dceb..c134c83abf 100644 --- a/tests/integration/stop_consumer.go +++ b/tests/integration/stop_consumer.go @@ -12,7 +12,17 @@ import ( ccv "github.com/cosmos/interchain-security/v6/x/ccv/types" ) -// Tests the functionality of stopping a consumer chain at a higher level than unit tests +// TestStopConsumerChain tests the functionality of stopping a consumer chain at a higher level than unit tests. +// @Long Description@ +// * Retrieve a validator from the provider chain's validators and then the delegator address. +// * Set up test operations, populating the provider chain states using the following operations: +// - Setup CCV channels; establishes the CCV channel and sets channelToChain, chainToChannel, and initHeight mapping for the consumer chain ID. +// - Delegate the total bond amount to the chosen validator. +// - Undelegate the shares in four consecutive blocks evenly; create UnbondingOp and UnbondingOpIndex entries for the consumer chain ID. +// - Set SlashAck state for the consumer chain ID. +// +// * After, the setup operations are executed, and the consumer chain is stopped. +// * Check that the state associated with the consumer chain is properly cleaned up after it is stopped. func (s *CCVTestSuite) TestStopConsumerChain() { providerKeeper := s.providerApp.GetProviderKeeper() providerStakingKeeper := s.providerApp.GetTestStakingKeeper() @@ -98,6 +108,11 @@ func (s *CCVTestSuite) TestStopConsumerChain() { s.checkConsumerChainIsRemoved(firstBundle.ConsumerId, true) } +// TestStopConsumerOnChannelClosed tests stopping a consumer chain correctly. +// @Long Description@ +// * Set up CCV channel and transfer channel, and send empty VSC packet. +// * Stop the consumer chain and verify that the provider chain's channel end is closed. +// // TODO Simon: implement OnChanCloseConfirm in IBC-GO testing to close the consumer chain's channel end func (s *CCVTestSuite) TestStopConsumerOnChannelClosed() { // init the CCV channel states diff --git a/tests/integration/throttle.go b/tests/integration/throttle.go index 010ba0c397..f7196d3e56 100644 --- a/tests/integration/throttle.go +++ b/tests/integration/throttle.go @@ -22,6 +22,16 @@ const fullSlashMeterString = "1.0" // TestBasicSlashPacketThrottling tests slash packet throttling with a single consumer, // two slash packets, and no VSC matured packets. The most basic scenario. +// @Long Description@ +// * Set up various test cases, all CCV channels and validator powers. +// * Retrieve the initial value of the slash meter, and the test verify it has the expected value. +// * All validators are retrieved as well, and it's ensured that none of them are jailed from the start. +// * Create a slash packet for the first validator and send it from the consumer to the provider. +// * Asserts that validator 0 is jailed, has no power, and that the slash meter and allowance have the expected values. +// * Then, create a second slash packet for a different validator, and check if the second validator is +// not jailed after sending the second slash packet. +// * Replenishes the slash meter until it is positive. +// * Assert that validator 2 is jailed once the slash packet is retried and that it has no more voting power. func (s *CCVTestSuite) TestBasicSlashPacketThrottling() { // setupValidatePowers gives the default 4 validators 25% power each (1000 power). // Note this in test cases. @@ -195,6 +205,17 @@ func (s *CCVTestSuite) TestBasicSlashPacketThrottling() { // TestMultiConsumerSlashPacketThrottling tests slash packet throttling in the context of multiple // consumers sending slash packets to the provider, with VSC matured packets sprinkled around. +// @Long Description@ +// * Set up all CCV channels and validator powers. +// * Choose three consumer bundles from the available bundles. +// * Send the slash packets from each of the chosen consumer bundles to the provider chain. They will each slash a different validator. +// * Confirm that the slash packet for the first consumer was handled first, and afterward, the slash packets for the second and +// third consumers were bounced. +// * Check the total power of validators in the provider chain to ensure it reflects the expected state after the first validator has been jailed. +// * Replenish the slash meter and handle one of the two queued slash packet entries when both are retried. +// * Verify again that the total power is updated. +// * Replenish the slash meter one more time, and handle the final slash packet. +// * Confirm that all validators are jailed. func (s *CCVTestSuite) TestMultiConsumerSlashPacketThrottling() { // Setup test s.SetupAllCCVChannels() @@ -315,8 +336,15 @@ func (s *CCVTestSuite) TestMultiConsumerSlashPacketThrottling() { } } -// TestPacketSpam confirms that the provider can handle a large number of -// incoming slash packets in a single block. +// TestPacketSpam confirms that the provider can handle a large number of incoming slash packets in a single block. +// @Long Description@ +// * Set up all CCV channels and validator powers. +// * Set the parameters related to the handling of slash packets. +// * Prepare the slash packets for the first three validators, and create 500 slash packets, alternating between +// downtime and double-sign infractions. +// * Simulate the reception of the 500 packets by the provider chain within the same block. +// * Verify that the first three validators have been jailed as expected. This confirms that the +// system correctly processed the slash packets and applied the penalties. func (s *CCVTestSuite) TestPacketSpam() { // Setup ccv channels to all consumers s.SetupAllCCVChannels() @@ -383,6 +411,12 @@ func (s *CCVTestSuite) TestPacketSpam() { } } +// TestDoubleSignDoesNotAffectThrottling tests that a large number of double sign slash packets +// do not affect the throttling mechanism. +// @Long Description@ +// * Set up a scenario where 3 validators are slashed for double signing, and the 4th is not. +// * Send 500 double sign slash packets from a consumer to the provider in a single block. +// * Confirm that the slash meter is not affected by this, and that no validators are jailed. func (s *CCVTestSuite) TestDoubleSignDoesNotAffectThrottling() { // Setup ccv channels to all consumers s.SetupAllCCVChannels() @@ -462,8 +496,15 @@ func (s *CCVTestSuite) TestDoubleSignDoesNotAffectThrottling() { } } -// TestSlashingSmallValidators tests that multiple slash packets from validators with small -// power can be handled by the provider chain in a non-throttled manner. +// TestSlashingSmallValidators tests that multiple slash packets from validators with small power can be handled by the provider chain +// in a non-throttled manner. +// @Long Description@ +// * Set up all CCV channels and delegate tokens to four validators, giving the first validator a larger amount of power. +// * Initialize the slash meter, and verify that none of the validators are jailed before the slash packets are processed. +// * Set up default signing information for the three smaller validators to prepare them for being jailed. +// * The slash packets for the small validators are then constructed and sent. +// * Verify validator powers after processing the slash packets. +// * Confirm that the large validator remains unaffected and that the three smaller ones have been penalized and jailed. func (s *CCVTestSuite) TestSlashingSmallValidators() { s.SetupAllCCVChannels() providerKeeper := s.providerApp.GetProviderKeeper() @@ -537,6 +578,10 @@ func (s *CCVTestSuite) TestSlashingSmallValidators() { } // TestSlashMeterAllowanceChanges tests scenarios where the slash meter allowance is expected to change. +// @Long Description@ +// * Set up all CCV channels, verify the initial slash meter allowance, and update the power of validators. +// * Confirm that the value of the slash meter allowance is adjusted correctly after updating the validators' powers. +// * Change the replenish fraction and assert the new expected allowance. // // TODO: This should be a unit test, or replaced by TestTotalVotingPowerChanges. func (s *CCVTestSuite) TestSlashMeterAllowanceChanges() { @@ -560,12 +605,18 @@ func (s *CCVTestSuite) TestSlashMeterAllowanceChanges() { s.Require().Equal(int64(1200), providerKeeper.GetSlashMeterAllowance(s.providerCtx()).Int64()) } -// Similar to TestSlashSameValidator, but 100% of val power is jailed a single block, -// and in the first packets recv for that block. -// This edge case should not occur in practice, but is useful to validate that +// TestSlashAllValidators is similar to TestSlashSameValidator, but 100% of validators' power is jailed in a single block. +// @Long Description@ +// * Set up all CCV channels and validator powers. +// * Set the slash meter parameters. +// * Create one slash packet for each validator, and then an additional five more for each validator +// in order to test the system's ability to handle multiple slashing events in a single block. +// * Receive and process each slashing packet in the provider chain and check that all validators are jailed as expected. +// +// Note: This edge case should not occur in practice, but it is useful to validate that // the slash meter can allow any number of slash packets to be handled in a single block when // its allowance is set to "1.0". -func (s CCVTestSuite) TestSlashAllValidators() { //nolint:govet // this is a test so we can copy locks +func (s *CCVTestSuite) TestSlashAllValidators() { //nolint:govet // this is a test so we can copy locks s.SetupAllCCVChannels() diff --git a/tests/integration/throttle_retry.go b/tests/integration/throttle_retry.go index f388c65f1c..685fa03d2d 100644 --- a/tests/integration/throttle_retry.go +++ b/tests/integration/throttle_retry.go @@ -11,6 +11,19 @@ import ( ) // TestSlashRetries tests the throttling v2 retry logic at an integration level. +// @Long Description@ +// * Set up the CCV channels and the provider. +// * Retrieve the validators and ensure that none are initially jailed. +// * Select two validators and set up their signing information. +// * Set up the consumer, and then construct and queue a slashing packet for the first validator. +// * Verify that the packet is sent. +// * Receive the packet on the provider side and handle it. +// * Confirm that the first validator has been jailed and check the provider's slash meter to ensure it reflects the correct state. +// * Acknowledge the packet on the consumer chain, and verify that the slash record has been deleted and no pending packets remain. +// * Confirm that packet sending is now permitted. +// * Queue a second slashing packet for the second validator and verify its pending status. +// * Handle the second packet, check that the second validator is jailed, and confirm +// the final state of the slash record and pending packets on the consumer chain. func (s *CCVTestSuite) TestSlashRetries() { s.SetupAllCCVChannels() s.SendEmptyVSCPacket() // Establish ccv channel diff --git a/tests/integration/unbonding.go b/tests/integration/unbonding.go index 7316e6c7dd..3f13d7cd1f 100644 --- a/tests/integration/unbonding.go +++ b/tests/integration/unbonding.go @@ -6,6 +6,13 @@ import ( // TestUndelegationCompletion tests that undelegations complete after // the unbonding period elapses on the provider, regardless of the consumer's state +// @Long Description@ +// * Set up CCV channel. +// * Perform initial delegation of tokens followed by a partial undelegation (1/4 of the tokens). +// * Verify that the staking unbonding operation is created as expected. +// * Increment provider block height. +// * Check that the unbonding operation has been completed. +// * Verify that the token balances are correctly updated and the expected amount of tokens has been returned to the account. func (s *CCVTestSuite) TestUndelegationCompletion() { s.SetupCCVChannel(s.path) diff --git a/tests/integration/valset_update.go b/tests/integration/valset_update.go index 831b9b2ac9..89078bb26e 100644 --- a/tests/integration/valset_update.go +++ b/tests/integration/valset_update.go @@ -14,7 +14,12 @@ import ( ccv "github.com/cosmos/interchain-security/v6/x/ccv/types" ) -// TestPacketRoundtrip tests a CCV packet roundtrip when tokens are bonded on provider +// TestPacketRoundtrip tests a CCV packet roundtrip when tokens are bonded on the provider. +// @Long Description@ +// * Set up CCV and transfer channels. +// * Bond some tokens on the provider side in order to change validator power. +// * Relay a packet from the provider chain to the consumer chain. +// * Relays a matured packet from the consumer chain back to the provider chain. func (s *CCVTestSuite) TestPacketRoundtrip() { s.SetupCCVChannel(s.path) s.SetupTransferChannel() @@ -37,8 +42,20 @@ func (s *CCVTestSuite) TestPacketRoundtrip() { relayAllCommittedPackets(s, s.consumerChain, s.path, ccv.ConsumerPortID, s.path.EndpointA.ChannelID, 1) } -// TestQueueAndSendVSCMaturedPackets tests the behavior of EndBlock QueueVSCMaturedPackets call -// and its integration with SendPackets call. +// TestQueueAndSendVSCMaturedPackets tests the behavior of EndBlock QueueVSCMaturedPackets call and its integration with SendPackets call. +// @Long Description@ +// * Set up CCV channel. +// * Create and simulate the sending of three VSC packets from the provider chain to the consumer chain at different times. +// * Send the first packet and validate its processing. +// * Simulate the passage of one hour. +// * Send the second packet and validate its processing. +// * Simulate the passage of 24 more hours. +// * Send the third packet and validate its processing. +// * Retrieve all packet maturity times from the consumer, and use this to check the maturity status of the packets sent earlier. +// * Advance the time so that the first two packets reach their unbonding period, while the third packet does not. +// * Ensure first two packets are unbonded, their maturity times are deleted, and that VSCMatured packets are queued. +// * The third packet is still in the store and has not yet been processed for unbonding. +// * Checks that the packet commitments for the processed packets are correctly reflected in the consumer chain's state. func (suite *CCVTestSuite) TestQueueAndSendVSCMaturedPackets() { consumerKeeper := suite.consumerApp.GetConsumerKeeper() diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index d70e2b5316..9c967a739e 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -207,7 +207,7 @@ func GetNewSlashPacketData() types.SlashPacketData { Power: int64(binary.BigEndian.Uint64(b1)), }, ValsetUpdateId: binary.BigEndian.Uint64(b2), - Infraction: stakingtypes.Infraction(binary.BigEndian.Uint64(b2) % 3), + Infraction: stakingtypes.Infraction(binary.BigEndian.Uint64(b3) % 3), } }