-
Notifications
You must be signed in to change notification settings - Fork 138
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
Conversation
- 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 |
There was a problem hiding this comment.
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()
}
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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)
}
There was a problem hiding this comment.
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
.
There was a problem hiding this 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 |
There was a problem hiding this comment.
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.
That's correct @danwt we need a new cached context for each prop execution |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
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 bugNew 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.