ABIs: derive StableEnum for all ContractAbi Operation/Response enums#6314
Draft
ma2bd wants to merge 4 commits into
Draft
ABIs: derive StableEnum for all ContractAbi Operation/Response enums#6314ma2bd wants to merge 4 commits into
StableEnum for all ContractAbi Operation/Response enums#6314ma2bd wants to merge 4 commits into
Conversation
Apply `#[derive(StableEnum)]` to every contract's `Operation` (and any `Response` that is an enum) across the example contracts, the in-crate `linera-sdk` ABIs (fungible, controller, wrapped-fungible), the fixtures used by the SDK integration tests, and the EVM bridge's `BridgeOperation` (which lives in `linera-bridge`, now a direct `linera-sdk` dependent — already transitive via `wrapped-fungible`). Format snapshots regenerated. Dead `scalar!(Operation)` calls in `amm` and `rfq` removed: the macro provides a JSON `ScalarType` impl that would silently fail because StableEnum's 4-byte ULEB128 variant tags are not JSON-deserializable; nothing in the codebase actually invoked them since `GraphQLMutationRoot` exposes individual fields rather than the whole enum.
The previous commit applied `#[derive(StableEnum)]` to `WrappedFungibleOperation`
(and other bridge-touching enums), which changes the BCS wire format: each
variant tag becomes a 4-byte ULEB128 encoding of a Keccak-derived `u32`, not
a sequential `uint8` index.
The `serde-generate` Solidity backend on crates.io (0.33.0) only emits
single-byte `uint8` discriminants, so the auto-generated `BridgeTypes.sol`
and `WrappedFungibleTypes.sol` would silently produce 1-byte tags that the
Wasm side would reject. While `linera-bridge`'s relay doesn't currently
*construct* `WrappedFungibleOperation` from Solidity, the codegen is run via
the `codegen` feature and we want the output to match Wasm's wire format
out of the box for future EVM-side callers.
Pin `serde-generate` and `serde-reflection` to the zefchain fork commit
8882df9 (added to `[patch.crates-io]`), which:
* widens the `choice` discriminant from `uint8` to `uint64`,
* emits each variant's BCS prefix as a precomputed `hex"..."` literal
(handles both sequential and non-contiguous tags), and
* decodes the prefix via `bcs_deserialize_offset_len` (ULEB128) instead
of a single `uint8` read.
Also fix `format_wrapped_fungible.rs`: trace `WrappedFungibleOperation` via
`tracer.trace_stable_enum_type` (which walks the enum variant-by-variant)
instead of `trace_type` (which probes contiguous indices and never
terminates on Keccak tags). Regenerate the snapshot accordingly, which is
what now drives the codegen to emit the correct stable tags.
`BridgeTypes.sol` is also regenerated; the discriminant widening is the
only material change for its (non-StableEnum) enums. Hand-written callers
(`FungibleBridge.sol`, `LightClient.sol`) compare `choice` against integer
literals so they remain compatible with the wider type.
`#[derive(StableEnum)]` references `serde` via the `linera_sdk::formats::__private::serde` re-export, so the four converted crates (event-emitter, event-subscriber, time-expiry, and the how-to-perform-http-requests example) no longer need a direct dependency on `serde`. cargo-machete flagged them.
* `evm_call_wasm_example_counter.sol`: this is a hand-written fixture
(not generated by `serde-generate`), so the codegen patch from the
previous commit doesn't reach it. Convert `CounterOperation`'s tag
from the old `uint8(0)` to the 4-byte ULEB128 stable tag
`BF D5 87 59` that matches `Keccak256("Increment")[..4]`, the format
emitted by `#[derive(StableEnum)]`. Without this, the REVM test
`test_evm_call_wasm_end_to_end_counter` fails with a Wasm trap on
Operation deserialization.
* `linera-sdk/tests/fixtures/Cargo.lock`: regenerate after dropping the
unused `serde` direct dep from event-emitter, event-subscriber, and
time-expiry. This is a separate workspace from the top-level one, so
its lockfile needs an independent refresh — the CI step that runs
`cargo test --locked` in that directory was failing.
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.
Motivation
Follow-up to #6309: now that
#[derive(StableEnum)]exists, switch everyContractAbioperation/response enum in the repo to use it. This locks each variant tag to a Keccak-derived 4-byte ULEB128 value, decoupling the wire format from declaration order so reorderings/insertions don't silently break consumers.Proposal
Apply
#[derive(StableEnum)](orStableEnumInCratefor code insidelinera-sdk) to everyOperationand enum-typedResponse:amm,call-evm-counter,counter-no-graphql,crowd-funding,ethereum-tracker,evm-bridge(vialinera-bridge),gen-nft,hex-game(bothOperationand theHexOutcomeresponse),matching-engine,non-fungible,rfq,social,task-processor,how-to/perform-http-requests.linera-sdkABIs:fungible::FungibleOperation+FungibleResponse,controller::Operation,wrapped_fungible::WrappedFungibleOperation.linera-sdkfixtures:contract-call,cost-tracking,publish-read-data-blob,event-emitter,create-and-call,time-expiry,event-subscriber.Other changes pulled in:
linera-bridgegets a direct (non-optional)linera-sdkdependency soBridgeOperationcan use the derive. This was already a transitive dep viawrapped-fungiblein therelayfeature.formatsmodule now traces the stable enum viatracer.trace_stable_enum_type::<T>(&samples)?(importingTracerExt), sinceTracer::trace_typecannot enumerate the non-contiguous Keccak tags.scalar!(Operation)calls inammandrfq.scalar!registers an async-graphqlScalarTypeimpl backed byserde_json, but StableEnum's serde impls use a numeric variant tag that serde_json cannot deserialize from a string variant name. Nothing in the codebase actually invokes them —GraphQLMutationRootexposes individual fields rather than the whole enum — so the impls are dead code that would silently break if exercised.Skipped because they have no enum to convert:
evm.rs(Operation =Vec<u8>),llm(Operation =()),meta-counter(Operation is a struct),track-instantiation(Operation =()).Test Plan
cargo check --workspaceandcargo checkon the examples workspace.formatsnapshot tests regenerated with the new Keccak tags and pass.linera-sdk'sformats::tests(includingstable_enum_round_trip) pass.cargo +nightly fmt --checkandcargo clippy --workspace --all-targetsare clean.Release Plan
Links
StableEnum#6309