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

Minimize storage #34

Merged
merged 9 commits into from
Oct 5, 2023
Merged

Minimize storage #34

merged 9 commits into from
Oct 5, 2023

Conversation

juliangruber
Copy link
Member

@juliangruber juliangruber commented Oct 3, 2023

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:

[PASS] test_AddMeasurements() (gas: 1911384)
[PASS] test_AdvanceRound() (gas: 1901709)
[PASS] test_SetNextRoundLength() (gas: 1899204)
[PASS] test_SetScores() (gas: 2007204)
[PASS] test_SetScoresEmptyRound() (gas: 1945811)
[PASS] test_SetScoresFractions() (gas: 2069759)
[PASS] test_SetScoresMultipleParticipants() (gas: 2125921)
[PASS] test_SetScoresNotEvaluator() (gas: 1855247)
[PASS] test_setRoundReward() (gas: 1850238)
[PASS] test_setRoundRewardNotAdmin() (gas: 1853482)

after:

[PASS] test_AddMeasurements() (gas: 1166367)
[PASS] test_AdvanceRound() (gas: 1230516)
[PASS] test_SetNextRoundLength() (gas: 1228349)
[PASS] test_SetScores() (gas: 1222263)
[PASS] test_SetScoresEmptyRound() (gas: 1190229)
[PASS] test_SetScoresFractions() (gas: 1269296)
[PASS] test_SetScoresMultipleParticipants() (gas: 1302958)
[PASS] test_SetScoresNotEvaluator() (gas: 1164214)
[PASS] test_setRoundReward() (gas: 1159622)
[PASS] test_setRoundRewardNotAdmin() (gas: 1162977)

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 matching setScores() transaction.

Copy link
Contributor

@bajtos bajtos left a 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.

src/ImpactEvaluator.sol Outdated Show resolved Hide resolved
README.md Show resolved Hide resolved
@bajtos
Copy link
Contributor

bajtos commented Oct 4, 2023

@juliangruber @patrickwoodhead I think this is such a major change that we should run it through another security audit, WDYT?

@juliangruber
Copy link
Member Author

@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

@bajtos
Copy link
Contributor

bajtos commented Oct 4, 2023

@juliangruber could you please merge main and resolve the conflicts before we do the final review?

@AmeanAsad
Copy link
Contributor

Hey @juliangruber trying to understand the openRounds concept here. Is there a scenario where we have two open rounds where evaluations are not submitted at the same time? Shouldn't the round process be synchronous?

The scenario im imagining is if the evaluations are being processed for one round and participants start submitting measurements for the following round?

Copy link
Member Author

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;
Copy link
Member Author

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

@juliangruber
Copy link
Member Author

Hey @juliangruber trying to understand the openRounds concept here. Is there a scenario where we have two open rounds where evaluations are not submitted at the same time? Shouldn't the round process be synchronous?

The scenario im imagining is if the evaluations are being processed for one round and participants start submitting measurements for the following round?

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];
Copy link
Member Author

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?

Copy link
Contributor

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?

Copy link
Member Author

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

Copy link
Member Author

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

@juliangruber
Copy link
Member Author

I'm going to merge and deploy this now, so that we can start gathering usage data.

Hindsight review would be appreciated @bajtos @AmeanAsad 🙏

@juliangruber juliangruber merged commit 08553b4 into main Oct 5, 2023
1 check passed
@juliangruber juliangruber deleted the minimize-storage branch October 5, 2023 11:43
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.

3 participants