> ## 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.

# 写节点

> 执行客户端上附加的 pipeline 集成：hook、启用方式与区块变更通知

写节点是 Leafage 里唯一参与 P2P 同步和区块执行的角色。它就是这条链原本的执行客户端（ETH 用 [Chaintable/go-ethereum](https://github.com/Chaintable/go-ethereum)，其他链用各自的分叉），Leafage 在它上面只附加了一层数据导出能力。

客户端本身的用法、同步模式和 RPC 请参考对应上游项目的文档。本页只说明附加的部分。

| 项        | 值                                                                                                                        |
| -------- | ------------------------------------------------------------------------------------------------------------------------ |
| ETH 参考实现 | [Chaintable/go-ethereum](https://github.com/Chaintable/go-ethereum)，当前合并到上游 v1.17.4                                      |
| 附加内容     | pipeline tracer 集成、`trace_debankBlock`、区块变更通知、历史裁剪                                                                       |
| 改动规模     | 相对上游约 2000 行，集中在少数文件                                                                                                     |
| 其他链      | op-geth、reth、nitro、erigon 等分叉，改动方式相同，[仓库列表](https://github.com/Chaintable/leafage-evm#supported-write-node-repositories) |

## 附加的集成点

| 文件                             | 附加内容                                              |
| ------------------------------ | ------------------------------------------------- |
| `core/tracing/hooks.go`        | 新增 `OnCommit` 和 `OnBlockDBStart` 两个 hook 类型       |
| `core/blockchain.go`           | 分发新 hook；确定 canonical head 时计算 reorg 并发布 Kafka 通知 |
| `core/state/statedb.go`        | 新增 `StateDiff()`，导出提交集里的状态变更                      |
| `eth/api_debank.go`            | `DebankAPI`，实现 `trace_debankBlock`                |
| `eth/backend.go`               | 注册 `trace` 命名空间                                   |
| `eth/tracers/live/pipeline.go` | 把 pipeline tracer 注册进 `LiveDirectory`             |
| `core/rawdb/chain_freezer.go`  | `--ancient.prune` 的持续裁剪                           |

<Note>
  保持改动集中是有意的：分叉需要长期跟随上游发版，触碰的上游文件越少，合并冲突越小。新增能力优先放进 pipeline 仓库，客户端侧只留调用点。
</Note>

## 启用 pipeline tracer

```bash theme={null}
geth --vmtrace pipeline --vmtrace.jsonconfig '{
  "region": "ap-northeast-1",
  "node_x_bucket": "nodex-internal",
  "chain_table_bucket": "chaintable-pipeline",
  "brokers": ["kafka-1:9092"],
  "etcd_endpoints": ["http://etcd:2379"],
  "grace_period": 10
}'
```

省略 `topic` 时按链 ID 生成 `nodex_pipeline_{chainID}`。字段完整说明见 [pipeline 配置](/components/pipeline#配置)。

tracer 挂载后，采集和上传都在 pipeline 内完成，客户端只负责在正确的时机调用 hook。采集逻辑见 [pipeline 组件文档](/components/pipeline)。

## `trace_debankBlock`

按需返回单个区块的完整执行输出（`BlockFile` + `Header` + `StateDiff` + `ValidationHash`），不依赖 Kafka 和 S3。

```bash theme={null}
curl -X POST http://localhost:8545 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"trace_debankBlock","params":["latest"],"id":1}'
```

两个用途：leafage-evm 的 HTTP 回退模式直接轮询它；调试时用来比对某个区块的 StateDiff。需要在 `--http.api` 中包含 `trace`。

高度为 0 时返回创世块——没有交易的创世分配会被合成为交易和 trace，让下游看到的格式与普通区块一致。

## 区块变更通知

Kafka 通知不在 tracer 里发出，而是在 `core/blockchain.go` 的 `writeBlockAndSetHead`：只有确定了新的 canonical head 才发，这样重组才能被正确表达。

```go theme={null}
// 与上一条已发布的区块比较，得到需要丢弃和需要新增的路径
_, dropBlocks, newBlocks := bc.getCommonAncestor(*lastPushedBlock, currentBlock)

if len(dropBlocks) > 0 {
    blockChange = &ptypes.BlockChangeNotification{ChangeType: 2, NewBlocks: newBlocks, DropBlocks: dropBlocks}
} else if len(newBlocks) > 0 {
    blockChange = &ptypes.BlockChangeNotification{ChangeType: 1, NewBlocks: newBlocks}
}
```

三个前提：tracer 已初始化、当前实例是 Leader、上一条已发布区块的高度不高于当前 head（回退时不发，等更新的区块一起发）。

## 历史数据裁剪

`--ancient.prune` 让写节点持续裁剪历史区块数据：body 和 receipt 在离开最近 90000 个区块窗口后被丢弃，已有 ancient 数据在后台逐步删除，header 和 canonical hash 始终保留。需要 `--syncmode full`。

这个开关针对 Leafage 的场景——下游消费者读 S3，不依赖写节点保留历史区块体。

## 相关页面

<Columns cols={2}>
  <Card title="pipeline" icon="share-2" href="/components/pipeline">
    tracer 的采集逻辑与数据分发。
  </Card>

  <Card title="接入新链" icon="git-branch-plus" href="/guides/new-chain">
    把 pipeline 附加到其他执行客户端。
  </Card>
</Columns>
