catapult-sdkを使って直接APIノードにトランザクションを送信する

# はじめに

カタパルトでは、APIノードとmongoDBとRESTゲートウェイを建てるのが一般的です。

そして、これに加えて、アプリケーションサーバーなどを建てることになると思います。

しかし、そんなリソースがない場合もあると思います。APIノードとアプリケーションだけでなんとかしたいと思うこともあるかと思います。

例えば、IoTのような。ミニPC内にノードを建てて定期的にトランザクションを送信するなど。

そんな場合に備えて、Node.jsを使ってAPIノードにトランザクションを送る方法を試してみようと思います。

# やる

## APIノード起動

https://github.com/tech-bureau/catapult-service-bootstrap/tree/v0.3.0 などでノードを起動します。その際、APIノードの7900番にアクセスできるようにしておきます。

## catapult-restの仕様

まず、catapult-restの仕様を調べます。

APIノードへの接続は、Node.jsのNetを使っているようです。catapult-sdkを使って、ノード認証して、そのあとでデータを送信しているようです。

以下のソースを読みました。

- index.js
- connectionService.js
- catapultConnection.js
- routeUtils.js
- transactionRoutes.js

あとはこれを真似して作ればいいだけです。

## フォルダ構成

まずは、プロジェクトを作ります。`npm init -y`とかで。

次に、下記リポジトリから`catapult-sdk`をフォルダをごとコピーしてきます。

https://github.com/nemtech/catapult-rest/tree/v0.7.14

そのあと、`package.json`にリンク情報を書きます。

```json:package.json
  "dependencies": {
    "catapult-sdk": "link:catapult-sdk"
  }
```

`yarn install`します（`npm i`ではだめです）。するとシンボリックリンクが作られます。

最後に、メインの処理を書くための`index.js`とかを作ります。

するとフォルダ構成は以下のようになってると思います。

```
/
│  index.js
│  package.json
│  yarn.lock
├─catapult-sdk
│  │  .babelrc
│  │  .eslintignore
│  │  .eslintrc
│  │  package-lock.json
│  │  package.json
│  │  yarn.lock
│  ├─node_modules
│  ├─src
│  ├─test
│  ├─_build
│  └─_build_browser
└─node_modules
```

## コード

```javascript:index.js
const catapult = require('catapult-sdk');
const net = require('net');

const config = {
    clientPrivateKey: '0000000000000000000000000000000000000000000000000000000000000000'
};
const node = {
    host: '13.115.141.239',
    port: '7910',
    publicKey: '6244B76B2385C47872E0EA2B6C746408D8021349A13DCAE007D3F6A58E472D01'
};

const serverSocket = net.createConnection(node.port, node.host);
const apiNodePublicKey = catapult.utils.convert.hexToUint8(node.publicKey);

const clientKeyPair = catapult.crypto.createKeyPairFromPrivateKeyString(config.clientPrivateKey);

const params = {
    payload: 'A5000000894A829717A6627C108BD69D56A188659C403A4E2C0CEDBB35F856AFD57713E9ACA553C68803E56E39683A9D18CBAF258BFCE043724C56175C53E425727C050FAC1A6E1D8DE5B17D2C6B1293F1CAD3829EEACF38D09311BB3C8E5A880092DE26039054410000000000000000F475D6151700000090FA39EC47E05600AFA74308A7EA607D145E371B5F4F1447BC0100010044B262C46CEABB850000000000000000',
    hash: '',
    signer: '',
    type: 16724,
    networkType: 144
};

serverSocket
    .on('error', err => {
        console.error(`error raised by ${node.host}:${node.port} connection`, err);
    })
    .on('close', () => {
        console.log('close');
    });

catapult.auth.createAuthPromise(serverSocket, clientKeyPair, apiNodePublicKey, console.log).then(() => {

    const connection = serverSocket;
    const packetHeader = catapult.packet.header;

    const createPacketFromBuffer = (data, packetType) => {
        const length = packetHeader.size + data.length;
        const header = packetHeader.createBuffer(packetType, length);
        const buffers = [header, Buffer.from(data)];
        return Buffer.concat(buffers, length);
    };

    const parser = params => {
        return catapult.utils.convert.hexToUint8(params.payload)
    };

    const packetType = catapult.packet.PacketType.pushTransactions;

    const packetBuffer = createPacketFromBuffer(parser(params), packetType);
    const payload = packetBuffer;

    return new Promise((resolve, reject) => {
        connection.write(payload, resolve);
    })

}).then(() => {
    const packetType = catapult.packet.PacketType.pushTransactions;
    console.log(
        { message: `packet ${packetType} was pushed to the network via transaction` }
    );
    serverSocket.destroy();
});
```

以下の箇所で、APIノードの設定をします。

```javascript
const config = {
    clientPrivateKey: '任意の秘密鍵'
};
const node = {
    host: 'ノードのIPかFQDN',
    port: 'ノードのポート番号',
    publicKey: 'ノードの公開鍵'
};
```

以下の箇所で、トランザクションを設定します。`payload`だけでよいです。

```javascript

const params = {
    payload: 'A5000000894A829717A6627C108BD69D56A188659C403A4E2C0CEDBB35F856AFD57713E9ACA553C68803E56E39683A9D18CBAF258BFCE043724C56175C53E425727C050FAC1A6E1D8DE5B17D2C6B1293F1CAD3829EEACF38D09311BB3C8E5A880092DE26039054410000000000000000F475D6151700000090FA39EC47E05600AFA74308A7EA607D145E371B5F4F1447BC0100010044B262C46CEABB850000000000000000',
    hash: '',
    signer: '',
    type: 16724,
    networkType: 144
};
```

## 実行

```
>node index.js
received data with size 8
received data with size 64
writing response of length: 169
received data with size 8
received data with size 64
client challenge verified? true
{ message: 'packet 9 was pushed to the network via transaction' }
close
```

`createAuthPromise`の段階で、なにかをやりとりしているようです。

その次に、トランザクションを送っています。この送ったトランザクションが妥当だったかどうかはわかりません。

RESTゲートウェイに問い合わせるか、mongoDBから情報を取得する必要があります。unconfirmedにすらなっていない場合は、失敗していると思います。

## 送信データ

APIノードへ送信したデータはこんな感じでした。

AD00000009000000A5000000894A829717A6627C108BD69D56A188659C403A4E2C0CEDBB35F856AFD57713E9ACA553C68803E56E39683A9D18CBAF258BFCE043724C56175C53E425727C050FAC1A6E1D8DE5B17D2C6B1293F1CAD3829EEACF38D09311BB3C8E5A880092DE26039054410000000000000000F475D6151700000090FA39EC47E05600AFA74308A7EA607D145E371B5F4F1447BC0100010044B262C46CEABB850000000000000000

最初の４バイトでデータのサイズを指定します。0xADなので173バイトのデータです。

次の４バイトでパケットタイプを指定します。0x09なので、9です。

パケットタイプが何なのかは、ここに少し載っています。

https://github.com/nemtech/catapult-rest/blob/v0.7.14/catapult-sdk/src/packet/PacketType.js

そのあとに、トランザクションが続いています。

# おわりに

おわり