-
Notifications
You must be signed in to change notification settings - Fork 0
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
Minimize storage #34
Minimize storage #34
Conversation
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 like the idea to reduce state size and gas costs 👏🏻
I don't have enough knowledge about smart contracts to be able to fully review the changes. Perhaps @AmeanAsad can help us?
Before we continue, I would like us to be sure that we can use past events to reconstruct the state (e.g. current round) - see my comment below.
@juliangruber @patrickwoodhead I think this is such a major change that we should run it through another security audit, WDYT? |
Agreed, once this and other changes have been merged, and running in stating for a bit, we should consider another audit |
@juliangruber could you please merge |
Hey @juliangruber trying to understand the The scenario im imagining is if the evaluations are being processed for one round and participants start submitting measurements for the following round? |
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.
whitespace changes from formatter
@@ -6,17 +6,15 @@ pragma solidity ^0.8.19; | |||
contract ImpactEvaluator is AccessControl { | |||
struct Round { | |||
uint end; | |||
string[] measurementsCids; | |||
address payable[] addresses; | |||
uint64[] scores; |
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.
we need to temporarily keep these now, for the case where the evaluator submits scores over multiple calls, and we need to merge them afterwards
This can happen if the evaluation service errors/fails, but the new rounds are still created so that the system can continue functioning. Plus, you will always have at least two open rounds when you switch to the next one, since it takes a bit of time for evaluation results (which always come in after the round ends) to come in |
delete round.addresses; | ||
delete round.scores; | ||
round.exists = false; | ||
delete openRounds[roundIndex]; |
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 hope this correctly cleans up the space. If it doesn't, we can convert openRounds
to a ring buffer, and access it like openRounds[currentRoundIndex % MAX_OPEN_ROUNDS]
. This way storage will never grow, but of course we introduce a limit to open rounds, which could be problematic if the evaluate service fails for multiple rounds.
I think the ring buffer will be safer and we have to prioritize that. If a few rounds don't pay rewards, it's not the end of the world. If the contact becomes too expensive to run, it is.
Wdyt @AmeanAsad @bajtos?
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 had a similar question on my mind. What happens when the evaluation service goes down on Friday evening, and we don't notice until Monday morning? There will be no evaluations submitted for more than 100 rounds.
The ring buffer will not help here unless the evaluation service comes back within MAX_OPEN_ROUNDS and submits evaluations for older rounds that haven't been evaluated yet. (IIRC, we call the second part "self-healing"; it's not implemented yet, and not even on the roadmap AFAICT.)
Thoughts?
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.
Agreed, but is that worth it making the good case significantly more expensive, if we can't properly clean up this way?
I need to do more research on how to free up space with nested structures
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.
Let's keep this as is, I think it's good enough
I'm going to merge and deploy this now, so that we can start gathering usage data. Hindsight review would be appreciated @bajtos @AmeanAsad 🙏 |
This "radical" PR removes as much internal storage as possible, relying on Events as a method of cost saving and also reducing complexity. See gas comparison, before:
after:
In some calls, this is almost a 50% cost reduction. And since we only keep track of the current open rounds (ie rounds that have started but for which evaluation results haven't been submitted yet), which should on average just be 2, the contract's state won't grow.
Unless I missed something, the only impact this will have on our current running system is that we can't track per-round measurement count from the IE any more. This shouldn't be a problem, as we also have this state in the measurement and evaluate services. Listening for and querying events is already the main method of getting data out of the system. I also removed the method
getParticipantScores
, if a participant wants to see their score in a round they can look up the matchingsetScores()
transaction.