Windows と TypeScript ではじめる Symbol 通信 04【ノード情報】


Rest の`/node/info`と同等の値を取得してみましょう。

## ノード情報の取得

### ノードへリクエスト

ブロック高を取得するパケットタイプは、`0x111`です。ペイロードはありません。`Catapult.ts`ファイルに追記します。

ノード公開鍵まで返ってこないので、ソケットを繋いだときにノード証明書が取得出来るので、これから取り出すようにします。

```typescript:Catapult.ts
/** パケットタイプ */
private PacketType = {
  CHAIN_STATISTICS: 0x0_05,
  NODE_DISCOVERY_PULL_PING: 0x1_11,
  TIME_SYNC_NETWORK_TIME: 0x1_20,
  FINALIZATION_STATISTICS: 0x1_32,
}

/**
 * /node/info 同等の値を持つクラスを取得
 * @returns NodeInfo
 */
async getNodeInfo(): Promise<NodeInfo | undefined> {
  console.info('NodeInfo')
  let nodeInfo: NodeInfo | undefined
  try {
    // ピア問合せ
    const socketData = await this.request(
      this.PacketType.NODE_DISCOVERY_PULL_PING
    )
    if (socketData)
      nodeInfo = NodeInfo.deserialize(socketData, this.x509Certificate)
  } catch (e) {
    if (e instanceof Error) console.error(e.message)
    else console.error(e)
  } finally {
    this.close()
  }
  return nodeInfo
}
```

### レスポンスデータの解析

シリアライズされたデータが返ってくるので、デシリアライズします。

| 項目                      | 長さ                      |
| :------------------------ | :------------------------ |
| version                   | 4 バイト                  |
| publicKey                 | 32 バイト                 |
| networkGenerationHashSeed | 32 バイト                 |
| roles                     | 4 バイト                  |
| port                      | 2 バイト                  |
| networkIdentifier         | 1 バイト                  |
| hostLength                | 1 バイト                  |
| friendlyNameLength        | 1 バイト                  |
| host                      | hostLength バイト         |
| friendlyName              | friendlyNameLength バイト |

せっかくノード証明書を持ってきたので、ついでに有効期限も取得してみます。

```typescript:src/models/ChainStatistics.ts
import { X509Certificate } from 'node:crypto'
import { PacketBuffer } from '../PacketBuffer.js'

export class NodeInfo {
  constructor(
    public version: number,
    public publicKey: string,
    public networkGenerationHashSeed: string,
    public roles: number,
    public port: number,
    public networkIdentifier: number,
    public host: string,
    public friendlyName: string,
    public nodePublicKey?: string,
    public certificateExpirationDate?: Date
  ) {}

  static deserialize(payload: Uint8Array, cert?: X509Certificate) {
    const nodeBufferView = new PacketBuffer(Buffer.from(payload))
    const version = nodeBufferView.readUInt32LE(4)
    const publicKey = nodeBufferView.readHexString(32).toUpperCase()
    const networkGenerationHashSeed = nodeBufferView
      .readHexString(32)
      .toUpperCase()
    const roles = nodeBufferView.readUInt32LE()
    const port = nodeBufferView.readUInt16LE()
    const networkIdentifier = nodeBufferView.readUInt8()
    const hostLength = nodeBufferView.readUInt8()
    const friendlyNameLength = nodeBufferView.readUInt8()
    const host = nodeBufferView.readString(hostLength)
    const friendlyName = nodeBufferView.readString(friendlyNameLength)
    // 証明書有効期限、ノード公開鍵取得
    let nodePublicKey: string | undefined
    let certificateExpirationDate: Date | undefined
    if (cert) {
      nodePublicKey = cert.publicKey
        .export({
          format: 'der',
          type: 'spki',
        })
        .toString('hex', 12, 44)
        .toUpperCase()
      const { validTo } = cert
      const validToDate = new Date(validTo)
      certificateExpirationDate = validToDate
    }
    return new NodeInfo(
      version,
      publicKey,
      networkGenerationHashSeed,
      roles,
      port,
      networkIdentifier,
      host,
      friendlyName,
      nodePublicKey,
      certificateExpirationDate
    )
  }
}
```

## メインの作成

実行するコードを作成します。

```typescript:src/mainNodeInfo.ts
import { Catapult } from './Catapult.js'

const catapult = new Catapult(
  'cert/ca.crt.pem',
  'cert/node.crt.pem',
  'cert/node.key.pem',
  'localhost'
)

const nodeInfo = await catapult.getNodeInfo()
if (nodeInfo) console.log(nodeInfo)
```

### 実行

```posh
yarn tsx .\src\mainNodeInfo.ts
```

実行すると以下の様にノード情報が出力されます。

```plaintext
NodeInfo
socket connected.
NodeInfo {
  version: 16777991,
  publicKey: 'B502FCBDA9147ABDB12CB2A9BBADCBC57716AEADCCCA9B11926C1A0BDDDB7D8E',
  networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
  roles: 3,
  port: 7900,
  networkIdentifier: 152,
  host: 'sakia.harvestasya.com',
  friendlyName: 'sakia/.',
  nodePublicKey: '19792496483B0B71D5F997DBE8660131CFEC49E55773C5B065C106258B70F475',
  certificateExpirationDate: 2025-11-27T12:18:30.000Z
}
socket close: 273
```

## バックナンバー

https://qiita.com/ccHarvestasya/items/7b9c66f145b636fe70ee

https://qiita.com/ccHarvestasya/items/46af59638c5aadc1f0be

https://qiita.com/ccHarvestasya/items/c7eca49984825eb3db29