Share
## https://sploitus.com/exploit?id=6C4D29FE-F5CF-538E-AB84-A4D2F4389DF7
# Soroban Verify PoC

This repository is an ultra-minimal proof-of-concept / proof-of-physics for a Soroban contract source verification service. It is the pre-MVP demo behind an SCF RFP proposal: small enough to read in one sitting, concrete enough to prove the verification mechanics, and intentionally disposable.

It is **not** the production service. There is no API, database, queue, hosted verifier fleet, Rust helper, package registry, IPFS integration, or federation layer here. The only implementation is a Python standard-library script that shells out to Docker.

## What problem this demonstrates

Soroban contracts are deployed as Wasm bytes. Once a contract is on-chain, the deployed artifact is opaque unless there is a trustworthy way to connect those bytes back to a source tree and build recipe.

This PoC demonstrates the core physical claim behind source verification:

1. Take a target Soroban Wasm artifact.
2. Rebuild a claimed source tree inside a trusted, digest-pinned Stellar CLI Docker image.
3. Use the SEP-58 Appendix A style recipe: pinned toolchain from the image, locked dependencies, optimized build, and metadata describing the build/source.
4. Byte-compare the rebuilt Wasm against the target Wasm.
5. Return a machine-readable verdict.

If the bytes match, the target Wasm was produced by that source and build recipe. If only the `contractmetav0` custom metadata section differs, the contract code still matches but the source/build metadata was patched differently. If normalized bytes still differ, verification fails.

## What the script does

`soroban_verify_poc.py` performs one local verification run:

1. **Input validation**
   - Reads a local target Wasm path from `--target-wasm`.
   - Reads a local source checkout from `--source-dir`.
   - Requires a Cargo manifest path relative to `/source` via `--manifest-path`.
   - Requires a Docker image reference containing `@sha256:` via `--image`.
   - Accepts an explicit Docker `--platform`.

2. **Source metadata**
   - If `--source-sha256` is provided, it is used as the `source_sha256=...` build metadata value.
   - If omitted, the script computes a deterministic demo-only hash of the source tree excluding `.git` and `target` directories.
   - Optional `--source-uri` is appended as a `source_uri=...` metadata value.
   - This source-tree hash is for the demo only; a production service should hash the exact submitted source package according to its canonical source artifact rules.

3. **Toolchain resolution**
   - Runs `rustup default` inside the selected image and platform.
   - Passes the resulting value back into the build container as `RUSTUP_TOOLCHAIN=...` so the build uses the image's intended Rust toolchain.

4. **Docker rebuild**
   - Runs `docker run --rm` with `/source` mounted from `--source-dir`.
   - Applies minimal demo guardrails: `--cap-drop=ALL`, `--security-opt no-new-privileges`, `--pids-limit 512`, plus configurable CPU and memory limits.
   - Executes:

     ```sh
     stellar contract build \
       --manifest-path= \
       --locked \
       --optimize \
       --meta bldimg= \
       --meta bldopt=--optimize \
       --meta source_sha256= \
       [--meta source_uri=]
     ```

5. **Rebuilt Wasm selection**
   - If `--rebuilt-wasm` is provided, that path is used. It can be absolute or relative to `--source-dir`.
   - If omitted, the script searches the source tree's `target/` directory and chooses a recent `.wasm` file, preferring optimized names when present. For reproducible demos, pass `--rebuilt-wasm` explicitly.

6. **Graded byte comparison**
   - First compares full target bytes to full rebuilt bytes.
   - If full bytes differ, it removes every Wasm custom section named `contractmetav0` from both artifacts and compares again.
   - Prints a JSON object containing the verdict, input/build metadata, Docker command, target hash, rebuilt hash, and normalized hashes when applicable.
   - Exits `0` for `exact` and `code_match_meta_diff`, `1` for `mismatch`, and `2` for errors.

## Verdicts

| Verdict | Meaning | Typical cause |
| --- | --- | --- |
| `exact` | Target Wasm bytes and rebuilt Wasm bytes are identical. | Same source, same image, same build options, same metadata. |
| `code_match_meta_diff` | Full bytes differ, but bytes match after removing `contractmetav0` custom sections from both Wasms. | Same contract code, but post-build metadata differs, for example a different `--meta source_sha256=...` or `--meta source_uri=...`. |
| `mismatch` | Bytes still differ after removing `contractmetav0`. | Different source code, different build-relevant inputs, wrong target Wasm, or another real code/data difference. |

The metadata-diff verdict exists because Stellar CLI stores build/source metadata in Wasm custom sections named `contractmetav0`. This PoC treats those sections as metadata-only: useful for provenance, but separable from the executable contract bytes for the purpose of explaining a near match.

## Dependencies

- Docker CLI and a running Docker daemon.
- Python 3.11+.
- No Python packages: the script uses only the Python standard library.
- `git`, only to fetch the public example repository.
- Network access for:
  - cloning `stellar/soroban-examples`,
  - pulling the Stellar CLI image,
  - downloading Cargo dependencies during the Docker build.

## Public example repository

All demos in this repository are based on the public official Stellar example contracts:

- Repository: 
- Local checkout used in commands: `/tmp/soroban-examples`
- Demo contracts used here:
  - `hello_world`
  - `increment`

Clone it fresh:

```sh
rm -rf /tmp/soroban-examples
git clone --depth 1 https://github.com/stellar/soroban-examples.git /tmp/soroban-examples
```

## Digest-pinned Stellar CLI image

This PoC requires the Docker image to be digest-pinned. Do not pass a mutable tag alone.

Arm64 image used for the captured demos in this repository:

```sh
IMAGE='docker.io/stellar/stellar-cli:27.0.0@sha256:80a667750133b88ed1a07c82ab80c5863db8f83145c4d98ac741b3e5c7b10841'
PLATFORM='linux/arm64'
```

Amd64 equivalent for x86_64 hosts:

```sh
IMAGE='docker.io/stellar/stellar-cli:27.0.0@sha256:a63366ef5fcd709759994f9f9109b68144f7158abd68e97421357fa8932df78d'
PLATFORM='linux/amd64'
```

To inspect the multi-arch tag and resolve single-architecture digests:

```sh
docker buildx imagetools inspect docker.io/stellar/stellar-cli:27.0.0
```

Use the digest for the platform you will run. The script only checks that `--image` contains `@sha256:`; it does not independently prove that the digest matches the selected platform.

## Full run instructions: arm64

From this repository:

```sh
cd soroban_verify_poc
chmod +x ./soroban_verify_poc.py

rm -rf /tmp/soroban-examples /tmp/soroban-verify-poc-fixtures
git clone --depth 1 https://github.com/stellar/soroban-examples.git /tmp/soroban-examples
mkdir -p /tmp/soroban-verify-poc-fixtures

IMAGE='docker.io/stellar/stellar-cli:27.0.0@sha256:80a667750133b88ed1a07c82ab80c5863db8f83145c4d98ac741b3e5c7b10841'
PLATFORM='linux/arm64'
```

Build a target fixture for `hello_world` directly in the digest-pinned image:

```sh
docker run --rm \
  --platform "$PLATFORM" \
  --cap-drop=ALL \
  --security-opt no-new-privileges \
  --pids-limit 512 \
  --cpus 2 \
  --memory 4g \
  -v /tmp/soroban-examples:/source \
  "$IMAGE" \
  contract build \
  --manifest-path=hello_world/Cargo.toml \
  --locked \
  --optimize
```

Then run the verifier once against the freshly built artifact so the fixture receives the same PoC metadata fields that future verification runs will add, and copy that artifact aside as the target fixture:

```sh
./soroban_verify_poc.py \
  --target-wasm /tmp/soroban-examples/hello_world/target/wasm32v1-none/release/soroban_hello_world_contract.wasm \
  --source-dir /tmp/soroban-examples \
  --manifest-path hello_world/Cargo.toml \
  --image "$IMAGE" \
  --platform "$PLATFORM" \
  --rebuilt-wasm hello_world/target/wasm32v1-none/release/soroban_hello_world_contract.wasm || true

cp /tmp/soroban-examples/hello_world/target/wasm32v1-none/release/soroban_hello_world_contract.wasm \
  /tmp/soroban-verify-poc-fixtures/hello_world.target.wasm
```

Now verify the same source and image against the target fixture:

```sh
./soroban_verify_poc.py \
  --target-wasm /tmp/soroban-verify-poc-fixtures/hello_world.target.wasm \
  --source-dir /tmp/soroban-examples \
  --manifest-path hello_world/Cargo.toml \
  --image "$IMAGE" \
  --platform "$PLATFORM" \
  --rebuilt-wasm hello_world/target/wasm32v1-none/release/soroban_hello_world_contract.wasm
```

Expected output shape:

```json
{
  "verdict": "exact",
  "target_wasm": "/tmp/soroban-verify-poc-fixtures/hello_world.target.wasm",
  "rebuilt_wasm": "/tmp/soroban-examples/hello_world/target/wasm32v1-none/release/soroban_hello_world_contract.wasm",
  "target_sha256": "fd7c2118636c4b3c6deb5c6ebf2e0ad3297101ab6e2e6b8f75fdbf91f24ae06f",
  "rebuilt_sha256": "fd7c2118636c4b3c6deb5c6ebf2e0ad3297101ab6e2e6b8f75fdbf91f24ae06f"
}
```

The real output includes additional fields such as `docker_command`, `image`, `platform`, `rustup_toolchain`, and metadata section counts.

### Metadata-diff run

Use the same target fixture, but rebuild with a different metadata hash:

```sh
./soroban_verify_poc.py \
  --target-wasm /tmp/soroban-verify-poc-fixtures/hello_world.target.wasm \
  --source-dir /tmp/soroban-examples \
  --manifest-path hello_world/Cargo.toml \
  --image "$IMAGE" \
  --platform "$PLATFORM" \
  --source-sha256 1111111111111111111111111111111111111111111111111111111111111111 \
  --rebuilt-wasm hello_world/target/wasm32v1-none/release/soroban_hello_world_contract.wasm
```

Expected verdict:

```json
{
  "verdict": "code_match_meta_diff"
}
```

### Mismatch run

Change the public example source in a build-relevant way, then verify against the original target fixture:

```sh
python3 - <<'PY'
from pathlib import Path
p = Path('/tmp/soroban-examples/hello_world/src/lib.rs')
s = p.read_text()
p.write_text(s.replace('String::from_str(&env, "Hello")', 'String::from_str(&env, "Hello!")', 1))
PY

./soroban_verify_poc.py \
  --target-wasm /tmp/soroban-verify-poc-fixtures/hello_world.target.wasm \
  --source-dir /tmp/soroban-examples \
  --manifest-path hello_world/Cargo.toml \
  --image "$IMAGE" \
  --platform "$PLATFORM" \
  --rebuilt-wasm hello_world/target/wasm32v1-none/release/soroban_hello_world_contract.wasm || true
```

Expected verdict:

```json
{
  "verdict": "mismatch"
}
```

Restore the checkout when done:

```sh
git -C /tmp/soroban-examples checkout -- hello_world/src/lib.rs
```

## Full run instructions: amd64

On an amd64 host, use the amd64 single-arch digest and `linux/amd64` platform. The commands are otherwise the same.

```sh
cd soroban_verify_poc
chmod +x ./soroban_verify_poc.py

rm -rf /tmp/soroban-examples /tmp/soroban-verify-poc-fixtures
git clone --depth 1 https://github.com/stellar/soroban-examples.git /tmp/soroban-examples
mkdir -p /tmp/soroban-verify-poc-fixtures

IMAGE='docker.io/stellar/stellar-cli:27.0.0@sha256:a63366ef5fcd709759994f9f9109b68144f7158abd68e97421357fa8932df78d'
PLATFORM='linux/amd64'
```

Then use the same build, exact, metadata-diff, and mismatch commands shown in the arm64 section.

## Demos captured in this repository

The `demo/*.json` files are captured outputs from the public `stellar/soroban-examples` repository using the arm64 digest-pinned image above.

| File | Contract | Verdict | What it shows |
| --- | --- | --- | --- |
| `demo/hello_world_exact.json` | `hello_world` | `exact` | Same public source, same digest-pinned image, same metadata; full Wasm hashes match. |
| `demo/hello_world_meta_diff.json` | `hello_world` | `code_match_meta_diff` | Public source rebuild with a different `source_sha256` metadata value; code-normalized hashes match. |
| `demo/hello_world_mismatch.json` | `hello_world` | `mismatch` | Public source changed from `"Hello"` to `"Hello!"`; normalized hashes differ. |
| `demo/increment_exact.json` | `increment` | `exact` | Same public source, same digest-pinned image, same metadata; full Wasm hashes match. |
| `demo/increment_meta_diff.json` | `increment` | `code_match_meta_diff` | Public source rebuild with a different `source_sha256` metadata value; code-normalized hashes match. |
| `demo/increment_mismatch.json` | `increment` | `mismatch` | Public source changed from `count += 1` to `count += 2`; normalized hashes differ. |

Captured hashes:

| File | Target SHA-256 | Rebuilt SHA-256 | Normalized target SHA-256 | Normalized rebuilt SHA-256 |
| --- | --- | --- | --- | --- |
| `demo/hello_world_exact.json` | `fd7c2118636c4b3c6deb5c6ebf2e0ad3297101ab6e2e6b8f75fdbf91f24ae06f` | `fd7c2118636c4b3c6deb5c6ebf2e0ad3297101ab6e2e6b8f75fdbf91f24ae06f` | n/a | n/a |
| `demo/hello_world_meta_diff.json` | `fd7c2118636c4b3c6deb5c6ebf2e0ad3297101ab6e2e6b8f75fdbf91f24ae06f` | `83e39609660e8b79373fbf8c3542a01c2c613789d13338500b15664482cba244` | `5600920c0560b317e129d9df3fb972ee75c7760ff565a0a8314c40bcf46c7377` | `5600920c0560b317e129d9df3fb972ee75c7760ff565a0a8314c40bcf46c7377` |
| `demo/hello_world_mismatch.json` | `fd7c2118636c4b3c6deb5c6ebf2e0ad3297101ab6e2e6b8f75fdbf91f24ae06f` | `ba6cdb88609b61254a8ca111e58b330ecb3a40f5359b8dba6039234f53527176` | `5600920c0560b317e129d9df3fb972ee75c7760ff565a0a8314c40bcf46c7377` | `0bfc602488ee23a16f39c0e54b47046c2d87ea1388e5172f8882da49c333462b` |
| `demo/increment_exact.json` | `52182334a86ba44f9700d67b9b20aa78d03120dab9dc751b91e65f28e1612652` | `52182334a86ba44f9700d67b9b20aa78d03120dab9dc751b91e65f28e1612652` | n/a | n/a |
| `demo/increment_meta_diff.json` | `52182334a86ba44f9700d67b9b20aa78d03120dab9dc751b91e65f28e1612652` | `a906fd505f8c78bc89b934f4edd81f955112e41a0e518916e896b121f0e53415` | `9c12f4be1055e32c8f1aef2342702af75f7b333d895aa3292ccbfb13f3d821be` | `9c12f4be1055e32c8f1aef2342702af75f7b333d895aa3292ccbfb13f3d821be` |
| `demo/increment_mismatch.json` | `52182334a86ba44f9700d67b9b20aa78d03120dab9dc751b91e65f28e1612652` | `6a2901193b75c531cc576a824289f66851097491f133dbccb8138a170d68885e` | `9c12f4be1055e32c8f1aef2342702af75f7b333d895aa3292ccbfb13f3d821be` | `7a0dbd28448c7bb77c524ba3113054d93a28513ab38a31eb511c97fd8ca95874` |

## CLI arguments

| Argument | Required | Description |
| --- | --- | --- |
| `--target-wasm` | yes | Path to the local target Wasm artifact to verify. |
| `--source-dir` | yes | Path to the local source directory. It is mounted into Docker as `/source`. |
| `--manifest-path` | yes | Cargo manifest path relative to `--source-dir`, for example `hello_world/Cargo.toml`. Absolute paths and `..` are rejected. |
| `--image` | yes | Digest-pinned Stellar CLI Docker image. Must contain `@sha256:`. |
| `--platform` | no | Docker platform, for example `linux/arm64` or `linux/amd64`. Defaults to `linux/amd64`. |
| `--source-sha256` | no | 64-hex source artifact hash to place in `--meta source_sha256=...`. If omitted, the script computes a demo-only source-tree hash. |
| `--source-uri` | no | Optional source URI to place in `--meta source_uri=...`. |
| `--rebuilt-wasm` | no | Rebuilt Wasm path after Docker build. Relative paths are resolved against `--source-dir`; absolute paths are used directly. Recommended for deterministic demos. |
| `--timeout` | no | Timeout in seconds for each Docker command. Default: `900`. |
| `--cpus` | no | Docker CPU limit passed to `docker run --cpus`. Default: `2`. |
| `--memory` | no | Docker memory limit passed to `docker run --memory`. Default: `4g`. |

## Scope / NOT in this PoC

This repository intentionally does not include production service concerns:

- No HTTP API or frontend.
- No database or persistence layer.
- No queue, worker fleet, scheduler, or job state machine.
- No Rust implementation or native Wasm parser beyond the tiny Python custom-section walker.
- No multi-verifier quorum.
- No IPFS, source package registry, federation, or discovery protocol.
- No on-chain fetch from Stellar RPC.
- No supply-chain policy engine.
- No sandbox-hardening claims beyond minimal Docker flags used for the local demo.

Those belong to the full source verification service design. This PoC only proves the rebuild-and-byte-compare mechanics on public Soroban example contracts.