Skip to content

Period-based authorizations#469

Closed
franciscoaguirre wants to merge 28 commits into
mainfrom
period-authorizations
Closed

Period-based authorizations#469
franciscoaguirre wants to merge 28 commits into
mainfrom
period-authorizations

Conversation

@franciscoaguirre
Copy link
Copy Markdown
Contributor

@franciscoaguirre franciscoaguirre commented Apr 29, 2026

Created on top of bko-authorizations to align both issues: no gaps in authorizations and unlimited low-priority store() transactions.

Background

Authorizations on Bulletin used to be block-number-based (AuthorizationPeriod = 90 days) and decoupled from the People-Chain claim period. The recent soft-cap PR made store not waste blockspace, but renew became unbounded and bytes_permanent grew across refreshes without ever decrementing — disk burden was no longer pinned by anything.

This PR re-aligns Bulletin's authorizations with the People-Chain claim period, swaps the per-account block expiry for a two-slot (current, next) period grant, and finalises the soft-store / hard-renew / tx-quota accounting. Refresh extrinsics are gone; period rollover does the work.

How a holder uses the chain (Alice, 14-day period, 10 MiB / 100 tx)

1. Basic period — soft cap, blockspace never wasted

Alice claims long-term storage on People for the current period P; People sends authorize_account(Alice, 100, 10 MiB, P) to Bulletin via XCM. Throughout P:

  • store(8 MiB) → in-budget. Top tier (base + ALLOWANCE_PRIORITY_BOOST). State: bytes = 8 MiB, tx_used = 1.
  • store(3 MiB) → combined 11 MiB > 10 MiB. Bottom tier (base, no boost). The tx still validates and rides leftover blockspace; bytes saturates to 11 MiB; tx_used stays at 1 because over-budget stores don't burn tx slots.

Alice can keep submitting low-priority stores indefinitely after her allowance is exhausted — they only land when there's spare blockspace. Blocks never sit empty because of allowance enforcement, and the bound on total temp storage comes from chain physics (MaxBlockBytes × RetentionPeriod ≈ 1.7 TiB), not from a per-tx rejection.

2. Two-slot model + forward claim

Alice wants continuous coverage across the period boundary so auto-renewals (see §4) survive. While still in P, she submits a second People-Chain claim with period = P + 1. The People predicate now accepts current + 1; the XCM lands as authorize_account(Alice, 100, 10 MiB, P + 1) on Bulletin, which routes it into the next slot:

Authorization { current: Some(PG{P}), next: Some(PG{P+1}) }

When the clock crosses into P + 1, the next call against Alice's authorization runs prune-and-shift: current (P) is expired and dropped, next (P + 1) is promoted into current. Both slots' counters start fresh at zero — no refresh extrinsic, no bytes_permanent carry-over.

If Alice forgets to forward-claim, period rollover leaves current = None. store and renew reject; her blobs lapse after their RetentionPeriod. Stale entries are removed by the existing remove_expired_account_authorization permissionless call.

3. Multiple claims in the same period add

A credential entitles its holder to N claims per period, each granting 1/N of the credential's per-period maximum. (E.g., PoP with N = 4 might give 4 claims of 2.5 MiB / 25 tx each, totalling 10 MiB / 100 tx per period.) The claims are independent calls of claim_long_term_storage(period, counter, account_id), where counter ∈ 0..N indexes the claim within the period. The holder can spend any subset, in any order, at any time during the period — People-Chain bookkeeping enforces that each (period, alias, counter) is spent at most once. PoP and PoP-Lite are mutually exclusive at the credential level; this mechanic stacks claims of the same credential, not different ones.

Bulletin's authorize routes every same-period claim into the same slot and adds. Worked example, Alice spends claims 0 and 1 of her four in period P:

  • After claim 0: bytes_allowance = 2.5 MiB, transactions_allowance = 25
  • After claim 1: bytes_allowance = 5 MiB, transactions_allowance = 50
  • Used counters (bytes, bytes_permanent, tx_used) are preserved across the second claim, so her in-period spending so far isn't erased by topping up.

Two design properties matter here:

  1. Holders can size their footprint mid-period. Spend two claims now, hold the other two in reserve, top up later if needed. There's no penalty for not pre-claiming the full budget.
  2. No abuse vector via re-authorize. Each claim is alias-spent on People before the XCM goes out, so a holder can't loop the same claim to refill bytes_allowance repeatedly.

This is the only mechanic by which a slot's allowance grows mid-period; any other source (a different credential, an out-of-period claim) is rejected by People-Chain validation before it ever reaches Bulletin.

4. Renew is hard-capped — composes with data-renewal PR (#313)

renew(...) consumes from the same per-period budget as store. The cap is a hard rejection on the combined byte counter and on transactions_used:

combined = bytes + bytes_permanent + size
if combined > bytes_allowance OR tx_used + 1 > tx_allowance: REJECT

Worked example, period P + 1 starts with Alice's next slot promoted to current with a fresh 10 MiB / 100 tx grant:

step call bytes bytes_perm combined tier
1 auto-renew → renew(8 MiB blob) 0 8 8 top
2 store(2 MiB) 2 8 10 top (at edge)
3 renew(3 MiB other blob) 2 8 would be 13 REJECTED
4 store(5 MiB) 7 8 15 bottom

Alice has full freedom to split her budget — keep 8 MiB alive AND store 2 MiB new at boost priority, AND throw 5 MiB more at low priority. She cannot force-renew past 10 MiB combined; that's what bounds disk burden under repeated auto-renewals.

Composition with the data-renewal PR: auto-renewal schedules renew(block, index) on the holder's behalf shortly before each blob's RetentionPeriod elapses. The renewal is subject to the hard cap above. The forward-claim mechanic from §2 is what makes auto-renewal correct across period boundaries: the next slot is in place when the renewal fires, prune-and-shift promotes it, and the renewal lands. Holders who stop claiming see their auto-renewals start failing — their blobs lapse cleanly without leaving permanently-orphaned data behind.

5. Transactions quota as DoS shield

Independently of the byte budget, transactions_allowance caps the number of boost-tier calls a holder can land per period. Why the boost tier specifically: a holder with 10 MiB / period byte allowance could otherwise land 10 M boost-priority 1-byte stores for free, occupying MaxBlockTransactions slots across blocks. The cap stops that.

Alice with transactions_allowance = 3, plenty of bytes:

call tx_used after tier
store(1 byte) ×3 1, 2, 3 top each time
4th store(1 byte) 3 (unchanged) bottom — over tx budget

The 4th store still validates (soft cap on the boost — never rejection on store); it just lands as low priority and the tx counter doesn't tick. Alice can submit unlimited low-priority 1-byte stores after the boost budget is exhausted, bounded only by chain capacity. Renew, by contrast, is hard-rejected once tx_used + 1 > tx_allowance, since it has no low-priority path.

Failure mode if the holder stops engaging

State Behavior
Forward-claimed next exists Period rolls over → prune-and-shift → renewals continue.
Forgot to forward-claim Period rolls over → current = None → renewals reject → blobs lapse one RetentionPeriod after their last renew. No silent revival.
Re-claims later for the new period Fresh grant in current. New stores work; previously-lapsed blobs are gone.

Surface changes

  • AuthorizationExtent gains bytes_permanent, transactions_used, transactions_allowance. Authorization is now { current: Option<PeriodGrant>, next: Option<PeriodGrant> }.
  • Config: drops AuthorizationPeriod; adds TimeProvider: UnixTime and PeriodDuration: Get<u64> (must match People's LongTermStoragePeriodDuration).
  • Extrinsics: authorize_account(who, transactions, bytes, for_period), authorize_preimage(content_hash, max_size, for_period). for_period ∈ {current, current + 1}. Same-slot re-authorize adds. Refresh extrinsics removed.
  • AllowanceBasedPriority boost matches Call::renew too; BoostStrategy::boost consults bytes + bytes_permanent AND tx_used against the post-this-tx state.
  • People-Chain: is_accepted_long_term_storage_period now also accepts current + 1; claim_long_term_storage threads for_period through, mapping grace claims to the current period before XCM. AllocateStorage trait gains for_period and current_period(), drops refresh_allocation.
  • v1 → v2 migration translates old {transactions, bytes} single-grant into the new two-slot shape with current.period = current_period_at_upgrade and used counters at zero.

Tests

  • pallet-bulletin-transaction-storage: 36/36
  • indiv-pallet-resources: 94/94
  • indiv-pallet-proof-of-ink: 40/40
  • Runtime builds clean: bulletin-westend-runtime, bulletin-paseo-runtime, next-people-paseo-runtime

Out of scope

  • Auto-renewal itself (renew storage by content_hash & auto renewal #313). This PR is what makes auto-renewal correct when it lands.
  • Proof-of-ink authorization-lifetime redesign. PoI compiles against the new trait and now grants last one period instead of 90 days — known regression, follow-up PR.
  • A shared bp-bulletin-people primitives crate for PeriodDuration. Currently inlined in both bulletin runtimes with TODO comments; alignment with People's LongTermStoragePeriodDuration is by convention.

bkchr and others added 28 commits April 22, 2026 15:28
This extension gives `store` transactions a proportional priority boost based on the already consumed allowance.
Introduces a chain-wide cap on bytes committed to permanent storage (via
`renew`) so each runtime can express its own permanent-storage budget.
The bulletin-westend runtime sets it to 1.7 TiB, the test mock leaves it
unbounded.
Re-authorizing an unexpired account/preimage replaces `bytes_allowance`
rather than adding to it. Three tests assumed the additive form. Renamed
`authorize_account_does_not_push_expiry` to
`re_authorize_account_replaces_allowance_and_keeps_expiry` to reflect both
invariants it now covers.
`AllowanceBasedPriority` is now generic over a `BoostStrategy` so the
runtime can pick its policy in the `TxExtension` tuple. Two impls ship:

- `ProportionalBoost`: original PR #448 behaviour — linear in remaining
  allowance.
- `FlatBoost`: constant boost while in-budget, `0` once over-budget.

The proportional form is vulnerable to censorship: a fresh-allowance
signer outranks partly-used ones and can starve them with small-tx spam
(see karolk91's analysis on PR #448). `FlatBoost` makes in-budget
signers all rank equal and over-budget ones strictly lower; pool nonce/
arrival ordering breaks ties. Both runtimes are wired to `FlatBoost`.

Three pallet unit tests (`proportional_scales_with_remaining_allowance`,
`flat_is_constant_while_in_budget`,
`flat_does_not_let_fresh_outrank_partly_used`) document the contracts
and the censorship-relevant difference.
bkontur added a commit that referenced this pull request Apr 29, 2026
Brings just the AuthorizationExtent + boost-strategy parts of #469;
the period / two-slot / bytes_permanent surface is left out.

- AuthorizationExtent: gain transactions_used / transactions_allowance
  fields. authorize_account now uses its 3rd parameter to set/extend
  transactions_allowance; check_authorization saturating-bumps
  transactions_used on consume.
- BoostStrategy: a shared in_budget helper gates both the byte and tx
  axes; FlatBoost is in-budget at-cap (matching #469); ProportionalBoost
  scales by the tighter of the two remainders.
- AllowanceBasedPriority::validate increments transactions_used in the
  post-this-tx extent before computing the boost.
- v1->v2 migration carries the old transaction-count field over to the
  new transactions_allowance.
- Genesis config tuple becomes (account, transactions_allowance,
  bytes_allowance); preimage authorize_* always sets
  transactions_allowance = 1.
- Tests + bulletin-{westend,paseo} runtime tests updated for the new
  expected counters; new boost_tests::tx_axis_gates_boost_independently.

Out of scope (per #469): bytes_permanent, the two-slot
current/next grant model, for_period parameter, renew matching in the
boost extension, the People-Chain-aligned period model.
bkontur added a commit that referenced this pull request Apr 29, 2026
Brings just the AuthorizationExtent + boost-strategy parts of #469;
the period / two-slot / bytes_permanent surface is left out.

- AuthorizationExtent: gain transactions_used / transactions_allowance
  fields. authorize_account now uses its 3rd parameter to set/extend
  transactions_allowance; check_authorization saturating-bumps
  transactions_used on consume.
- BoostStrategy: a shared in_budget helper gates both the byte and tx
  axes; FlatBoost is in-budget at-cap (matching #469); ProportionalBoost
  scales by the tighter of the two remainders.
- AllowanceBasedPriority::validate increments transactions_used in the
  post-this-tx extent before computing the boost.
- v1->v2 migration carries the old transaction-count field over to the
  new transactions_allowance.
- Genesis config tuple becomes (account, transactions_allowance,
  bytes_allowance); preimage authorize_* always sets
  transactions_allowance = 1.
- Tests + bulletin-{westend,paseo} runtime tests updated for the new
  expected counters; new boost_tests::tx_axis_gates_boost_independently.

Out of scope (per #469): bytes_permanent, the two-slot
current/next grant model, for_period parameter, renew matching in the
boost extension, the People-Chain-aligned period model.

Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
bkontur added a commit that referenced this pull request Apr 30, 2026
* Adds `AllowanceBasedPriority` extension

This extension gives `store` transactions a proportional priority boost based on the already consumed allowance.

* Nit plus test

* Do not degrade user's performance in time - reset usage when refresh_authorization

* Migration for Paseo

* Fix migration

* fmt

* Ping CI

* Fix tests to match assignment semantics of `authorize`

Re-authorizing an unexpired account/preimage replaces `bytes_allowance`
rather than adding to it. Three tests assumed the additive form. Renamed
`authorize_account_does_not_push_expiry` to
`re_authorize_account_replaces_allowance_and_keeps_expiry` to reflect both
invariants it now covers.

* Compilation for paseo after main merge

* Doc nit for refresh_authorization

* Migration for paseo, which was merged meantime

* Refactor allowance boost behind `BoostStrategy`, default to `FlatBoost`

`AllowanceBasedPriority` is now generic over a `BoostStrategy` so the
runtime can pick its policy in the `TxExtension` tuple. Two impls ship:

- `ProportionalBoost`: original PR #448 behaviour — linear in remaining
  allowance.
- `FlatBoost`: constant boost while in-budget, `0` once over-budget.

The proportional form is vulnerable to censorship: a fresh-allowance
signer outranks partly-used ones and can starve them with small-tx spam
(see karolk91's analysis on PR #448). `FlatBoost` makes in-budget
signers all rank equal and over-budget ones strictly lower; pool nonce/
arrival ordering breaks ties. Both runtimes are wired to `FlatBoost`.

Three pallet unit tests (`proportional_scales_with_remaining_allowance`,
`flat_is_constant_while_in_budget`,
`flat_does_not_let_fresh_outrank_partly_used`) document the contracts
and the censorship-relevant difference.

* Docs nits

* Compute allowance boost against post-this-tx state

* Minimalistic alignment with paritytech/individuality#785 (part 1)

* Pick transactions_used / transactions_allowance from #469

Brings just the AuthorizationExtent + boost-strategy parts of #469;
the period / two-slot / bytes_permanent surface is left out.

- AuthorizationExtent: gain transactions_used / transactions_allowance
  fields. authorize_account now uses its 3rd parameter to set/extend
  transactions_allowance; check_authorization saturating-bumps
  transactions_used on consume.
- BoostStrategy: a shared in_budget helper gates both the byte and tx
  axes; FlatBoost is in-budget at-cap (matching #469); ProportionalBoost
  scales by the tighter of the two remainders.
- AllowanceBasedPriority::validate increments transactions_used in the
  post-this-tx extent before computing the boost.
- v1->v2 migration carries the old transaction-count field over to the
  new transactions_allowance.
- Genesis config tuple becomes (account, transactions_allowance,
  bytes_allowance); preimage authorize_* always sets
  transactions_allowance = 1.
- Tests + bulletin-{westend,paseo} runtime tests updated for the new
  expected counters; new boost_tests::tx_axis_gates_boost_independently.

Out of scope (per #469): bytes_permanent, the two-slot
current/next grant model, for_period parameter, renew matching in the
boost extension, the People-Chain-aligned period model.

Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>

* transactions_used -> transactions

* Refresh: only extend expiry, do not reset consumed counters

Per review feedback (#448 r3163274071): resetting `bytes` and
`transactions` on refresh implicitly grants a fresh boost-tier window,
which conflates two operations under one extrinsic. Refresh now does
exactly what its name says — extend the expiration block. Holders who
want more capacity call `authorize_account` (additive on the unexpired
path).

Doc updates on `refresh_account_authorization`,
`refresh_preimage_authorization` and the `refresh_authorization` helper
make the split explicit. `authorize_account`'s doc also gets updated
to describe the new tx-axis additivity.

* Move back to 14

* Tighten priority constants for store / renew

- `AuthorizationPeriod` 90 days -> 14 days, aligning with the
  `LongTermStoragePeriodDuration` (2 weeks) on the People-Chain side.
- Drop the unused `SudoPriority` and `SetPurgeKeysPriority` constants;
  they were only chained into `RemoveExpiredAuthorizationPriority` and
  not wired anywhere else.
- `RemoveExpiredAuthorizationPriority` is now `TransactionPriority::MAX`
  so permissionless cleanups always outrank stores.
- `StoreRenewPriority` is `TransactionPriority::MAX / 4`. With
  `AllowanceBasedPriority` adding `ALLOWANCE_PRIORITY_BOOST` for
  in-budget signers, in-budget txs land just above over-budget ones
  without saturating `u64` and with plenty of headroom both above
  generic transactions and below the cleanup ceiling.

* Nit

* More nits

---------

Co-authored-by: Branislav Kontur <bkontur@gmail.com>
Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
Base automatically changed from bko-authorizations to main May 5, 2026 15:55
bkontur added a commit that referenced this pull request May 5, 2026
* Adds `AllowanceBasedPriority` extension

This extension gives `store` transactions a proportional priority boost based on the already consumed allowance.

* Nit plus test

* Do not degrade user's performance in time - reset usage when refresh_authorization

* Migration for Paseo

* Fix migration

* fmt

* Ping CI

* Adjust `AuthorizationExtent` to keep  bytes/bytes_permanent/bytes_allowance

* Add MaxPermanentStorageSize Config bound

Introduces a chain-wide cap on bytes committed to permanent storage (via
`renew`) so each runtime can express its own permanent-storage budget.
The bulletin-westend runtime sets it to 1.7 TiB, the test mock leaves it
unbounded.

* Fix tests to match assignment semantics of `authorize`

Re-authorizing an unexpired account/preimage replaces `bytes_allowance`
rather than adding to it. Three tests assumed the additive form. Renamed
`authorize_account_does_not_push_expiry` to
`re_authorize_account_replaces_allowance_and_keeps_expiry` to reflect both
invariants it now covers.

* Compilation for paseo after main merge

* Doc nit for refresh_authorization

* Migration for paseo, which was merged meantime

* Refactor allowance boost behind `BoostStrategy`, default to `FlatBoost`

`AllowanceBasedPriority` is now generic over a `BoostStrategy` so the
runtime can pick its policy in the `TxExtension` tuple. Two impls ship:

- `ProportionalBoost`: original PR #448 behaviour — linear in remaining
  allowance.
- `FlatBoost`: constant boost while in-budget, `0` once over-budget.

The proportional form is vulnerable to censorship: a fresh-allowance
signer outranks partly-used ones and can starve them with small-tx spam
(see karolk91's analysis on PR #448). `FlatBoost` makes in-budget
signers all rank equal and over-budget ones strictly lower; pool nonce/
arrival ordering breaks ties. Both runtimes are wired to `FlatBoost`.

Three pallet unit tests (`proportional_scales_with_remaining_allowance`,
`flat_is_constant_while_in_budget`,
`flat_does_not_let_fresh_outrank_partly_used`) document the contracts
and the censorship-relevant difference.

* Docs nits

* Compute allowance boost against post-this-tx state

* Compute allowance boost against post-this-tx state

* Updated soft allowance design

* Updated hard allowance design and renewals (draft, TODO: challange with auto-renewal)

* Soft-side lifecycle: preserve bytes_permanent across re-authorize and remove_expired

Two paired rules from the authorizations design were missing in code:

1. `authorize_account` on an expired-but-present entry now re-grants the
   cap and resets `bytes` only — it preserves `bytes_permanent`. Previously
   the expired branch overwrote the whole extent with a fresh one, zeroing
   `bytes_permanent` and silently bypassing the per-account hard cap on
   the next renew cycle.

2. `remove_expired_authorization` now refuses with
   `Error::AuthorizationHasPermanentStorage` while `bytes_permanent > 0`.
   Removing the entry would orphan the (lazy) ledger drain — its decrement
   would have nowhere to land. The entry becomes removable once
   `bytes_permanent` has dropped back to `0` naturally.

Together these guarantee the ledger drain (introduced when the hard-side
work lands) is the only path that ever decrements `bytes_permanent`. No
other code path can clobber or orphan the counter.

Tests cover both rules: `authorize_account_after_expiry_preserves_bytes_permanent`
and `remove_expired_account_authorization_refuses_while_bytes_permanent_outstanding`.

* paseo: back MaxPermanentStorageSize with `parameter_types! { pub storage }`

Replaces the compile-time `MAX_PERMANENT_STORAGE_SIZE` const with a
storage-backed `parameter_types!` entry. The Config trait constant is
unchanged on the pallet side; the runtime just chooses a mutable backing,
so governance (root) can raise or lower the cap at runtime via
`system.set_storage` without a runtime upgrade.

Initial value is still 1.7 TiB, seeded on first read.

* WIP: hard cap (very draft)

* Minimalistic alignment with paritytech/individuality#785 (part 1)

* Pick transactions_used / transactions_allowance from #469

Brings just the AuthorizationExtent + boost-strategy parts of #469;
the period / two-slot / bytes_permanent surface is left out.

- AuthorizationExtent: gain transactions_used / transactions_allowance
  fields. authorize_account now uses its 3rd parameter to set/extend
  transactions_allowance; check_authorization saturating-bumps
  transactions_used on consume.
- BoostStrategy: a shared in_budget helper gates both the byte and tx
  axes; FlatBoost is in-budget at-cap (matching #469); ProportionalBoost
  scales by the tighter of the two remainders.
- AllowanceBasedPriority::validate increments transactions_used in the
  post-this-tx extent before computing the boost.
- v1->v2 migration carries the old transaction-count field over to the
  new transactions_allowance.
- Genesis config tuple becomes (account, transactions_allowance,
  bytes_allowance); preimage authorize_* always sets
  transactions_allowance = 1.
- Tests + bulletin-{westend,paseo} runtime tests updated for the new
  expected counters; new boost_tests::tx_axis_gates_boost_independently.

Out of scope (per #469): bytes_permanent, the two-slot
current/next grant model, for_period parameter, renew matching in the
boost extension, the People-Chain-aligned period model.

Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>

* transactions_used -> transactions

* Refresh: only extend expiry, do not reset consumed counters

Per review feedback (#448 r3163274071): resetting `bytes` and
`transactions` on refresh implicitly grants a fresh boost-tier window,
which conflates two operations under one extrinsic. Refresh now does
exactly what its name says — extend the expiration block. Holders who
want more capacity call `authorize_account` (additive on the unexpired
path).

Doc updates on `refresh_account_authorization`,
`refresh_preimage_authorization` and the `refresh_authorization` helper
make the split explicit. `authorize_account`'s doc also gets updated
to describe the new tx-axis additivity.

* Move back to 14

* Tighten priority constants for store / renew

- `AuthorizationPeriod` 90 days -> 14 days, aligning with the
  `LongTermStoragePeriodDuration` (2 weeks) on the People-Chain side.
- Drop the unused `SudoPriority` and `SetPurgeKeysPriority` constants;
  they were only chained into `RemoveExpiredAuthorizationPriority` and
  not wired anywhere else.
- `RemoveExpiredAuthorizationPriority` is now `TransactionPriority::MAX`
  so permissionless cleanups always outrank stores.
- `StoreRenewPriority` is `TransactionPriority::MAX / 4`. With
  `AllowanceBasedPriority` adding `ALLOWANCE_PRIORITY_BOOST` for
  in-budget signers, in-budget txs land just above over-budget ones
  without saturating `u64` and with plenty of headroom both above
  generic transactions and below the cleanup ceiling.

* Nit

* More nits

* Update design

* Nit

* Hard-side hardening: try_state invariants, validate-time guard, capacity events

- Doc: Hard Limit header reflects implementation (was "Proposed; not yet wired").
- try_state: assert PermanentStorageUsed == Σ bytes_permanent across Authorizations
  == Σ size across PermanentStorageLedger entries; cursor <= current_block + 1;
  no ledger entries before cursor; used <= MaxPermanentStorageSize.
- check_authorization_expired: reject remove_expired_* at validate when
  bytes_permanent > 0 (new AUTHORIZATION_HAS_PERMANENT_STORAGE = Custom(7)),
  mirrors the dispatch-time guard so pool ingress doesn't accept soon-to-fail txs.
- Events: PermanentStorageUsedUpdated { used } on every renew bump and every drain
  decrement; PermanentStorageNearCap { used, cap } on rising-edge crossing of
  PERMANENT_STORAGE_NEAR_CAP_PERCENT (80%). Single helper update_permanent_storage_used
  owns the read, write, and event emission.

9 new tests cover: remove_expired validate guard (account + preimage); renew/drain
event emission; rising-edge near-cap behaviour; happy-path + 4 corruption variants
of the new try_state invariants.

* Add note

* Clippy a nits

* Nit for bytes_permanent

* nit

* Simplify renewal hard cap (#486)

* feat: simplify

* Fix migration

* Drop expired authorizations

---------

Co-authored-by: Branislav Kontur <bkontur@gmail.com>

* Nit

* More nits

* Fix migration?

---------

Co-authored-by: Bastian Köcher <info@kchr.de>
Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
@franciscoaguirre
Copy link
Copy Markdown
Contributor Author

Closing in favor of #509

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