SIMD-0392
Relax Post-Execution Minimum Balance Check
TL;DR
To allow for non-disruptive rent increases, relax post-execution account minimum balance checks. The new lower-bound on post-exec balance is `min(acc.pre_exec_balance, calculate_min_balance(acc.size()))`. This maintains the invariant that every account has a balance at or above the minimum balance requirement calculated at any point since the most recent allocation occurred. When enabled, only newly allocated accounts will be subject to rent increases. Sidenote: this proposal doesn't include any mechanism for increasing rent, but when such a mechanism is added in the future it should cap the effective rent-per-byte at the legacy rent-exempt per-byte rate. This limitation isn't strictly necessary but it avoids needing to significantly modify any core system programs (e.g. stake program). If increasing the per-byte rate beyond this cap is desirable then the relevant core programs need to be updated to be compatible.
Summary
To allow for non-disruptive rent increases, relax post-execution account minimum balance checks. The new lower-bound on post-exec balance is `min(acc.pre_exec_balance, calculate_min_balance(acc.size()))`. This maintains the invariant that every account has a balance at or above the minimum balance requirement calculated at any point since the most recent allocation occurred. When enabled, only newly allocated accounts will be subject to rent increases. Sidenote: this proposal doesn't include any mechanism for increasing rent, but when such a mechanism is added in the future it should cap the effective rent-per-byte at the legacy rent-exempt per-byte rate. This limitation isn't strictly necessary but it avoids needing to significantly modify any core system programs (e.g. stake program). If increasing the per-byte rate beyond this cap is desirable then the relevant core programs need to be updated to be compatible.
Motivation
In order to safely reduce rent there must be a mechanism available for non-disruptive rent increases. Without this change, a rent increase would either place existing accounts in a gray area undefined by the protocol or prevent write-locking all accounts with balances below the new rent value.
Key Changes
- calculate_min_balance(acc_size) = acc_size current_rent_per_byte. Note, acc_size includes both the account data size and the 128 overhead bytes.
- legacy_rent_per_byte: the fixed rent-exempt per-byte rate used prior to this proposal.
- As the base case, it's clear this holds for new account creations or upwards reallocations: the post-execution rent price and account size is used to determine the minimum balance.
- The first subsequent transaction write-locking the same account but not performing any upwards reallocations can either have (1) a balance higher than the original min_balance or (2) a lower balance that's bounded below by a reduced rent price.
- With this, we can inductively prove that the post-execution balance of every account is bounded below by the lowest rent since the last allocation.
- The pre-execution balance MUST be captured before any state is modified (e.g. before fee collection, instruction execution, etc). This same pre_exec_balance snapshot MUST be reused when enforcing the minimum balance invariant across both instruction execution and fee/commission debiting/distribution.
- The pre and post-execution sizes are compared to determine if upwards reallocation occurred.
- The pre-execution owner MUST be captured. If the account owner changes during execution, the min(pre_exec_balance, …) clause MUST NOT apply; enforce the current rent-exempt minimum for the post-exec size.
- The calculate_min_balance() function uses the current rent_per_byte value, which may vary based on active feature gates and rent policies.
- Newly created accounts have pre_exec_balance = 0 and are not subject to the min() clause.
- As before, 0 post-balance is allowed and equivalent to closing an account.
- Pre-execution: pre_exec_balance = 0
- Post-execution: only calculate_min_balance(post_exec_size) is enforced
- No change to existing behavior
- post_exec_size <= pre_exec_size
- Owner MUST remain unchanged for the min() clause to apply: min_balance = min(calculate_min_balance(size), pre_exec_balance)
- This is the key behavioral change: allows accounts to retain their original rent price even if current rent increases
- Note that if the balance increases but is still below the current rent price, the new balance becomes the effective minimum balance for the given account. This means that the balance can no longer be reduced back to the original rent price.
- If the account owner changes, always enforce the current rent-exempt minimum for the post-exec size; the min(pre_exec_balance, …) clause does not apply.
Impact
- Dapp developers: Enables non-disruptive rent increases. Existing accounts are grandfathered at their creation-time rent price until they increase in size. New accounts and accounts that have increased in size adopt the current rent price. Minimal behavior change for typical applications; most applications do not reduce account balances below their minimum during normal operation. - Validators: Minimal performance impact; requires storing pre-execution balance (one u64) and size per write-locked account during transaction execution. - Core contributors: Changes to transaction processing logic to capture pre-execution state and implement the relaxed balance check.
Backwards Compatibility
This is a **relaxation** of existing constraints: - The change makes the balance check less strict by allowing accounts to retain their original rent price when not upwards reallocating. - This is backwards compatible in the sense that transactions that currently succeed will continue to succeed. - However, it changes consensus rules and must be activated behind a feature gate.
Security Considerations
- Enables safe rent increases without disrupting existing accounts: only newly allocated accounts are subject to new rent prices. - Maintains the invariant that accounts always satisfy the minimum rent price since the time of their most recent allocation. - No mechanism for forcing accounts to "upgrade" to current rent prices without upwards reallocation; this is intentional to preserve non-disruptiveness. - It's essentially free to adopt a new rent rate that's lower than the rent rate an account was subject to at allocation time. This isn't problematic in itself but it may cause users to be less sensitive to rent increases. - For similar reasons, there's potential for a secondary state market to develop, leading to an additional sudden increase in state allocation in anticipation of a future rent hike. This can further exacerbate an excessive state growth event. The owner change check during minimum balance calculation is intended to limit this by making reselling accounts more difficult.