PLT-457: StorageRWv1 contract + pinned ABI + binding - #42
Conversation
Add a mapping-backed StorageRWv1 contract (read/write/rmw over a caller-selected slot, with a trailing bytes calldata pad for tx-size variation) plus its generated Go binding. read is a state-touching (non-view) tx so the SLOAD is real. recordcount/keyspace is purely generator-side (no baked array size), so the keyspace resizes with no redeploy. Versioned name so a v2 can coexist. Added alongside ERC20Conflict (deprecate-don't-delete); no scenario/distribution wiring (deferred to PLT-461/465). solc 0.8.19, abigen pinned to the repo's go-ethereum version. go build ./... green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR SummaryMedium Risk Overview The Makefile now pins solc 0.8.19 (sha256-verified download), EVM A new Bindings Check GitHub Actions workflow runs Reviewed by Cursor Bugbot for commit cd43b29. Bugbot is set up for automated code reviews on this repo. Configure here. |
read()'s `readAccumulator += store[slot]` used checked math: with caller-controlled write values it could overflow and revert (Panic 0x11). Since the accumulator is monotonic and unrecoverable, the first overflow would brick every subsequent read permanently and silently collapse goodput. Wrap it in unchecked so every tx keeps succeeding at constant gas. Wrap rmw's increment the same way for gas-profile parity (overflow there is unreachable, but the result is never asserted on). Add `uint256 public constant VERSION = 1` so downstream consumers can pin to v1 once StorageRWv2 coexists on a persistent chain. Regenerated the Go binding (solc 0.8.19, abigen v1.16.1): adds the VERSION getter (0xffa1ad74); read/rmw/write selectors unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Expert review — converged (no remaining concerns)Reviewed by independent specialists (not the authoring agent), two rounds:
Scenario/factory wiring is intentionally out of scope (deferred to PLT-461/465); the contract + pinned binding are what this ticket delivers. |
generate-bindings rebuilds only the .sol->abi/bin->binding chain (no scenarios/factory). install-abigen pins abigen to the go.mod go-ethereum version for reproducible output. check-bindings regenerates and fails on diff. New bindings-check workflow runs it on PRs to main. Verified: regenerating reproduces the committed StorageRWv1.go byte-identically (macOS solc -> linux solc-static-linux, abigen v1.16.1). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Reviewed by Cursor Bugbot for commit aa93ebf. Configure here.
- check-bindings: force clean rebuild via `make -B generate-bindings` so Make's mtime logic can't skip regen (CI checkout can leave a committed binding newer than rebuilt .abi/.bin); `git add -N` makes the gate also fail on untracked NEW bindings; print full diff (not --stat) on failure. - Pin solc 0.8.19 by sha256 (SOLC_SHA256), verified after download in both setup-node and the CI step. Hash cross-verified against the official Solidity release index (binaries.soliditylang.org list.json). - bindings-check.yml: add top-level `permissions: contents: read`. - Fix `make help` dash-column alignment (pad to 17 chars). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
check-bindings reset the index for $(BINDINGS_DIR) regardless of drift outcome so a local run leaves git status untouched (it previously left `git add -N` intent staged). solc downloads in setup-node and CI now use `curl --fail` so an HTTP error fails loudly instead of writing an HTML error page that only the hash check would catch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pin the solc compile target explicitly and strip the CBOR metadata hash. Why paris: it is solc 0.8.19's highest supported target (its implicit default), so pinning it makes that default a written invariant. A future solc bump can no longer silently emit newer opcodes (PUSH0/MCOPY/TSTORE) and shift the bytecode/gas surface. paris is a strict subset of Sei's Cancun/Pectra-era forks (paris ⊂ Sei), so paris-targeted bytecode is unconditionally safe to deploy; runtime gas is set by the chain's active fork regardless of compile target, so the target never distorts the load-benchmark measurements. Why --metadata-hash none: strips the trailing CBOR IPFS metadata hash (which embeds source paths), making bytecode reproducible across repo paths / build hosts and slightly smaller. It does not touch the ABI or function selectors. Regenerates all 6 bindings: only the Bin (bytecode) field changes — the metadata trailer drops from a264...0033 to the minimal a164...000a form. ABI is byte-identical across all contracts; StorageRWv1 selectors (read/rmw/write/VERSION) are signature-derived and unchanged. check-bindings and go build pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Systems review — APPROVE (+ EVM-target decision, researched)Final systems-engineer pass: APPROVE, no blocking items. Two strongly-recommended solc-flag hardenings (the angle security/idiom structurally don't cover — what the compiler stamps into the deployed artifact) are now applied:
Both flags regenerate all 6 bindings — verified ABIs are byte-identical (only bytecode changed) and all selectors are preserved. Deferred (not now): matching Cancun via a solc bump (≥0.8.24) buys nothing measurable for the current SLOAD/SSTORE contracts; un-defer only if we add a transient-storage ( |

Implements PLT-457 — the contention contract for the sei-load workload modeler. Contract + binding only; no scenario wiring (PLT-461/465).
What
generator/contracts/StorageRWv1.sol— mapping-backedstore;write/read/rmwover a caller-selectedslot.recordcount/keyspace is purely generator-side (no baked array size) → resize with no redeploy.readis a state-touching tx, notview(folds the loaded value intoreadAccumulatorso the SLOAD can't be elided and the SUT pays for it).bytes calldata _padon each function lets callers vary tx size independently of the key.generator/bindings/StorageRWv1.go, committed.Locked decisions honored
Mapping-backed;
readnon-view; versionedStorageRWv1; added alongside ERC20Conflict (untouched). Function selectorswrite(uint256,uint256,bytes)/read(uint256,bytes)/rmw(uint256,bytes)are the pinned one-way door.Toolchain
solc 0.8.19 (repo-pinned) + abigen pinned to the repo's go-ethereum version.
go build ./...passes. On CI/Linux the binding regenerates viamake setup-node && make generate.Decision brief:
designs/sei-load-workload-modeler/PLT-457-storagerwv1-contract.md(sei-protocol/bdchatham-designs).🤖 Generated with Claude Code