フルノード職人の朝は早い


## ビットコインとか忘年会

[こちらのイベント](https://cryptocurrency.connpass.com/event/303416/)で「フルノード職人の朝は早い」というタイトルでフルノードの建て方をLTしました。

この記事ではEVM系のチェーンであるEthereum（メインネット）およびArbitrum、Optimism、Binance Smart Chain、Avalancheと、LTでは触れられなかったPolygonのフルノードの建て方を紹介します。



## マシン構成

### クラウド課金地獄

クラウドを使ってもいいのですが、普通にこれらすべてのチェーンを動かそうとするとメモリが最低でも64GB、SSDが15TBとか必要になりますので、月額何千ドルもかかってしまいます。

実際、DigitalOceanという格安VPSで作っていたのですが、月1,200ドル（当時のレートで約20万円）を請求されて泣きそうになりました。

そこで僕は自宅にワークステーション（という名の自作PC）を作り、自宅サーバとして運用しています。

ちなみにOSはUbuntu 22.04 LTSです。

### マシン構成

CPUは8コア以上、メモリ64GB以上、SSDが15TB以上あれば十分かと思います。

僕の場合には以下のような構成で組みました。

| 種類 | モデル | 参考価格 |
|:---:|:------:|:------:|
|マザーボード|ASRock B550M Phantom Gaming 4|11,680円|
|CPU|AMD Ryzen 9 3950X 16コア|廃盤（現世代では7950X=80,000円相当）|
|メモリ|A-DATA 32GB DDR4 3,200MT/s x 4 = 128GB|1枚15,000円 x 4 = 60,000円|
|SSD (OS)|NVMe 2TB x 2 (RAID 1)|1枚10,000円 x 2 = 20,000円|
|SSD (データ)|SATA 8TB x 4 (RAID 5 + LVM)|1枚56,800円 x 4 = 227,200円|
|その他|電源、ケース、GPUなど|40,000円くらい|
|合計||438,880円|

初期投資としてはかなり高額かと思います。

特にSSD代で半分以上を占めており、ストレージに一番お金がかかっています。

ちなみに安いHDDで組むと、IOが足らずに同期が終わらなくなりますのでSSDへの課金は必須です。

CPUについては16コアのものを使いましたが、8コアぐらいでも大丈夫かと思います（試していないので分かりませんが）。

メモリについては最低でも64GBくらいないとgethがキャッシュとしてメモリを大量に食いますのでスワップしまくって大変になります。

メモリは正直載せれば載せるほどディスクキャッシュに回りますので、多ければ多いほどよいです。

ただ、コンシュマー向けのCPUだとAMDのThredripperを除いて128GBまでしか載りませんので、上限を載せています。

まぁメモリはそんな高くないですしね。

なおブロックチェーンは永遠に成長してどんどんディスクを食っていきますので、必ずいずれ足りなくなります。

将来的に拡張できるようにSATAポートが多いマザボを買うか、最初から多めに積んでおくことをおすすめします。

今回のケースでは8TBが4台のRAID5構成なので、8TB×3台=24TBの容量が使えます。



## SSDの設定

まずはSSDを使えるように設定していきます。

### フォーマット

GPTパーティションを`gdisk`を使って作っていきます。

```bash
$ sudo gdisk /dev/sd[a-d]
```

### RAID構築

```bash
$ sudo mdadm --create --raid-devices=4 --level=raid5 /dev/md1 /dev/sda1 /dev/sdb1 /dev/sdc1 /dev/sdd1
```

これで/dev/md1に三台分の容量のブロックデバイスが出来上がります。

状態確認は

```bash
$ cat /proc/mdstat
```

でできます。

RAIDの構築におおむね半日〜一日程度くらいかかります。

RAIDの構築中にこれ以降の作業をしてもいいのですが、IOが発生しているとRAIDの同期がどんどん遅くなっていってしまいますので、素直に待ったほうがいいと思います。

### LVM構築

先程作ったRAIDデバイスをそのままフォーマットして使ってもいいのですが、今後の拡張性のためにLVMを利用しました。

```bash
$ sudo pvcreate /dev/md1
$ sudo vgcreate ssd01 /dev/md1
$ sudo lvcreate --name dat01 --size 15T ssd01
```

ボリュームグループ名は`ssd01`、論理ボリューム名は`ssd01`としています。

### FS作成

最後にファイルシステムを作っていきマウントします。

```bash
$ sudo mkfs -t ext4 /dev/mapper/ssd01-dat01
$ sudo mkdir /mnt/dat01
$ sudo mount /dev/mapper/ssd01-dat01 /mnt/dat01
$ sudo vi /etc/fstab
/dev/mapper/ssd01-dat01 /mnt/dat01	ext4		defaults,nofail	0	0
```

これでブロックチェーンデータ用の大容量SSDの作成は終わりです。

容量が足りなくなったら`lvextend`で拡張しましょう。



## Ethereum (geth)

### インストール

公式のPPAリポジトリがありますのでこちらからインストールします。

```bash
$ sudo add-apt-repository -y ppa:ethereum/ethereum
$ sudo apt-get update
$ sudo apt-get install ethereum
```

### 起動

gethはデフォルトで`$HOME/.ethereum`を使いますので、大容量SSDへのシンボリックリンクを張ります。

```bash
$ ln -s /mnt/dat01/ethereum $HOME/.ethereum
```

あとは普通に起動します。

```bash
$ geth
```

デフォルトでスナップショット同期が行われますが、同期が完全に終わるまでだいたい2〜3日くらい（もちろんCPUはネットワーク帯域にも依存しますが）かかります。

状態確認は

```bash
$ geth attach
> eth.syncing
> eth.blockNumber
```

でできます。

### 自動起動

さて、マシンを再起動するたびに手動で起動するのは非常に面倒です。

そこでsystemdデーモン化しましょう。

```bash
$ sudo vi /etc/systemd/system/geth.service
[Unit]
Description=Go Ethereum
After=syslog.target network.target

[Service]
User=visvirial
Group=visvirial
Type=simple
ExecStart=/usr/bin/geth --http --http.api debug,eth,net,rpc,txpool,web3 --http.addr 0.0.0.0 --http.vhosts '*' --http.corsdomain '*'
KillMode=process
KillSignal=SIGINT
TimeoutStopSec=600    // ⇦ 止めるときに10分くらいまで待たないとDBがぶっ壊れるので注意！
estart=on-failure
RestartSec=10s

[Install]
WantedBy=multi-user.target
```

一つ注意なのが`TimeoutStopSec`に十分大きな値（10分とか）を指定することです。

デフォルトでは90秒だったと思いますが、この時間ではgethがオンメモリキャッシュをディスクにフラッシュするのが終わらず、強制終了になってしまい、最悪データベースが壊れて同期を一からやり直しになります。

実際、僕もこれもDBをぶっ壊して泣きながら同期し直しましたので気をつけてください。



## Arbitrum

### スナップショット

チェーンのジェネシスブロック（？）的なデータが必要なのでこれをダウンロードします。

https://snapshot.arbitrum.foundation/index.html

執筆時点で350GiBくらいありました。

非常に巨大なので計画的にダウンロードしましょう。

ちなみにwgetやcurlなどを使うと、途中でネットワークが不安定になった場合にダウンロードがエラーとなってやり直しになってしまい非常に辛いです。

ですのでaria2cなどのダウンロードツールを使うことを強く推奨します。

### Docker

公式でDockerイメージを配布しており、こちらを使うのが最も簡単です。

```bash
$ docker run --name arbitrum -itd --restart unless-stopped -v /mnt/dat01/arbitrum:/home/user/.arbitrum -p 0.0.0.0:8547:8547 -p 0.0.0.0:8548:8548 offchainlabs/nitro-node:v2.1.3-e815395 --parent-chain.connection.url http://172.17.0.1:8545 --chain.id=42161 --http.api=net,web3,eth,debug --http.corsdomain=* --http.addr=0.0.0.0 --http.vhosts=*
```

`--restart unless-stopped`を指定するとマシンを再起動しても自動的に起動してくれますので便利です。



## Optimism

### インストール

`op-geth`と`op-node`の二つのプロセスが必要です。

ビルドの仕方は以下のサイトに詳しく載っていますので、こちらに従ってビルドしてください。

https://community.optimism.io/docs/developers/nodes/intro/#software-requirements

### スナップショット

Arbitrumと同様、ジェネシスブロックのデータ的なもの (Bedrock) がありますので、こちらをダウンロードします。

https://community.optimism.io/docs/developers/nodes/mainnet/

300GBくらいありますので計画的にダウンロードしましょう。


### 共通鍵作成

`op-geth`と`op-node`をつなげるのにJWTファイルを作る必要があります。

```bash
$ cd op-geth
$ openssl rand -hex 32 > jwt.txt
$ cp jwt.txt ../optimism
```
### 起動

#### op-geth

```bash
#!/bin/bash

SEQUENCER_URL=https://mainnet-sequencer.optimism.io/

./build/bin/geth \
  --datadir=./datadir \
  --http \
  --http.port=8549 \
  --http.addr=0.0.0.0 \
  --http.corsdomain '*' \
  --authrpc.addr=localhost \
  --authrpc.port=8552 \
  --authrpc.jwtsecret=./jwt.txt \
  --verbosity=3 \
  --rollup.sequencerhttp=$SEQUENCER_URL \
  --nodiscover \
  --syncmode=full \
  --maxpeers=0 \
  --port=30304
```

こんな感じのスクリプトファイルを作って起動しましょう。

#### op-node

```bash
#!/bin/bash

L1URL=http://localhost:8545
L1KIND=debug_geth
NET=mainnet

./op-node/bin/op-node \
    --l1=$L1URL  \
    --l1.rpckind=$L1KIND \
    --l2=ws://localhost:8552 \
    --l2.jwt-secret=./jwt.txt \
    --network=$NET \
    --rpc.addr=0.0.0.0 \
    --rpc.port=8550
```

こちらも同様でこんな感じのスクリプトを作ります。

公式との違いは、`L1KIND`に`debug_geth`を指定することです。

デフォルトの`basic`だと非常に遅く、同期が何週間もかかります。

`debug_geth`を指定すれば一週間くらいで同期できます。



## Binance Smart Chain

### インストール

```bash
$ wget $(curl -s https://api.github.com/repos/bnb-chain/bsc/releases/latest |grep browser_ |grep geth_linux |cut -d\" -f4)
$ mv geth_linux geth
$ chmod -v u+x geth
```

### genesis.json/config.tomlのダウンロード

```bash
$ wget $(curl -s https://api.github.com/repos/bnb-chain/bsc/releases/latest |grep browser_ |grep mainnet |cut -d\" -f4)
$ unzip mainnet.zip
```

### スナップショットのダウンロード

公式のスナップショットもあるのですが、BNB48Clubという人たちが提供してくれているもののほうがpruningされているため、データ量が少ないです。

https://github.com/48Club/bsc-snapshots#geth-fast-node

約400GBあります。

### 起動

```bash
$ ./geth --config ./config.toml --datadir ./node --cache 8000 --rpc.allow-unprotected-txs --txlookuplimit 0 --tries-verify-mode none --snapshot=false --syncmode full --http.port 8546
```


## Avalanche

### インストール

```bash
$ wget -nd -m https://raw.githubusercontent.com/ava-labs/avalanche-docs/master/scripts/avalanchego-installer.sh
$ chmod 755 avalanchego-installer.sh
$ ./avalanchego-installer.sh
```

これを実行すると`$HOME/avalanche-node/avalanchego`という実行ファイルがダウンロードされます。

### 起動

```bash
$ $HOME/avalanche-node/avalanchego --config-file=/home/visvirial/.avalanchego/configs/node.json
```


## Polygon

### インストール

`Heimdall`と`bor`の二つが必要です。

#### Heimdall

```bash
$ curl -L https://raw.githubusercontent.com/maticnetwork/install/main/heimdall.sh | bash -s -- v1.0.3 mainnet heimdall
$ sed -i 's|^seeds =.*|seeds = "seed_details"|g' /var/lib/heimdall/config/config.toml
```

#### Bor

```bash
$ curl -L https://raw.githubusercontent.com/maticnetwork/install/main/heimdall.sh | bash -s -- v1.0.3 mainnet bor
sed -i 's|.*bootnodes =.*|bootnodes = "mainnet_bootnodes"|g' /var/lib/bor/config.toml
```

### スナップショット

#### Heimdall

```bash
curl -L https://snapshot-download.polygon.technology/snapdown.sh | bash -s -- --network mainnet --client heimdall --validate-checksum true
```

だいたい400GBくらいあります。

aria2cを使って分割されたファイルをダウンロードしたあとに合体し、チェックサムの検証とアーカイブの展開までやってくれます。

#### Bor

```bash
curl -L https://snapshot-download.polygon.technology/snapdown.sh | bash -s -- --network mainnet --client bor --validate-checksum true
```

だいたい3TBくらい（！）あるので計画的にダウンロードしましょう。

100Mbpsの回線だと3日かかりますので、大変です。

### 起動

インストールスクリプトが`/lib/systemd/system/(heimdalld|bor).service`を作ってくれますので、普通に`systemctl`コマンドで起動できます。

まずはHeimdallを起動して同期を完了させましょう。

Heimdallが同期できるまでBorの同期は開始できないようなので注意してください。

```bash
$ sudo systemctl start heimdalld
```

状態は

```bash
curl localhost:26657/status
```

で確認できます。

`.result.sync_info.catching_up`が`false`になれば同期完了です。

同期できたらBorを起動しましょう。

```bash
$ sudo systemctl start bor
```

同期に二週間くらいかかります。



## まとめ

フルノードはTB級のデータを高速に捌かないといけないため、非常に運用が大変です。

スナップショットのダウンロードに時間がかかりますし、スナップショット時点から最新のブロックまで同期するまでも大量のIOが発生するため、SSDでも非常に時間がかかります。

さらっとやり方を書きましたが、全部やり終えるまで二ヶ月くらいかかりました。

それでもなんでフルノードを自前でも使って？

**Don't trust, verify.**

ですよ！！！

まぁ実務的にはRPCのホスティングサービスがどれも不安定で（ちゃんと課金しているにもかかわらず）よく通信に失敗するので、自前のサーバにつないでよしなにやるために必要で仕方なく運用しているというのが実情です。



## おまけ

### 同期ダッシュボード

サーバにいちいちログインして一つ一つ同期状態を確認するのはなかなか大変です。

そのため同期状態が一発で分かるダッシュボードを作りました。

https://node.visvirial.com/

オープンソースなので、よろしければ改造して使ってください。

https://github.com/visvirial/node.visvirial.com

（※RPCは全世界に公開していますが、基本的に自分用なので勝手に繋がないでください）

### NATを越えろ！

自宅サーバで運用しているため、外部からアクセスできるようにするためにはNAT超えをする必要があります。

僕の場合にはWireGuardでVPNとしてつないで、nginxでリバースプロキシしています。

ローカルでしか使わないのであれば必要ないと思います。



## 最後に

フルノード建てるの楽しいよ！みんなもやってみてね！！！