Skip to main content

How to verify contracts for stylus programs

caution

This page will walk you through how to verify your Stylus contracts locally. Stylus contract verification is also available on Arbiscan. Please note, however, that Stylus contract verification on Arbiscan is only supported for Stylus contracts deployed using cargo-stylus 0.5.0 or higher.

Background

Stylus programs written in Rust and deployed onchain can be verified against a local codebase by using the cargo stylus tool.

Goals

  • To ensure Stylus contract deployments are reproducible by anyone who is running the same architecture as the deployed item
  • To sandbox the reproducible environment and standardize it as much as possible to prevent foot guns
  • To guarantee that programs reproducibly deployed with a cargo stylus version >= 0.4.2 are verifiable

Opting out

By default, cargo stylus deploy is reproducible as it runs in a Docker container. Users can opt-out by specifying --no-verify as a flag.

Reproducible deployments

Required knowledge and setup:

  • System architecture of your host computer (x86 / ARM)
  • The git commit of your project used for deployment
  • A Rust stylus project, such as OffchainLabs/stylus-hello-world which contains a rust-toolchain.toml file
  • Your cargo stylus version (run cargo stylus --version to obtain this value)
  • Docker installed and running on your machine

Your project's toolchain file must contain the Rust version you wish to use for your deployment, such as major.minor.patch

[toolchain]
channel = "1.79.0"

It cannot be stable, nightly, or beta by itself, as a specific version must be added. For instance, you can specify nightly-YYYY-MM-DD or major.minor.patch for your channel. This is so that deployments have a very specific version to prevent potential mismatches from being more generic.

# Replace {PRIV_KEY} with your actual private key or set it as a local variable
cargo stylus deploy --private-key={PRIV_KEY} --verbose

Upon completion, you will obtain the deployment transaction hash:

deployment tx hash: 0x1d8ae97e245e1db21dd188e5b64ad9025c1fb4e5f82a8d38bc8ae2b7a387600b

Save this tx hash, as verifiers will need it.

Reproducible verification

To verify a program, the verifier will need Docker installed and also know:

  • System architecture the deployer used (x86 / ARM). Note: ARM devices that can emulate x86, such as Mac M series via rosetta, can verify x86 Stylus deployments
  • The git commit of the project the deployer used
  • Your cargo stylus version the deployer used
  • The deployment transaction hash

Navigate to the project's directory and check out the git commit that was used at deployment. Ensure your cargo stylus --version matches what the deployer used.

# Replace {DEPLOYMENT_TX_HASH} with the actual DEPLOYMENT_TX_HASH or set it as a local variable
cargo stylus verify --deployment-tx={DEPLOYMENT_TX_HASH}

This command will run the verification pipeline through a Docker environment, recreate the project metadata hash, and verify that the deployed program matches what the command reconstructed locally.

How it works

On deployment, a keccak256 hash is created from the contents of all Rust source files in the project, sorted by file path, along with a rust-toolchain.toml, Cargo.toml and Cargo.lock files by default. This hash is injected in as a custom section of the user wasm's code. This means all data in the source files will be used for reproducible verification of a Stylus program, including code comments.

This means the codehash onchain of the program will change due to this deployment metadata hash.

The verification routine fetches the deployment transaction by hash via RPC, then attempts to build the local project to reconstruct the deployment init code and WASM using cargo build. It then checks that the deployment tx data matches the created init code.

Important details

Docker image The Docker container used for reproducibility standardizes all builds to x86, and it looks like this:

FROM --platform=linux/amd64 rust:1.79 as builder
RUN rustup toolchain install $VERSION-x86_64-unknown-linux-gnu
RUN rustup default $VERSION-x86_64-unknown-linux-gnu
RUN rustup target add wasm32-unknown-unknown
RUN rustup target add wasm32-wasi
RUN rustup target add x86_64-unknown-linux-gnu
RUN cargo install cargo-stylus

The docker container uses the rust:1.79 version as a base for all projects. This will install cargo tooling and rust targets, but the toolchain actually used for compilation will be specified by the project being deployed in its rust-toolchain.toml file.

For instance, a future toolchain can be used despite the base image being 1.79, as when cargo stylus is installed, it will use that particular toolchain. Future cargo stylus updates could update this base image but may not impact the compiled WASM as the image will be using the specified toolchain. However, this is why knowing the specific cargo stylus version used for the reproducible verification from the deployer is important.

The build toolchain

All verifiable Stylus programs in Rust must have a standard rust-toolchain.toml file which specifies the channel for their deployment. It cannot be stable, nightly, or beta by itself, as a specific version must be added. For instance, you can specify nightly-YYYY-MM-DD or major.minor.patch for your channel. This is so that deployments have a very specific version to prevent potential mismatches from being more generic.