> ## Documentation Index
> Fetch the complete documentation index at: https://docs.monad.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# MIP-8 激活与页存储迁移

## 简介

本文档介绍激活 MIP-8 的运维方面：将节点的数据库从 **slot 编码迁移到 page 编码**，实时进行且无需从创世块重新同步。每个节点在**双写**两种编码，直到分叉将网络切换到 **page 编码**数据库，然后删除旧的 **slot 编码**数据库。

## 背景

### MIP-8

[MIP-8](https://monad.xyz/blog/mip-8) 重新组织了 EVM 状态 trie 使其页感知：存储 slot 被分组到 4 KB 的页中，而不是通过哈希独立分散在磁盘上。对于节点运营者来说，重要的是这改变了磁盘上的 trie 布局 — 这就是为什么需要迁移。

### TrieDB 时间线

**时间线**是磁盘上 trie 状态的完整副本，通过将每个已提交的区块以给定编码写入其中构建而成。节点可以一次运行一个或两个时间线，每个都有自己的编码：

* **Slot 编码时间线**使用当前的 trie 布局，每个存储 slot 的磁盘位置由哈希独立确定。这是 MIP-8 之前的格式。
* **Page 编码时间线**使用 MIP-8 布局，slot 被打包到 128 个连续 slot 的页中。这是 MIP-8 之后的格式。

**双模式**是一种过渡状态，节点同时运行两种编码，作为磁盘上的两个时间线，并将每个已提交的区块写入两者。节点在激活 page 时间线和停用 slot 时间线之间的整个窗口期都处于双模式。

**主**和**次**只是标记磁盘上的两个时间线。它们对执行或共识没有影响。在迁移过程中，主和次会交换，然后次被丢弃（参见[迁移计划](#migration-plan)）。

### 状态机类型

节点的数据库工具（`monad-mpt`、`monad-cli`）将 slot 和 page 编码称为状态机类型：

| CLI 中的类型   | 编码   |
| ---------- | ---- |
| `ethereum` | slot |
| `monad`    | page |

对于执行而言，重要的是哪个时间线是**规范的**还是**影子的**。规范时间线是其 `state_root` 用于规范链上区块的时间线；影子时间线在另一种编码上并行计算相同的区块，但其 `state_root` 不会在任何地方使用。

MIP-8 激活正是这两者之间的翻转：

* 激活前，`ethereum`（slot）是规范的，`monad`（page）是影子
* 在硬分叉时交换，`monad`（page）成为规范的，`ethereum`（slot）成为影子。

这就是为什么激活需要通过全网共识发生。

## 迁移计划

迁移分三个阶段进行：两个运营者操作和一个硬分叉。阶段 A 在每个节点上激活 page 时间线。阶段 B 是 MIP-8 硬分叉：在固定时间戳，规范链从 slot 时间线切换到 page 时间线。阶段 C 停用现在已成为遗留的 slot 时间线。

<img src="https://mintcdn.com/monadfoundation-40611fb6/BP6rYLBOYSsAWjlK/static/img/node-ops/upgrade-instructions/mip8-migration-timeline.svg?fit=max&auto=format&n=BP6rYLBOYSsAWjlK&q=85&s=b66540eb00f1a9d210b5b6e95c98d6b6" width="900" style={{marginLeft: "auto", marginRight: "auto"}} data-path="static/img/node-ops/upgrade-instructions/mip8-migration-timeline.svg" />

<Steps>
  <Step title="初始状态">
    DB 模式是 Legacy/Slot — 单个 slot 时间线。执行为 MIP-8 激活前状态。
  </Step>

  <Step title="阶段 A — Page 激活">
    运营者激活 page 次时间线，将 DB 模式变为 Dual。执行仍处于 MIP-8 激活前状态 — 在执行层面尚未发生变化。
  </Step>

  <Step title="阶段 B — 硬分叉">
    在分叉时间戳，MIP-8 在全网激活：page 时间线成为规范，slot 时间线成为其影子。节点保持在 Dual 模式。
  </Step>

  <Step title="阶段 C — Slot 停用（最终）">
    运营者将 page 时间线提升为主时间线，并停用现在成为次时间线的 slot 时间线。DB 模式翻转到 Page — 单个 page 时间线。节点现在已完全迁移。
  </Step>
</Steps>

## 运维

### 网络状态

| 网络      | 阶段      | 硬分叉时间戳                              | MIP-8 已激活 |
| ------- | ------- | ----------------------------------- | --------- |
| Testnet | 完成      | `1786545000` (2026-08-12 14:30 UTC) | 是         |
| Mainnet | Phase-C | `1788359400` (2026-09-02 14:30 UTC) | 是         |

### 获取当前状态

直接使用 `monad-mpt` 检查节点当前的磁盘状态：

```bash theme={null}
monad-mpt --storage /dev/triedb
```

**Legacy/Slot** — 未开始迁移：

```
Active timelines:
     Primary:
        State machine kind: ethereum
```

**Dual** — 迁移进行中：

```
Active timelines:
     Primary:
        State machine kind: ethereum
     Secondary:
        State machine kind: monad
```

**Page** — 迁移完成：

```
Active timelines:
     Primary:
        State machine kind: monad
```

### Prometheus 指标

您可以通过 Prometheus 指标 `monad_triedb_migration_phase` 监控双 DB 迁移阶段：

* `0`：slot 编码（初始阶段的预期值）
* `1`：双模式（阶段 A 和阶段 B 的预期值）
* `2`：page 编码（阶段 C 的预期值）

### Monad 状态

[`monad-status`](/zh/node-ops/general-operations#node-status-with-monad-status) 脚本已更新以反映 TrieDB 模式，例如：

```yq theme={null}
triedb:
  model: SAMSUNG MZQL21T9HCJR-00A07
  device: nvme0n1p1
  mode: dual-timeline
  timelines:
    primary: ethereum
    secondary: monad
```

### 硬重置节点

硬重置流程本身没有改变 — 仍然遵循[硬重置说明](/zh/node-ops/node-recovery/hard-reset)。下面的 DB 模式逻辑在同一个 `restore-from-snapshot` 步骤中自动发生；无需学习新的手动操作。

硬重置不会迁移您现有的数据库 — 它会清除数据库并从快照中重建。这意味着重置工具无法查看节点先前的磁盘状态来决定要重建什么（重置会在其他操作运行之前截断 DB），因此它会使用以下逻辑选择目标 DB 模式：

* 如果 MIP-8 硬分叉已经通过，它会仅创建 page 时间线，并将快照导入其中。
* 如果分叉尚未通过且它也可以创建 page 时间线，则结果为 Dual 模式，快照会导入两个时间线。
* 如果分叉尚未通过且无法创建 page 时间线，则结果为 Legacy/Slot，快照仅导入 slot 时间线。

| MIP-8 激活时间戳 | 支持创建 Page 时间线 | 结果 DB 模式    |
| ----------- | ------------- | ----------- |
| MIP-8 已激活   | —             | Page        |
| 未激活         | 是             | Dual        |
| 未激活         | 否             | Legacy/Slot |

第一行是有意为之的：一旦 MIP-8 在网络上激活，就没有理由建立一个 slot 时间线，只是为了在下一阶段再次停用它 — 重置的节点直接进入 page-only 模式。

## 迁移任务

这适用于全节点和验证者。
迁移分三个阶段进行。阶段 A 和 C 是您在每个节点上执行的操作；阶段 B 是按计划发生的全网硬分叉。

### 阶段 A - Page 激活

<Warning>
  **请勿执行此程序。请等待 Monad 基金会的官方公告。**

  **验证者迁移将分批进行。**
  请确保您知道自己所在的批次，仅在您所在批次被宣布时才执行这些步骤。
  部署日期和批次将通过官方渠道传达。
</Warning>

此过程从 `0.15.2` 开始支持。
此步骤使用从该版本开始的新 `monad-mpt` 和 `monad-cli` 二进制文件。

**停止 monad 服务：**

```bash theme={null}
sudo systemctl stop monad-bft monad-execution monad-rpc
```

快照转储将打开许多文件描述符（每个 shard 8 个）。如果您的系统打开文件限制为默认的 1024，转储将崩溃。

**在运行之前提高限制：**

```bash theme={null}
ulimit -n 65536
```

**准备快照文件夹并清理任何预先存在的次时间线：**

```bash theme={null}
SNAP_DIR="/home/monad/monad-bft/snapshots/page-migration"
rm -rf "$SNAP_DIR" && mkdir -p "$SNAP_DIR"
monad-mpt --storage /dev/triedb --deactivate-secondary || true
```

**激活次时间线：**

```bash theme={null}
monad-mpt --storage /dev/triedb --repair
monad-mpt --storage /dev/triedb --activate-secondary --state-machine monad
```

预期输出：`Activated secondary timeline; stamped state-machine kind to monad.`

**转储当前主时间线的快照：**

```bash theme={null}
monad-cli --db /dev/triedb --version latest_finalized --dump-binary-snapshot "$SNAP_DIR"
```

预期输出：`snapshot dump success=true`。大约需要 5 分钟。

**将快照加载到 page 次时间线中：**

```bash theme={null}
monad-cli --db /dev/triedb --version latest_finalized --load-binary-snapshot "$SNAP_DIR" --secondary
```

预期输出：`load_to_secondary=true`。大约需要 3 分钟。

**清理快照：**

```bash theme={null}
rm -rf "$SNAP_DIR"
```

**验证 TrieDB 的状态：**

```bash theme={null}
monad-mpt --storage /dev/triedb
```

该命令应打印类似于以下的输出：

```text theme={null}
Active timelines:
     Primary:
        State machine kind: ethereum
        History: 19513 versions, earliest is 47516671, latest is 47536183
        Auto expire version: 47516671
     Secondary:
        State machine kind: monad
        History: 256 versions, earliest is 47535928, latest is 47536183
        Auto expire version: 47535929
```

验证两个时间线的 `latest` 值接近（此示例中为 `47536183`）：

* Primary: `latest is 47536183`
* Secondary: `latest is 47536183`

几个区块的差异（最多约 10 个）是正常的。

**启动 Monad 服务：**

```bash theme={null}
sudo systemctl start monad-bft monad-execution monad-rpc
sudo systemctl status monad-bft monad-execution monad-rpc --no-pager -l
```

所有服务都应显示 Active：`active (running)`。确认节点重新加入并推进区块。

完成后，节点处于双写模式 — 下次启动时它将把每个新区块提交到两个时间线。
`monad-execution` 日志会打印 `state_root primary=<slot root> secondary=<page root>`，两个时间线都应出现在每个区块中。

在每个节点上运行此操作以将节点带入双模式。
每个节点必须在 MIP-8 硬分叉时间戳之前完成此阶段。

### 阶段 B - MIP-8 硬分叉

**MIP-8 激活**

阶段 B 是计划中的协议升级，而不是运营者的操作。其时间戳编码在全链范围的版本中，对每个节点都相同 — 只有当整个网络完成阶段 A 后才会发布。

在该时间戳，MIP-8 在执行层激活，规范的 `state_root` 从 slot 时间线翻转到 page 时间线，全网范围内，在同一个区块中：

* **分叉之前** — slot 时间线是规范的；page 时间线跟随它。
* **分叉时及之后** — page 时间线是规范的；slot 时间线跟随它。

磁盘上的主时间线是哪个与规范的是哪个无关。

### 阶段 C - Slot 停用

此阶段将停用 TrieDB 中的 slot 时间线。此操作只能在 MIP-8 激活硬分叉时间戳之后进行。

<Warning>
  **验证者迁移将分批进行。**
  请确保您知道自己所在的批次，仅在您所在批次被宣布时才执行这些步骤。
  部署日期和批次将通过官方渠道传达。
</Warning>

<Warning>
  **状态归档节点**

  状态归档节点不运行此阶段 — 请参见[状态归档节点](#state-archive-nodes)。
</Warning>

另请注意，在 MIP-8 激活时间戳后进行硬重置的节点将仅恢复为 Page 时间线，无需停用任何时间线。

**验证节点是否需要此阶段：**

```bash theme={null}
monad-mpt --storage /dev/triedb
```

预期输出：

```text theme={null}
Active timelines:
     Primary:
        State machine kind: ethereum
     Secondary:
        State machine kind: monad
```

只有当主时间线是 `ethereum`（slot）且次时间线是 `monad`（page）时才继续。任何其他组合都意味着节点不应运行此阶段。

**停止 monad 服务：**

```bash theme={null}
sudo systemctl stop monad-bft monad-execution monad-rpc
```

**将 page 次时间线提升为主时间线：**

```bash theme={null}
monad-mpt --promote-secondary --storage /dev/triedb
```

**停用现在已过时的 slot 时间线，回收其磁盘空间：**

```bash theme={null}
monad-mpt --deactivate-secondary --storage /dev/triedb
```

**验证 TrieDB 的状态：**

```bash theme={null}
monad-mpt --storage /dev/triedb
```

该命令应打印类似于以下的输出：

```text theme={null}
Active timelines:
     Primary:
        State machine kind: monad
```

应仅保留一个状态机类型为 `monad` 的主时间线 — slot 时间线现在已消失。

**启动 Monad 服务：**

```bash theme={null}
sudo systemctl start monad-bft monad-execution monad-rpc
sudo systemctl status monad-bft monad-execution monad-rpc --no-pager -l
```

所有服务都应显示 Active：`active (running)`。确认节点重新加入并推进区块。现在它是一个单一的 page 时间线。

Monad 未来的版本将强制要求 slot 编码时间线被停用。

## 状态归档节点

[状态归档节点](/zh/node-ops/archive-data/genesis-replay)(也称为历史全节点)在其本地 TrieDB 数据库中存储自创世以来的每个区块，而不是对旧状态进行剪枝。这使其在 MIP-8 迁移中成为特殊情况：

* **阶段 A** — 无差异。状态归档节点像其他任何节点一样升级并运行 Dual-DB 迁移。
* **阶段 B** — 在硬分叉时间戳时，在状态归档节点上重新运行[阶段 A](#phase-a-page-activation) 程序。这会在主时间线当前位置重新播种辅助时间线 — 这是仅适用于状态归档节点的特殊情况。
* **阶段 C** — **状态归档节点不运行 slot 停用。** 跳过此阶段：`slot-encoded` 时间线永久保持活动状态，以便节点保留其完整历史。

## 延伸阅读

* [工程运行手册](https://github.com/category-labs/monad/blob/max/page-store-migration-runbook/docs/page_store_migration_runbook.md) — 完整的运营者程序，包括故障处理、回滚和护栏
* [网络信息](https://docs.monad.xyz/networks.json) — 当前网络状态和分叉时间戳
* [MIP-8](https://monad.xyz/blog/mip-8) — 页感知存储背后的提案
* [Mipland](https://mipland.com/mip-8) — MIP-8 跟踪和讨论
* [MIP-8: page-ified storage state](https://forum.monad.xyz/t/mip-8-page-ified-storage-state/407) — 论坛讨论
