Skip to content

Add --docker option and stellar contract verify for reproducible builds #2506

@leighmcculloch

Description

@leighmcculloch

What problem does your feature solve?

Contract builds are not reproducible across different machines and environments. Different host OS, architecture, toolchain versions, and system libraries can produce different .wasm outputs from the same source code. This makes it impossible for third parties to independently verify that a deployed contract was built from a given source.

Related: stellar/stellar-protocol#1802 explored reproducing builds without Docker, but that approach hasn't proven reliable.

What would you like to see?

Building Reproducibly

Add a --docker option to stellar contract build that runs the cargo rustc build step inside a Docker container using an official Rust image pinned to a specific digest, always targeting amd64 architecture to eliminate host architecture differences.

  • --docker (no value): Pulls the latest official Rust image (docker.io/library/rust:latest) as --platform linux/amd64. After pulling, the resolved @sha256:... digest is recorded in contract metadata so the exact image can be replayed later.
  • --docker=<image> (with value): Uses the specified Docker image, allowing users to reproduce past builds by passing the exact image with digest (e.g. docker.io/library/rust@sha256:abcdef...).
  • Only the cargo rustc compilation runs inside Docker. The CLI's own post-processing of the .wasm file (optimization, metadata, etc.) continues to run on the host, outside Docker.
  • The fully qualified Docker image name including its @sha256:... digest is recorded as a contract metadata entry, alongside the existing CLI version metadata.

Defaults are powerful and after sometime it might make sense to make this the default build option, meaning the docker build process is used automatically if a docker host is detected, and if the non-docker build process is used a warning is displayed indicating that reproducibility will be limited.

Verifying

stellar contract verify command:

Add a stellar contract verify command that automates verification of a contract build. It accepts the same wasm inputs as other contract commands:

  • --wasm <path> — a local .wasm file
  • --wasm-hash <hash> — fetch the wasm by its hash from the network
  • --contract-id <id> — fetch the wasm for a deployed contract from the network

The command:

  1. Reads the Docker image and CLI version from the contract's metadata.
  2. Errors if the CLI version in metadata does not match the currently running stellar-cli version (the user must install the matching version first).
  3. Rebuilds the contract from source using stellar contract build --docker=<image> with the Docker image from metadata.
  4. Compares the rebuild output to the original .wasm and reports whether they match.

Manual verification:

The same verification can also be done manually:

  1. Install the same version of stellar-cli (recorded in contract metadata).
  2. Run stellar contract build --docker=<image> using the same Docker image (also recorded in contract metadata).
  3. Compare the resulting .wasm output.
flowchart TD
    A[stellar contract build] --> B{--docker flag?}
    B -- No --> C[cargo rustc on host]
    B -- "--docker" --> D[Pull latest rust image\namd64, record resolved digest]
    B -- "--docker=image" --> E[Pull specified image\namd64]
    D --> F[cargo rustc inside Docker container]
    E --> F
    C --> G[CLI post-processes .wasm on host]
    F --> G
    G --> H[Embed metadata:\n- CLI version\n- Docker image + digest]
    H --> I[Final .wasm output]

    style D fill:#ccffcc,stroke:#00ff00
    style E fill:#ccffcc,stroke:#00ff00
    style F fill:#ccffcc,stroke:#00ff00
    style H fill:#ccffcc,stroke:#00ff00
Loading

Legend:

  • Green = New/proposed components
  • Default = Existing/unchanged components

Why not include the CLI in the Docker image?

Bundling the CLI into a custom Docker image would create a single blessed image that SDF must maintain and host. But instead using community images (like the official rust image) and allowing any image to be specified, builds stay decentralised. No single entity controls the build environment, and users aren't locked into a specific image provider.

What alternatives are there?

  • Pinning a specific image version in each CLI release, but this ties CLI releases to image updates and adds maintenance burden. Pulling latest and recording the digest is simpler and always gives the newest toolchain.
  • Reproducing builds without Docker by pinning toolchain versions (explored in https://github.com/orgs/stellar/discussions/1802, but has not proven reliable due to host OS and system library differences).
  • A fully self-contained Docker image with both Rust and the CLI baked in, but this centralises control and limits flexibility.

Thanks

This idea is not unique or mine, a few people over time have mentioned ideas very much like this, and other blockchain ecosystems rely on docker to isolate and reproduce as well. Thanks to everyone who has raised this ideas at different times.

Metadata

Metadata

Type

No type

Projects

Status

In Progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions