quantum phase 4: lock parity with tests, fixtures, docs, and CI#5
quantum phase 4: lock parity with tests, fixtures, docs, and CI#5eacet wants to merge 14 commits intofeature/eng-2099from
Conversation
- Pin composite cosigned RLP envelope against drift with golden-hex assertion in `detached_p256_cosigner_is_attached_to_composite_signature`: raw_len=2563, primary_only_len=2495, raw_hash=0x8c6ef4e5…. Any change to field ordering, scheme-byte placement, or cosigner layout flips the constants and fails the regression.
- Rewrite `README.md` Quantum-first (mirroring tempo-foundry layout): intro, Installation, and Changeset listing `cast send --quantum`, `cast quantum {bootstrap,add-key,remove-key,update-key-auth}`, `cast call` lifecycle rejection, `forge create --quantum`, and `forge script` paths. Upstream Foundry README preserved below.
- Rename `ci-tempo.yml` → `ci-quantum.yml` and rebuild job structure: `cargo fmt --all --check`, `cargo clippy --workspace --all-targets -- -D warnings`, Quantum-scoped package tests (`-p foundry-common -p foundry-primitives -p foundry-cli`), and Quantum fixture regression (`generated_fixture_matches_checked_in_phase0_example`, `detached_p256_cosigner_is_attached_to_composite_signature`). Inline comment documents why the broader workspace test is intentionally not gated (pre-existing upstream tests hit external networks). Retains QUANTUM_FOUNDRY_BASE_COMMIT and QUANTUM_HARNESS_COMMIT env metadata.
- Register `ci-quantum.yml` and `README.md` in `docs/dev/quantum-adapter-touchpoints.md`.
- Fix pre-existing `include_str!` path (`../../../` → `../../../../`) that pointed at `crates/testdata/` instead of repo-root `testdata/`; add missing `use alloy_network::ReceiptResponse as _;`; cluster of clippy fixes surfaced by the first-time `-D warnings` gate (const fn, use_self, redundant_clone, bool::then, format! inline args, unwrap-after-is_none pattern, too_many_arguments). No behavior changes.
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e7959f042b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex[agent] review |
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
5 similar comments
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@claude[agent] review |
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
5 similar comments
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c401b43647
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…otstrap cosigner guard)
|
@codex[agent] review |
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
3 similar comments
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
1 similar comment
|
@eacet The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
…igner, value, and call guard Addresses five findings from thoughts/shared/reviews/2026-04-20-foundry-fork-review-findings.md. Finding 1 (High) — `cast send --quantum` lifecycle fence bypass via name/ENS: the pre-check inspected the unresolved NameOrAddress, so a name that resolves to the KeyVault slipped past the "use cast quantum" guard and skipped the bootstrap gas-floor. Destination is now resolved against the provider up front, and the fence + bootstrap gas-floor block observe the resolved Address. Finding 2 (High) — `--auth` silently dropped on Quantum 0x7A: the Quantum envelope does not carry EIP-7702 authorization lists (`QuantumTxEnvelope::authorization_list()` returns None). `cast send --quantum` now rejects non-empty `--auth` with an explicit error instead of building a signed envelope that omits the auth list. Finding 3 (Medium) — bootstrapKey cosigner policy inconsistency: `cast quantum bootstrap` rejects cosigner artifacts but `cast send --quantum` to bootstrapKey() silently attached one, producing an envelope shape the dedicated lifecycle CLI refuses. `cast send --quantum` now rejects `--quantum.cosigner-artifact` when the call is bootstrapKey(), aligning both sanctioned entry points. Finding 4 (Medium) — `cast quantum` lifecycle --value claimed ignored but preserved: help text promised value was ignored, but builder.rs copied value straight into QuantumSingleCall. `submit_lifecycle` now rejects non-zero `--value` explicitly rather than silently forwarding ETH to bootstrapKey()/addKey()/etc. Finding 5 (Medium) — `cast call` guard rejected by selector alone: any unrelated contract whose selector collided with a KeyVault lifecycle selector was blocked on the read path. The guard now requires `to == QUANTUM_KEYVAULT_ADDRESS` in addition to the selector match. Drops `quantum_send_requests_bootstrap` and `quantum_destination_is_keyvault` helpers (no longer needed after the resolution-first rewrite). Adds regression `cast_call_allows_colliding_selector_on_non_keyvault_destination` pinning finding 5.
- envelope round-trip corruption: store semantic pubkey bytes in QuantumTxEnvelope so sign-verify-resubmit does not double-wrap init_primary_pubkey / init_cosigner_pubkey through the list-string framing - cast send bare-name bypass: reject bootstrapKey / unsupported lifecycle names even without '(...)' or '0x' selector prefix - cast call bare-name bypass: apply the same bare-name rejection on the read path - cast quantum: verify caller-supplied init_primary_pubkey matches derived pubkey - cast quantum: enforce --from vs --sender consistency before broadcast - forge create --quantum: explicitly reject --auth since the 0x7a envelope does not carry 7702 authorization lists - CI: include cast in the quantum package test gate
- cast send --quantum and shared signer now reject a caller-supplied init_primary_pubkey that does not match the key derived from the signing seed, matching the dedicated lifecycle command. - forge create --quantum rejects the legacy-fee path up front so --legacy or legacy-marked chains fail with a clear invariant instead of failing late in EIP-1559 signing. - Quantum envelope decoder now requires the reserved fee-payer placeholder fields to be empty lists; any non-empty value is rejected so decode→re-encode cannot silently change the tx hash. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Findings from thoughts/shared/reviews/2026-04-20-foundry-fork-review-findings.md: - cast call: resolve name/ENS destination before the KeyVault lifecycle fence so a name that resolves to QUANTUM_KEYVAULT_ADDRESS cannot bypass the fail-closed check and reach eth_call. - cast quantum: reject conflicting --primary-seed-file and --cosigner-artifact vs the shared --quantum.* forms instead of silently preferring one side. - cast quantum: reject --browser, Tempo, blob/EIP-4844, and --legacy flags up front on the lifecycle path; mirrors the guards in cast send --quantum and forge create --quantum. - cast send --quantum: reject --legacy up front; matches the early clear rejection already implemented on forge create --quantum.
cast send --quantum and forge create --quantum applied --blob / --eip4844 / --blob-gas-price through the shared TransactionOpts, but the Quantum transaction builder leaves blob setters on default no-op implementations, so those flags were silently dropped rather than producing a deterministic rejection. Mirrors the cast quantum lifecycle guard.
ML-DSA signatures are not recoverable; authoritative verification requires the sender's registered pubkey from KeyVault state, which only the Quantum node can resolve. `SignerRecoverable` for `QuantumTxEnvelope` therefore returns the declared sender, matching the precedent set by non-ECDSA envelopes like `OpTxEnvelope::Deposit`. Add a comment block that makes the rationale explicit, plus a cheap structural guard that rejects envelopes with an empty `sender_sig` so trivially malformed inputs cannot masquerade as recovered signers on consumers such as `cast decode_raw_transaction` and Anvil mempool admission. Cover the guard with a focused unit test. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a quantum-foundry install flow that coexists with upstream Foundry: foundryup and install scripts default to ~/.foundry-quantum/ (not ~/.foundry/), foundryup -U self-updates from multivmlabs/quantum-foundry, and --network quantum points at the fork's GitHub Releases. Extend the binary-download fast-path to accept multivmlabs/quantum-foundry and tempoxyz/tempo-foundry as known release repos. Document the install path in the root README and foundryup README, add a Devnet section with the public RPC, chain ID, and explorer, and demote "build from source" to a collapsible for contributors and unsupported platforms. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ignore Fixes `deny / cargo deny check` on PR #5: - GHSA-xphw-cqx3-667j: thin-vec 0.2.15 has a double-drop on panic during `IntoIter`/`clear`; bump to 0.2.16 via `cargo update`. - RUSTSEC-2025-0141: bincode is no longer in the dependency tree, so the ignore entry now fails `advisory-not-detected`. Removed.

Motivation
Solution
PR Checklist