How to Verify a Solana Program's On-Chain Bytecode Matches Its Source
You verify a Solana program's on-chain bytecode matches its source by building the program from source in a reproducible way, then comparing the resulting hash against the hash of the deployed program's bytes. This is called deterministic or reproducible build verification. It protects against supply-chain attacks where a deployed program's logic differs from what the source code claims to do.
Why Verification Matters
Solana programs are immutable once deployed. If a program's on-chain bytecode doesn't match its published source, someone could have deployed different logic than what auditors or users reviewed. This happened in several major exploits across crypto. For programs you interact with - especially those handling tokens or user funds - verifying bytecode is the only way to confirm the source code is what actually runs.
The core challenge: compiling Rust to Solana's BPF bytecode (now transitioning to SBF) isn't perfectly deterministic by default. Compiler versions, dependencies, and build flags all affect the output. You need a controlled, repeatable build environment.
Prerequisites
Before starting, you need:
- The program's source code (usually from a public repository)
- The program's on-chain address (program ID)
- Access to an RPC endpoint to fetch the deployed program
- Docker installed on your machine (for consistent build environments)
- The same Solana toolchain version the developer used to build the original deployment
Step-by-Step Verification
1. Build the Source Reproducibly
Most Solana programs use Anchor. The Anchor framework provides a anchor build --verifiable command that runs the build inside a pinned Docker container. This eliminates environment differences.
# Clone the program's source
git clone https://github.com/example/program.git
cd program
# Build inside the Anchor verifiable builder
anchor build --verifiable
This produces a .so file (the compiled program binary) in target/deploy/. The key point: the --verifiable flag ensures the build uses a specific Docker image with known toolchain versions. Without this flag, your local Rust version, system libraries, or LLVM version could produce different bytes.
If the program wasn't built with Anchor, or you need to match a specific Solana toolchain version, you can run the build manually inside a pinned Docker image:
docker run --rm -v $(pwd):/workdir solanalabs/solana:1.18.12 /bin/bash -c "cd /workdir && cargo build-bpf"
Replace 1.18.12 with the exact version the developer used. This version matters: different Solana releases produce different SBF bytecode from identical source.
2. Get the On-Chain Bytecode Hash
Fetch the deployed program's data from the cluster:
solana program show <PROGRAM_ID> --output json-compact
This returns the program's on-chain data, including its base58-encoded bytes. Note the programData field - that's the actual bytecode.
To get just the hash for comparison:
solana program show <PROGRAM_ID> | grep -oP 'Program Id: \K.*'
# Or compute the hash yourself
solana program dump <PROGRAM_ID> program.so
sha256sum program.so
The solana program dump command downloads the exact bytecode from the cluster. Computing its SHA-256 hash gives you a fingerprint.
3. Compare Hashes
Now compare the hash of your local build against the on-chain hash:
# Hash your local build
sha256sum target/deploy/program.so
# Hash the on-chain dump
sha256sum program.so
If both hashes match, the deployed bytecode is exactly what you compiled from the source. If they differ, something is wrong.
When hashes don't match
A hash mismatch doesn't automatically mean malicious code. Common causes:
- Different Solana toolchain version: The developer built with 1.17.x, you built with 1.18.x. Even minor version bumps change compiler behavior.
- Different Anchor version: Anchor's code generation changes between releases.
- Dependency differences: The developer locked different dependency versions in their
Cargo.lock. Always use the repository'sCargo.lockfile. - Build flags: The developer used
--featuresor--no-default-featuresyou didn't include. - Non-deterministic dependencies: Some Rust crates embed timestamps or git commit hashes. Pin these to known versions.
Using Verification Services
Several sites automate this process. You provide the program ID and source repository URL, and they build and compare. Examples include:
- Explorers like Solscan: Some show a "Verified" badge if the bytecode matches a known build.
- OtterSec Verify: A dedicated verification service for Solana programs.
- Anchor's built-in verification:
anchor verify <PROGRAM_ID>checks against Anchor's database of verified builds.
These services run builds in controlled environments and cache results. They're convenient but trust them only as much as you trust the service operator. For high-value programs, run verification yourself.
Practical Tips
- Always check the build version: The README or deployment script should state the exact Solana and Anchor versions used. If it doesn't, ask the developers.
- Verify after every upgrade: Programs can be upgraded via the "upgradeable loader" mechanism. Each upgrade is a new deployment with new bytecode.
- Compare against multiple sources: If you build from source and get one hash, but the developer's published hash is different, that's a red flag.
- For critical programs: Run verification in an air-gapped environment. A compromised build machine could inject malicious code during compilation.
No Solana program should be trusted solely because its source looks clean. The bytecode is the truth. Verification connects that truth back to the code you can read.
Not financial advice. orymsolana.xyz publishes market data and general information about digital assets. Crypto assets are volatile and you can lose everything you put in. Nothing here is a recommendation to buy, sell or hold, and we make no price predictions.
Prices are sourced from third parties and may be delayed or wrong. Verify anything you intend to act on against a primary source.