Add --verifiable flag to stellar contract build#2585
Conversation
bdca27b to
629046f
Compare
557647b to
6e7c125
Compare
6e7c125 to
1780110
Compare
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
|
@fnando Thanks for updating this PR to the latest SEP-58 revision, this is really helping prototyping a verification service. While toying around with The current approach to select the default image uses I think we should try to parse the contract being built When these values are not available or we can't find an appropriate image we can default to the latest image (and warn the user). After fetching the correct values from the package being built When listing images from Docker Hub using the My suggestion: wire the published tags search from Docker Hub not only when the wanted image isn't found, use it to find the correct image having the built package This will fix the current behavior where not passing |
|
@saimeunt the auto image selection still needs to be worked on. I'm focusing on explicit setting the image for now on my tests. We didn't have the images ready while I started doing this draft. |
What
Adds a
--verifiableflag tostellar contract buildthat performs a reproducible build inside a digest-pinned Docker container and stamps SEP-58 metadata (bldimg,source_uri,source_sha256,bldopt) into the resulting WASM so third parties can re-run the build and verify the output byte-for-byte.New flags, all grouped under a
Verifiablehelp section:--verifiable— opt in to the reproducible build mode; implies--locked, infers--package(the default-member cdylib contracts) when omitted, and requires a clean git working tree.--image— override the auto-selected container image. Must be digest-pinned (...@sha256:...); tag-only refs are rejected sobldimgstays content-addressed.--source-sha256— SEP-58 source identification: 64-char SHA-256 of the source. Optional — the archive is always generated and its SHA-256 computed for you. When supplied it acts as a pin: the build fails if it doesn't match the generated archive.--source-uri— SEP-58 source identification: URI where the source can be obtained (any scheme, e.g.https://example.com/src.tar.gz). Optional.-d/--docker-host(also readsDOCKER_HOST) — override the docker daemon endpoint.A
--verifiablebuild always generates the reproducible source archive, records its SHA-256 assource_sha256, writes a content-addressed copy to the data dir'sarchives/<sha256>.tar.gz, and builds from the extracted (permission-hardened) copy so the WASM comes from exactly the bytes that were hashed.Each contract — explicit
--packageor inferred — is built with its own--package, which is forwarded to the build and recorded as abldopt, so every WASM is independently reproducible and stable even if the workspace's default members change later. Multi-contract workspaces build every contract in a single container so the crates download, compiled dependencies, andtarget/are shared.Every
bldoptis recorded as valid shell syntax: each build option is shell-escaped once at the source, quoting only the value side, so a verifier can join the recordedbldopts and replay the exact invocation through a shell. A flag like--env B='this is very nice'is stored as--env=B='this is very nice'(flag and key outside the quotes), which round-trips back to the original argument. Run with--verboseto print the fulldocker run …command the build executes (the same command surfaced in the error message if the container build fails).--env NAME=VALUESome contracts read environment variables at build time (build scripts, proc-macros,
env!).--env(repeatable) sets them for the build and lives on the shared build options, socontract build,deploy, anduploadall accept it:--verifiableit's set on the local build process.--verifiableit's passed to the container (docker-e) and recorded as its ownbldopt(shell-escaped, e.g.--env=B='this is very nice') so a verifier replays the same build. Because it's embedded in the WASM meta, avoid secrets here.Names are validated (
[A-Za-z_][A-Za-z0-9_]*); values are kept verbatim.Source archives
The source archive is built by walking the working directory and tarring it, honoring the project's own
.gitignore/.ignorefiles; the.gitdirectory itself is always skipped. The archive is rooted at the current working directory — runcontract build --verifiable/contract archivefrom the project (or workspace) root you want archived, so a workspace member's build still gets the whole workspace (its rootCargo.toml/Cargo.lock).Selection depends only on the in-tree files plus the
.gitignore/.ignorefiles inside the archived tree — never on machine-specific state (the global gitignore,.git/info/exclude, and parent-directory ignore files are not consulted) — so the same tree always hashes to the samesource_sha256across machines. Paths that usually shouldn't ship but weren't ignored (.env,target/,.idea, …) are listed in a warning so you can add an ignore rule.Because the archive is the working tree rather than a committed snapshot, both
contract build --verifiableandcontract archiverefuse a dirty git tree (with a warning explaining why), so a recordedsource_sha256always corresponds to a committed state. When the source isn't a git repo, there's nothing to check and they proceed.stellar contract archiveA standalone command to generate — or inspect — the same reproducible archive a verifiable build uses, sharing the exact byte-generation logic so the output is identical:
-o/--out-file <PATH>— write the gzipped tarball. Must end in.tar.gzor.tgz. Required unless--dry-run.--dry-run— list the entries that would be archived plus the computedsource_sha256, without writing anything. Handy for confirming contents before a verifiable build, or for producing the artifact to host at a--source-uri.Why
SEP-58 defines how to verify that a deployed contract WASM came from a specific source built with a specific toolchain image. Until now the CLI had no built-in way to produce such a build — users had to assemble the docker invocation, run cargo inside it, and stamp the custom sections by hand. This makes it a first-class option on
stellar contract build, and the source archive (auto-generated on build, or produced/inspected viastellar contract archive) closes the loop by being the exact artifact thatsource_sha256refers to.Known limitations
source_uri, checksource_sha256, rebuild, byte-compare) is not part of this PR.