実践低レベル Symbol -ピア情報-

## ピア情報格納クラス
以下は、ピア情報を格納するクラスです。
`/node/peers` と同じ物が作れるようになります。
```NodePeer.ts
/**
 * NodePeer
 */
export class NodePeer {
  /**
   * コンストラクタ
   * @param version バージョン
   * @param publicKey 公開鍵
   * @param networkGenerationHashSeed ネットワークジェネレーションハッシュシード
   * @param roles ロール
   * @param port ポート
   * @param networkIdentifier ネットワーク識別子
   * @param host ホスト
   * @param friendlyName フレンドリーネーム
   */
  constructor(
    /**
     * バージョン
     */
    public readonly version: number,

    /**
     * 公開鍵
     */
    public readonly publicKey: string,

    /**
     * ネットワークジェネレーションハッシュ
     */
    public readonly networkGenerationHashSeed: string,

    /**
     * ロール
     */
    public readonly roles: number,

    /**
     * ポート
     */
    public readonly port: number,

    /**
     * ネットワークID
     */
    public readonly networkIdentifier: number,

    /**
     * ホスト
     */
    public readonly host: string,

    /**
     * フレンドリー名
     */
    public readonly friendlyName: string
  ) {}
}
```

## ノード情報取得クラス
`Catpult.ts` を編集します。
```Catpult.ts
import { NodeInfo } from './NodeInfo.js';
import { PacketBuffer } from './PacketBuffer.js';
import { SslSocket } from './SslSocket.js';

export class Catpult extends SslSocket {
  /** パケットタイプ */
  private PacketType = {
    CHAIN_STATISTICS: 5,
    NODE_DISCOVERY_PULL_PING: 0x111,
    NODE_DISCOVERY_PULL_PEERS: 0x113,
    FINALIZATION_STATISTICS: 0x132,
  };

  /**
   * NodePeers取得
   * @returns 成功: NodePeer[], 失敗: undefined
   */
  async getNodePeers(): Promise<NodePeer[] | undefined> {
    let nodePeers: NodePeer[] | undefined = [];
    try {
      // ピア問合せ
      const NODE_DISCOVERY_PULL_PEERS = 0x113;
      const socketData = await this.requestSocket(NODE_DISCOVERY_PULL_PEERS);
      if (!socketData) return undefined;
      const nodeBufferView = new PacketBuffer(Buffer.from(socketData));
      // 編集
      while (nodeBufferView.index < nodeBufferView.length) {
        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);

        const nodePeer = new NodePeer(
          version,
          publicKey,
          networkGenerationHashSeed,
          roles,
          port,
          networkIdentifier,
          host,
          friendlyName
        );
        nodePeers.push(nodePeer);
      }
    } catch (e) {
      nodePeers = undefined;
    }
    return nodePeers;
  }

  // 略...
  
}
```

## メイン
`Catpult` のインスタンスを生成して呼び出すだけです。
```main.ts
import { Catpult } from './Catpult.js';

const catpult = new Catpult('sakia.harvestasya.com');
const result = await catpult.getNodePeers();
console.log(result);
```


## 実行
```
> yarn tsx ./src/main.ts
[
  NodePeer {
    version: 16777991,
    publicKey: '154A2B669BDE15924FF6FEE9ECB9CDD79EEADA4131665748A12132EAA465E575',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 7,
    port: 7900,
    networkIdentifier: 152,
    host: 'sym-test-03.opening-line.jp',
    friendlyName: 'sym-test-03.opening-line.jp.'
  },
  NodePeer {
    version: 0,
    publicKey: '8A66BDB319C210B5C37C908C5379DB7F98BAC8FCF4FAF2947EE19ACE30E69343',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 7,
    port: 7900,
    networkIdentifier: 152,
    host: '2.dusanjp.com',
    friendlyName: 'dusanjp2@sai_fakevoting5'
  },
  NodePeer {
    version: 0,
    publicKey: 'CEAAE51C6D294AEFC1A3C84FEFE9D60B0CE815DBC66567211A41B7DE2DC164B8',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 7,
    port: 7900,
    networkIdentifier: 152,
    host: 'testnet1.symbol-mikun.net',
    friendlyName: 'mikun_test1'
  },
  NodePeer {
    version: 16777991,
    publicKey: '07D38BAE29464C1F54CC1C8202DD83B65AF21C8E5FF1FB01ACEEE2243C55EF2A',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 7,
    port: 7900,
    networkIdentifier: 152,
    host: '001-sai-dual.symboltest.net',
    friendlyName: '001-sai-dual'
  },
  NodePeer {
    version: 16777991,
    publicKey: '22F62745DF1B3E343D7C52F93EB6DA9FD5D0347886FF8EA68F0EAD848C1780B0',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 3,
    port: 7900,
    networkIdentifier: 152,
    host: 'zf-xym1.test6.zaif.jp',
    friendlyName: '22F6274'
  },
  NodePeer {
    version: 0,
    publicKey: '7BCDB84A784EFC5ACF4E008E7DB4752AD08C22EB73B4C39C86B911D47714B92F',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 7,
    port: 7900,
    networkIdentifier: 152,
    host: 'test01.xymnodes.com',
    friendlyName: 'test node01 from xymnodes'
  },
  NodePeer {
    version: 16777991,
    publicKey: '9B8B41A85D7777A124BFFED43D2EA009F717C5AF3988E72C0BC80DD46100D3B4',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 3,
    port: 7900,
    networkIdentifier: 152,
    host: '3.114.177.33',
    friendlyName: '9B8B41A'
  },
  NodePeer {
    version: 0,
    publicKey: '03A9EF5A45C8D4D059D3E9C95DC5956091E3E855DC812F363DB41D987BF927A6',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 7,
    port: 7900,
    networkIdentifier: 152,
    host: 'test02.xymnodes.com',
    friendlyName: 'test node02 from xymnodes'
  },
  NodePeer {
    version: 16777991,
    publicKey: 'CC1287250B978C0638FD0461EB86952BAEAB4F04266A09FDB3D96D5412BD5B57',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 3,
    port: 7900,
    networkIdentifier: 152,
    host: 'symbol-azure.0009.co',
    friendlyName: 'symbol-azure.0009.co'
  },
  NodePeer {
    version: 16777991,
    publicKey: '24A1C604DAAB6D23CE19BD00FF272A87382B019ADDEA99193F15E469B5646080',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 3,
    port: 7900,
    networkIdentifier: 152,
    host: 'vmi831828.contaboserver.net',
    friendlyName: 'MAMESHIBA'
  },
  NodePeer {
    version: 0,
    publicKey: '4540B7010550CAA12F78DD3466A2645212F705F39E25A2333E9CB12DFF1A91A0',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 5,
    port: 7900,
    networkIdentifier: 152,
    host: '4t.dusanjp.com',
    friendlyName: 'peervoting@4'
  },
  NodePeer {
    version: 16777991,
    publicKey: '97568D8EF5EC0325989CFBBB8E683DA5F0BA79AA3EC6F0769AABFF9B8854C17D',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 3,
    port: 7900,
    networkIdentifier: 152,
    host: '20.48.92.124',
    friendlyName: '97568D8'
  },
  NodePeer {
    version: 16777991,
    publicKey: 'D1F9B06B72D66098DEFD8DCD85DFB1849EC11A059B60441F83F7708134839331',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 6,
    port: 7900,
    networkIdentifier: 152,
    host: '5t.dusanjp.com',
    friendlyName: 'apivoting@5'
  },
  NodePeer {
    version: 0,
    publicKey: 'BF61564C09B8A44A940EB09B9D062020A39BA848BAB838D597F27B11F1FBE717',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 7,
    port: 7900,
    networkIdentifier: 152,
    host: 'sym-test-01.opening-line.jp',
    friendlyName: 'sym-test-01.opening-line.jp.'
  },
  NodePeer {
    version: 0,
    publicKey: 'C1773740461C97FF172E966213716E089A93DC43F10BE55DC27CC49CBA67E692',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 7,
    port: 7900,
    networkIdentifier: 152,
    host: '401-sai-dual.symboltest.net',
    friendlyName: '401-sai-dual'
  },
  NodePeer {
    version: 0,
    publicKey: '645E2E56B5F8680B69BC0255F4930169DBC052BB25F8121055DE2072D37E2C30',
    networkGenerationHashSeed: '49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4',
    roles: 3,
    port: 7900,
    networkIdentifier: 152,
    host: '201-sai-dual.symboltest.net',
    friendlyName: '201-sai-dual'
  }
]
```