⛓️

SymbolメインネットのDual(Peer+API)ノード起動

に公開

Symbol のビルド

https://zenn.dev/ccharvestasya/articles/symbol-build-on-ubuntu24

各種ファイルを置くディレクトリの作成

/optsymbol-nodeディレクトリを作成し、この中で作業します。
ユーザー名harvestasyaは各自変更してください。

bash
sudo mkdir -p /opt/symbol-node
sudo chown harvestasya: /opt/symbol-node
cd /opt/symbol-node

証明書の作成

CA 証明書と Node 証明書が必要です。さらに X.509 v1 フォーマットでないといけません。公式の証明書作成ツールが古い SDK を使用していて動かすのが少し面倒なので、私が公式ツールをコピー作成したsymbol-peertoolsを使用します。

Volta で Node.js をインストール

Node.js をインストールします。すでにインストール済の場合はスキップしてください。
apt だと結構古いバージョンがインストールされるので、Node.js のバージョン管理ができる Volta 経由でインストールします。

bash
sudo curl https://get.volta.sh | bash
exec $SHELL -l
volta install node@20

OpenSSL をインストール

OpenSSL が必要なのでインストールします。X.509 v1 の生成がらみで OpenSSL のバージョンに縛りがありますが、2024 年 11 月現在の Ubuntu 24 では、その縛りに引っかからないので、apt でインストールします。

bash
sudo apt update
sudo apt install -y openssl

symbol-peertools をインストール

symbol-peertoolsをインストールします。

bash
npm i -g symbol-peertools

新規に証明書を作成

新規に証明書を作成する場合は、このまま以下のコマンドを実行します。

bash
symbol-peertools certGen --certdir certificates

実行すると秘密鍵等を保存する際の暗号化に使うパスワードを聞かれるので、入力します。これで証明書が certificates ディレクトリに作成されます。

既存ノードから証明書を引き継ぐ場合

他のノードから引き継ぐ場合は、privatekeys.yamlを作成し main または transport の秘密鍵を入力して、上記コマンドを実行します。

冗長化のために引き継ぐ場合は、transportのみ引き継ぎます。

privatekeys.yaml
transport:
  privateKey: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB...

公開鍵とアドレスの確認

公開鍵とアドレスは、以下コマンドで確認できます。

bash
symbol-peertools certInfo -n mainnet --certdir certificates

ネメシス(ジェネシス)ブロックとコンフィグファイルの配置

ダウンロードして zip を展開します。

bash
curl -OL https://github.com/symbol/symbol/releases/download/client%2Fcatapult%2Fv1.0.3.7/configuration-mainnet.zip
unzip configuration-mainnet.zip
rm configuration-mainnet.zip shoestring.ini README.md

ネメシス(ジェネシス)ブロック

seed ディレクトリに格納されています。

server エクステンションの設定

server エクステンションの設定をするためにresources/config-extensions-server.propertiesを編集します。

resources/config-extensions-server.properties
[extensions]

# api extensions
extension.filespooling = true
extension.partialtransaction = true
...

broker エクステンションの設定

broker エクステンションの設定をするためにresources/config-extensions-broker.propertiesを編集します。

resources/config-extensions-broker.properties
[extensions]

# addressextraction must be first because mongo and zeromq depend on extracted addresses
extension.addressextraction = true
extension.mongo = true
extension.zeromq = true

extension.hashcache = true

recovery エクステンションの設定

recovery エクステンションの設定をするためにresources/config-extensions-recovery.propertiesを編集します。

resources/config-extensions-recovery.properties
[extensions]

# addressextraction must be first because mongo and zeromq depend on extracted addresses
extension.addressextraction = true
extension.mongo = true
extension.zeromq = true

extension.filespooling = false
extension.hashcache = true

ノード情報の設定

ノード情報を設定するためにresources/config-node.propertiesを編集します。

  • enableAutoSyncCleanup: 無効にする
  • host: 自 IP やドメインを設定
  • friendlyName: ノードの名前(なんでもいいです)※日本語不可
  • version: 未設定
  • roles: Peer, Api
resources/config-node.properties
...
enableAutoSyncCleanup = false
...
[localnode]

host = <YOUR_NODE_IP>
friendlyName = <YOUR_FRIENDLY_NAME>
version =
roles = Peer, Api
...

同時にこの辺りも変更しておくと、最初の同期が安定すると思います。

resources/config-node.properties
...
maxChainBytesPerSyncAttempt = 10MB
...
blockDisruptorMaxMemorySize = 1000MB
...

データベースの設定

resources/config-database.propertiesを編集します。
databaseUriをループバック IP に変更する。

resources/config-database.properties
[database]

databaseUri = mongodb://127.0.0.1:27017
...

ハーベストの設定

ハーベスティングエクステンションが有効になっているか、resources/config-extensions-server.propertiesを開いて確認します。

resources/config-extensions-server.properties
# p2p extensions
...
extension.harvesting = true
...

ハーベストの設定をresources/config-harvesting.propertiesに設定します。

  • harvesterSigningPrivateKey: bootstrap では remote と呼ばれるアカウントの秘密鍵を設定します
  • harvesterVrfPrivateKey: VRF アカウントの秘密鍵
  • enableAutoHarvesting: true
  • maxUnlockedAccounts: 委任を受け入れる最大値
  • delegatePrioritizationPolicy: 委任者の最大を超えたときの追い出し挙動(Importance または Age)
  • beneficiaryAddress: ハーベスト報酬の受け取りアドレス
resources/config-harvesting.properties
[harvesting]

harvesterSigningPrivateKey = <HARVESTER_SIGNING_PRIVATE_KEY>
harvesterVrfPrivateKey = <HARVESTER_VRF_PRIVATE_KEY>

enableAutoHarvesting = true
maxUnlockedAccounts = 100
delegatePrioritizationPolicy = Importance
beneficiaryAddress = <BENEFICIARY_ADDRESS>

新規にノードを立てる場合、harvesterSigningPrivateKeyharvesterVrfPrivateKeyは、未使用の新しいアカウントを設定します。
以下のコマンドで、メインネット用のアカウント 2 つが作成出来ます。

bash
catapult.tools.addressgen -n mainnet -c 2

ノードのメインアカウントでハーベストする場合はノード立ち上げ後、AccountKeyLink と VRFKeyLink トランザクションを発行する必要があります。

ハーベストキーリンク

メインアカウントでハーベストを行う場合は、キーリンクが必要です。
キーリンクトランザクションを発行するために、symbol-cliをインストールします。

bash
npm i -g symbol-cli

インストールが終わったらプロファイルを設定します。
--url http://localhost:3000はトランザクションをアナウンスするノードなので、立ち上げ前のローカルではなく別の稼働中のノードを指定してください。

bash
symbol-cli profile import --network MAIN_NET --url http://localhost:3000 --default
✔ Enter a profile name: … MainNetProfile
✔ Select an import type: › PrivateKey
✔ Enter your wallet password: … ********
✔ Enter your account private key: … ****************************************************************
  • Enter a profile name: 任意のプロファイル名を入力
  • Select an import type: PrivateKey を選択
  • Enter your wallet password: プロファイル保護のために任意のパスワードを入力
  • Enter your account private key: ノードのメインアカウントの秘密鍵を入力

プロファイルは~/symbol-cli.config.jsonに保存されます。

リモートキーリンク
bash
symbol-cli transaction accountkeylink --sync --action Link \
           --max-fee 20000 --mode normal
✔ Enter your wallet password: … ********
✔ Enter the public key of the remote account:  … ****************************************************************
  • Enter your wallet password: プロファイルのパスワードを入力
  • Enter the public key of the remote account: harvesterSigningPrivateKey に設定したアカウントの公開鍵を入力
VRF キーリンク
bash
symbol-cli transaction vrfkeylink --sync --action Link \
           --max-fee 20000 --mode normal
✔ Enter your wallet password: … ********
✔ Enter the public key to link:  … ****************************************************************
  • Enter your wallet password: プロファイルのパスワードを入力
  • Enter the public key to link: harvesterVrfPrivateKey に設定したアカウントの公開鍵を入力

各ディレクトリの場所を設定

各ディレクトリの場所がresources/config-user.propertiesに設定されています。基点はcatapult.serverを起動した時の作業ディレクトリです。

resources/config-user.properties
[account]

enableDelegatedHarvestersAutoDetection = true

[storage]

seedDirectory = /opt/symbol-node/seed
certificateDirectory = /opt/symbol-node/certificates
dataDirectory = /opt/symbol-node/data/rocks
pluginsDirectory = /usr/local/catapult/lib
votingKeysDirectory = /opt/symbol-node/votingkeys

接続ピアリストの作成

起動時に接続する他のノード一覧resources/peers-p2p.jsonを作成します。
statistics-service(https://symbol.services/nodes?filter=suggested&limit=10) を参照して作成すると良いでしょう。
面倒な人のためにランダム作成したリストを置いておきます。

resources/peers-p2p.json
{
  "_info": "this file contains a list of peers",
  "knownPeers": [
    {
      "publicKey": "9FF3ED0814113E038810A1D4AD6128BB67143FDA103D8DB48A3EB7DF833889AA",
      "endpoint": {
        "host": "94.176.239.16",
        "port": 7900
      },
      "metadata": {
        "name": "9FF3ED0",
        "roles": "Peer, Api"
      }
    },
    {
      "publicKey": "C221C5E700DF2AB50909B31D171DDAA87DC99EE6FBF5A8CE56D60ACCCBCF853E",
      "endpoint": {
        "host": "03.symbol-jp.net",
        "port": 7933
      },
      "metadata": {
        "name": "symbol-jp.net(03)",
        "roles": "Peer, Api"
      }
    },
    {
      "publicKey": "B0C333CE072CE0B20CF03D305B8F913BDB64430EF217E36123D03A32F4A39017",
      "endpoint": {
        "host": "xym862.allnodes.me",
        "port": 7900
      },
      "metadata": {
        "name": "Allnodes862",
        "roles": "Peer, Api"
      }
    },
    {
      "publicKey": "6F0D20768154F8AEEF1F667401FFEAA6F8D9F84ACE040AB61022E5A481738AB9",
      "endpoint": {
        "host": "xym707.allnodes.me",
        "port": 7900
      },
      "metadata": {
        "name": "Allnodes707",
        "roles": "Peer, Api"
      }
    },
    {
      "publicKey": "79837F0B59BC67F95F5D8430781231519500F695BB5A8F070610D86B4776D364",
      "endpoint": {
        "host": "xym542.allnodes.me",
        "port": 7900
      },
      "metadata": {
        "name": "Allnodes542",
        "roles": "Peer, Api"
      }
    },
    {
      "publicKey": "B2D8B8EED9D17462B36B992ED0C4522289DAF3F996C0C6CDCC7D12B0811A953B",
      "endpoint": {
        "host": "89.47.164.194",
        "port": 7900
      },
      "metadata": {
        "name": "B2D8B8E",
        "roles": "Peer, Api"
      }
    },
    {
      "publicKey": "DD1CB539ED4DBE955EDAB87E3EAB98ACE3961BFF8FD09F8267D2BC1395CE421F",
      "endpoint": {
        "host": "0-0-0-0-0-0-2.harvesting.fan",
        "port": 7900
      },
      "metadata": {
        "name": "0-0-0-0-0-0-2.Harvesting.Fan",
        "roles": "Peer, Api"
      }
    },
    {
      "publicKey": "80A905CC70A5C620000DD5BA74EA6AF1480DAF753D4639D20E39FA7796A85287",
      "endpoint": {
        "host": "xym590.allnodes.me",
        "port": 7900
      },
      "metadata": {
        "name": "Allnodes590",
        "roles": "Peer, Api"
      }
    },
    {
      "publicKey": "D7733D31B701D478CF1F22B25DAA249BCDAB3FEFF1E374D813435078D78AD085",
      "endpoint": {
        "host": "0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-6.tokyo-node.jp",
        "port": 7900
      },
      "metadata": {
        "name": "Tokyo-Node.JP",
        "roles": "Peer, Api"
      }
    },
    {
      "publicKey": "62D3DB69489C3F93A974500A005079C67324E57D01F9E71538DD7C8B8FC6C904",
      "endpoint": {
        "host": "xym321.allnodes.me",
        "port": 7900
      },
      "metadata": {
        "name": "Allnodes321",
        "roles": "Peer, Api"
      }
    }
  ]
}

接続 API リストの作成

API 持ちノードのリストです。上記で作成したピアリストは全て API 持ちなのでコピーで問題ないです。

bash
cp resources/peers-p2p.json resources/peers-api.json

MongoDB のインストール

公開鍵のインポート

MongoDB の公開鍵 GPG キーをインポートします。

bash
curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | \
   sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg \
   --dearmor

リストファイルを作成する

Ubuntu 24 用のリストファイルを作成する。

bash
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list

インストール

bash
sudo apt update
sudo apt install -y mongodb-org

Rest の準備

Rest のコピー

クローンした symbol ディレクトリの中にあるので/opt/symbol-nodeに持って行きます。

bash
cd /opt/symbol-node
cp -r ~/symbol/client/rest .
cd rest
mv rest.json resources
npm i
cd ..

コンフィグの編集

rest/rest.jsonを編集します(app/resources 内の rest.json は使用しません)。
各ホストをループバック IP に変更します。また、ノードの証明書とプロパティへのパスを設定します。deploymentnodeMetadataの変更は任意です。

rest/rest.json
...
        "db": {
                "url": "mongodb://127.0.0.1:27017/",
...
        "apiNode": {
                "host": "127.0.0.1",
                "port": 7900,
                "timeout": 1000,
                "tlsClientCertificatePath": "../certificates/node.crt.pem",
                "tlsClientKeyPath": "../certificates/node.key.pem",
                "tlsCaCertificatePath": "../certificates/ca.crt.pem",

                "inflationPropertyFilePath": "../resources/config-inflation.properties",
                "networkPropertyFilePath": "../resources/config-network.properties",
                "nodePropertyFilePath": "../resources/config-node.properties"
        },

        "websocket": {
                "mq": {
                        "host": "127.0.0.1",
...
                "file": {
                        "formats": ["prettyPrint"],

                        "level": "verbose",
                        "handleExceptions": true,

                        "filename": "../logs/rest/catapult-rest.log",
                        "maxsize": 20971520,
                        "maxFiles": 100
                }
...
        "deployment": {
                "deploymentTool": "n/a",
                "deploymentToolVersion": "n/a",
                "lastUpdatedDate": "n/a"
        },

        "nodeMetadata": {
                "_info": "replace the body of this object with custom fields and objects to personalize your node"
        }
}

サービスファイルの作成

ユーザー、グループは各自変更してください。

MongoDB

bash
sudo vi /etc/systemd/system/symbol-db.service
/etc/systemd/system/symbol-db.service
[Unit]
Description=Symbol MongoDB
Wants=network.target
After=network.target

[Service]
Type=forking
PIDFile=/run/mongo/symbol-db.pid
ExecStart=/usr/bin/mongod --dbpath=data/mongo --wiredTigerCacheSizeGB 2 --pidfilepath /run/mongo/symbol-db.pid --fork --syslog
KillSignal=SIGINT
User=harvestasya
Group=harvestasya
WorkingDirectory=/opt/symbol-node
LimitNOFILE=65536
UMask=077

[Install]
WantedBy=multi-user.target

pid ファイルの保存先を作成します。

bash
sudo mkdir /run/mongo
sudo chown harvestasya: /run/mongo

再起動するとディレクトリが消えてしまうので、再起動時に作成するようにします。

bash
sudo vi /etc/tmpfiles.d/mongo.conf
/etc/tmpfiles.d/mongo.conf
#Type   Path                    Mode    UID            GID          Age  Argument
d       /run/mongo              0755    harvestasya    harvestasya  -

Rest Gateway

bash
sudo vi /etc/systemd/system/symbol-rest.service
/etc/systemd/system/symbol.service
[Unit]
Description=Symbol Rest Gateway
Requires=network.target symbol-db.service

[Service]
Type=idle
ExecStart=/home/harvestasya/.volta/bin/npm start ./resources/rest.json
Restart=on-failure
RestartSec=2
User=harvestasya
Group=harvestasya
WorkingDirectory=/opt/symbol-node/rest
StandardOutput=journal
StandardError=journal
LimitNOFILE=65536
UMask=077

[Install]
WantedBy=multi-user.target

Symbol Recovery

異常終了などで lock ファイルが残った際に起動します。

bash
sudo vi /etc/systemd/system/symbol-recovery.service
/etc/systemd/system/symbol-recovery.service
[Unit]
Description=Symbol Recovery

[Service]
Type=oneshot
ExecCondition=/bin/sh -c '/usr/bin/test -f /opt/symbol-node/data/rocks/server.lock || /usr/bin/test -f /opt/symbol-node/data/rocks/broker.lock'
ExecStart=/usr/local/catapult/bin/catapult.recovery .
KillSignal=SIGINT
Restart=on-failure
RestartSec=2
TimeoutStopSec=60
User=harvestasya
Group=harvestasya
WorkingDirectory=/opt/symbol-node
Environment="LD_LIBRARY_PATH=/usr/local/catapult/deps"
StandardOutput=journal
StandardError=journal
LimitNOFILE=65536
UMask=077

[Install]
WantedBy=multi-user.target

Symbol Broker

Server からファイルスプールを通してデータを取得するサービスです。MongoDB への接続や Rest からの ZeroMQ 接続があるので、これらを依存関係に含めています。

bash
sudo vi /etc/systemd/system/symbol-broker.service
/etc/systemd/system/symbol-broker.service
[Unit]
Description=Symbol Broker
Wants=symbol-recovery.service
Requires=network.target symbol-db.service symbol-rest.service

[Service]
Type=idle
ExecStart=/usr/local/catapult/bin/catapult.broker .
ExecStopPost=/bin/bash -c 'while systemctl is-active --quiet symbol-broker.service; do sleep 1; done; sudo systemctl stop symbol-rest.service'
ExecStopPost=/bin/bash -c 'while systemctl is-active --quiet symbol-broker.service; do sleep 1; done; sudo systemctl stop symbol-db.service'
KillSignal=SIGINT
Restart=on-failure
RestartSec=2
TimeoutStopSec=20
User=harvestasya
Group=harvestasya
WorkingDirectory=/opt/symbol-node
Environment="LD_LIBRARY_PATH=/usr/local/catapult/deps"
StandardOutput=journal
StandardError=journal
LimitNOFILE=65536
UMask=077

[Install]
WantedBy=multi-user.target

Symbol Server

前々回に作成したsymbol.serviceを編集します。broker の起動後に起動することになります。

bash
sudo vi /etc/systemd/system/symbol.service
/etc/systemd/system/symbol.service
[Unit]
Description=Symbol Node
Wants=symbol-recovery.service
Requires=network.target symbol-broker.service

[Service]
Type=idle
ExecStart=/usr/local/catapult/bin/catapult.server .
ExecStopPost=/bin/bash -c 'while systemctl is-active --quiet symbol.service; do sleep 1; done; sudo systemctl stop symbol-broker.service'
KillSignal=SIGINT
Restart=on-failure
RestartSec=2
TimeoutStopSec=300
User=harvestasya
Group=harvestasya
WorkingDirectory=/opt/symbol-node
Environment="LD_LIBRARY_PATH=/usr/local/catapult/deps"
StandardOutput=journal
StandardError=journal
LimitNOFILE=65536
UMask=077

[Install]
WantedBy=multi-user.target

systemctl stopのパスワード不要化

ExecStopPostsudo systemctl stopしているので、パスワードなしで実行できるようにします。

nano で編集する場合は、EDITOR=vi不要です。

bash
sudo EDITOR=vi visudo

@includedir /etc/sudoers.dの前に追記する。

harvestasya ALL=(ALL:ALL) NOPASSWD: /bin/systemctl stop symbol-db.service
harvestasya ALL=(ALL:ALL) NOPASSWD: /bin/systemctl stop symbol-rest.service
harvestasya ALL=(ALL:ALL) NOPASSWD: /bin/systemctl stop symbol-broker.service

サービスをリロード

bash
sudo systemctl daemon-reload

MongoDB の準備

DB データ格納ディレクトリの作成

Symbol のデータを格納するディレクトリを作成する。

bash
mkdir -p /opt/symbol-node/data/mongo

MongoDB の起動

bash
sudo systemctl start symbol-db

MongoDB の初期化

初回起動時は、データベースもコレクションも無い状態なので、catapult データベースを作成し、初期化します。
もう一つコンソールを開いて、以下を実行します。

bash
cd /opt/symbol-node/mongo
mongosh catapult mongoDbPrepare.js
cd ..

MongoDB の停止

bash
sudo systemctl stop symbol-db

ノードの起動とログの参照

サービスを有効化

bash
sudo systemctl enable symbol

サービスを開始

依存関係を設定しているので、これだけで必要なサービス一式起動します。

bash
sudo systemctl start symbol

ログの確認

bash
sudo journalctl -u symbol -r

リアルタイムで確認したい場合は-fオプションを付けます。

bash
sudo journalctl -u symbol -f

さらに色を付けたい場合は、-o catオプションを付けます。

bash
sudo journalctl -u symbol -f -o cat

まとめてログを見たいときは-uでサービス名を繋ぎます。

bash
sudo journalctl -u symbol -u symbol-rest -u symbol-broker -f -o cat

サービスを停止

終了時の依存関係も設定しているので、これだけでサービス一式終了します。

bash
sudo systemctl stop symbol

Discussion