Smart contract verification plays a crucial role in ensuring the security and integrity of blockchain-based applications. By matching the source code with the deployed byte-code, developers and users can gain better visibility into the functionality and behaviour of smart contracts. This article explores the typical workflow for contract verification and discusses the challenges and solutions associated with Wasm Smart contracts
The process of smart contract verification involves two main components: the verification service (backend) and the block explorer (frontend). The following steps outline the general workflow:
- The smart contract developer uploads the complete contract source code, along with the required build metadata (e.g., compiler version, optimization flags), to the verification service.
- For multi-file projects, the developer may choose to flatten the source code into a single file or upload multiple files/zip archives, depending on the verification service’s capabilities.
- The verification service receives the source code and metadata and compiles the code to generate the bytecode.
- The compiled output is compared with the bytecode deployed on the blockchain to ensure consistency and correctness.
- The verification service maintains a catalog in a database, storing the verification status and the associated source code.
- The block explorer acts as the frontend interface, providing users with access to the verification status and source code.
- It retrieves the verification status from the verification service and displays it to users.
- If the contract is not verified, the block explorer may provide a user-friendly UI/UX to upload the source code to the verification service.
Since there is not a tight coupling between the above two components any entity can opt to implement either of one and provide a service on top of it OR build both of them in-house.
Managing multi-file projects with external dependencies can be challenging during the verification process. Here are some common solutions:
One approach is to flatten all the source code into a single file. This process involves replacing import statements in each source file with the actual code from the imported module. By flattening the code, the verification service can compile it more easily.
Alternatively, some verification services support uploading multiple files or a zip archive containing all the necessary source code files. In this case, the verification service handles the dependencies while compiling the code.
Verifying Wasm-based smart contracts, compared to Solidity contracts, presents additional challenges due to the nature of the Rust compiler. Here’s why:
The Rust compiler may produce different bytecode outputs for the same source code across different build environments or dependencies. That means if you compile your smart contract in two different machines, let’s say linux and other macOS, the output bytecode will be different from each other even tough source code and their functionality remains same.
This non-deterministic behaviour complicates the verification process, as for a verification service in order to match the bytecode deployed on blockchain it needs to compile your given source code on it’s servers which will produce different bytecode for the exact same code.
To address the non-determinism issue, a recommended solution is to utilize a Dockerfile that provides a consistent build environment for producing deterministic bytecode. The same Dockerfile used to build the production/release artifacts locally should be employed by the verification service for the verification process.
- There are many verification platform available for wasm contract verification, such as Subscan, Sirato, Patron (still under development), etc.
- In the absence of a commonly agreed build environment, each verification service was forced to come up with their own in order to provide verifiable builds to enable Wasm smart contract verification service.
- That led to fragmentation of the ecosystem as a contract verified on one service cannot be verified on other since they all uses different build environments that are not compatible with each other which is clearly not good for the ecosystem which is at it’s infancy stage.
There was a clear need to define a standard build environment for Wasm contracts to make the bytecode deterministic and it make sense to add this functionality in the CLI tool that is use to build wasm contracts, i.e
The above PR adds a new subcommand
cargo contract verify and provides a standard build environment for producing verifiable wasm contract
In detail, the command spawns the container with the binding volume to the contact folder (See image documentation for more details), then executes
cargo contract build --release --output-json <some additional flags> > target/build_result.jsonto build the contract in the release mode and save the serialized
BuildResultoutput to a file. We need that in order to allow the host machine to access the results and print them in the STDOUT in the requested format.
After the execution, the container and
build_result.jsonfiles are deleted. The host then accesses metadata files, deserializes them back into the rust object, add the
imagename, and writes them back.
cargo contract verify is released it will become very seamless for contract developers to compile their contracts that are verifiable. Unfortunately all of the verification services uses their own build environments that is not compatible with each other that’s results in fragmentation, but that can be changed if all of the verification services start using
cargo contract verify.
If you are a developer of one of the verification service, take a look at the above PR and give your valuable inputs and look into possibility of integrating this into your service.