-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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 bad rent in Bank::deposit as if since epoch 0 #10468
Conversation
runtime/src/accounts.rs
Outdated
@@ -155,7 +155,8 @@ impl Accounts { | |||
AccountsDB::load(storage, ancestors, accounts_index, key) | |||
.map(|(mut account, _)| { | |||
if message.is_writable(i) && !account.executable { |
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.
Well, these inconsistent predicates are concerning me. Ideally, I want to put everything into RentCollector::update
/RentCollector::from_existing_account()
. But, to avoid any additional risks, I've punted it. ;)
@@ -738,8 +739,7 @@ impl Accounts { | |||
); | |||
if message.is_writable(i) { | |||
if account.rent_epoch == 0 { |
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.
Well, these inconsistent predicates are concerning me. Ideally, I want to put everything into RentCollector::update
/RentCollector::collect_from_existing_account()
. But, to avoid any additional risks, I've punted it. ;)
@@ -154,8 +154,9 @@ impl Accounts { | |||
let (account, rent) = | |||
AccountsDB::load(storage, ancestors, accounts_index, key) | |||
.map(|(mut account, _)| { | |||
if message.is_writable(i) && !account.executable { | |||
let rent_due = rent_collector.update(&key, &mut account); | |||
if message.is_writable(i) { |
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 removed !account.executable
because this one was fairly easy to prove it's redundant.
Codecov Report
@@ Coverage Diff @@
## master #10468 +/- ##
=======================================
Coverage 82.4% 82.4%
=======================================
Files 314 314
Lines 73713 73765 +52
=======================================
+ Hits 60740 60795 +55
+ Misses 12973 12970 -3 |
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.
Nice catch!
These changes look good to me!
One nit, the from_
prefix felt like a conversion before rereading deeper. Maybe collect_
would read a little better
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. |
This stale pull request has been automatically closed. Thank you for your contributions. |
Now #10206 is landed, I'll work on this!! |
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. |
This stale pull request has been automatically closed. Thank you for your contributions. |
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. |
d1b6b0a
to
25adb08
Compare
Thanks for suggestion! I fixed that! @t-nelson Could you review this pr? I've finished to work on this. And I think this is ready for merge. :) Also, while writing tests, I found yet another bug, though: https://github.com/solana-labs/solana/pull/10468/files#r462385554 |
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!
runtime/src/bank.rs
Outdated
let account = self.get_account(pubkey); | ||
let created = account.is_none(); | ||
|
||
let mut account = account.unwrap_or_default(); | ||
account.lamports += lamports; |
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.
@t-nelson Ugh, I've found some concerns while testing against local/testnet/mainnet-beta....
Specifically, the order of deposit
and rent
is significant in a corner case when the account is to be reclaimed by rent, meaning there is too little lamports at the time of loading..
Also, there is some inconsistency for rent collection timing semantics with regards to #10426 (comment). Strictly abide by the rules, we would end up doing rent collection twice in deposit because we're both loading and storing at once. But I think the divergent behavior here is tolerable because bank::deposit()
is fairly limited use.
I'm thinking for a good compromise... At bottom, I want to finish this without much complexities introduced and without any cluster consistency issue when updating.
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.
Ok, my thinking is making progress with a fresh brain after weekend.
Come to think of it, update_rewards()
doesn't care about rents at all.
So, maybe deposit()
should follow the suit as well? So, the general idea would be that any kinds of protocol-level account manipulation is entirely exempt from rent manipulation. That will simplify the reasoning a lot.
Even if we do that, the accounts will be rent-collected at least once an epoch by eager rent collection. So skipping rents here shouldn't be an escape hatch.
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've checked the origin of rent in deposit()
: https://github.com/solana-labs/solana/pull/6947/files#r347114185
As far as I understand, it seems that the reasoning is that all updates should be rent-collected. This is now realized by the eager rent collection now.
Also, I found another protocol-level account manipulation: collected rent distribution. That should be free of rent collection to avoid recursion..
So, I'm more inclined to the general consistent idea of no rent collection for protocol-level account manipulation.
Also, this would slightly alleviate rather hefty processing of update_rewards
by avoiding rent collection there for the consistency of rent-collection coverage.
no merge please, I found some issues to be addressed after local testing..: https://github.com/solana-labs/solana/pull/10468/files#r463443390 |
@t-nelson I've completely turned around the solution after this course of thinking: #10468 (comment). In short, this is my preferred new solution: https://github.com/solana-labs/solana/pull/10468/files#diff-80339caf6f22201fa3399a7761c91b88R17 How does this look for you? :) |
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.
This LGTM! Let's give @rwalker-com a few days to comment on the proposal changes, then go ahead and merge
- The distribution of transaction fee at the end of every epoch | ||
|
||
Even if those processes are out of scope of rent collection, all of manipulated accounts will eventually be handled by the \(2\) mechanism. | ||
|
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.
This sounds good to me. It'd be great to get a comment from @rwalker-com to be sure we aren't missing anything. 🙏
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, and this is consistent with what I intended, but the call flow to load and store accounts is pretty tortured, and has many paths. were the bank to document which APIs were for runtime access of accounts, it would go a long way to making these easier to reason about.
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.
the call flow to load and store accounts is pretty tortured ...
@rwalker-com (UPDATED) Yeah, I think so too... I'll address the documetation work along with #10426
Anyway, thanks for confirming this pr!
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.
Err, wrong user @rwalker-apple...
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
* Fix bad rent in Bank::deposit as if since epoch 0 * Remove redundant predicate * Rename * Start to add tests with some cleanup * Forgot to add refactor code... * Enchance test * Really fix rent timing in deposit with robust test * Simplify new behavior by disabling rent altogether (cherry picked from commit 6c242f3) # Conflicts: # runtime/src/accounts.rs # runtime/src/rent_collector.rs
* Fix bad rent in Bank::deposit as if since epoch 0 * Remove redundant predicate * Rename * Start to add tests with some cleanup * Forgot to add refactor code... * Enchance test * Really fix rent timing in deposit with robust test * Simplify new behavior by disabling rent altogether (cherry picked from commit 6c242f3)
todo
|
* Fix bad rent in Bank::deposit as if since epoch 0 * Remove redundant predicate * Rename * Start to add tests with some cleanup * Forgot to add refactor code... * Enchance test * Really fix rent timing in deposit with robust test * Simplify new behavior by disabling rent altogether (cherry picked from commit 6c242f3) Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
* Fix bad rent in Bank::deposit as if since epoch 0 (#10468) * Fix bad rent in Bank::deposit as if since epoch 0 * Remove redundant predicate * Rename * Start to add tests with some cleanup * Forgot to add refactor code... * Enchance test * Really fix rent timing in deposit with robust test * Simplify new behavior by disabling rent altogether (cherry picked from commit 6c242f3) # Conflicts: # runtime/src/accounts.rs # runtime/src/rent_collector.rs * Fix conflict * Fix clippy Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
First of all, the manual test for this fix needed to be rather complicated because voting validators cause its fee-paying accounts (= identity accounts) to be touched via the vote transactions by themselves. This causes rent collection, meaning In short, run a local cluster with two validators, which are both staked. One is running with unpatched version, the other with patched version. And intentionally make one of validator not to vote at all, meaning the validator is only producing blocks while collecting fees. (= which touches Specifically, like this:
Then wait to see the cluster dies without or with mix of differing validators. |
Problem
When an account is created via
Bank::deposit()
,Bank
charges too much rent on it: all the epochs since genesis if the account doesn't exist yet.... In other words, it collects rent for the number of epochs.Also, the rent timing was wrongly delayed: #10468 (comment)
Mostly, this only affects tests. The only case which hits the production is
collector_id
's fee distribution because it usesBank::deposit()
. So the impact should be fairly small.This should be a rolling update, otherwise this is a DOS vulnerability. These old/new behaviors are themselves consistent across validators.
Summary of Changes
Calculate rent for newly-funded accounts since the current epoch not epoch 0I've changed mind: Disable rent collection entirely; for the reasoning, see the comments and document updates.
context
found while finishing #10099
FYI: @mvines (About rolling update).