Skip to content

contracts()/committedPath coupling to crates/bindings has no cross-lane oracle: dropped or retargeted binding artifacts go silently stale #205

Description

@thedavidmeister

Unit

script/lib/LibCopyArtifacts.sol (contracts() / committedPath()) together with crates/bindings/src/lib.rs.

Intent oracle

  • contracts() doc: "Contract artifacts that the rust crate consumes via alloy::sol!. Adding a new contract here also requires the rust crate to reference it."
  • committedPath() doc: "Path of the committed ABI copy that the rust crate reads at compile time."
  • .github/workflows/copy-artifacts.yaml claim: "build from committed sources and assert the committed abi artifacts are still current."

Violated property

Every artifact the rust crate consumes stays fresh. The coupling between the Solidity list/path and the rust crate's hardcoded concat!(env!("CARGO_MANIFEST_DIR"), "/abi/<name>.json") reads is maintained by hand with no oracle in either lane.

Verified repro (mutation probe, commit bba50a7)

Both mutations below survived the full rainix-sol suite (forge build + forge script script/CopyArtifacts.sol regen + forge test, all green):

  1. contracts() returning only ["IMetaBoardV1_2"] (dropping IDescribedByMetaV1): the copy script and the freshness test iterate the same shrunken list, so the orphaned crates/bindings/abi/IDescribedByMetaV1.json stops being refreshed and would go silently stale on the next ABI change. The rs lane also stays green: the stale file still exists and still compiles under alloy::sol!.
  2. committedPath() returning crates/bindings/abi/<name>.alt.json: writer and checker share the function, so copy and freshness both retarget to the new location and stay green while crates/bindings/src/lib.rs keeps reading the old .json forever.

State after AMT PR

The AMT coverage PR for this group adds literal pins (testContractsAreExactlyTheRustCrateBindings, testCommittedPathIsWhereTheRustCrateReads) that kill both mutants for the CURRENT list and path. The converse direction remains unchecked in any lane: a binding added in crates/bindings/src/lib.rs without a contracts() entry, or a path change on the rust side, produces no signal anywhere.

Triage framing (neutral)

Flagged, not adjudicated: maintainers may consider the residual risk acceptable (the list changes rarely), or may want a cross-lane check — e.g. a rust test asserting the set of files under crates/bindings/abi/ equals the set alloy::sol! consumes, or a generated manifest both lanes read.

Metadata

Metadata

Assignees

No one assigned

    Labels

    adversarialFound by adversarial review/mutation testingauditAudit finding; counted by the org health scan

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions