Bitfinity EVM Archiver

An application to locally reproduce and store EVM blocks history and state.

Purpose

The purpose of this tool is to reproduce state of an EVM canister off-chain and keep it in-sync using Ethereum JSON-RPC API as data source.

The evm-archiver provides read-only Ethereum JSON-RPC API too. This allows to use it as data source for another instance of the evm-archiver, or just to get evm-canister data from off-chain source.

Usage

To run the archiver use CLI command:

evm-archiver [OPTIONS]

CLI options

  • --logger-filter <LOGGER_FILTER>

Sets the logger EnvFilter. Valid values: trace, debug, info, warn, error. Example of a valid filter: warn,my_crate=info,my_crate::my_mod=debug,[my_span]=trace. Default: info.

  • --server-port <SERVER_PORT>

The port that serves the tool API. This port may be used to perform read-only Ethereum JSON-RPC requests to the archiver. Default: 8080.

  • --remote-rpc-url <REMOTE_RPC_URL>

URL of the remote Ethereum JSON RPC server which will be used to import data from. Default: http://127.0.0.1:8545

  • --blocks-remote-rpc-url <REMOTE_RPC_URL>

URL of the remote Ethereum JSON RPC server which will be used to import blocks from if some of the blocks are missing in --remote-rpc-url. Default: unspecified (do not use secondary blocks source).

  • --request-delay <REQUEST_DELAY>

Delay in seconds between consequent requests in case of retry, no data, etc. Default: 10

  • --max-batch-size <MAX_BATCH_SIZE>

Max number of requests in a single JSON RPC batch. Default: 25

  • --retry-count <RETRY_COUNT>

Number of retries for operations that may fail before exiting [default: 10]. Default: 10

  • --storage-directory <STORAGE_DIRECTORY>

Directory where main storage files are kept. These files represent current EVM state and blockchain history snapshot. The "memory-mapped files" mechanism used to achieve persistent storage in combination with fast access to data. Default: storage_data

  • --backup-directory <BACKUP_DIRECTORY>

Path to the directory where to put backups. This directory stores copies of storage-directory for a certain block. Default: ./

  • --backup-job-cron <BACKUP_JOB_CRON>

Backup job cron schedule. This job executes at the given time and stores copy of the current state to the backup directory. The backup can be used to revert state of the archiver to a previous block. To do it, user should replace content of the storage directory with backup for the desired block. The default value is "0 0 2 * * *" (every day at 2:00am).

  • --block-synchronization-job-interval-seconds <BLOCK_SYNCHRONIZATION_JOB_INTERVAL_SECONDS>

Block synchronization job interval schedule. The block synchronization job querying new blocks from the remote Ethereum JSON-RPC URL and adds them to the archiver state. Default: 60

  • --identity <PATH_TO_IC_IDENTITY_PEM_FILE>

Optional path to your identity pem file. If provided, the tool will use it for calling the remote JSON RPC endpoint.

  • --evmc_principal <EVMC_PRINCIPAL>

Optional evmc canister Principal. Required if identity is set. If provided, the tool will use it for calling the remote JSON RPC endpoint.

  • --logs_synchronization_job_interval_seconds <SECONDS>

Logs synchronization job interval in seconds. This job executes is executed every <logs_synchronization_job_interval_seconds> seconds and download the evmc logs to a file on the local filesystem. The job is enables only if both identity and evmc_principal are provided. Default is 10 seconds.

  • --logs_synchronization_job_max_logs_per_call <MAX_LOGS_PER_CALL>

The max number of logs to be downloaded on each log synchronization job loop. Default is 10_000.

  • --logs_directory <LOGS_DIRECTORY>

Path to the directory where the EVM downloaded logs are written into.

Example

This command will run the application with default settings. The archiver will synchronize blocks every minute, keep the latest state in the evm-archiver-storage and save backups to the target/evm-archiver-backup directory.

Requirements:

  • The evm-archiver binary should have permission for execution.
  • The storage and backup directories should exist and allow writing.

Example command to run a evm-archiver instance:

evm-archiver \
    --remote-rpc-url http://127.0.0.1:8545 \
    --storage-directory target/evm-archiver-storage \
    --backup-directory target/evm-archiver-backup

API calls

The running evm-archiver provides http://<IP_ADDR>:8080/rpc endpoint, which accepts Ethereum JSON-RPC calls.

eth_getChainId

On request:

curl -X POST \
    -H "Content-Type: application/json" \
    --data \
    '{
        "jsonrpc": "2.0",
        "method": "eth_chainId",
        "params": [],
        "id":1
    }' \
    'http://localhost:8080/rpc'

the evm-archiver will reply like:

{
    "jsonrpc": "2.0",
    "result": "0x56b29",
    "id": 1
}

eth_getBlockByNumber

On request:

curl -X POST \
    -H "Content-Type: application/json" \
    --data \
    '{
        "jsonrpc": "2.0",
        "method": "eth_getBlockByNumber",
        "params": ["0x1", false],
        "id":1
    }' \
    'http://localhost:8080/rpc'

the evm-archiver will return block#1 data:

{
  "jsonrpc": "2.0",
  "result": {
    "baseFeePerGas": "0x3b9aca00",
    "difficulty": "0x0",
    "extraData": "0x",
    "gasLimit": "0x1c9c380",
    "gasUsed": "0x2f0a12",
    "hash": "0x196420485bbe1959ab6a7e534521b14d956a42283e365141dee3b0aee64a31e4",
    "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "miner": "0x0000000000000000000000000000000000000000",
    "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "nonce": "0x0000000000000000",
    "number": "0x1",
    "parentHash": "0x89a20ee01a71622f5e1d1765d85a69476fc288fc7889408691b30ce1d055789f",
    "receiptsRoot": "0x95f6ed8a055e8d6648d5ff73cbc62af5a01c08e411d3d46c885a456369b9bf1c",
    "sealFields": [],
    "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
    "size": "0x756e",
    "stateRoot": "0x152eb962d679aa12de5b88f37d466df5eb02165d434c54fb0a00a5857cbeb23e",
    "timestamp": "0x654c973e",
    "totalDifficulty": "0x0",
    "transactions": [
      "0x6280dca15626aaea263e44544586525c6ec5243c40950c0663d7e1216c7c95dd"
    ],
    "transactionsRoot": "0xc515e57fa66759b575ef4a651a5e8246148e1fc15f94126ec4a0b02836f108fa",
    "uncles": []
  },
  "id": 1
}

eth_getTransactionByHash

On request:

curl -X POST \
    -H "Content-Type: application/json" \
    --data \
    '{
        "jsonrpc": "2.0",
        "method": "eth_getTransactionByHash",
        "params": ["0x6280dca15626aaea263e44544586525c6ec5243c40950c0663d7e1216c7c95dd"],
        "id":1
    }' \
    'http://localhost:8080/rpc'

the evm-archiver will reply like:

{
  "jsonrpc": "2.0",
  "result": {
    "blockHash": "0x196420485bbe1959ab6a7e534521b14d956a42283e365141dee3b0aee64a31e4",
    "blockNumber": "0x1",
    "chainId": "0x56b29",
    "from": "0x2bc455fe0447fb07976a943a8eb8d63e66cc6e39",
    "gas": "0x3567e0",
    "gasPrice": "0x3b9aca00",
    "hash": "0x6280dca15626aaea263e44544586525c6ec5243c40950c0663d7e1216c7c95dd",
    "input": "<binary tx input here>",
    "nonce": "0x0",
    "r": "0xab1413f1d19ec35a43fcf04343c3072033850712f2f105c78c69b0f3b7d7590c",
    "s": "0x3b8ae0e7d3a6ce571c23399506bf07adbdbfc51f1eb42575eacddd97f2a7117b",
    "to": null,
    "transactionIndex": "0x0",
    "v": "0xad675",
    "value": "0x0"
  },
  "id": 1
}