Skip to content

LibSnapshot already implements the release freeze in Solidity; which layer owns it? #305

Description

@thedavidmeister

rain-sol-codegen's LibSnapshot already implements the release freeze, in Solidity, callable from a build script. #302 is reimplementing it in Rust, and two repos hand-roll it in bash. This asks which layer owns it before #302 lands.

What LibSnapshot already provides

rain.sol.codegen/src/lib/LibSnapshot.sol:

  • deployTag(Vm)[package].version from foundry.toml, dots to underscores. Its NatSpec calls itself "the single definition of the tag form".
  • dirForTag(string)src/generated/<tag>
  • frozenPathForContract(string, string)
  • freezeSnapshot(Vm, string[]) — creates the dir, copies each generated file to its frozen path, and enforces append-only:
require(
    keccak256(bytes(vm.readFile(frozenPath))) == keccak256(bytes(content)),
    "LibSnapshot: frozen snapshot would change; bump [package].version for a new release"
);

Where the same work is duplicated

implementation language layer
LibSnapshot.freezeSnapshot Solidity build script
script/cut-release.sh in rain.factory.deploy bash CI step
script/cut-release.sh in S01-Issuer/st0x.deploy bash CI step
rainix-static cut-release (#302, open, HELD) Rust CI step

Plus deployTag restated locally in rain.deploy's script/Build.sol (being fixed in rain.deploy#26), and in rain.factory.deploy's and st0x.deploy's BuildPointers.sol.

The question

Which layer owns the freeze? #302's stated value is making the regenerate-then-freeze ordering unrepresentable, after the inverted ordering was found live in st0x.deploy — it copies candidate and only then runs forge script, so a drifted candidate is frozen stale while regeneration moves candidate on, publishing one address and permanently recording another.

But LibSnapshot puts the freeze in the same forge script run as the generation, which is where #304 argues it belongs — pointer generation is part of build, not a separate step. If generation and freezing are one script, a CI-layer freeze may be solving an ordering problem that only exists because they were split.

Points to settle, from the source rather than from this summary:

  • Does freezeSnapshot do everything cut-release.sh does? Missing candidates: the strict X.Y.Z version check, the refusal when candidate/ is absent, the diff -r assertion, the forge fmt step.
  • Does it prevent the ordering bug? The require fires only when a frozen path ALREADY exists, so on a NEW tag a stale freeze writes silently. Prevented, merely visible, or neither?
  • Does calling it from the build make the sequence structural, since both would then live in one forge script run?
  • If the Solidity layer is right, what is left for rainix owns the deploy-repo release freeze: rainix-static cut-release #302 — the guards that need to run before any generation, or nothing?

Why this is blocking-shaped

#302 is already HELD pending #303 and #304. This adds a third reason to settle the design before merging: it may be reimplementing a published library in a different language at a different layer. Landing it would make five implementations of one operation.

Related: #301, #302, #303, #304; rain.deploy#26 (dropping its local deployTag and address-constant emitter for the shared ones).

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions