feat: read-only contract inputs (integrate fuel-vm #1014)#3317
Conversation
PR SummaryHigh Risk Overview Adds a temporary Reviewed by Cursor Bugbot for commit 04b511d. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4f24aaf. Configure here.
Point the 9 fuel-vm workspace crates at the read-only-contract-inputs branch of fuel-vm (PR #1014) via [patch.crates-io]. Only the 0.66.4 tree is redirected; the legacy 0.56.0 tree is untouched, keeping the Cargo.lock change minimal. TEMPORARY: this whole [patch.crates-io] block MUST be removed and the fuel-vm-private dependency bumped to a released version once fuel-vm #1014 merges and is released. fuel-core cannot merge before then. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Consume fuel-vm's read-only contract inputs (PR #1014): a contract input without a matching contract output is now valid and denotes a read-only contract access. Support is automatic with the new fuel-vm version — there is no consensus parameter or chain-config flag to enable, and no fuel-core API surface changes. The executor already handles this correctly with no logic change: every root-writing / output-persisting path (compute_state_of_not_utxo_outputs, persist_output_utxos) is driven by the OUTPUT list, so a read-only input (which has no output) is never visited — no state/balance root is computed or rewritten for it, and its ContractsLatestUtxo pointer never moves. Adds executor end-to-end tests: - a read-only contract input executes and leaves ContractsLatestUtxo unchanged (no output / root produced); - a script that writes to the read-only contract reverts with a ContractIsReadOnly panic and preserves state. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4f24aaf to
3621b3c
Compare
Drive a running node through the client API to verify read-only contract access: - a contract input without a matching contract output can read the contract's state and returns the stored value; - the same read-only access can be repeated (nothing is mutated); - a call whose contract code writes (SWW) to the read-only contract is rejected with a ContractIsReadOnly panic; - the normal read-write path (contract input WITH output) still works. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Linked Issues/PRs
Depends on fuel-vm #1014 (read-only contract inputs) — not yet merged. This PR cannot merge until that lands and is released.
Description
Integrates fuel-vm's read-only contract inputs into fuel-core. A contract input without a matching contract output is now valid and denotes a read-only contract access: the VM executes the contract's code and reads its state/balances but panics
ContractIsReadOnlyon any mutation. Because a read-only contract's state/balance roots cannot change, no output slot is needed to commit new roots.Support is automatic with the new fuel-vm — there is no consensus parameter or chain-config flag to enable, and no fuel-core API surface change. Activation is coordinated by node/VM version.
Executor audit (the substance)
fuel-core master's executor is already structurally correct for read-only inputs, so no executor logic changed. Every root-writing / output-persisting path is driven by the output list, not the input list, so a read-only input (which has no output) is simply never visited:
compute_state_of_not_utxo_outputs— iterates outputs, matchesOutput::Contract, then looks upinputs.get(output.input_index)to recompute the balance/state hashes. A read-only contract has noOutput::Contract, so no root is computed or rewritten for it.persist_output_utxos— same output-driven shape, so itsContractsLatestUtxopointer is never moved.compute_inputs— the only input-driven path; it writes malleable fields from storage but touches no contract storage, so it's harmless for read-only inputs.The proven invariant is therefore: a read-only contract's persisted storage is untouched and no output root is committed for it. (Note: fuel-core does not preserve a caller-supplied input root —
compute_inputsoverwrites the input's root fields every block, and those fields are access-list commitments, not full contract merkle roots. The test asserts the persistedContractsLatestUtxois unchanged, which is the real executor-level guarantee.)Changes
Net diff: 5 files, ~253 insertions — mostly tests. No GraphQL / schema / chain-config changes are needed, because there is no new consensus parameter to expose.
[patch.crates-io]pointing the fuel-vm family at the PR branch (see Open items).Tests
Executor unit tests (
crates/fuel-core/src/executor.rs):read_only_contract_input_executes_and_does_not_rewrite_root— an input-without-output CALL to a read-only contract succeeds, produces noOutput::Contract, andContractsLatestUtxois unchanged.read_only_contract_input_write_panics_and_preserves_state— a script writing to the read-only contract reverts with aContractIsReadOnlypanic; state untouched.End-to-end integration tests (
tests/tests/read_only_contract.rs) — driven through a running node via the client API:read_only_contract_input__can_read_contract_state— a contract input without an output reads the contract's state and returns the stored value.read_only_contract_input__can_be_read_repeatedly— the same read-only access repeats, always returning the same value (nothing mutated).read_only_contract_input__rejects_state_write— a call whose contract code writes (SWW) to the read-only contract is rejected with aContractIsReadOnlypanic receipt.contract_input_with_output_still_allows_state_write— the normal read-write path (contract input WITH output) still works.All pass; the pre-existing
contracts_balance_and_state_roots_*suite (7 tests) still passes unchanged.Open items (why this is a draft)
[patch.crates-io]redirects the 9 fuel-vm-family crates to the fuel-vm PR branchfeat/read-only-contract-inputs. Must be removed andfuel-vm-privatebumped to a released version once fuel-vm Restarting node causesInvalidDatabaseVersionerror #1014 merges.InvalidDatabaseVersionerror #1014 merge + release.ConsensusParametersand the validity change rides the fuel-vm bump).ReqwestExtstarwars doctest fails to compile on master (CWD-relative schema path) — not touched here, flagged so it isn't mistaken for a regression.Checklist
Before requesting review
After merging, notify other teams
🤖 Generated with Claude Code