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

# Write node

> The pipeline integration attached to the execution client: hooks, how to enable it, and block change notifications

The write node is the only role in Leafage that takes part in P2P sync and block execution. It is the chain's original execution client (ETH uses [Chaintable/go-ethereum](https://github.com/Chaintable/go-ethereum); other chains use their own forks), and Leafage only attaches a data export layer on top of it.

For the client's own usage, sync modes, and RPC, refer to the documentation of the corresponding upstream project. This page only covers the attached part.

| Item                         | Value                                                                                                                                                             |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ETH reference implementation | [Chaintable/go-ethereum](https://github.com/Chaintable/go-ethereum), currently merged up to upstream v1.17.4                                                      |
| Additions                    | pipeline tracer integration, `trace_debankBlock`, block change notifications, history pruning                                                                     |
| Change size                  | About 2000 lines relative to upstream, concentrated in a few files                                                                                                |
| Other chains                 | Forks of op-geth, reth, nitro, erigon, etc., changed the same way; [repository list](https://github.com/Chaintable/leafage-evm#supported-write-node-repositories) |

## Added integration points

| File                           | Additions                                                                                                               |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| `core/tracing/hooks.go`        | Adds two new hook types, `OnCommit` and `OnBlockDBStart`                                                                |
| `core/blockchain.go`           | Dispatches the new hooks; computes the reorg and publishes the Kafka notification when the canonical head is determined |
| `core/state/statedb.go`        | Adds `StateDiff()`, which exports the state changes in the commit set                                                   |
| `eth/api_debank.go`            | `DebankAPI`, implements `trace_debankBlock`                                                                             |
| `eth/backend.go`               | Registers the `trace` namespace                                                                                         |
| `eth/tracers/live/pipeline.go` | Registers the pipeline tracer in `LiveDirectory`                                                                        |
| `core/rawdb/chain_freezer.go`  | Continuous pruning for `--ancient.prune`                                                                                |

<Note>
  Keeping the changes concentrated is deliberate: the fork has to follow upstream releases over the long term, and the fewer upstream files it touches, the smaller the merge conflicts. New capabilities go into the pipeline repository first; the client side keeps only the call sites.
</Note>

## Enabling the 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
}'
```

When `topic` is omitted, it is generated from the chain ID as `nodex_pipeline_{chainID}`. See [pipeline configuration](/en/components/pipeline#configuration) for the full field descriptions.

Once the tracer is attached, collection and upload both happen inside pipeline; the client is only responsible for calling the hooks at the right time. See the [pipeline component documentation](/en/components/pipeline) for the collection logic.

## `trace_debankBlock`

Returns the complete execution output of a single block on demand (`BlockFile` + `Header` + `StateDiff` + `ValidationHash`), without depending on Kafka or 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}'
```

It has two uses:

* leafage-evm's HTTP fallback mode polls it directly.
* Comparing the StateDiff of a given block when debugging.

`trace` must be included in `--http.api`.

Height 0 returns the genesis block — the genesis allocation, which has no transactions, is synthesized into transactions and traces so that downstream sees the same format as a normal block.

## Block change notifications

The Kafka notification is not sent from the tracer but from `writeBlockAndSetHead` in `core/blockchain.go`: it is only sent once the new canonical head is determined, so that reorgs can be expressed correctly.

```go theme={null}
// Compare with the last published block to get the paths to drop and the paths to add
_, 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}
}
```

Publishing requires all three preconditions to hold:

* The tracer is initialized.
* The current instance is the Leader.
* The height of the last published block is not higher than the current head. Nothing is sent on a rollback; it waits to send together with newer blocks.

## History pruning

`--ancient.prune` makes the write node continuously prune historical block data; it requires `--syncmode full`:

* Bodies and receipts are dropped once they leave the window of the most recent 90000 blocks.
* Existing ancient data is deleted gradually in the background.
* Headers and canonical hashes are always kept.

This switch targets Leafage's scenario — downstream consumers read S3 and do not depend on the write node keeping historical block bodies.

## Related pages

<Columns cols={2}>
  <Card title="pipeline" icon="share-2" href="/en/components/pipeline">
    The tracer's collection logic and data distribution.
  </Card>

  <Card title="Adding a new chain" icon="git-branch-plus" href="/en/guides/new-chain">
    Attach pipeline to other execution clients.
  </Card>
</Columns>
