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

# 架构总览

> Leafage 的组件划分、运行拓扑，以及数据面与控制面的职责边界

Leafage 是一套 EVM 状态查询与区块数据分发架构。它把传统全节点里耦合在一起的四件事拆开：**同步执行**、**状态存储**、**RPC 服务**、**数据导出**。

## 为什么要拆

在生产规模下，用“多部署几个 Geth”来扩展查询能力会遇到几个硬性问题：

| 问题       | 表现                                                   |
| -------- | ---------------------------------------------------- |
| 存储冗余     | 每个全节点 1.3TB+（Archive 2–6.5TB），100 个节点就是 130TB+ 的相同数据 |
| 带宽放大     | 每个节点独立参与 P2P gossip，同一份区块被从网络上拉取上百次                  |
| 副本不一致    | P2P 同步非确定性，负载均衡器后的不同节点可能处于不同高度，同一个 `eth_call` 结果不同   |
| 工作负载互相干扰 | 同步（CPU + 磁盘写）与查询共享同一进程，查询高峰拖慢同步，反之亦然                 |
| 恢复慢      | 节点故障后需要数小时到数天重新同步                                    |
| 缺少数据导出   | 提取交易/追踪/事件只能靠 `debug_traceBlock`，与生产流量争抢资源           |

Leafage 的答案是：**只让一个节点执行区块**，其余节点消费执行结果。

## 组件职责

<Columns cols={2}>
  <Card title="写节点" icon="server" href="/components/go-ethereum-x">
    执行客户端加上 pipeline 集成，在执行区块时导出状态变更、调用追踪和事件。
  </Card>

  <Card title="pipeline" icon="share-2" href="/components/pipeline">
    分发库。序列化执行数据，双桶写 S3，Leader 节点向 Kafka 发布区块变更通知。
  </Card>

  <Card title="leafage-evm" icon="cpu" href="/components/leafage-evm">
    查询节点。消费 Kafka + S3 重建状态，用 revm 执行 `eth_call`，不做 P2P、不存交易。
  </Card>

  <Card title="consistency-checker" icon="shield-check" href="/components/consistency-checker">
    校验层。等待副本收敛、标记分叉块、维护 etcd 中的节点健康状态、向外部 Kafka 发布确认通知。
  </Card>

  <Card title="nodex-proxy" icon="route" href="/components/nodex-proxy">
    网关。通过 etcd 发现节点，按区块上下文路由到 State / Archive / Native 节点池。
  </Card>
</Columns>

## 运行拓扑

```mermaid theme={null}
flowchart TB
    P2P["区块链 P2P 网络"] -->|"同步区块"| GETH

    subgraph WRITE["写侧（每条链 1 主 + N 备）"]
        GETH["写节点<br/>执行客户端 + pipeline tracer"]
    end

    GETH -->|"Header + StateDiff"| S3IN["S3 内部桶<br/>NodeX Bucket"]
    GETH -->|"BlockFile + Validation"| S3OUT["S3 外部桶<br/>ChainTable Bucket"]
    GETH -->|"BlockChangeNotification<br/>仅 Leader"| KIN["Kafka 内部 topic"]

    KIN --> LE1["leafage-evm<br/>State 节点"]
    KIN --> LE2["leafage-evm<br/>Archive 节点"]
    KIN --> CC["consistency-checker"]

    S3IN --> LE1
    S3IN --> LE2

    CC -->|"轮询 eth_blockNumber"| LE1
    CC -->|"轮询 eth_blockNumber"| LE2
    CC -->|"标记 is_fork"| S3OUT
    CC -->|"确认后的区块通知"| KOUT["Kafka 外部 topic"]
    KOUT --> EXT["外部消费者<br/>索引器 / 分析平台"]
    S3OUT --> EXT

    CC -->|"节点状态 + 链高度"| ETCD[("etcd")]
    LE1 -.->|"自注册"| ETCD
    LE2 -.->|"自注册"| ETCD
    ETCD -->|"watch"| PROXY["nodex-proxy"]

    PROXY --> LE1
    PROXY --> LE2
    CLIENT["JSON-RPC 客户端"] --> PROXY
```

## 数据面与控制面

理解 Leafage 的关键是把两条通路分开看。它们使用不同的中间件，故障影响范围也不同。

### 数据面：Kafka + S3

区块数据只走一个方向，从写节点流向消费者。

* **Kafka（内部 topic）** 只传通知，不传数据体。消息里是区块哈希、父哈希、高度、时间戳，以及 reorg 时被丢弃的区块列表。
* **S3（内部桶）** 存 Header 和 StateDiff，是 leafage-evm 重建状态的数据源。
* **S3（外部桶）** 存 BlockFile（交易、追踪、事件）和 BlockValidation，供外部消费者和分析平台使用。
* **Kafka（外部 topic）** 由 consistency-checker 发布，是外部消费者应当订阅的入口，带一致性保障。

数据面故障时，leafage-evm 停止追块但仍可服务已有状态的查询。

### 控制面：etcd

etcd 是所有运行时状态的事实来源，三个组件在上面协作：

| 写入方                 | 键                             | 读取方                             | 用途                        |
| ------------------- | ----------------------------- | ------------------------------- | ------------------------- |
| leafage-evm         | `{chainID}/nodes/{ip}_{port}` | consistency-checker、nodex-proxy | 节点自注册                     |
| consistency-checker | `{chainID}/nodes/{ip}_{port}` | nodex-proxy                     | 更新节点健康状态，离线时删除            |
| consistency-checker | `{chainID}/lastBlockNumber`   | nodex-proxy                     | 当前链高度，决定 State/Archive 路由 |
| pipeline            | `{chainID}/writers/{nodeID}`  | nodex-proxy                     | 写节点注册（带租约）                |
| pipeline            | `{chainID}/writers/leader`    | pipeline 各实例                    | 写节点 Leader 选举             |
| 运维 / nodex-proxy    | `{chainID}/gateway`           | nodex-proxy                     | 节点权重与方法路由                 |
| 运维                  | `{chainID}/version`           | consistency-checker、nodex-proxy | 版本切换                      |

完整键空间见[接口契约](/architecture/interfaces#etcd-键空间)。

<Tip>
  控制面这条闭环容易被忽略：leafage-evm 只把自己注册进 etcd 并标记为 `Delay`，真正判断它是否健康、是否追上链头的是 consistency-checker；nodex-proxy 只是这份状态的消费者。要理解“为什么某个节点没有流量”，应当从 consistency-checker 的轮询结果查起。
</Tip>

## 节点类型

| 类型         | 存储（ETH 主网） | 可查询范围               | 典型用途                 |
| ---------- | ---------- | ------------------- | -------------------- |
| State 节点   | \~90 GB    | 最新状态（内存保留最近 64 块差异） | 绝大多数 RPC 查询          |
| Archive 节点 | \~360 GB   | 任意历史高度              | 历史 `eth_call`、分析类查询  |
| Native 节点  | 取决于原链客户端   | 原链全节点能力             | Cosmos 预编译等无法本地执行的调用 |

nodex-proxy 按请求携带的区块参数在三个节点池之间路由，规则见 [nodex-proxy 组件文档](/components/nodex-proxy#节点选择)。

## 部署单元

一条链是一个独立的部署单元，各链之间不共享 Kafka topic、S3 前缀或 etcd 前缀（都以 `chainID` 开头）。一个典型的单链部署包含：

```text theme={null}
1 × 共识客户端（如 lighthouse，仅 PoS 链需要）
1 × 写节点（可加备节点，由 pipeline leader 选举决定谁发 Kafka）
1 × consistency-checker
N × leafage-evm（State 节点为主，按需加 Archive 节点）
1 × nodex-proxy（可多实例，共享同一 etcd）
共享基础设施：Kafka、S3、etcd
```

nodex-proxy 是多链的：同一个进程按路径里的 `chainId` 管理多条链的节点池，因此通常整个集群只部署一组 proxy。

## 多链支持

写侧和读侧分别适配：

* **写侧**：pipeline 需要嵌入目标链的执行客户端。已适配 40+ 条链的客户端分叉，列表见 [leafage-evm README](https://github.com/Chaintable/leafage-evm#supported-write-node-repositories)。适配方式见[接入新链](/guides/new-chain)。
* **读侧**：leafage-evm 通过 `--evm-type` 选择执行器，当前支持 `mainnet`、`arbitrum`、`op`、`base`、`bsc`、`cosmos`、`mantlev2`、`tempo`、`citrea`、`iotex`、`moonbeam`、`moonriver`、`polygon`、`hemi`。

## 设计取舍

<AccordionGroup>
  <Accordion title="leafage-evm 不存交易数据">
    `eth_call` 只需要账户状态（balance、nonce、code、storage），交易体、收据和日志都可以丢弃。代价是区块查询接口（`eth_getBlockByNumber` 等）只返回 header，`transactions` 和 `uncles` 恒为空。需要交易数据的消费者应当读 S3 外部桶或订阅外部 Kafka topic。
  </Accordion>

  <Accordion title="内存里只保留 64 个区块的差异">
    最近 64 块的状态以差异层链表形式留在内存，超出深度的层被刷写到 RocksDB。这个窗口同时是 reorg 的处理能力上限：更深的重组需要走 S3 回补路径。窗口大小由 `--diff-depth-limit` 控制。
  </Accordion>

  <Accordion title="一致性不由 leafage-evm 自己保证">
    所有查询节点消费同一个 Kafka 分区，按相同顺序应用相同区块，因此不存在 P2P 的非确定性。但“副本是否都追上了”这件事由 consistency-checker 判断，并只在达到 `ready_ratio`（默认 0.8）后才向外部通知。
  </Accordion>

  <Accordion title="Kafka 只发通知，数据体走 S3">
    通知消息很小，Kafka 只承担顺序和低延迟；大体积的 StateDiff 和 BlockFile 走 S3，消费者可以并行拉取，也便于冷启动时按需回补历史。
  </Accordion>
</AccordionGroup>

## 下一步

<Columns cols={2}>
  <Card title="数据链路" icon="git-branch" href="/architecture/data-flow">
    跟着一个区块走完执行、分发、摄入、终结化、查询五个阶段。
  </Card>

  <Card title="接口契约" icon="plug" href="/architecture/interfaces">
    Kafka、S3、etcd 和 RPC 的精确格式定义。
  </Card>
</Columns>
