Repository navigation
feat(cli): derive the accounts drive layout from the built machine - #551
brunomenezes wants to merge 12 commits into
Conversation
…om the built machine
🦋 Changeset detectedLatest commit: 740fabb The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Coverage Report
📁 File Coverage (20 files)
|
tuler
left a comment
There was a problem hiding this comment.
Thanks for this. I'm closing #547 in favor of this PR. The test work here is what makes the difference, and it's worth keeping. My main disagreement is with two premises in the config design. Most of the extra source code serves those premises, so dropping them should shrink the PR considerably.
Keeping [withdrawal.config]
We're in alpha, and this key isn't a good one to preserve. [withdrawal.config] reads as a subtable of a [withdrawal] table that doesn't otherwise exist. The compatibility also doesn't hold in practice:
- Existing configs break anyway. An existing
[withdrawal.config]is rejected unless a drive is marked, so every user has to edit theircartesi.tomlregardless. - Old values fail the new check. The CLI requires
cartesi-machine ^0.21.0, and 0.21.0 is where the emulator started placing drives just past RAM (machine-emulatord573f1b8). Start indexes computed for the old layout (1 << 55plus a1 << 52stride) no longer match the build. The changeset says an existing[withdrawal.config]"keeps working once its drive is marked", but in practice it won't.
I'd remove it. The design proposed in #516 had [withdrawal] hold only optional overrides: guardian, withdrawal_output_builder, and the account size. When someone still writes [withdrawal.config], the CLI should fail with a message explaining how to migrate.
Overriding accounts_drive_start_index and log2_max_num_of_accounts
I couldn't find a use case for writing these by hand. Because they must equal the derived values, writing them never changes what gets deployed; they only add a check.
- Pinning the layout: any rebuild changes the template hash and needs a new deployment, which derives the values again, so there's nothing to protect.
- Accounts at an offset: that would need a different value, which the match check forbids. The node's proof tool also requires an exact start.
- Old emulator layouts: can't happen, since
^0.21.0is required. - An independent check in tests: this is the only current use, in
recovery.test.ts. The test can compare its own calculation fromconfig.jsonagainst the CLI's printed or derived config, without a user-facing key.
I'd reject both keys with "derived from the built machine, remove it".
This also makes the bigint work unnecessary: parseRequiredBigInt, the global integersAsBigInt: "asNeeded" TOML option, the hand-built JSON in stringifyWithdrawalConfig, and the precision changeset. The derived index is the start divided by the size, and resolveAccountsDriveLayout already rejects any start above 2^53, so the index can't exceed 2^53. A large value can only come from the user, and it then fails the match check. It was a real bug on the base branch, which passes the written value straight through, but it goes away once the value is derived.
Smaller points
- Account size.
account_sizeandlog2_leaves_per_accountare two keys for one value and need a check that they agree. I'd keep onlyaccount_size, which is the friendlier one. - Fork requiring
guardian. A fork still has the devnet accounts funded, and the node still signs with the devnet mnemonic, so the default guardian works there. Onlywithdrawal_output_buildertruly has no default on a fork. - Changesets. There are four, all marked patch, for a feature. Consider a single minor one, and drop the claim that existing configs keep working.
- Fractional sizes. Something like
accounts_drive_size = "1.1Ki"reachesBigInt()and throws a rawRangeErrorinstead of a config error.parseSizeshould require an integer number of bytes. - Drive size parser. The
sizefix indb093ad("4Mi"meant 4 bytes) is a real bug fix that stands on its own. It could land as a separate PR, so it isn't held up by the node release.
What's good here
- Tests. The label-based test app, which can only withdraw if the derived layout is correct, and the 5 layouts proven end to end, including the DEX-like nvram image. This is what was missing.
- The nvram proving gap. Getting the machine-tool fix into rollups-node
next/2.0, and skipping the nvram layouts until it's released. - Guardian default. Devnet account 1 means the guardian doesn't compete for nonces with the node signer when
cartesi forecloseruns. - Account size check. Rejecting a non-32-byte account size with the devnet output builder.
- Rebuild from the run menu. Re-reading
cartesi.toml, and deriving the new config before undeploying.
Summary
Derives the accounts-drive part of the emergency withdrawal configuration from the built machine, as proposed in #516. A raw drive or nvram marked
accounts_drive = trueturns emergency withdrawal on. The CLI readslog2_max_num_of_accountsandaccounts_drive_start_indexfrom.cartesi/image/config.json, so developers no longer compute them, and a value that no longer matches the machine can't be deployed without an error.The change is additive.
[withdrawal.config]keeps working, every key in it becomes optional, and layout keys written by hand are checked against the build. It's an alternative to #547, which removes[withdrawal.config]. Both use the same marker syntax.Warning
The last commit,
wip(temp): build the SDK with the rollups-node next/2.0 nvram fix, is temporary and blocks the merge. The machine-tool in rollups-nodev2.0.0-alpha.13can't prove an accounts drive kept in an nvram;next/2.0(e9da54b2) can. This commit builds the PR's SDK with that unreleased node and runs the nvram layouts against it. Once a rollups-node release ships the fix: drop the commit, unskip the nvram layouts, and bump the SDK's node version.How it works
Features
accounts_drive = truegoes on a raw flash drive (builder = "empty",format = "raw") or on an nvram.accounts_drive_sizekeeps the accounts at the beginning of a larger drive, for applications that keep their own state after them, like a DEX.[withdrawal.config]and all its keys are optional.log2_max_num_of_accountsandaccounts_drive_start_indexare derived.buildandrunfail, showing both values and the likely cause.account_sizein bytes.log2_leaves_per_account, which still works but must agree with it.guardiandefaults to devnet account 1. It's separate from the node's signer, so the two don't compete for nonces, andcartesi foreclosestill finds it in the node's mnemonic.withdrawal_output_builderdefaults to the devnetTestUsdWithdrawalOutputBuilder.cartesi buildprints the layout:[withdrawal.config]value, marked as default, derived, or matchingcartesi.toml;--withdrawal-config.cartesi runderives the config before every deploy.cartesi.tomland derives the config before undeploying the running application.Fixes
accounts_drive_start_indexlost precision above 2^53. The node reads it as auint64. It's now abigintall the way fromcartesi.toml(an integer, or a quoted decimal or hex string) to the--withdrawal-configJSON.sizeof"4Mi"meant 4 bytes. Drivesizeandextra_sizenow use the nvram size parser:Refactoring
parseSize. Thebytesdependency is removed.src/withdrawal.ts.resolveWithdrawallocates the accounts drive and derives the config in one place, for bothbuildandrun.stringifyWithdrawalConfiginexec/rollups.tswrites the node's JSON.tests/integration/devnet.ts: devnet accounts, deposits, building, starting and stopping an application, and waiting for an accepted epoch.recovery.test.tsnow runs on the same test application as the layout tests. The olderc20-withdrawalfixture is removed.CI and tooling
These are temporary, in the
wip(temp)commit only:packages/sdk/temp/fetch.shdownloads thenext/2.0build artifact, checks it against GitHub's published sha256, and extracts the two.debfiles.Dockerfileinstalls those debs, checking their pinned sha256.cli.yamlcallssdk.yaml, which builds and pushesghcr.io/cartesi/*:pr-<number>. The tests then run against those images throughCARTESI_TEST_SDKandCARTESI_TEST_RUNTIME_VERSION.Verification
Full suite, unit and integration:
0.12.0-alpha.44, without the temporary commitdocker.test.ts, sqfs drive).tsc,biome ciand the unit tests.withdrawal-layouts.test.ts:memoryrange,readmmap/writemmap). Itscartesi.tomlonly marks the drive, so it can only withdraw if the derivation is correct.0before and as the full amount after.alpha.13these fail in the machine-tool withaccounts drive not found in stored machine. Withnext/2.0they pass.accounts_drive_start_index: fails the build.recovery.test.tsruns on the same application. It writes all five keys by hand, working out the layout from the built machine'sconfig.jsonindependently of the CLI, and they pass the CLI's check against the build.