committee-controlled contract upgrades (WIP — review the architecture, scenarios still need mechanical updates) - #121
Draft
zjma wants to merge 1 commit into
Draft
committee-controlled contract upgrades (WIP — review the architecture, scenarios still need mechanical updates)#121zjma wants to merge 1 commit into
zjma wants to merge 1 commit into
Conversation
…oting Contract upgrades are no longer signed by a single admin private key. The @ace address becomes an Aptos resource account whose SignerCapability lives inside network::SignerStore; the only path to producing a signer for @ace is through the network module, gated by the existing voting framework. ## Sealed bootstrap 1. Phase A: admin EOA calls 0x1::resource_account::create_resource_account with optional_auth_key = admin's auth_key → resource account materialized at X = createResourceAddress(admin, seed), signable by admin's SK. 2. Phase B: admin runs `aptos move publish --sender-account X` 11 times, landing all packages under X. 3. Phase C: admin calls network::start_initial_epoch(x, admin_addr, ...). This retrieves the SignerCapability via 0x1::resource_account::retrieve_resource_account_cap (which atomically burns X's auth_key to zero), stores it in SignerStore, and builds the initial State. After this tx, admin's key has no signing power for @ace. ## Upgrade flow - network::new_upgrade_proposal(proposer, package_name, metadata, code, ...) is callable by current committee members + admin EOA. Admin retains a non-voting proposer slot (parallel to ProposedEpochConfig slot model). - State.upgrade_proposals (n+1 slots) point at separately-stored UpgradeBlob objects holding the compiled bytecode (kept out of the State BCS view to avoid bloating the per-node state fetch). - network::touch() iterates upgrade_proposals, touches their voting sessions, and on the first approved proposal calls code::publish_package_txn with a signer minted from SignerStore. All upgrade slots are cleared on execute so losers can re-propose. ## Components touched - contracts/network: SignerStore.extend_ref:ExtendRef → signer_cap:SignerCapability; start_initial_epoch refactored; UpgradeBlob/UpgradeProposalRef/UpgradeProposalView + StateViewV1 + new view fns (current_epoch, state_view_v1_bcs); new_upgrade_proposal entry; try_execute_upgrade in touch(). StateViewV0 unchanged → SDK/worker BCS mirrors stay compatible with no V1 migration needed. - cli/deploy-contracts.ts: createAceResourceAccount + deriveAceAddr; publishMovePackage threads --sender-account; deployContracts now creates the resource account first and returns the resulting aceAddr. - cli/commands/deployment-new.ts: seed prompt with sensible default (ace-${network}-${tag}); start_initial_epoch call updated to sender=aceAddr + admin_addr arg; profile persists bootstrapSeed + aceAddr. - cli/commands/update-contracts.ts: rewritten. No longer publishes; instead builds each package via `aptos move build-publish-payload`, parses the resulting JSON, and submits new_upgrade_proposal as the admin EOA. Prints next-step instructions for committee voting. - cli/config.ts: TrackedDeployment gains bootstrapSeed: string. - scenarios/common/helpers.ts: createAceResourceAccount helper; deployContracts returns the resource account address; submitTxn gains optional sender override. - scenarios/common/ace-network.ts: AceNetworkOptions gains aceAddr; sealed start_initial_epoch call. - scenarios/run-local-network-forever.ts: switched to the new flow as the canonical local-test entry point. ## Known incomplete (follow-up commits in this PR) ~16 individual CI scenarios still call deployContracts/start_initial_epoch with the old signatures. They typecheck (submitTxn.args is any[]) but will runtime-fail in CI until the same mechanical pattern is applied: const aceContract = adminAccount.accountAddress.toStringLong(); → const aceContract = await deployContracts(adminAccount, [...packages]); + add adminAddr as the first arg to start_initial_epoch + add sender: aceContract to its submitTxn call. Local testing via `pnpm run-local-network-forever` or `ace deployment new` works. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
SignerCapabilitylives insidenetwork::SignerStore. Admin's private key has no signing power for @ace after bootstrap.0x1::resource_account::create_resource_account(admin pubkey as auth_key) →aptos move publish --sender-account X× 11 →network::start_initial_epoch(x, admin_addr, ...)which atomically burns admin's signing path and installs the cap.network::new_upgrade_proposal(committee members + admin EOA can submit; admin is non-voting) → existingvoting::framework →network::touch()executescode::publish_package_txnvia theSignerCapabilityonce threshold is reached.Design choices worth your eyes
UpgradeBlob(bytecode + metadata) lives at sticky-object addresses, not insideState.State.upgrade_proposals: vector<Option<UpgradeProposalRef>>only stores pointers. This avoids bloating the per-nodestate_view_*_bcsfetch with 1–2 MB of bytecode.touch()clears every slot.StateViewV0unchanged — the v0 view fn still returns exactly the original BCS layout, so the TS SDK + Rust worker mirrors keep working without changes. Added a newstate_view_v1_bcsfor future consumers who want upgrade visibility (no consumer in this PR).current_epochview — added a tiny#[view] public fun current_epoch(): u64so the CLI doesn't have to parse full state just to fill intarget_epoch.start_initial_epoch. You called this out as misplaced threat modeling (correct): during bootstrap, admin's key is root of trust by necessity. The "window" doesn't matter; what matters is that after bootstrap the key has zero power. So no atomic script-tx merge.Known incomplete in this PR
~16 individual CI scenario files still call
deployContracts/start_initial_epochwith the OLD signatures. They typecheck (becausesubmitTxn.argsisany[]) but will runtime-fail in CI until each is updated with the mechanical pattern:and
(await submitTxn({ signer: adminAccount, + sender: aceContract, entryFunction: `${aceContract}::network::start_initial_epoch`, - args: [<nodes>, threshold, dur], + args: [adminAccount.accountAddress.toStringLong(), <nodes>, threshold, dur], }))Affected files:
test-solana-example.ts,large-committee.ts,test-fault-tolerance.ts,test-custom-flow-aptos.ts,full-happy-path.ts,test-network-protocol-shortpk.ts,test-auto-epoch-change.ts,test-network-protocol.ts,test-custom-flow-solana.ts,cli-testbed.ts, plus the dkg/dkr/vss protocol tests if they drive a full network.Already updated (and sufficient for local end-to-end testing):
scenarios/common/helpers.ts— sealed bootstrap helpersscenarios/common/ace-network.ts— driver for access-failure scenariosscenarios/run-local-network-forever.ts— the canonical local-test scenariodeploy-contracts.ts,commands/deployment-new.ts,commands/update-contracts.ts,config.tsTest plan
pnpm run-local-network-foreverbrings up a committee, epoch 0 starts, secrets get generated. Verifies the resource-account bootstrap end-to-end.ace deployment newwizard creates a resource-account deployment on localnet/devnet, seals admin, prints the operator blob withaceAddr≠adminAddr.ace deployment update-contracts --version X.Y.Zsubmits 11new_upgrade_proposaltxns;ace proposal lsshows the open voting sessions; committee members can vote withace vote <session_addr>.aptos move testincontracts/{group,vss,...}— pass (verified).aptos move compileforcontracts/network— pass (verified).npx tsc --noEmitincli/— pass (verified).npx tsc --noEmitinscenarios/— pass (verified, but per the "Known incomplete" note above, runtime semantics are wrong for the unmigrated scenarios).🤖 Generated with Claude Code