Skip to content

BM-601: Simplify callback logic by removing (somewhat weak) call-once property #372

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

Merged
merged 3 commits into from
Mar 8, 2025

Conversation

nategraf
Copy link
Member

@nategraf nategraf commented Mar 7, 2025

This PR is a change I am proposing, but do not have consensus on. Opening it to discuss.

With #335, we added support for callbacks on fulfillment and we aimed to have the callback trigger at most once per request. Since that PR, I realized two issues:

  1. As implemented, there is a potential re-entrancy attack on this property in that the called contract can call one of the fulfill methods and have the callback trigger again for the same request (ID). This is because we use the fulfilled flag to indicate whether the callback has run, but that flag is not set until after the callback is complete.
  2. Even if a given request ID can only be used to trigger a callback once, any user can create an arbitrary number of unique requests that are fulfilled by the same proof. Using a single receipt, they can fulfill an arbitrary number of requests and trigger an identical callback for each. This is indistinguishable on the called contract from multiple calls associated with the same request.

My opinion is that the call-once property does not buy us much. If there is an action which modifies state, the called application will have its own notion of what to do when the callback arrives, and ensuring it behaves reasonably if the same call is repeated is a common requirement for a wide range of applications.

As an advantage to dropping this property as a goal, benchmarks show it would save ~800 gas per fulfillment.

@github-actions github-actions bot changed the title Simplify callback logic by removing (somewhat weak) call-once property BM-601: Simplify callback logic by removing (somewhat weak) call-once property Mar 7, 2025
Copy link
Contributor

@willpote willpote left a comment

Choose a reason for hiding this comment

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

As implemented, there is a potential re-entrancy attack on this property in that the called contract can call one of the fulfill methods and have the callback trigger again for the same request (ID). This is because we use the fulfilled flag to indicate whether the callback has run, but that flag is not set until after the callback is complete.

Good catch, this was actually why I originally implemented the callback to be the last action. I totally forgot when reviewing the refactor

Even if a given request ID can only be used to trigger a callback once, any user can create an arbitrary number of unique requests that are fulfilled by the same proof. Using a single receipt, they can fulfill an arbitrary number of requests and trigger an identical callback for each. This is indistinguishable on the called contract from multiple calls associated with the same request.

Great point. Something we should definitely capture in our docs for this feature

@willpote willpote enabled auto-merge (squash) March 8, 2025 02:35
@willpote willpote merged commit 4739ec1 into main Mar 8, 2025
16 checks passed
@willpote willpote deleted the victor/callback-on-each-fulfill branch March 8, 2025 02:53
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