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

fix: verify consumer proposals execution to prevent provider halts #602

Merged
merged 3 commits into from
Dec 16, 2022

Conversation

sainoe
Copy link
Contributor

@sainoe sainoe commented Dec 15, 2022

Description

This PR prevents consumer proposal executions to halt the provider in BeginBlock.

Proposals are now always executed in a cached context in order to either write or rollback the states, depending on the returned errors and the calling methods.

Therefore, the provider's BeginBlocker logic either writes or discards the proposals resulting states preventing the chain to halt due to invalid proposals.

The behavior of the proposal handlers has been changed in two ways: first, proposal execution is always verified, and second, successfully checked proposals are added to the pending proposals store regardless of their spawn/stop time.

Linked issues

Closes: #463, #458

Type of change

If you've checked more than one of the first three boxes, consider splitting this PR into multiple PRs!

  • Fix: Changes and/or adds code behavior, specifically to fix a bug

New behavior tests

TestHandleConsumerAdditionProposal & TestHandleConsumerAdditionProposal UTs are changed to test that only valid proposals can be added to the pending proposals regardless of their timestamp.

A new test case is added to TestBeginBlockInit & TestBeginBlockCCR in order to check that invalid proposals are dropped as expected.

    - Verify proposals execution using a cached context in BeginBlock and consumer proposal handlers
    - Drop invalid consumer props in BeginBlock instead of panicing
@@ -332,11 +335,18 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) {
propsToExecute := k.ConsumerAdditionPropsToExecute(ctx)

for _, prop := range propsToExecute {
p := prop
err := k.CreateConsumerClient(ctx, &p)
// create consumer client in a cached context to handle errors
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any significant performance impact when a new cached ctx is created for every prop inside the loop?

Is there a problem with using

cacheCtx, writeCacheFunc = ctx.CacheContext()
for _, props := range propsToExecute {
    	err := k.CreateConsumerClient(cacheCtx, prop)
        if err != nil {
            continue
        }
        writeCacheFunc()
}

Copy link
Contributor Author

@sainoe sainoe Dec 16, 2022

Choose a reason for hiding this comment

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

I don't see any, good catch
EDIT: Actually there is, see @danwt's comment below

Copy link
Contributor

Choose a reason for hiding this comment

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

Re your snippet @MSalopek I'm not sure if I understand the ctx caching exactly, but isn't it a problem if you continue and then afterwards writeCacheFunc() in a later iteration? The bogus data from the iteration when you continued will still be in the cacheCtx and then it will be written.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure how this works, but what you state might be why there was a separate cache context for each iteration.

Good catch!

Copy link
Contributor

Choose a reason for hiding this comment

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

That would just undo all props, not just the one that failed


// CreateConsumerClientInCachedCtx creates a consumer client
// from a given consumer addition proposal in a cached context
func (k Keeper) CreateConsumerClientInCachedCtx(ctx sdk.Context, p types.ConsumerAdditionProposal) (cc sdk.Context, writeCache func(), err error) {
Copy link
Contributor

@MSalopek MSalopek Dec 16, 2022

Choose a reason for hiding this comment

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

I think this naming scheme is clear, but I was wondering if it would be beneficial to call this something like ConsumerClientDryRun or VerifyCreateConsumerClient?

There's only 1 use case where writeCache is used and that's in BeginBlock, so maybe this should only return an error?

// VerifyCreateConsumerClient uses a cache context to create a consumer client in isolation.
// If consumer client cannot be created an error is returned.
func (k Keeper) VerifyCreateConsumerClient(ctx sdk.Context, p types.ConsumerAdditionProposal) error {
	cc, _ := ctx.CacheContext()
	return  k.CreateConsumerClient(cc, &p)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Valid point but I prefer the ...CachedCtx naming scheme bc it's more generic so that it can be used in BeginBlockInit and HandleConsumerAdditionProposal.

Copy link
Contributor

@danwt danwt left a comment

Choose a reason for hiding this comment

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

Looks good!

@@ -332,11 +335,18 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) {
propsToExecute := k.ConsumerAdditionPropsToExecute(ctx)

for _, prop := range propsToExecute {
p := prop
err := k.CreateConsumerClient(ctx, &p)
// create consumer client in a cached context to handle errors
Copy link
Contributor

Choose a reason for hiding this comment

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

Re your snippet @MSalopek I'm not sure if I understand the ctx caching exactly, but isn't it a problem if you continue and then afterwards writeCacheFunc() in a later iteration? The bogus data from the iteration when you continued will still be in the cacheCtx and then it will be written.

@sainoe
Copy link
Contributor Author

sainoe commented Dec 16, 2022

That's correct @danwt we need a new cached context for each prop execution

Copy link
Contributor

@jtremback jtremback left a comment

Choose a reason for hiding this comment

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

LGTM

@jtremback jtremback merged commit 1c348af into main Dec 16, 2022
@jtremback jtremback deleted the sainoe/gate-consumer-props-halt branch December 16, 2022 15:22
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.

Ensure that erroring proposals cannot halt the provider or result in inconsistent state
4 participants