> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://docs.ampdb.io/api/mcp` to find what you need.

# ampctl verify

Status: Experimental.

`ampctl verify` checks that the blocks a [raw dataset](/datasets/raw-datasets) stores match the commitments the chain
itself makes to them. For an EVM chain, it recomputes each block's hash from its stored header fields, and the roots of
its transactions and receipts from its stored transactions and logs, then compares them with the ones in the block
header. A block whose data changed in storage, lost a transaction or a log, or was extracted wrong fails. For Solana,
it checks each transaction's signatures, that each slot links to its parent's block hash, and that the stored rows are
consistent with each other.

It reads the blocks through the [query server](/running-amp/query-server), like any client, so it checks the data
queries return. To learn what a passing run proves and what it doesn't, like that the blocks belong to the canonical
chain, see [Verification](/managing-amp/verification). To verify blocks as they're extracted instead, deploy the
dataset with [`ampctl dataset deploy --verify`](/clis/ampctl/dataset#ampctl-dataset-deploy).

## Prerequisites

* A running Amp, like the one the [Quickstart](/get-started/quickstart) starts with
  [solo mode](/running-amp/solo-mode).
* A raw dataset of kind `evm-rpc` or `solana`, extracted through the blocks you verify, like the Quickstart's
  `acme/eth_mainnet@1.0.0`, which holds blocks 24,000,000 to 24,000,099.
* `ampctl`, [installed](/clis/ampctl#install), and access to the query server's
  [Arrow Flight](/querying/arrow-flight) endpoint, `http://localhost:1602` in solo mode.
* To compare failed blocks with the chain: an RPC endpoint for it that serves `eth_getBlockByNumber` and
  `eth_getBlockReceipts` for those blocks, in an environment variable:

  ```bash
  export RPC_URL=https://ethereum-rpc.example.com
  ```

## Usage

```
ampctl verify [OPTIONS] --end-block END_BLOCK
```

| Flag                          | Type                   | Default                        | Description                                                                                                                                                                                                                    |
| ----------------------------- | ---------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--dataset <DATASET>`         | string                 | `edgeandnode/ethereum_mainnet` | The dataset to verify, as `namespace/name` for its latest version, or `namespace/name@version`, like `--dataset acme/eth_mainnet@1.0.0`.                                                                                       |
| `--encoding <ENCODING>`       | `ethereum` or `solana` | `ethereum`                     | The checks to run, which must match the dataset's kind: `ethereum` for `evm-rpc` datasets, and `solana` for `solana` datasets, like `--encoding solana`.                                                                       |
| `--start-block <START_BLOCK>` | integer                | `0`                            | The first block to verify, like `--start-block 24000000`. Set it to the dataset's start block or later: a range that starts before the dataset's first block fails (see [Errors](/clis/ampctl/verify#errors)).                                    |
| `--end-block <END_BLOCK>`     | integer                | required                       | The last block to verify, included, like `--end-block 24000099`.                                                                                                                                                               |
| `--parallelism <PARALLELISM>` | integer, 1 or more     | `1`                            | How many workers fetch and verify blocks at once, like `--parallelism 4`. Each takes 8,192 blocks at a time, so more workers only speed up longer ranges.                                                                      |
| `--rpc-url <RPC_URL>`         | URL                    | none                           | An RPC endpoint to fetch each failed block from, to show how the stored block differs, like `--rpc-url $RPC_URL`. `ethereum` encoding only. See [Compare failed blocks with the chain](/clis/ampctl/verify#compare-failed-blocks-with-the-chain). |
| `--amp-url <AMP_URL>`         | URL                    | `http://localhost:1602`        | The query server's Arrow Flight endpoint, like `--amp-url https://amp.example.com:1602`. Unlike [`ampsql`](/clis/ampsql), `verify` doesn't read `AMP_QUERY_URL`.                                                               |
| `--amp-auth <AMP_AUTH>`       | string                 | none                           | The bearer token for the query server, for a deployment that requires one. Unlike `ampsql`, `verify` doesn't read `AMP_AUTH_TOKEN`.                                                                                            |

Block numbers are plain digits, like `24000099`: `24,000,099` fails.

`verify` only talks to the query server, so it takes none of the [global flags](/clis/ampctl#global-flags):
`--admin-url`, `--auth-token`, and `--json` fail with an error like `error: unexpected argument '--json' found`. Its
output is always text.

## Verify a range of blocks

:::terminal
```bash
ampctl verify --dataset acme/eth_mainnet@1.0.0 --start-block 24000000 --end-block 24000099
```

```
Successfully verified blocks 24000000..=24000099
```
:::

`--encoding` picks the checks, so pass `--encoding solana` for a `solana` dataset. `verify` doesn't check the dataset's
kind: with the wrong encoding, it can't read the tables it expects, and retries without end (see [Errors](/clis/ampctl/verify#errors)).
Without a version in `--dataset`, `verify` reads the latest version.

In a terminal, a progress bar also shows how far it is:

```
[00:00:00] [===========================>            ] 68/100 blocks (68%)
```

When the output isn't a terminal, like in a script, or `TERM` is unset or `dumb`, the progress bar is left out, and so
are the lines that say which blocks failed and why. Check the exit code there: 0 when every block passes, and 1 when
any fails or `verify` can't finish. See [Exit codes](/clis/ampctl#exit-codes).

To verify part of a dataset, pass a later `--start-block`:

:::terminal
```bash
ampctl verify --dataset acme/eth_mainnet@1.0.0 --start-block 24000090 --end-block 24000099
```

```
Successfully verified blocks 24000090..=24000099
```
:::

For Solana, the block numbers are slots, and the slots the chain skipped count as verified: a range of 16 slots with
four skipped prints `Successfully verified blocks 444960110..=444960125`.

Two things go wrong with Solana in Amp v0.0.42:

* **Version 1 transactions** can't be verified, and Solana has carried them since epoch 1036, in September 2026. A run
  over a slot that holds one fails with `Verification worker(s) failed`, after
  `unsupported transaction version 'v1' for slot 456192076, tx index 58` in a terminal.
* **A correct block can fail**, about one run in ten, with `signature verification failed for slot …, tx index …`:
  `verify` rebuilds each transaction from its instructions in the order the query server returns them, which isn't
  always their original order. Run it again before you trust a Solana failure.

## When blocks fail

`verify` checks every block in the range, then reports the ones that failed. Here, one block's `gas_used` was changed
in its stored file, so its header no longer hashes to the block's hash (the progress bar is left out):

:::terminal
```bash
ampctl verify --dataset acme/eth_mainnet@1.0.0 --start-block 24000000 --end-block 24000099
```

```
  FAIL block 24000042: verify block 24000042: block hash mismatch: computed 0x05079fdc25f53c900c38eb033f96b8d8fc8bdb5d7bd44ec9c117490456921a0d, expected 0xd10b87c52a771e86cb29d655281913f74507f158eab999f337aade55e994c299
Verified 99/100 blocks (1 failed)
✗ Verification failed for 1 block(s)
```
:::

Each `FAIL` line names a block and the check it failed. `computed` is the hash `verify` computed from the stored data,
and `expected` the one the stored block header records.

| Check                        | What doesn't match                                                          |
| ---------------------------- | --------------------------------------------------------------------------- |
| `block hash mismatch`        | The block's header fields, like its `gas_used` or `timestamp`, or its hash. |
| `transactions root mismatch` | Its transactions: one is missing, added, out of order, or changed.          |
| `receipts root mismatch`     | Its transactions' results: their status, gas used, or logs.                 |

Blocks with transactions of a type `verify` can't check yet, like OP Stack deposits and Arbitrum's own types, fail
too. [Verification](/managing-amp/verification) lists the types it supports.

Only the `Verified 99/100 blocks (1 failed)` line goes to standard output; the rest goes to standard error.

## Compare failed blocks with the chain

With `--rpc-url`, `verify` fetches each failed block from the RPC endpoint, and adds the fields that differ to the
`FAIL` line. EVM datasets only: with `--encoding solana`, `--rpc-url` fails (see [Errors](/clis/ampctl/verify#errors)).

:::terminal
```bash
ampctl verify --dataset acme/eth_mainnet@1.0.0 --start-block 24000040 --end-block 24000049 --rpc-url $RPC_URL
```

```
  FAIL block 24000042: verify block 24000042: .gas_used: left=12637365, right=12637364
.transactions[0].gas_price: left=null, right=21527058
.transactions[1].gas_price: left=null, right=21527058
…
.transactions[126].gas_price: left=null, right=21527058: block hash mismatch: computed 0x05079fdc25f53c900c38eb033f96b8d8fc8bdb5d7bd44ec9c117490456921a0d, expected 0xd10b87c52a771e86cb29d655281913f74507f158eab999f337aade55e994c299
Verified 9/10 blocks (1 failed)
✗ Verification failed for 1 block(s)
```
:::

Each line is a field, then its value in Amp (`left`) and from the RPC endpoint (`right`). Here, `gas_used` is the field
that changed. The comparison also lists, for every transaction, fields that Amp and RPC endpoints record differently,
like `gas_price` and `max_fee_per_gas`, so look past those.

If the endpoint can't serve the block, the `FAIL` line says why instead, like for an endpoint that no longer keeps old
receipts:

```
  FAIL block 24000042: verify block 24000042: RPC verification also failed: fetch block from RPC: eth_getBlockReceipts: RPC response missing result field: block hash mismatch: computed 0x05079fdc25f53c900c38eb033f96b8d8fc8bdb5d7bd44ec9c117490456921a0d, expected 0xd10b87c52a771e86cb29d655281913f74507f158eab999f337aade55e994c299
```

If the endpoint's block fails verification too, like when it serves the same bad data, the line gives that failure
after `verify block from RPC`:

```
  FAIL block 24000042: verify block 24000042: RPC verification also failed: verify block from RPC: block hash mismatch: computed 0x05079fdc25f53c900c38eb033f96b8d8fc8bdb5d7bd44ec9c117490456921a0d, expected 0xd10b87c52a771e86cb29d655281913f74507f158eab999f337aade55e994c299: block hash mismatch: computed 0x05079fdc25f53c900c38eb033f96b8d8fc8bdb5d7bd44ec9c117490456921a0d, expected 0xd10b87c52a771e86cb29d655281913f74507f158eab999f337aade55e994c299
```

## Errors

Most errors stop `verify` with exit code 1, and wrong arguments with exit code 2.

* `error: invalid value 'foo' for '--encoding <ENCODING>': unsupported verification encoding: foo`: use `ethereum` or
  `solana`.

* `--rpc-url` with `--encoding solana`:

  ```
  ✗ --rpc-url fallback is only supported with --encoding ethereum
  ```

* A range with blocks the dataset doesn't have, like ones it hasn't extracted yet, or a range that starts before the
  dataset's first block, as without `--start-block`. Check how far it got with
  [`ampctl dataset status`](/clis/ampctl/dataset#ampctl-dataset-status). The block the message names isn't always
  the missing one: `verify` counts the blocks it found from the start of the range, and names the block after them.
  So a range that starts before the dataset's first block, like `--start-block 23999990`, names a later block that
  exists, like `24000090`.

  ```
    Worker error: worker 0: fetch blocks 24000000..=24000100: ethereum fetch failed: failed to convert Ethereum record batches: dataset missing block 24000100
  Verification worker(s) failed
  ✗ verification worker task(s) failed
  ```

* The query server can't be reached. Check `--amp-url`. `verify` doesn't read `AMP_QUERY_URL`, so it needs the flag even
  when [`ampsql`](/clis/ampsql) reaches the query server through that variable:

  ```
    Worker error: worker 0: failed to create Amp client: transport error: tcp connect error: tcp connect error: Connection refused (os error 61)
  Verification worker(s) failed
  ✗ verification worker task(s) failed
  ```

The lines starting with `Worker error` show only in a terminal.

`verify` doesn't check that `--start-block` comes before `--end-block`. When it doesn't, it verifies nothing, and
still reports success.

:::warning
When the query server answers a fetch with an error, `verify` retries without end, waiting up to about 20 seconds
between attempts. That happens for a dataset or version that doesn't exist or was never deployed, a table with no
active revision, a [derived dataset](/datasets/derived-datasets) without the raw tables `verify` reads, and an
`--encoding` that doesn't match the dataset's kind. In a terminal, it prints
`Ethereum fetch failed (retry in 1.473834246s): ethereum fetch failed` (or `Solana fetch failed`) at each attempt;
otherwise, it waits without a word. Stop it with Ctrl+C, and check that the dataset answers a query,
like with [`ampsql`](/clis/ampsql).
:::
