# Anchors

The log root and the state root are written to Solana every hour and timestamped on Bitcoin every day, so a checkpoint can be checked against public chains the operator can't edit.

## Why anchors

A checkpoint you saved protects you; an anchor protects everyone. Once a log root is on Solana or Bitcoin, the sequencer can't later show anyone a history that doesn't extend it, because a [consistency proof](/integrity/proofs#consistency-proofs) from the anchored size would fail. Both anchors are sent by the produce job (`lib/chain/anchor.ts`), only when the checkpoint or the state commitment has changed since the last one.

## Solana memos

At most once an hour the anchor account sends a Solana transaction with one [Memo program](https://spl.solana.com/memo) instruction:

| | |
|---|---|
| Anchor account | `AeWxrwqF51Ln4xzNZeccR1J4WiaNAkG9rj6XcsHTJTgJ` |
| Memo program | `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr` |
| Memo text | `<chain> log <size> <log root> state <height> <state root> usdc <total>` |

```text title="A memo from mainnet"
mlx-1 log 398 cb97aebd459faf519f4d967d5b6a98fafff2df0f6a681f4bf89d34560b253d7e state 394 0cc077af7c40681c5a24702c0e71bed5c67333af42f1f4f16317a2123fd88f63 usdc 11285277511
```

The log part is the newest checkpoint; the state part is the newest [state commitment](/integrity/state) at that moment, which may be a few blocks older (state is committed at most hourly). Before the first commitment the memo has only the log part.

The anchor account's key is derived from the sequencer's secret, so only the sequencer can write from it. It pays the Solana fee from its own SOL; when it runs low, anchoring pauses and `GET /api/log` shows `anchorStatus.solana: "waiting for SOL"`.

### Read the memos from Solana yourself

Ask any Solana RPC, not mlx.app:

```bash
curl https://api.mainnet-beta.solana.com -H 'content-type: application/json' -d '{
  "jsonrpc": "2.0", "id": 1, "method": "getSignaturesForAddress",
  "params": ["AeWxrwqF51Ln4xzNZeccR1J4WiaNAkG9rj6XcsHTJTgJ", { "limit": 1 }]
}'
```

```json title="Response"
{
  "jsonrpc": "2.0",
  "result": [
    {
      "blockTime": 1793115364,
      "confirmationStatus": "finalized",
      "err": null,
      "memo": "[170] mlx-1 log 398 cb97aebd459faf519f4d967d5b6a98fafff2df0f6a681f4bf89d34560b253d7e state 394 0cc077af7c40681c5a24702c0e71bed5c67333af42f1f4f16317a2123fd88f63 usdc 11285277511",
      "signature": "4oShU8RoQKw9BQr1yY2cFAm2go5tBeagcgxeouzZ1rD9U68GdTnr7fN9LPY5GFw3zwysWrtYXnBEKBssb4eDhUFn",
      "slot": 462123500,
      "transactionIndex": 206
    }
  ],
  "id": 1
}
```

The RPC puts the memo's length in brackets before its text. Each memo is also on [Solscan](https://solscan.io/account/AeWxrwqF51Ln4xzNZeccR1J4WiaNAkG9rj6XcsHTJTgJ), and `GET /api/log` lists recent ones under `anchors` with their `tx`.

### Check a memo against the log

:::steps
### Read the anchored size and root
From the memo: `log 398 cb97aebd…`.

### Fetch a consistency proof
From the anchored size to the newest checkpoint: `GET /api/log/consistency?from=398&to=<size>`. If the sizes are equal, the roots must be equal.

### Verify it
With `verifyConsistency` from [Proofs](/integrity/proofs#verify-both-in-javascript), the anchored root as the old root and the checkpoint's root as the new one. A proof that fails means the log you are shown is not the one that was anchored.
:::

The state part works the same way: its root is the `root` of the commitment at that height (`GET /api/log/state/<name>?height=394` returns it as `commit`).

## Bitcoin timestamps

Once a day the sequencer timestamps the newest checkpoint and state commitment on Bitcoin through [OpenTimestamps](https://opentimestamps.org), using three public calendars. The timestamped digest is:

```text
SHA-256( checkpoint note ‖ state statement )
```

where *checkpoint note* is the full signed text served by [`/api/log/checkpoint`](/integrity/checkpoints) at that time and *state statement* is the commitment's [signed statement](/integrity/state#the-signed-statement) (left out when there is none). Each Bitcoin entry in `anchors` gives the `digest`, the `size`, `logRoot`, `height`, `stateRoot` and `usdc` it covers, and which `calendars` accepted it:

```json title="A Bitcoin anchor from GET /api/log"
{
  "chain": "bitcoin",
  "at": 1793035086989,
  "size": 354,
  "logRoot": "370ab7aa09ee8a6418614941e15a5c292c28125342b10e4c6e85678fc4196e51",
  "height": 352,
  "stateRoot": "50f9504d4712a204119bad09de00d6d1d3f160d5dc611848b046d59bd36b4b35",
  "usdc": "12285277511",
  "digest": "eeb9fda28df49ff17926b81752e0ac71e2e0a8faa153f0320638c08630c95065",
  "calendars": ["https://a.pool.opentimestamps.org", "https://b.pool.opentimestamps.org", "https://a.pool.eternitywall.com"]
}
```

### Download a receipt

```endpoint try
GET /api/log/ots/eeb9fda28df49ff17926b81752e0ac71e2e0a8faa153f0320638c08630c95065?cal=0
```

A standard `.ots` file for one calendar's receipt of that digest.

| Parameter | Type | Notes |
|---|---|---|
| `digest` | path | The anchor's `digest`, 64 hex characters. |
| `cal` | integer | Which calendar: `0` a.pool.opentimestamps.org, `1` b.pool.opentimestamps.org, `2` a.pool.eternitywall.com. Default `0`. |

Errors are plain text: `400` `bad request` (digest or `cal` malformed), `404` `not found` (no receipt from that calendar).

### Verify it

A fresh receipt is pending; the calendar adds it to a Bitcoin transaction within a few hours. Then, with the [OpenTimestamps client](https://github.com/opentimestamps/opentimestamps-client):

```bash
ots upgrade mlx-eeb9fda28df4-0.ots   # fetch the Bitcoin attestation from the calendar
ots verify -d eeb9fda28df49ff17926b81752e0ac71e2e0a8faa153f0320638c08630c95065 mlx-eeb9fda28df4-0.ots
```

`ots verify` checks the attestation against Bitcoin block headers (the client reads them from a Bitcoin node; see its README). That proves the digest existed by that block. To tie the digest to a log root, hash the checkpoint note and statement yourself and compare: this needs the exact note that was anchored. Fetch it with `GET /api/log/checkpoint?size=N`, where N is the log size the anchor names.
