SWIP-067: Custody separation - #108
Open
0xCardiE wants to merge 22 commits into
Open
Conversation
Splits each fund-holding storage-incentive contract into a frozen custody core and a replaceable policy contract, and specifies the fork-migration protocol that policy replacement runs under. Consolidates the security thread from storage-incentives#310 and the migration thread from Andrew Macpherson's "Forking Swarm" into one proposal, on the basis that both stem from state and logic sharing a contract. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
storage-incentives#310 is an upgradeability proposal, not a security one; describe it that way rather than as "a security thread". Remove the claim that full redeployment cost the network ten days of downtime and half its target replication. The 2025 gap was a consequence of the old stake registry's pause being scheduled well after the client release, not a lower bound on migration cost. The v0.9.3/v0.9.4 evidence is kept, but recast as two scheduling failures that F3/F4 fix by construction, and the argument for Part 1 no longer leans on it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… fixes Correctness fix. The first draft put price ingestion in PostagePolicy. A batch's normalisedBalance is denominated in the issuing contract's outpayment accumulator, so a policy-side accumulator would rebase every batch on every policy replacement — wrong expiry and premature reserve eviction, once per upgrade instead of once per migration. The accumulator and normalised-balance arithmetic move into PostageAccounting, and the frozen-outpayment-model consequence is stated (C2.7, C4). Follow-on: `accrue` is gone. The core derives remaining balances itself, and `expire` is permissionless and self-verifying, so policy has no pot-accrual authority at all. C3.1 eligibility clock: StakingPolicy counts eligibility from min(depositBlock, preRegistrationBlock), so a mass re-staking event does not open a rolling participation trough. Staggering to avoid a gas spike lengthens the trough rather than fixing it. C3.2 accounts and nodes: deposits are per account, overlay mapping is policy-side. Fleet operations scale with accounts, withdrawal authority separates from the node signer, and the shared-account slashing question is recorded as open. F7 cutover typology: Type A (wire-breaking, single game ABI, no dual-mode) vs Type B (contract-only, dual bindings required). F7.1 requires any cutover needing a branch in consensus-critical computation to be Type A, since a dual-mode sampler is itself a dissent source. Old F7 renumbered to F8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds a Contents block and a "Normative requirements at a glance" table so an 880-line spec can be navigated and reviewed rule by rule. Fixes two defects introduced by the previous edit: a list item in C2.7 was orphaned below the accumulator discussion, breaking the list, and F8 still described a stake migration as "the ten-day outage" after that attribution was removed everywhere else. Prose: breaks the Abstract's Part 1 sentence, removes first-person hedging in C2.5, C5, Motivation and Rationale, and normalises cross-references to bare rule ids. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Factual corrections, checked against the deployed sources: - StakeRegistry declares no PAUSER_ROLE; pause() checks DEFAULT_ADMIN_ROLE and the OnlyPauser() error name is misleading. Corrected in Motivation and in the C5 residual-trust table. - remainingBalance clamps at zero; the quoted formula now says so. - Overlay derivation is network-scoped, not bound to the wire protocol — NetworkId is admin-mutable via changeNetworkId. - The round-306865 freeze multiple is not derivable from the freeze formula alone, so the unquantified claim replaces it. Contradictions: - Simple Summary and the C1 table called cores "no admin", which C2.5 explicitly forbids. Both now say the core has no admin power over the money it holds. - Roadmap stage 6 proposed extending POLICY_TIMELOCK, which C2.5 declares immutable and C2.7 gives no upgrade path to change. - C2.2 said policy "may assert an accrual" while C4 says accrual is not a policy primitive at all. The clause is gone. - F5 claimed a "deliberate exception" to F4 and then argued no second authorisation exists. It is a payment overlap, not an authority overlap, and the funding assertion moves back to open question 4. - F7 Type B required un-restarted operators to keep earning, which F2.1 makes impossible; it now says operators running a release that carries both bindings. - Backwards compatibility put an admin-gated migration function inside the core; the assisted path now goes through the ordinary fund() call. - The StakingCore invariant test subtracted totalSlashed from a balance that still holds it, since slashing burns in place. Unenforceable or dead requirements: - F3 required the outgoing redistributor to stop accepting commits a round early. Commit, reveal and claim all fall inside one ROUND_LENGTH, so a boundary-aligned cutover already orphans nobody and the bullet only created a dead round. The matching cutover test went with it. - C2.4's per-node slash cap is unenforceable once C3.2 makes the core account-scoped, and one constant cannot be both the per-node and the aggregate cap. Reduced to a single aggregate bound. - C2.4 windows were tied to ROUND_LENGTH, which is per-branch and replaceable; they are now core-owned block counts. - C2.6 guarded against policy state the core cannot hold; it now names the lock, which is the real mechanism. - C2.1 wrote a prohibition with RFC 2119 MAY. Consistency: slash/lock/depositOf take an account, not a node (C3.2); claimPot(amount) and withdraw(amount) match the interface; C2.5 is retitled for the pointers it actually governs; exit() is the two-step requestExit form everywhere. Cuts: the v0.9.3 incident was told twice in Motivation; the Rationale proxies entry duplicated Motivation; the accumulator and copyBatch explanations were doubled. Assorted self-congratulation and announcement sentences removed throughout. 920 lines to 870. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…d migration, windowed cutover Design changes, each forced by checking the spec against the deployed contracts: - Batch migration: PostageStamp has no path that releases an unexpired deposit (withdraw moves only the pot), so "assisted migration through ordinary fund()" was unimplementable. Replaced with a treasury-matched genesis: batch state seeded at deployment, matching BZZ transferred in, genesis sealed in the same ceremony, treasury reimbursed from the old pot as seeded batches expire — the final, announced use of withdraw(beneficiary). No unbacked creation path ever exists. - Core accounting: the C4 interface had no batch size, so the core could not compute normalised balances, conservation, or pot accrual. fund() now records depth, resize() replaces increaseDepth, and validChunkCount / lastExpiryBalance are core aggregates with the accrual identity from expireLimited written out normatively. Consequence: the expiry ordering moves back into the core — live-chunk accrual is only sound when the core can prove no expired batch is still counted, which requires knowing the minimum normalised balance. C2.7 names this the second-largest frozen-core risk instead of calling the tree a rebuildable policy-side index. - fund() call topology specified: creation is policy-gated (admissibility), exits and expiry are direct on the core; new ids bind to (originator, nonce) so announced ids cannot be front-run; seeded ids exist only before genesis seal. - StakingCore records firstDepositBlock, making the C3.1 eligibility clock computable. C3.2 resolved with a coverage requirement instead of an open question. - F3: exact-block multisig execution replaced by an execution window [activationBlock, activationBlock + EXECUTION_WINDOW), aligned to the outgoing game's round length; ROUND_LENGTH changes are Type A. C2.5 pointers are cancellable. F2.3 added: Cutover governance is liveness-only. - F5: wind-down must be pre-funded before cutover; a retired Redistribution never regains pot access. Resolves the F4 contradiction and the stranded-pot question. - Honesty fixes: C1 table no longer says "no admin"; Redistribution acknowledged as transient pot custodian on the claimPot path; the C2.4 pot bound described as capping acceleration, not stopping outflow below the honest rate; exit() named as withdrawable stake with EXIT_DELAY >= the freeze horizon; refundBatch named as a change to the storage promise whose introduction is a Type A cutover. Open questions cut from eight to five; resolved ones (wind-down funding, migration tail, shared-account slashing, stranded pot) folded into the spec. Concision pass throughout.
Use the queued withdraw/exit workflow instead of a parallel requestExit design, and keep the custody split of that contract.
Policy execute is not round-aligned; Redistribution freeze can delay a staking payout, admin pause cannot.
Refund forfeits a slice to the pot; postage genesis is a self-top-up plus withdraw from the stopped contract; a later pricing model is a new core.
Rejected alternatives belong with the problem statement, before the specification.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Read SWIP-67
Change the storage-incentive rules without moving user BZZ, and without leaving an admin path that can steal it.
What this gets us
PostageAccounting,StakingCore). No proxy, nowithdraw(address), nocopyBatch.RedistributionandPriceOraclestay unsplit — they hold no deposits and are redeployed as-is.Sequencing and parameters are in the SWIP.