Running a Private Network 

# 概要

[マニュアルビルド](https://qiita.com/ishidad2/items/ad9d2c07898667db72df)したカタパルトサーバーを使ってプライベートネットワークを立ち上げます。
※この記事はメモ書き程度の精度しかないので、後日再度検証してまとめます。

https://qiita.com/ishidad2/items/ad9d2c07898667db72df

基本的には以下の公式に従ってやればOKなのですが、非常に設定が複雑です。

https://github.com/symbol/symbol/blob/client/catapult/v1.0.3.6/client/catapult/docs/RUNNETWORKLIN.md

公式の手順は複雑すぎて大変なのでここでは楽をしてsymbol-networkツールを使います。
以下よりgit clone するので、適当なディレクトリで作業してください。

## symbol-networkを取得

```bash
$ git clone https://github.com/fboucquez/symbol-network.git
$ cd symbol-network
```
NPMからでもsymbol-networkはダウンロードできますが、NPMのものは内部で参照しているsymbol-bootstrapのバージョンが古いのでここではGithubからCloneしてローカルインストールをします。

## symbol-bootstrapのバージョンを上げる

symbol-networkの`package.json`のsymbol-bootstrapのバージョンを`1.1.4`から`1.1.10`に変更します。
ついでにわかりやすいようにバージョンも変更しておきます。
sdkのバージョンも上げておきたいところですが、今のところどこに影響が出るのかわからないので今回はそのままにしておきます。

```diff
{
    "name": "symbol-network",
-   "version": "0.0.2",
+   "version": "0.0.3",
    "description": "CLI tools to create node clusters for new or existing Symbol networks.",
    "bin": {
        "symbol-network": "bin/run"
    },
    "homepage": "https://github.com/fboucquez/symbol-network",
    "bugs": "https://github.com/fboucquez/symbol-network/issues",
    "scripts": {
        "doc": "shx mkdir -p ts-docs && typedoc --out \"ts-docs\" src && touch ./ts-docs/.nojekyll",
        "clean": "npx shx rm -rf ./lib",
        "purge": "npx shx rm -rf lib coverage node_modules target .nyc_output logs.log ts-docs",
        "lint": "eslint --cache src/ test/ --ext .ts",
        "lint:fix": "eslint src/ test/ --ext .ts --fix",
        "prettier": "prettier --write ./src",
        "style:fix": "npm run create-index-files && npm run prettier && npm run lint:fix && npm run oclif-doc",
        "create-index-files": "cti create ./src -b -n -e commands",
        "postpack": "npx shx rm -f oclif.manifest.json",
        "posttest": "eslint src/ test/ --ext .ts",
        "oclif-doc": "oclif-dev manifest && oclif-dev readme --multi",
        "watch": "tsc --watch",
        "compile": "tsc ",
        "build": "npx shx rm -rf lib && npm run compile",
        "prepack": "npx shx rm -rf lib && npm run compile && npm run oclif-doc",
        "test": "nyc --reporter=lcov --extension .ts mocha -r ts-node/register --timeout 900000 --forbid-only \"test/**/*.test.ts\"",
        "e2e": "nyc --reporter=lcov --extension .ts mocha -r ts-node/register --timeout 900000 --forbid-only \"test/**/*.e2e.ts\"",
        "coveralls-report": "cat ./coverage/lcov.info | coveralls",
        "version": "echo $npm_package_version",
        "install-cli": "npm pack && npm i -g"
    },
    "engines": {
        "node": ">=12.0.0"
    },
    "files": [
        "/bin",
        "/lib",
        "/npm-shrinkwrap.json",
        "/oclif.manifest.json"
    ],
    "oclif": {
        "commands": "./lib/commands",
        "bin": "symbol-network",
        "plugins": [
            "@oclif/plugin-autocomplete",
            "@oclif/plugin-help"
        ]
    },
    "keywords": [],
    "author": "Fernando Boucquez <fboucquez@gmail.com>",
    "license": "Apache-2.0",
    "mocha": {
        "timeout": 40000
    },
    "types": "lib/index.d.ts",
    "dependencies": {
        "@oclif/command": "^1.8.16",
        "@oclif/config": "^1.18.3",
        "@oclif/plugin-autocomplete": "^1.2.0",
        "@oclif/plugin-help": "^5.1.11",
        "cross-fetch": "^3.1.5",
        "figlet": "^1.5.2",
        "inquirer": "^8.2.0",
        "lodash": "^4.17.21",
        "remove": "^0.1.5",
        "rxjs": "^7.5.2",
-       "symbol-bootstrap": "^1.1.4",
+       "symbol-bootstrap": "^1.1.10",
        "symbol-sdk": "^1.0.4-alpha-202112211435",
        "tslib": "^2.3.1",
        "winston": "^3.5.1"
    },
    "devDependencies": {
        "@oclif/dev-cli": "^1.26.10",
        "@oclif/test": "^2.0.3",
        "@types/chai": "^4.3.0",
        "@types/figlet": "^1.5.4",
        "@types/inquirer": "^8.2.0",
        "@types/lodash": "^4.14.178",
        "@types/mocha": "^9.1.0",
        "@types/node": "^17.0.14",
        "@types/semver": "^7.3.9",
        "@types/winston": "^2.4.4",
        "@typescript-eslint/eslint-plugin": "^5.10.2",
        "@typescript-eslint/parser": "^5.10.2",
        "chai": "^4.3.6",
        "coveralls": "^3.1.1",
        "create-ts-index": "^1.14.0",
        "dir-compare": "^4.0.0",
        "eslint": "^8.8.0",
        "eslint-config-prettier": "^8.3.0",
        "eslint-plugin-prettier": "^4.0.0",
        "mocha": "^9.2.0",
        "mocha-lcov-reporter": "^1.3.0",
        "mock-stdin": "^1.0.0",
        "nyc": "^15.1.0",
        "prettier": "^2.5.1",
        "prettier-plugin-organize-imports": "^2.3.4",
        "shx": "^0.3.4",
        "sinon": "^13.0.1",
        "ts-node": "^10.4.0",
        "typedoc": "^0.22.11",
        "typescript": "^4.5.5"
    }
}
```

## ローカルインストール

```bash
$ npm install -g .
$ symbol-network -v
>symbol-network/0.0.3 linux-x64 node-v18.19.0
```
バージョンが0.0.3　になっていればOKです。


## ネメシス用の設定ファイルの生成

先ほどカタパルトサーバーをビルドしたディレクトに移動して作業を行います。

```bash
cd catapult-client/_build
```


`init`コマンドを実行すると色々と聞かれるので、色々と聞かれるのでご自分の設定を作成してください。（長いので説明は割愛します）
試すだけならひたすらEnterでOKです。最後に`Do you want to create more nodes?`と聞かれるので`n`をするとツールが終了します。

```bash
$ symbol-network init
                         _             _                      _                          _    
  ___  _   _  _ __ ___  | |__    ___  | |        _ __    ___ | |_ __      __ ___   _ __ | | __
 / __|| | | || '_ ` _ \ | '_ \  / _ \ | | _____ | '_ \  / _ \| __|\ \ /\ / // _ \ | '__|| |/ /
 \__ \| |_| || | | | | || |_) || (_) || ||_____|| | | ||  __/| |_  \ V  V /| (_) || |   |   < 
 |___/ \__, ||_| |_| |_||_.__/  \___/ |_|       |_| |_| \___| \__|  \_/\_/  \___/ |_|   |_|\_\
       |___/                                                                                  
2023-12-29T16:10:49.446Z info     
2023-12-29T16:10:49.447Z info     Welcome to the symbol-network tool. 
2023-12-29T16:10:49.447Z info     
2023-12-29T16:10:49.447Z info     This tool will allow you creating a new network or a node cluster for an existing network.
2023-12-29T16:10:49.447Z info     
2023-12-29T16:10:49.447Z info     First you need to decide if you are creating a new network or creating nodes to join an existing network.
2023-12-29T16:10:49.447Z info     
? Are you creating a new network? Yes
2023-12-29T16:15:21.306Z info     
2023-12-29T16:15:21.306Z info     The new network will be based on an existing network. You can select an out-of-the box preset from Symbol Bootstrap or you can provide a custom network preset to be based one
2023-12-29T16:15:21.306Z info     
? Select the Bootstrap profile to base your new network from: Bootstrap Preset
? Enter the domain to be used to be used in your nodes mycompany.com
? Is the Domain mycompany.com correct? Yes
? Enter a suffix for node generated domain names and urls myc
? Is the Suffix myc correct? Yes
? Enter the pattern used to generate hostnames and friendly names $suffix-$nickname-$friendlyNumber
? Is the friendlyNamePattern $suffix-$nickname-$friendlyNumber correct? Yes
? Enter the password used to encrypt and decrypt the local key store (MASTER PASSWORD). 
2023-12-29T16:15:36.628Z warn     Password has not been provided (empty text)! It's recommended to use one for security!
? What's the network type you want to create? Testnet
? Enter a name for the network. My Company Testnet Network
? Is the Network Name My Company Testnet Network correct? Yes
? Enter the generation hash seed to identify the network 5F45C8F5CEA32ACC7385BBC3E4FBADF1B5812AED095118567BD12099C7BF7650
? Is the Generation Hash Seed 5F45C8F5CEA32ACC7385BBC3E4FBADF1B5812AED095118567BD12099C7BF7650 correct? Yes
? Enter the epoch adjustment value to offset deadlines. 1703866541
? Is the Epoch Adjustment 1703866541 correct? Yes
? Enter the basename for the network aliases cat
? Is the Network basename Alias cat correct? Yes
? Enter the alias for the Network Currency currency
? Is the Network Currency Name currency correct? Yes
? Enter the alias for the Harvest Currency harvest
? Is the Harvest Currency Name harvest correct? Yes
? Enter the 64 HEX private key Nemesis Signer Account (or press enter to accept the auto generated): ****************************************************************
? Is the Nemesis Signer Account address TCYJT6IFZ5SULPFPL4HCH2K4EHXQTGJI42YQXGY public key 2474619FF9AFAFCE511686541E359409E739BA6F465F1246EBEAB67620360853 correct? Yes
? Enter the 64 HEX private key Founder Account (or press enter to accept the auto generated): ****************************************************************
? Is the Founder Account address TAHVAZ75UCVK6RE5FD4AZPSGXCLAPBYSULR7JJY public key 1A89A1791EF686E97AAA4F16B17BCC889A8C07A90FA356DEBE6ED7532DD94AC8 correct? Yes
? Do you want to have a Faucet account? Yes
? Enter the 64 HEX private key Faucet Account (or press enter to accept the auto generated): ****************************************************************
? Is the Faucet Account address TCG7EXSVKZAI3O534KXPSLOPY7BZM624JIPC2MQ public key 33791E4C1B63AA364F1EFD32674A906CE9BD28C529DAAC4269F77D46521B7667 correct? Yes
? What's the initial currency coin balance for the Faucet Account TCG7EXSVKZAI3O534KXPSLOPY7BZM624JIPC2MQ? 449949995
? Is the Balance 449949995 correct? Yes
? What's the initial harvest coin balance for the Faucet Account TCG7EXSVKZAI3O534KXPSLOPY7BZM624JIPC2MQ? 750
? Is the Balance 750 correct? Yes
2023-12-29T16:16:28.963Z info     
2023-12-29T16:16:28.964Z info     In addition to the node, faucet and founder accounts, you can include (opt-in) more accounts into the nemesis block by distributing currency coins.
? Do you want to distribute coin currency to different accounts on the nemesis block? No
2023-12-29T16:16:30.516Z info     
2023-12-29T16:16:30.517Z info     In addition to the node, faucet and founder accounts, you can include (opt-in) more accounts into the nemesis block by distributing harvest coins.
? Do you want to distribute coin harvest to different accounts on the nemesis block? No
? Enter the 64 HEX private key Harvest Network Fee Sink Account (or press enter to accept the auto generated): ****************************************************************
? Is the Harvest Network Fee Sink Account address TAZIMZPHTUGQTV2NSYKCQP3W5PFHXO756CEUYUI public key CED044F7EB9D1A7E0CD2DFA8D2A74B3E0B67D73072020369B61580B0FE56E31E correct? Yes
? Enter the 64 HEX private key Namespace Rental Fee Sink Account (or press enter to accept the auto generated): ****************************************************************
? Is the Namespace Rental Fee Sink Account address TAOLJZG6GH3FFAJ25BCCMY6IK7MAMUNLXOVSNNA public key 40AD377CC34C08209F1EE05287789FD12414D1962821B544F61243BC8F965DAD correct? Yes
? Enter the 64 HEX private key Mosaic Rental Fee Sink Account (or press enter to accept the auto generated): ****************************************************************
? Is the Mosaic Rental Fee Sink Account address TAXSMYMTSPVPOIGC5OUT35BNFRDHT6FR7TBKD7A public key 510A2A553527A01A8662BC982E81E3166F80A33A0049F4F9EFDFCB35674537D9 correct? Yes
2023-12-29T16:16:36.262Z info     
2023-12-29T16:16:36.263Z info     The initial network preset custom-network-preset.yml for the new network has been stored. This file will be updated in the following steps.
2023-12-29T16:16:36.263Z info     
2023-12-29T16:16:36.263Z info     
2023-12-29T16:16:36.263Z info     
? Select the node type you want to create Voting Dual
? How many nodes of type Voting Dual do you want to create? 3
? What's the initial currency balance for the Voting Dual nodes? 3000000
? Is the Balance 3000000 correct? Yes
? What's the initial harvest balance for the Voting Dual nodes? 150
? Is the Balance 150 correct? Yes
? The nick name of the these nodes dual
? Is the Nodes's Nick Name dual correct? Yes
? Select the HTTP protocol for your Rest Gateways HttpAndHttps
? Do you want to create 3 nodes of type Voting Dual each with balance of 3000000, 150? Yes
? Do you want to create more nodes? No
2023-12-29T16:17:39.076Z info     
2023-12-29T16:17:39.076Z info     You have created the initial network-input.yml. Have a look and once once you are happy, run: 
2023-12-29T16:17:39.076Z info     
2023-12-29T16:17:39.076Z info     $ symbol-network expandNodes
2023-12-29T16:17:39.077Z info     
```

全ての問いに答えると`custom-network-preset.yml` `key-store.yml` `network-input.yml`が作成されていると思います。
以下は適当にEnterを押して作った`custom-network-preset.yml`ファイルです。

```yml
networkDescription: My Company Testnet Network
initialCurrencyAtomicUnits: 8998999998000000
maxMosaicAtomicUnits: 9000000000000000
totalChainImportance: 15000000
minHarvesterBalance: 500
maxHarvesterBalance: 50000000000000
networkType: 152
minVoterBalance: 50000
baseNamespace: cat
explorerUrl: http://localhost:90/
faucetUrl: http://localhost:100/
beneficiaryAddress: ''
votingKeyDesiredLifetime: 720
votingKeyDesiredFutureLifetime: 120
lastKnownNetworkEpoch: 1
restExtensions: accountLink, aggregate, lockHash, lockSecret, mosaic, metadata, multisig, namespace, receipts, restrictions, transfer
nemesis:
    mosaics:
        -
            name: currency
            divisibility: 6
            duration: 0
            supply: 8998999998000000
            isTransferable: true
            isSupplyMutable: false
            isRestrictable: false
            accounts: 5
        -
            name: harvest
            divisibility: 3
            duration: 0
            supply: 15000000
            isTransferable: true
            isSupplyMutable: true
            isRestrictable: false
            accounts: 2
inflation:
    starting-at-height-1: 0
    starting-at-height-10000: 0
nemesisSeedFolder: nemesis-seed
nemesisSignerPublicKey: 2474619FF9AFAFCE511686541E359409E739BA6F465F1246EBEAB67620360853
harvestNetworkFeeSinkAddress: TAZIMZPHTUGQTV2NSYKCQP3W5PFHXO756CEUYUI
namespaceRentalFeeSinkAddress: TAOLJZG6GH3FFAJ25BCCMY6IK7MAMUNLXOVSNNA
mosaicRentalFeeSinkAddress: TAXSMYMTSPVPOIGC5OUT35BNFRDHT6FR7TBKD7A
nemesisGenerationHashSeed: 5F45C8F5CEA32ACC7385BBC3E4FBADF1B5812AED095118567BD12099C7BF7650
epochAdjustment: 1703866541s

```

このファイルは今回、特に変更する必要はありませんが、諸々の追加設定を行うファイルになります。

続けて、以下のコマンドを実行します。

```bash
$ symbol-network expandNodes
```

`network.yml`が生成されます。

```yml
preset: custom-network-preset.yml
cloneFromPreset: bootstrap
domain: mycompany.com
suffix: myc
friendlyNamePattern: $suffix-$nickname-$friendlyNumber
networkType: 152
isNewNetwork: true
faucetBalances:
    - 449949995
    - 750
additionalCurrencyDistributions:
    - []
    - []
customNetworkPreset:
    nemesis:
        mosaics:
            -
                name: currency
            -
                name: harvest
    networkDescription: My Company Testnet Network
    nemesisGenerationHashSeed: 5F45C8F5CEA32ACC7385BBC3E4FBADF1B5812AED095118567BD12099C7BF7650
    epochAdjustment: 1703866541s
    baseNamespace: cat
nodes:
    -
        number: 1
        friendlyName: myc-dual-001
        assembly: dual
        hostname: myc-dual-001.mycompany.com
        customPreset:
            privateKeySecurityMode: PROMPT_MAIN_TRANSPORT
            nodes:
                -
                    friendlyName: myc-dual-001
                    host: myc-dual-001.mycompany.com
                    voting: true
                    harvesting: true
                    dockerComposeDebugMode: false
                    brokerDockerComposeDebugMode: false
            gateways:
                -
                    openPort: true
            httpsProxies:
                -
                    excludeDockerService: false
        nickName: dual
        nodeType: VotingDual
        balances:
            - 3000000
            - 150
        restProtocol: HttpAndHttps
    -
        number: 2
        friendlyName: myc-dual-002
        assembly: dual
        hostname: myc-dual-002.mycompany.com
        customPreset:
            privateKeySecurityMode: PROMPT_MAIN_TRANSPORT
            nodes:
                -
                    friendlyName: myc-dual-002
                    host: myc-dual-002.mycompany.com
                    voting: true
                    harvesting: true
                    dockerComposeDebugMode: false
                    brokerDockerComposeDebugMode: false
            gateways:
                -
                    openPort: true
            httpsProxies:
                -
                    excludeDockerService: false
        nickName: dual
        nodeType: VotingDual
        balances:
            - 3000000
            - 150
        restProtocol: HttpAndHttps
    -
        number: 3
        friendlyName: myc-dual-003
        assembly: dual
        hostname: myc-dual-003.mycompany.com
        customPreset:
            privateKeySecurityMode: PROMPT_MAIN_TRANSPORT
            nodes:
                -
                    friendlyName: myc-dual-003
                    host: myc-dual-003.mycompany.com
                    voting: true
                    harvesting: true
                    dockerComposeDebugMode: false
                    brokerDockerComposeDebugMode: false
            gateways:
                -
                    openPort: true
            httpsProxies:
                -
                    excludeDockerService: false
        nickName: dual
        nodeType: VotingDual
        balances:
            - 3000000
            - 150
        restProtocol: HttpAndHttps

```

最後に以下のコマンドを実行してネメシスファイルを生成します。

```
$ symbol-network generateNemesis
```

実行すると、`nemesis-seed` `nemesis-target`という２フォルダが生成されていると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/934471/dcc775b0-591f-3179-b417-8b452e86e066.png)

今回必要になるのは、`nemesis-target/nemesis`フォルダと`nemesis-target/nodes/node/server-config/resources`フォルダと`nemesis-target/nodes/node/cert`フォルダです。
上記の3つのフォルダをカレントディレクトにコピーします。
また、

```bash
$ cp -r nemesis-target/nemesis .
$ cp -r nemesis-target/nodes/node/server-config/resources .
$ cp -r nemesis-target/nodes/node/cert .
# $ cp nemesis/server-config/block-properties-file.properties resources/testnet.properties
```

# ディレクトリの修正

`resources/config-user.properties`を以下のように修正します。

```diff
[account]

enableDelegatedHarvestersAutoDetection = true

[storage]

- seedDirectory = ./seed
+ seedDirectory = ../nemesis/seed
- certificateDirectory = ./cert
+ certificateDirectory = ../cert
- dataDirectory = ./data
+ dataDirectory = ../data
- pluginsDirectory = /usr/catapult/lib
+ pluginsDirectory = .
votingKeysDirectory = ./votingkeys

```

# ノードの実行

それでは、ノードの起動準備ができたので実際にノードを起動してみます。

まずは、データの保存される領域を作成します。

```bash
$ mkdir data
```

カタパルトサーバーを実行します。
```
$ cd bin
$ ./catapult.server
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/934471/12e35830-3ae9-d701-755f-64fe3a5ff830.png)

適当にノードを作ったせいで色々とエラーになっていますが、ブロックは進んでいるようです。
（ノードを３台で作成したので接続しにいっているがそもそも１台でしか実行してないのでエラーになっている）

# mongodbのインストール

続けてmongodbの設定を行います。
ノードはControl+Cで停止しておいてください。
ノードが停止したら以下の手順でmongodbをインストールします。

```bash
#　GPG公開鍵の追加
$　curl -fsSL https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -
#　リポジトリの追加
$　echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list
#　インストール
$　sudo apt update
$　sudo apt install mongodb-org

#　自動起動の設定
$　sudo systemctl enable mongod
#　サービス起動
$ sudo systemctl start mongod.service
```

# データベースの実行
 
```bash
$ mongod --dbpath=dbfiles --wiredTigerCacheSizeGB 2 --bind_ip 127.0.0.1
{"t":{"$date":"2023-12-30T21:08:14.811+09:00"},"s":"I",  "c":"NETWORK",  "id":4915701, "ctx":"-","msg":"Initialized wire specification","attr":{"spec":{"incomingExternalClient":{"minWireVersion":0,"maxWireVersion":13},"incomingInternalClient":{"minWireVersion":0,"maxWireVersion":13},"outgoing":{"minWireVersion":0,"maxWireVersion":13},"isInternalClient":true}}}
{"t":{"$date":"2023-12-30T21:08:14.812+09:00"},"s":"I",  "c":"CONTROL",  "id":23285,   "ctx":"-","msg":"Automatically disabling TLS 1.0, to force-enable TLS 1.0 specify --sslDisabledProtocols 'none'"}
{"t":{"$date":"2023-12-30T21:08:14.813+09:00"},"s":"W",  "c":"ASIO",     "id":22601,   "ctx":"main","msg":"No TransportLayer configured during NetworkInterface startup"}
{"t":{"$date":"2023-12-30T21:08:14.813+09:00"},"s":"I",  "c":"NETWORK",  "id":4648601, "ctx":"main","msg":"Implicit TCP FastOpen unavailable. If TCP FastOpen is required, set tcpFastOpenServer, tcpFastOpenClient, and tcpFastOpenQueueSize."}
{"t":{"$date":"2023-12-30T21:08:14.815+09:00"},"s":"W",  "c":"ASIO",     "id":22601,   "ctx":"main","msg":"No TransportLayer configured during NetworkInterface startup"}
{"t":{"$date":"2023-12-30T21:08:14.815+09:00"},"s":"I",  "c":"REPL",     "id":5123008, "ctx":"main","msg":"Successfully registered PrimaryOnlyService","attr":{"service":"TenantMigrationDonorService","ns":"config.tenantMigrationDonors"}}
{"t":{"$date":"2023-12-30T21:08:14.815+09:00"},"s":"I",  "c":"REPL",     "id":5123008, "ctx":"main","msg":"Successfully registered PrimaryOnlyService","attr":{"service":"TenantMigrationRecipientService","ns":"config.tenantMigrationRecipients"}}
{"t":{"$date":"2023-12-30T21:08:14.815+09:00"},"s":"I",  "c":"CONTROL",  "id":5945603, "ctx":"main","msg":"Multi threading initialized"}
{"t":{"$date":"2023-12-30T21:08:14.815+09:00"},"s":"I",  "c":"CONTROL",  "id":4615611, "ctx":"initandlisten","msg":"MongoDB starting","attr":{"pid":552099,"port":27017,"dbPath":"dbfiles","architecture":"64-bit","host":"vmi649314.contaboserver.net"}}
{"t":{"$date":"2023-12-30T21:08:14.815+09:00"},"s":"I",  "c":"CONTROL",  "id":23403,   "ctx":"initandlisten","msg":"Build Info","attr":{"buildInfo":{"version":"5.0.23","gitVersion":"3367195a14d0ba2734d2ba2719294fb974ad0834","openSSLVersion":"OpenSSL 1.1.1f  31 Mar 2020","modules":[],"allocator":"tcmalloc","environment":{"distmod":"ubuntu2004","distarch":"x86_64","target_arch":"x86_64"}}}}
{"t":{"$date":"2023-12-30T21:08:14.815+09:00"},"s":"I",  "c":"CONTROL",  "id":51765,   "ctx":"initandlisten","msg":"Operating System","attr":{"os":{"name":"Ubuntu","version":"20.04"}}}
{"t":{"$date":"2023-12-30T21:08:14.815+09:00"},"s":"I",  "c":"CONTROL",  "id":21951,   "ctx":"initandlisten","msg":"Options set by command line","attr":{"options":{"net":{"bindIp":"127.0.0.1"},"storage":{"dbPath":"dbfiles","wiredTiger":{"engineConfig":{"cacheSizeGB":2}}}}}}
{"t":{"$date":"2023-12-30T21:08:14.816+09:00"},"s":"E",  "c":"NETWORK",  "id":23024,   "ctx":"initandlisten","msg":"Failed to unlink socket file","attr":{"path":"/tmp/mongodb-27017.sock","error":"Operation not permitted"}}
{"t":{"$date":"2023-12-30T21:08:14.816+09:00"},"s":"F",  "c":"-",        "id":23091,   "ctx":"initandlisten","msg":"Fatal assertion","attr":{"msgid":40486,"file":"src/mongo/transport/transport_layer_asio.cpp","line":1131}}
{"t":{"$date":"2023-12-30T21:08:14.816+09:00"},"s":"F",  "c":"-",        "id":23092,   "ctx":"initandlisten","msg":"\n\n***aborting after fassert() failure\n\n"}
```

# インデックス構築

```bash
$ mongo 127.0.0.1/catapult < ../scripts/mongo/mongoDbPrepare.js
MongoDB shell version v5.0.23
connecting to: mongodb://127.0.0.1:27017/catapult?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("8cb73084-f487-4553-860d-392be66724b9") }
MongoDB server version: 5.0.23
================
Warning: the "mongo" shell has been superseded by "mongosh",
which delivers improved usability and compatibility.The "mongo" shell has been deprecated and will be removed in
an upcoming release.
For installation instructions, see
https://docs.mongodb.com/mongodb-shell/install/
================
Loading LockHash
{"t":{"$date":"2023-12-30T12:09:37.072Z"},"s":"E",  "c":"-",        "id":22779,   "ctx":"js","msg":"file [{filename}] doesn't exist","attr":{"filename":"mongoLockHashDbPrepare.js"}}
Error: error loading js file: mongoLockHashDbPrepare.js :
@(shell):7:3
preparePluginDbCollections@(shell):5:2
@(shell):1:11
===== accounts INDEXES =====
{ "_id" : 1 }
{ "account.publicKey" : 1 }
{ "account.address" : 1 }
===== addressResolutionStatements INDEXES =====
{ "_id" : 1 }
{ "statement.height" : 1, "statement.unresolved" : 1 }
===== blocks INDEXES =====
{ "_id" : 1 }
{ "block.signerPublicKey" : 1 }
{ "block.timestamp" : -1 }
{ "block.height" : -1 }
{ "block.type" : 1, "block.height" : -1 }
{ "block.signerPublicKey" : 1, "block.height" : -1 }
{ "block.beneficiaryAddress" : 1, "block.height" : -1 }
===== finalizedBlocks INDEXES =====
{ "_id" : 1 }
{ "block.finalizationEpoch" : -1 }
{ "block.height" : -1 }
{ "block.finalizationEpoch" : -1, "block.finalizationPoint" : -1 }
===== mosaicResolutionStatements INDEXES =====
{ "_id" : 1 }
{ "statement.height" : 1, "statement.unresolved" : 1 }
===== partialTransactions INDEXES =====
{ "_id" : 1 }
{ "transaction.signerPublicKey" : 1, "_id" : -1 }
{ "transaction.recipientAddress" : 1, "_id" : -1 }
{ "meta.hash" : 1 }
{ "meta.addresses" : 1 }
{ "meta.aggregateId" : 1 }
{ "meta.aggregateHash" : 1 }
===== system.profile INDEXES =====
===== transactionStatements INDEXES =====
{ "_id" : 1 }
{
        "statement.height" : 1,
        "statement.source.primaryId" : 1,
        "statement.source.secondaryId" : 1
}
===== transactionStatuses INDEXES =====
{ "_id" : 1 }
{ "status.hash" : 1 }
{ "status.deadline" : -1 }
===== transactions INDEXES =====
{ "_id" : 1 }
{ "transaction.signerPublicKey" : 1, "_id" : -1 }
{ "transaction.recipientAddress" : 1, "_id" : -1 }
{ "meta.hash" : 1 }
{ "meta.addresses" : 1 }
{ "meta.aggregateId" : 1 }
{ "meta.height" : -1 }
{ "transaction.deadline" : -1 }
{ "transaction.cosignatures.signerPublicKey" : 1 }
{ "transaction.id" : 1, "transaction.type" : 1 }
===== unconfirmedTransactions INDEXES =====
{ "_id" : 1 }
{ "transaction.signerPublicKey" : 1, "_id" : -1 }
{ "transaction.recipientAddress" : 1, "_id" : -1 }
{ "meta.hash" : 1 }
{ "meta.addresses" : 1 }
{ "meta.aggregateId" : 1 }
{ "meta.aggregateHash" : 1 }
bye
```

# ノードの実行

```bash
$ cd bin
$ ./catapult.server
```

# 設定ファイルの修正
(これは不要かもしれない)

`resources/config-database.properties`ファイルを以下のように修正します

```diff
[database]

- databaseUri = mongodb://db:27017
+ databaseUri = mongodb://127.0.0.1:27017
databaseName = catapult
maxWriterThreads = 8
maxDropBatchSize = 10
writeTimeout = 10m

[plugins]

catapult.mongo.plugins.accountlink = true
catapult.mongo.plugins.aggregate = true
catapult.mongo.plugins.lockhash = true
catapult.mongo.plugins.locksecret = true
catapult.mongo.plugins.metadata = true
catapult.mongo.plugins.mosaic = true
catapult.mongo.plugins.multisig = true
catapult.mongo.plugins.namespace = true
catapult.mongo.plugins.restrictionaccount = true
catapult.mongo.plugins.restrictionmosaic = true
catapult.mongo.plugins.transfer = true

```

# brokerの実行

別の端末を開いてbinフォルダ以下でコマンドを実行します

```bash
$ cd bin
$ ./catapult.broker ..
```

## エラーが出る場合

```bash
Copyright (c) Jaguar0625, gimre, BloodyRookie, Tech Bureau, Corp.
catapult version: 1.0.3.6 afea1ae8 [v1.0.3.6]
loading resources from "../resources"
loading configuration from "../resources/config-inflation.properties"
loading configuration from "../resources/config-extensions-broker.properties"
loading configuration from "../resources/config-user.properties"
loading configuration from "../resources/config-logging-broker.properties"
loading configuration from "../resources/config-node.properties"
loading configuration from "../resources/config-network.properties"
2023-12-30 22:42:44.414491 0x00007fc297d2f2c0: <warning> (io::FileLock.cpp@75) LockOpen failed: 17 
2023-12-30 22:42:44.414516 0x00007fc297d2f2c0: <fatal> (process::ProcessMain.cpp@139) could not acquire instance lock "../data/broker.lock" 
```

`data/broker.lock`が残っているとエラーが発生するので手動で削除してください

```bash
$ rm -fr ../data/broker.lock
```

# RESTの起動

続いてRESTを起動します。
RESTは`client`フォルダ内の`rest`をそのまま使います。
別のターミナルを開いて、`rest`を`_build`フォルダへコピーします。

```bash
$ cp -r ../../rest .
```
# 設定変更

そのままではrestが正常に動かないので何箇所か修正します。

`src/index.js`で参照している`resources/rest.json`までのパスが相対パスだと読み込みができないので絶対パスへ変更します。（各々の環境に合わせてパスを設定してください）

```rest/src/index.js
/*
 * Copyright (c) 2016-2019, Jaguar0625, gimre, BloodyRookie, Tech Bureau, Corp.
 * Copyright (c) 2020-present, Jaguar0625, gimre, BloodyRookie.
 * All rights reserved.
 *
 * This file is part of Catapult.
 *
 * Catapult is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Catapult is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Catapult.  If not, see <http://www.gnu.org/licenses/>.
 */

const catapult = require('./catapult-sdk/index');
const { createConnectionService } = require('./connection/connectionService');
const { createZmqConnectionService } = require('./connection/zmqService');
const CatapultDb = require('./db/CatapultDb');
const dbFormattingRules = require('./db/dbFormattingRules');
const routeSystem = require('./plugins/routeSystem');
const allRoutes = require('./routes/allRoutes');
const bootstrapper = require('./server/bootstrapper');
const formatters = require('./server/formatters');
const messageFormattingRules = require('./server/messageFormattingRules');
const sshpk = require('sshpk');
const winston = require('winston');
const fs = require('fs');

const createLoggingTransportConfiguration = loggingConfig => {
	const transportConfig = Object.assign({}, loggingConfig);

	// map specified formats into a winston function
	delete transportConfig.formats;
	const logFormatters = loggingConfig.formats.map(name => winston.format[name]());
	transportConfig.format = winston.format.combine(...logFormatters);
	return transportConfig;
};

const configureLogging = config => {
	const transports = [new winston.transports.File(createLoggingTransportConfiguration(config.file))];
	if ('production' !== process.env.NODE_ENV)
		transports.push(new winston.transports.Console(createLoggingTransportConfiguration(config.console)));

	// configure default logger so that it adds timestamp to all logs
	winston.configure({
		format: winston.format.timestamp(),
		transports
	});
};

const validateConfig = config => {
	if (config.crossDomain && (!config.crossDomain.allowedHosts || !config.crossDomain.allowedMethods))
		throw Error('provided CORS configuration is incomplete');
};

const loadConfig = () => {
	let configFiles = process.argv.slice(2);
	if (0 === configFiles.length)
-		configFiles = ['../resources/rest.json'];
+		configFiles = ['/home/xxx/symbol/client/catapult/_build/rest/resources/rest.json'];

	let config;
	configFiles.forEach(configFile => {
		winston.info(`loading config from ${configFile}`);
		const partialConfig = JSON.parse(fs.readFileSync(configFile, 'utf8'));
		if (config) {
			// override config
			catapult.utils.objects.checkSchemaAgainstTemplate(config, partialConfig);
			catapult.utils.objects.deepAssign(config, partialConfig);
		} else {
			// primary config
			config = partialConfig;
		}
	});

	validateConfig(config);

	return config;
};

const createServiceManager = () => {
	const shutdownHandlers = [];
	return {
		pushService: (object, shutdownHandlerName) => {
			shutdownHandlers.push(() => { object[shutdownHandlerName](); });
		},
		stopAll: () => {
			while (0 < shutdownHandlers.length)
				shutdownHandlers.pop()();
		}
	};
};

const connectToDbWithRetry = (db, config) => catapult.utils.future.makeRetryable(
	() => db.connect(config.url, config.name, config.connectionPoolSize, config.connectionTimeout),
	config.maxConnectionAttempts,
	(i, err) => {
		const waitTime = (2 ** (i - 1)) * config.baseRetryDelay;
		winston.warn(`db connection failed, retrying in ${waitTime}ms`, err);
		return waitTime;
	}
);

const createServer = config => {
	const modelSystem = catapult.plugins.catapultModelSystem.configure(config.extensions, {
		json: dbFormattingRules,
		ws: messageFormattingRules
	});
	return {
		server: bootstrapper.createServer(config, formatters.create(modelSystem.formatters), config.throttling),
		codec: modelSystem.codec
	};
};

const registerRoutes = (server, db, services) => {
	// 1. create a services view for extension routes
	const servicesView = {
		config: {
			network: services.config.network,
			pageSize: {
				min: services.config.db.pageSizeMin || 10,
				max: services.config.db.pageSizeMax || 100,
				default: services.config.db.pageSizeDefault || 20
			},
			apiNode: services.config.apiNode,
			websocket: services.config.websocket,
			numBlocksTransactionFeeStats: services.config.numBlocksTransactionFeeStats,
			deployment: services.config.deployment,

			uncirculatingAccountPublicKeys: services.config.uncirculatingAccountPublicKeys,
			nodeMetadata: services.config.nodeMetadata
		},
		connections: services.connectionService
	};

	// 2. configure extension routes
	const { transactionStates, messageChannelDescriptors } = routeSystem.configure(services.config.extensions, server, db, servicesView);

	// 3. augment services with extension-dependent config and services
	servicesView.config.transactionStates = transactionStates;
	servicesView.zmqService = createZmqConnectionService(services.config.websocket.mq, services.codec, messageChannelDescriptors, winston);

	// 4. configure basic routes
	allRoutes.register(server, db, servicesView);
};

(() => {
	const config = loadConfig();

	configureLogging(config.logging);
	winston.verbose('finished loading rest server config', config);

	// Loading and caching certificates.
	config.apiNode = {
		...config.apiNode,
		certificate: fs.readFileSync(config.apiNode.tlsClientCertificatePath),
		key: fs.readFileSync(config.apiNode.tlsClientKeyPath),
		caCertificate: fs.readFileSync(config.apiNode.tlsCaCertificatePath)
	};
	const nodeCertKey = sshpk.parsePrivateKey(config.apiNode.key);
	config.apiNode.nodePublicKey = nodeCertKey.toPublic().part.A.data;

	const network = catapult.model.networkInfo.networks[config.network.name];
	if (!network) {
		winston.error(`no network found with name: '${config.network.name}'`);
		return;
	}

	const serviceManager = createServiceManager();
	const db = new CatapultDb({
		networkId: network.id,

		// to be removed when old pagination is not used anymore
		// json settings should also be moved from config.db to config.api or similar
		pageSizeMin: config.db.pageSizeMin,
		pageSizeMax: config.db.pageSizeMax
	});

	serviceManager.pushService(db, 'close');

	winston.info(`connecting to ${config.db.url} (database:${config.db.name})`);
	connectToDbWithRetry(db, config.db)
		.then(() => {
			winston.info('registering routes');
			const serverAndCodec = createServer(config);
			const { server } = serverAndCodec;
			serviceManager.pushService(server, 'close');

			const connectionConfig = {
				apiNode: config.apiNode
			};
			const connectionService = createConnectionService(connectionConfig, winston.verbose);
			registerRoutes(server, db, { codec: serverAndCodec.codec, config, connectionService });

			winston.info(`listening on port ${config.port}`);
			server.listen(config.port);
		})
		.catch(err => {
			winston.error('rest server is exiting due to error', err);
			serviceManager.stopAll();
		});

	process.on('SIGINT', () => {
		winston.info('SIGINT detected, shutting down rest server');
		serviceManager.stopAll();
	});
})();

```

続いて、`rest/resources/rest.json`の修正を行います。
まず`symbol-network`で作成した設定ファイル(`nemesis-target/gateways/rest-gateway`フォルダ)をコピーします。

```bash
$ cp -r ../nemesis-target/gateways/rest-gateway/* resources/
```

コピーした`rest.json`を以下のように修正します。

```diff
{
  "network": {
    "name": "testnet",
    "description": "My Company Testnet Network"
  },
  "port": 3000,
  "protocol": "HTTP",
-  "sslKeyPath": "/symbol-workdir/restSSL.key",
+  "sslKeyPath": "",
-  "sslCertificatePath": "/symbol-workdir/restSSL.crt",
+  "sslCertificatePath": "",
  "crossDomain": {
    "allowedHosts": [
      "*"
    ],
    "allowedMethods": [
      "GET",
      "POST",
      "PUT",
      "OPTIONS"
    ]
  },
  "uncirculatingAccountPublicKeys": [],
  "extensions": [
    "accountLink",
    "aggregate",
    "lockHash",
    "lockSecret",
    "mosaic",
    "metadata",
    "multisig",
    "namespace",
    "receipts",
    "restrictions",
    "transfer"
  ],
  "db": {
-    "url": "mongodb://db:27017/",
+    "url": "mongodb://127.0.0.1:27017/",
    "name": "catapult",
    "pageSizeMin": 10,
    "pageSizeMax": 100,
    "maxConnectionAttempts": 15,
    "baseRetryDelay": 750,
    "connectionPoolSize": 10
  },
  "apiNode": {
-    "host": "node",
+    "host": "127.0.0.1",
    "port": 7900,
-    "tlsClientCertificatePath": "/symbol-workdir/api-node-config/cert/node.crt.pem",
+    "tlsClientCertificatePath": "/home/xxx/symbol/client/catapult/_build/rest/resources/api-node-config/cert/node.crt.pem",
-    "tlsClientKeyPath": "/symbol-workdir/api-node-config/cert/node.key.pem",
+    "tlsClientKeyPath": "/home/xxx/symbol/client/catapult/_build/rest/resources/api-node-config/cert/node.key.pem",
-    "tlsCaCertificatePath": "/symbol-workdir/api-node-config/cert/ca.cert.pem",
+    "tlsCaCertificatePath": "/home/xxx/symbol/client/catapult/_build/rest/resources/api-node-config/cert/ca.cert.pem",
    "timeout": 1000,
-    "networkPropertyFilePath": "/symbol-workdir/api-node-config/config-network.properties",
+    "networkPropertyFilePath": "/home/xxx/symbol/client/catapult/_build/rest/resources/api-node-config/config-network.properties",
-    "nodePropertyFilePath": "/symbol-workdir/api-node-config/config-node.properties",
+    "nodePropertyFilePath": "/home/xxx/symbol/client/catapult/_build/rest/resources/api-node-config/config-node.properties",
-    "inflationPropertyFilePath": "/symbol-workdir/api-node-config/config-inflation.properties"
+    "inflationPropertyFilePath": "/home/xxx/symbol/client/catapult/_build/rest/resources/api-node-config/config-inflation.properties"
  },
  "websocket": {
    "mq": {
      "host": "broker",
      "port": 7902,
      "monitorInterval": 500,
      "connectTimeout": 10000,
      "monitorLoggingThrottle": 60000,
      "maxSubscriptions": 300
    },
    "allowOptionalAddress": true
  },
  "throttling": {
    "burst": 80,
    "rate": 60
  },
  "logging": {
    "console": {
      "formats": [
        "colorize",
        "simple"
      ],
      "level": "verbose",
      "handleExceptions": true
    },
    "file": {
      "formats": [
        "prettyPrint"
      ],
      "level": "verbose",
      "handleExceptions": true,
-      "filename": "/symbol-workdir/logs/catapult-rest.log",
+      "filename": "/home/xxx/symbol/client/catapult/_build/rest/logs/catapult-rest.log",
      "maxsize": 20971520,
      "maxFiles": 100
    }
  },
  "numBlocksTransactionFeeStats": 300,
  "deployment": {
    "deploymentTool": "symbol-bootstrap",
    "deploymentToolVersion": "1.1.10",
    "lastUpdatedDate": "2023-12-29"
  }
}

```

（各々の環境に合わせてパスを設定してください）

# REST起動

```bash
$ npm run start

> symbol-api-rest@2.4.3 start
> node src/index.js

[winston] Attempt to write logs with no transports, which can increase memory usage: {"message":"loading config from /home/xxx/src/symbol/client/catapult/_build/rest/resources/rest.json","level":"info"}
(node:569738) [DEP0111] DeprecationWarning: Access to process.binding('http_parser') is deprecated.
(Use `node --trace-deprecation ...` to show where the warning was created)
info: loading config from /home/xxx/src/symbol/client/catapult/_build/rest/resources/rest.json
verbose: finished loading rest server config {"apiNode":{"host":"node","inflationPropertyFilePath":"/home/xxx/src/symbol/client/catapult/_build/rest/resources/api-node-config/config-inflation.properties","networkPropertyFilePath":"/home/xxx/src/symbol/client/catapult/_build/rest/resources/api-node-config/config-network.properties","nodePropertyFilePath":"/home/xxx/src/symbol/client/catapult/_build/rest/resources/api-node-config/config-node.properties","port":7900,"timeout":1000,"tlsCaCertificatePath":"/home/xxx/src/symbol/client/catapult/_build/rest/resources/api-node-config/cert/ca.cert.pem","tlsClientCertificatePath":"/home/xxx/src/symbol/client/catapult/_build/rest/resources/api-node-config/cert/node.crt.pem","tlsClientKeyPath":"/home/xxx/src/symbol/client/catapult/_build/rest/resources/api-node-config/cert/node.key.pem"},"crossDomain":{"allowedHosts":["*"],"allowedMethods":["GET","POST","PUT","OPTIONS"]},"db":{"baseRetryDelay":750,"connectionPoolSize":10,"maxConnectionAttempts":15,"name":"catapult","pageSizeMax":100,"pageSizeMin":10,"url":"mongodb://127.0.0.1:27017/"},"deployment":{"deploymentTool":"symbol-bootstrap","deploymentToolVersion":"1.1.10","lastUpdatedDate":"2023-12-29"},"extensions":["accountLink","aggregate","lockHash","lockSecret","mosaic","metadata","multisig","namespace","receipts","restrictions","transfer"],"logging":{"console":{"formats":["colorize","simple"],"handleExceptions":true,"level":"verbose"},"file":{"filename":"/home/xxx/src/symbol/client/catapult/_build/rest/logs/catapult-rest.log","formats":["prettyPrint"],"handleExceptions":true,"level":"verbose","maxFiles":100,"maxsize":20971520}},"network":{"description":"My Company Testnet Network","name":"testnet"},"numBlocksTransactionFeeStats":300,"port":3000,"protocol":"HTTP","sslCertificatePath":"","sslKeyPath":"","throttling":{"burst":80,"rate":60},"timestamp":"2023-12-30T14:23:09.703Z","uncirculatingAccountPublicKeys":[],"websocket":{"allowOptionalAddress":true,"mq":{"connectTimeout":10000,"host":"broker","maxSubscriptions":300,"monitorInterval":500,"monitorLoggingThrottle":60000,"port":7902}}}
info: connecting to mongodb://127.0.0.1:27017/ (database:catapult) {"timestamp":"2023-12-30T14:23:09.725Z"}
verbose: connecting to mongo at mongodb://127.0.0.1:27017/catapult {"timestamp":"2023-12-30T14:23:09.772Z"}
info: registering routes {"timestamp":"2023-12-30T14:23:09.773Z"}
info: Using protocol: HTTP {"timestamp":"2023-12-30T14:23:09.788Z"}
info: listening on port 3000 {"timestamp":"2023-12-30T14:23:09.800Z"}
```
上記のようなログが表示されたら成功です。
`IP:3000/node/info`のようにREST APIへアクセスできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/934471/7a1ff3d4-1621-b418-acac-0f5003637106.png)

# 参考

https://github.com/symbol/symbol/blob/client/catapult/v1.0.3.6/client/catapult/docs/RUNNETWORKLIN.md

https://docs.symbol.dev/ja/guides/network/running-a-symbol-node-manually.html#