Skip to content

CVLR: set up SDK 3 and solana-program 1.18 targets, on platform-tools v1.53 - #299

Open
ericeil wants to merge 5 commits into
masterfrom
eric/cvlr-sdk3
Open

ericeil wants to merge 5 commits into
masterfrom
eric/cvlr-sdk3

Conversation

@ericeil

@ericeil ericeil commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Point preflight at a Solana program on the split solana-* 3.x crates and today it refuses it: master's reference set has one platform generation, solana-program 2.x, and builds with platform-tools v1.43, whose cargo cannot parse the edition-2024 manifests the SDK 3 crates use. This PR gives each Solana platform generation its own reference set, picks the one the target is on, and builds every generation with platform-tools v1.53 under one host toolchain.

Generation cvlr-solana Specializations
solana-* 3.x 0.6.0 cvlr-solana-stake, cvlr-spl-token 0.6.0
solana-program 2.x 0.5.1 cvlr-solana-stake, cvlr-spl-token 0.5.1
solana-program 1.18 0.4.6 none

All three are on the cvlr 0.6.2 core, and all are published on crates.io.

Why is this needed?

A chain crate works only with its own generation's AccountInfo, so one CVLR line can't serve every target. Master refuses anything that isn't on 2.x, including the Solana stake program, which is on SDK 3. Each step below came out of running that program or a large Anchor program through preflight:

  • v1.53. v1.43's cargo is 1.79, and SDK 3 lockfiles are full of edition-2024 manifests. v1.53's rustc 1.89 names the allocator shims __rustc::__rust_alloc. So the never-inline directives in cvlr_inlining_core.txt and the frames SOLANA_TRACE drops now accept both spellings.
  • One host toolchain. HOST_RUST_TOOLCHAIN replaces the target's rust-toolchain.toml for every cargo command. That pin exists for the project's own builds. Following it fails wherever the runtime user can't write the rustup home, because a toolchain that isn't installed can't be installed. The program itself is compiled by the platform tools' rustc either way.
  • An SBF check, not a host check. The preflight gate runs cargo check for the SBF target with the platform tools' cargo, rustc and C tools (sbf_check). The host can't always compile what the chain build does: from rustc 1.77, u128 is 16-byte aligned on x86_64, so a program's own layout assertions can fail there and hold on SBF.
  • The never-inline blanket stays on the monolith. Master widened ^solana_program::.*$ to every split crate. A function that isn't inlined is external: it returns an arbitrary value and writes nothing. So Sysvar::from_account_info never returned Ok, and the stake program's interface crate became an oracle. Unwidened, the split crates are analyzed.
  • No allocator conflict. The stake program calls entrypoint! unconditionally and has no no-entrypoint feature. Its allocator then writes the heap at a fixed address, and a rule that also allocates fails with Prover error 3309. The scaffold enables custom-heap from certora for such a package.
  • Smaller fixes. The manifest editor can now edit a [package] table split by another table, as stake's is. $CERTORA_PLATFORM_TOOLS_ROOT is honoured and passed to cargo certora-sbf explicitly, because the launcher drops it. Preflight's cargo metadata fetches into the confined run's cargo home, so the gate's warm step doesn't fetch it again, with the cold-fetch timeout.

Where to start

  1. The reference sets. composer/spec/cvlr/reference.py: SOLANA_V3, SOLANA_V2, SOLANA_V1_18, and generations(), which replaces reference_for(). The SDK 3 path aliases were read off the stake program's symbols, tests/data/stake_sdk3_sbf_symbols.txt. GitCommit and CommitPatch let a release be read from its commit before it is on crates.io. No shipped set uses them today; the scaffold tests do.
  2. Picking the generation. select_generation and _read_platform in composer/spec/cvlr/scaffold.py, and select_package in preflight.py. The evidence is the resolved graph's normal dependencies of the target's library (Workspace.linked_into in composer/cargo/metadata.py). A copy only a sibling member or a dev-dependency pulls in is never compiled with the harness. Below 2.0, a generation is keyed on major.minor, because Solana's 1.x minors broke compatibility.
  3. The toolchain. PLATFORM_TOOLS_VERSION and HOST_RUST_TOOLCHAIN in composer/spec/cvlr/conf.py. Then CargoSession.toolchain / check_with (HostCheck | CrossCheck) in composer/cargo/session.py, and sbf_check in composer/cargo/sbf.py. The build script carries the toolchain to the Prover, which runs it with its own environment. SandboxConfig.env carries it into a confined child.
  4. Tests. tests/test_cvlr_reference.py, test_cvlr_scaffold.py (generation selection, the 1.18 and SDK 3 cases, custom-heap, the commit patches), test_cvlr_env_paths.py (each alias against real symbol tables) and test_cvlr_plumbing.py (the toolchain and the SBF check). None needs a toolchain or the network.

The vault and examples tests that build for SBF (tests/test_cvlr_preflight.py, tests/test_cvlr_end_to_end.py) are expensive and were not run for this PR. They need platform-tools v1.53 installed, and the end-to-end test needs SOLANA_EXAMPLES_REPO. Run them by hand before merging.

🤖 Generated with Claude Code

ericeil and others added 2 commits November 5, 2026 14:40
… v1.53

The reference set becomes one per platform generation: cvlr-solana 0.6.0
for the split solana-* 3.x crates, 0.5.1 for solana-program 2.x, and
0.4.6 for 1.18, all on the cvlr 0.6.2 core. select_package reads which
generation the target's library links (Workspace.linked_into, from the
resolved graph's normal dependencies) and returns that reference set,
or refuses a target on none of them.

- reference: SOLANA_V3 / SOLANA_V2 / SOLANA_V1_18 and generations();
  the SDK 3 path aliases, read off the stake program's symbols, plus
  anchor-lang-error's for Anchor 1.1+. The never-inline blanket
  solana_program::.* is no longer widened to the split crates: widened,
  it made sysvar reads and interface crates external. GitCommit reads
  an unpublished release from its commit through [patch.crates-io].
- scaffold: select_generation; 1.x generations are keyed on
  major.minor; a package that calls entrypoint! with no no-entrypoint
  feature gets custom-heap, which keeps the SDK's allocator out.
- Platform-tools v1.53 (rustc and cargo 1.89): v1.43's cargo cannot
  parse an edition-2024 manifest. rustc 1.89 names the allocator shims
  __rustc::__rust_alloc, so the inlining directives and the trace's
  dropped frames accept both spellings.
- Every cargo command runs with one host toolchain (HOST_RUST_TOOLCHAIN)
  rather than the target's rust-toolchain.toml, and the platform tools
  are found at $CERTORA_PLATFORM_TOOLS_ROOT when it is set.
- The preflight gate checks the SBF target with the platform tools'
  cargo and compiler (sbf_check), since the host cannot always compile
  what the chain build does.
- Preflight's cargo metadata reads fetch into the confined run's cargo
  home, with the cold-fetch timeout.
- The manifest editor edits a table split by another table, as stake's
  [package] is.
- BASE_CONF turns on -solanaTACSoundSignedMath and pins
  prover_version: master until the Prover releases the fixes that make
  it affordable with -solanaTACMathInt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-solanaTACSoundSignedMath needs Prover fixes that are on its master
branch only, so it came with a prover_version: master pin. Neither is
part of building SDK 3 targets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ericeil added a commit that referenced this pull request Nov 5, 2026
Both were cut from eric/solana-staging onto master on 2026-11-05. Record
their sizes and what each cut took or left against the plan. P1b
carries the sound signed math flag and its prover_version pin, so the
older sections that held them on the branch now say they are
superseded; C7b reads generations(), which replaces reference_for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ericeil added a commit that referenced this pull request Nov 5, 2026
#299 no longer carries -solanaTACSoundSignedMath or the prover_version
pin. Both stay held for a later PR, which also takes the heap bound.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ericeil and others added 3 commits November 5, 2026 16:57
- dialect_for keeps an alias only when the package links its crate, not
  when any member or dev-dependency resolves it. Otherwise an Anchor 1.0
  program beside a sibling that resolves anchor-lang-error had its error
  paths respelled, and its handlers failed with [3003].
- The generation refusal no longer calls every chain Solana.
- The preflight cargo-home comment says only registry crates are shared
  with the warm step; git sources fetched by the host cargo are not.
- entrypoint! is found across a line break and not inside comments.
- Print full commit revs; rename CrateRelease.unpublished to commit;
  tighten comments that told the story of a run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The commit-pin loops never ran, and the reference, sbf, and metadata
tests copied the fields and dicts they called. What remains is the
decision each of those was for: one core across generations, the SDK 3
crates at one version, which platform-tools tree the check uses, and
which dependencies the library links.
Describe the code as it is. Drop release-history notes and comments that
only restate a name. The constraints stay: generation selection, the
monolith blanket, custom-heap, the host toolchain, and the platform-tools
pin.
@ericeil
ericeil marked this pull request as ready for review November 6, 2026 22:27

This branch has not been deployed

No deployments
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.

1 participant