MCPcopy Index your code
hub / github.com/ChainSecurity/deployment_validation

github.com/ChainSecurity/deployment_validation @v0.9.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.9.0 ↗ · + Follow
369 symbols 736 edges 32 files 54 documented · 15%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Deployment Validation

Please note: This project is currently in BETA status

After a complex smart deployment it is very cumbersome to identify any potential (malicious) mistakes during deployment which later could put security at risk. Even harder, smart contract upgrades and configuration changes can result in security accidents. For example, audited smart contracts that are updated with unverified changes on-chain or deployed with a configuration that was not originally reflected in the audit might expose users to unforeseen consequences. These users are not immediately made aware of such changes and therefore still maintain high confidence towards the contract due to a published audit report.

Deployment Validation ensures that smart contracts have been deployed with the expected bytecode and configuration. Trusted entities can sign and publish Deployment Validation Files (DVF) which can subsequently be checked against the on-chain smart contracts to ensure a correct deployment at any given block number. Each DVF describes the correct state of exactly one smart contract and may reference other DVFs on whose correctness it depends. Thereby, Deployment Validation checks security not just during development, but also during deployment and later updates.

During DVF initialization, dv compiles a given project (Foundry- or Hardhat-based) and compares the generated bytecode with the on-chain bytecode of a given address. This ensures that a deployed smart contract corresponds to a certain repository/commit combination (e.g., an audited version of the code).

dv also automatically retrieves a smart contract's full decoded state and all events emitted since deployment until the end of a given block. DVF creators can choose which state and events are important and define appropriate constraints (e.g., equivalence to a certain value).

Once a DVF is published, any user can choose to trust the signer of that DVF and validate the contained bytecode and constraints against the on-chain smart contract at any given block number. As long as the DVF has been carefully crafted to ensure security of the smart contract, a successful validation indicates that the smart contract has been deployed correctly and, since then, not changed in any way that compromises security.

Content

  1. Prerequisites
  2. Installation

  3. Building From Source

  4. Using Docker

  5. Configuration

  6. Basic Usage

  7. Create DVF

  8. Validate DVF
  9. Update DVF
  10. Check Bytecode
  11. Handle errors

  12. Advanced Usage

  13. Proxies and Delegated calls

  14. Factories
  15. References
  16. Registry
  17. Etherscan Verified Contracts

  18. Common Problems

  19. Getting Help
  20. Examples
  21. Known Limitations and Bugs
  22. Supported Networks

Prerequisites

Depending on your use case, dv has different requirements.

  1. If you only want to validate a DVF received by a trusted signer, go to DVF validation.
  2. If you want to create DVF files, go to DVF creation.

DVF Validation

To successfully validate DVFs, you need access to the following APIs:

  1. An RPC node for the given chain ID.
  2. (Optional) An Etherscan API key.

To run dv, you can either build from source it or use the pre-configured Docker image.

If you choose to install dv, Rust has to be installed on your system.

Once you have it installed you can continue to validate.

DVF Creation

To successfully create DVFs for on-chain smart contracts, you need access to the following APIs:

  1. An RPC archive node for the desired chain ID.
  2. (Optional) A BitQuery API key.
  3. (Optional) An Etherscan API key.

Please note the following restrictions/requirements:

  1. While BitQuery and Etherscan API keys are optional, at least one of them is required to determine the deployment transaction of a contract. If you provide neither, you are limited to local RPC nodes with less than 100 blocks.
  2. A BitQuery API key allows for faster execution.
  3. Your RPC node must support either debug_traceTransaction or trace_transaction.
  4. Your RPC node should support debug_traceTransaction with opcode logger enabled. Otherwise, dv won't be able to decode mapping keys.
  5. For faster execution, your RPC node may support debug_storageRangeAt.

The RPC provider QuickNode supports all aforementioned requirements. A full list of supported RPC providers may be added here at a later point in time.

To run dv, you can either build from source it or use the pre-configured Docker image.

If you choose to install dv, the following dependencies have to be installed on your system:

  1. Rust
  2. Foundry
  3. (Optional) NodeJS

NodeJS is only required if you are running dv in a Hardhat project. Foundry is always required even when you are not interacting with any Foundry projects.

Installation

Building From Source

To install dv, clone this repository and build:

git clone TODO: add repo URI
cd deployment-validation
cargo install --path .

This creates a binary at ~/.cargo/bin. You can add the location to your PATH with the following command:

echo "export PATH=$PATH:$HOME/.cargo/bin" >> ~/.profile

Depending on your system's configuration, this command might have to be adapted.

Using Docker

To run dv with the pre-configured Docker image, clone this repository and run:

git clone TODO: add repo URI
cd deployment-validation
docker build -t dv .

The docker image can then be started from the directory containing all required files (DVFs and/or project folders):

docker run --rm -v $PWD:/home/dv/shared -it dv

The folder shared in your Docker home directory now contains all files of the directory you executed the command in.

Configuration

Before dv can be used for validation and/or DVF creation, a configuration file has to be created. dv searches for the file in ~/.dvf_config.json by default. If you want to store the file at a different location, the --config parameter has to be used any time you run dv:

dv --config <PATH> <COMMAND>

The config file can be generated interactively with the following command:

dv generate-config

To be able to sign DVFs, a "signer" configuration can be added during the interactive command. It should be noted that the address you use for signing should also be added to the "trusted signers" so that you are able to validate your own DVFs.

If you wish to create the file manually or change it at a later point in time, please refer to the configuration specification.

Basic Usage

Create DVF

This section describes how to create a DVF for a simple smart contract. If the desired smart contract is a factory, proxy or requires any other special handling, please refer to Advanced Usage.

Step 1 - Initialize a DVF

To create a DVF for a simple smart contract, run the following command:

dv init --project <PROJECT_PATH> --address <ADDRESS> --contractname <NAME> new.dvf.json

Replace the placeholders with:

  • <PROJECT_PATH>: The root directory of the project on your local system.
  • <ADDRESS>: The on-chain address of the contract.
  • <NAME>: The name of the contract.

dv compiles the Foundry project in <PROJECT_PATH>, compares the compiled bytecode of <NAME> with the bytecode of <ADDRESS> deployed on the Ethereum Mainnet and decodes the storage as well as gathers all emitted events in the deployment block.

To check a contract on another EVM chain, you can pass the respective chain ID with --chainid:

dv init --project <PROJECT_PATH> --address <ADDRESS> --contractname <NAME> --chainid <CHAIN_ID> new.dvf.json

An RPC endpoint for the given <CHAIN_ID> must be present in your configuration file.

If the project uses Hardhat instead of Foundry, you can pass the Hardhat environment with --env:

dv init --project <PROJECT_PATH> --address <ADDRESS> --contractname <NAME> --env hardhat new.dvf.json

If the Hardhat project does not store its compilation artifacts in the default directory, you can pass the correct directory with --artifacts:

dv init --project <PROJECT_PATH> --address <ADDRESS> --contractname <NAME> --env hardhat --artifacts <ARTIFACTS> new.dvf.json

In many cases, deployments are not completed in one block as parameters may be set in subsequent transactions. To receive the storage at a later block (and emitted events up to that block), you can pass the desired block number with --initblock:

dv init --project <PROJECT_PATH> --address <ADDRESS> --contractname <NAME> --initblock <B> new.dvf.json

Please note that <B> must be equal to or larger than the deployment block of the contract. Additionally, it is recommended to use only block numbers of finalized blocks in order to prevent the DVF containing wrong data due to possible re-orgs in the future.

Step 2 - Validate data and select constraints

After Step 1, a new JSON file has been created that contains the following data:

  • Immutable variables.
  • Constructor arguments.
  • Critical storage variables.
  • Critical events.
  • insecure flag.

You must now perform the following tasks:

  1. Verify that all immutable variables (and possibly constructor arguments) have been set to the correct values depending on the project's security requirements.
  2. Select all storage variables that are critical to the security of the project and delete the rest.
  3. If necessary, update the comparison_operator and value (e.g., if the balance of a token should be at least a certain amount, you can set the GreaterThan comparison operator and the specified amount). The available operators are:

  4. Equal.

  5. GreaterThan.
  6. LessThan.
  7. GreaterThanOrEqual.
  8. LessThanOrEqual.

  9. Select the events that are critical to the security of the project and delete the rest.

  10. If the deployment is not secure in its current state, set the insecure flag to true.
  11. (Optional) Fill in the unvalidated_metadata.
  12. (Optional) Set an expiry timestamp in the expiry_in_epoch_seconds field.

For a detailed description of all fields contained in a DVF, please refer to the technical specification.

Once the DVF is validated against an on-chain smart contract, changes that do not satisfy the given constraints anymore result in the validation to fail. It is therefore important that the DVF only contains constraints that are not violated during normal activity (e.g., a constraint that requires the totalSupply of a token to be a specific value does not work here). Additionally, the constraints should only be related to security. This means, any storage variables / events that would not compromise security in any way if changed / emitted should be deleted.

Step 3 - Finalize the DVF

When the DVF is finished, you can sign it using the following command:

dv sign new.dvf.json

If you do not wish to sign the DVF, you can instead finalize it by generating an ID:

dv id new.dvf.json

Step 4 - Test your DVF

Once your DVF is signed, it is ready to be shipped. However, you should first check that it validates correctly:

dv validate new.dvf.json

Validate DVF

If you want to validate DVFs, you first have to decide which DVF publishers you can trust. This can include auditors or any other entities who you consider capable of understanding the intricacies of the smart contracts you want to validate.

The addresses of these signers have to be set in your configuration file, see Configuration for details.

After you have added the appropriate addresses, you can start validating any DVFs signed by them:

dv validate new.dvf.json

This will validate the DVF against any security-relevant changes the respective smart contract has undergone from its deployment to the end of the latest block on its chain. If you would like to perform the same task for a different end block, use the following command:

dv validate --validationblock <B> new.dvf.json

<B> must be greater than the deployment block of the contract and smaller than or equal to the current block of the smart contract's chain.

If you wish to validate DVFs that have not been signed, you can add the --allowuntrusted flag:

dv validate --allowuntrusted new.dvf.json

Update DVF

Please note: The update command is currently only updating existing storage variables in a DVF and might not be suitable for fully updating a DVF to the current state of a smart contract. This behavior will be changed in future releases.

To update the values of storage variables in a DVF to the state of the latest block and gather all events up to this block, run the following command:

dv update new.dvf.json

If you want to update storage variables and events to a certain block number, you can pass the desired block number with --validationblock:

dv update --validationblock <B> new.dvf.json

<B> must be greater than the deployment block of the contract an

Extension points exported contracts — how you extend this code

BasicDVF (Interface)
(no doc) [2 implementers]
lib/dvf/parse.rs

Core symbols most depended-on inside this repo

print_progress
called by 26
src/dvf.rs
send_blocking_web3_post
called by 15
lib/web3.rs
set_chain_id
called by 12
lib/dvf/config.rs
write_to_file
called by 8
lib/dvf/config.rs
get_number_of_bytes
called by 7
lib/bytecode_verification/types.rs
get_all_txs_for_contract
called by 6
lib/web3.rs
get_eth_block_number
called by 6
lib/web3.rs
get_eth_debug_trace
called by 5
lib/web3.rs

Shape

Method 177
Function 126
Class 54
Enum 11
Interface 1

Languages

Rust99%
TypeScript1%

Modules by API surface

lib/web3.rs88 symbols
lib/dvf/parse.rs41 symbols
lib/dvf/config.rs37 symbols
lib/state/contract_state.rs36 symbols
lib/bytecode_verification/parse_json.rs28 symbols
tests/test_end_to_end.rs25 symbols
lib/utils/pretty.rs24 symbols
lib/state/forge_inspect.rs14 symbols
src/dvf.rs12 symbols
lib/bytecode_verification/compare_bytecodes.rs12 symbols
lib/dvf/registry.rs10 symbols
lib/dvf/abstract_wallet.rs10 symbols

For agents

$ claude mcp add deployment_validation \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact