Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zoneconcierge: fuzz tests for various indexers #203

Merged
merged 9 commits into from
Nov 17, 2022

Conversation

SebastianElvis
Copy link
Member

Fixes BM-267

This PR provides fuzz tests for various functionalities of ZoneConcierge, including

  • canonical chain indexer
  • fork indexer
  • chain info indexer
  • the FinalizedChainInfo query

Most of them follow the same paradigm: Generating a random number of canonical headers and fork headers, then check the consistency between the index and the underlying IBC light client.

Along the way we also fixed some bugs and added some datagen functionalities.

Copy link
Contributor

@gitferry gitferry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice work. Left some comments for possible improvement

hooks := zcKeeper.Hooks()

// invoke the hook a random number of times to simulate a random number of blocks
numHeaders := SimulateHeadersViaHook(ctx, hooks, czChain.ChainID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to generate a number of headers outside SimulateHeadersViaHook and pass it as a parameter? For example,

Suggested change
numHeaders := SimulateHeadersViaHook(ctx, hooks, czChain.ChainID)
numHeaders := datagen.RandomInt(100) + 1
headers := datagen.GenSequenceIBCTMHeaders(chainID, numHeaders)
SimulateHeadersViaHook(ctx, hooks, czChain.ChainID, headers)

We need to implement datagen.GenSequenceIBCTMHeaders. Suggesting this because (1) it feels a bit confusing to have a random number as the return value, and (2) changing it also allows the caller to compare the expected headers and the actual headers retrieved from zcKeeper.

require.NotNil(t, header)
require.Equal(t, czChain.ChainID, header.ChainId)
require.Equal(t, i, header.Height)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also compare LastCommitHash?

@@ -39,7 +39,7 @@ func (k Keeper) updateLatestHeader(ctx sdk.Context, chainID string, header *type
}
// NOTE: we can accept header without ancestor since IBC connection can be established at any height
chainInfo := k.GetChainInfo(ctx, chainID)
chainInfo.LatestHeader = header
chainInfo.TryToUpdateHeader(header)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems not very natual to have a updateLatestHeader calls TryToUpdateHeader. Maybe a more natural way is to rename the former to tryToUpdateLatestHeader, which does some checks, then calls UpdateLatestHeader, which does not need to do any checks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. Since after moving checks to the keeper function the UpdateLatestHeader function is just an assignment, I merged UpdateLatestHeader into the keeper function and rename the keeper function to tryToUpdateLatestHeader. Also did this for trpToUpdateLatestForkHeader. 👍

"github.com/stretchr/testify/require"
)

func FuzzChainInfoIndexer_Canonical(f *testing.F) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this test can be done in FuzzCanonicalChainIndexer altogether

hooks := zcKeeper.Hooks()

// invoke the hook a random number of times to simulate a random number of blocks
_, numForkHeaders := SimulateHeadersAndForksViaHook(ctx, hooks, czChain.ChainID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as SimulateHeadersViaHook. Maybe we can generate numForkHeaders first and then pass it in as a parameter

"github.com/stretchr/testify/require"
)

func FuzzForkIndexer(f *testing.F) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fee like this can be done in FuzzCanonicalChainIndexer altogether

// simulate the scenario that a random epoch has ended and finalised
epochNum := datagen.RandomInt(10)
hooks.AfterEpochEnds(ctx, epochNum)
hooks.AfterRawCheckpointFinalized(ctx, epochNum)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check error? I noticed that the current implementation will not trigger any errors but maybe we can still check error here just in case this will return errors in the future

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC hooks can only invoked by other modules but not users. If the code is correctly written then the hook will not return any error. Any error would be a programming error and we'd better panic there.

Comment on lines +35 to +36
require.Equal(t, numHeaders-1, chainInfo.LatestHeader.Height)
require.Equal(t, numForkHeaders, uint64(len(chainInfo.LatestForks.Headers)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you can only compare the height and number of headers but not the contents because the headers are generated within SimulateHeadersAndForksViaHook

Comment on lines 35 to 36
// SimulateHeadersViaHook generates a random non-zero number of canonical headers via the hook
func SimulateHeadersViaHook(ctx sdk.Context, hooks zckeeper.Hooks, chainID string) uint64 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See previous comments. It would be better to generate headers and pass them in as a parameter. Otherwise, it hides the headers that you created.

}

// SimulateHeadersViaHook generates a random non-zero number of canonical headers and fork headers via the hook
func SimulateHeadersAndForksViaHook(ctx sdk.Context, hooks zckeeper.Hooks, chainID string) (uint64, uint64) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dito

@SebastianElvis SebastianElvis merged commit 3a29e71 into dev Nov 17, 2022
@SebastianElvis SebastianElvis deleted the zoneconcierge-indexer-test branch November 17, 2022 00:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants