SSL パケット通信を使って API を介さずにネットワークにアナウンスすることも出来ます。
「Blockchain Symbol の転送トランザクションを発行する」をピアアナウンスに変更してみます。
証明書の作成
証明書は下記を参照して作成。
OpenSSL のバージョンは 3.1.4 で確認しています。
ピアノードにアナウンスするクラス
SymbolAnnouncer.ts
import fs from "fs";
import tls, { ConnectionOptions } from "tls";
enum PacketType {
/** トランザクションアナウンス */
PUSH_TRANSACTIONS = 0x009,
}
export class SymbolCatapult {
private connectionOptions: ConnectionOptions;
/**
* コンストラクタ
* @param nodeHost ノードホスト
* @param nodePort ノードポート(デフォルト: 7900)
*/
constructor(private nodeHost: string, private nodePort: number = 7900) {
this.connectionOptions = {
host: this.nodeHost,
port: this.nodePort,
timeout: 3000,
cert: fs.readFileSync("cert/node.full.crt.pem"),
key: fs.readFileSync("cert/node.key.pem"),
rejectUnauthorized: false,
};
}
/**
* トランザクションアナウンス
* @param payload トランザクションペイロード
*/
public async announceTransaction(payload: Uint8Array): Promise<void> {
await this.catapult(PacketType.PUSH_TRANSACTIONS, payload, false);
}
/**
* カタパルトサーバへパケットを送信する
* @param packetType パケットタイプ
* @param payload ペイロード
* @param isResponse レスポンス有無(デフォルト: true)
* @returns レスポンスデータ
*/
private async catapult(
packetType: PacketType,
payload?: Uint8Array,
isResponse: boolean = true
): Promise<Uint8Array | undefined> {
return new Promise<Uint8Array | undefined>((resolve, reject) => {
const payloadSize = payload ? payload.length : 0;
let responseSize = 8; // ヘッダ分のサイズを前もって付与
let responseData: Uint8Array | undefined = undefined;
let socket = tls.connect(this.connectionOptions, () => {
// Symbolパケット生成
const headerSize = 8;
const packetSize = headerSize + payloadSize;
const symbolPacketBuffer = new ArrayBuffer(packetSize);
// Symbolヘッダー編集
const symbolHeader = new DataView(symbolPacketBuffer);
symbolHeader.setUint32(0, packetSize, true);
symbolHeader.setUint32(4, packetType, true);
if (payload) {
// Symbolペイロード編集
const symbolPayload = new Uint8Array(
symbolPacketBuffer,
headerSize,
payloadSize
);
symbolPayload.set(payload);
}
// Symbolパケット送信
socket.write(new Uint8Array(symbolPacketBuffer));
console.debug(`packet type ${packetType} written`);
if (!isResponse) {
// ソケット切断
socket.destroy();
}
});
// SSL接続時
socket.on("secureConnect", () => {
const peerX509 = socket.getPeerX509Certificate();
if (!peerX509) return;
console.log(`${peerX509}`);
});
// データ受信
socket.once("data", (data) => {
// レスポンスデータ(ヘッダ)取得
const nodeBufferView = Buffer.from(new Uint8Array(data).buffer);
// レスポンスサイズチェック
const responseDataSize = nodeBufferView.readUInt32LE(0);
if (responseDataSize === 0) {
socket.destroy();
reject("empty data");
}
// レスポンスパケットタイプチェック
const responsePacketType = nodeBufferView.readUInt32LE(4);
if (responsePacketType !== packetType) {
socket.destroy();
reject(
`mismatch packet type: expect: ${packetType} actual: ${responsePacketType}`
);
}
// ヘッダが問題なければデータ部取得
socket.on("data", (data) => {
const tempResponseData = new Uint8Array(data);
console.debug(tempResponseData);
responseSize += tempResponseData.length;
if (!responseData) {
// 初回
responseData = tempResponseData;
} else {
// 連結
const merged = new Uint8Array(
responseData.length + tempResponseData.length
);
merged.set(responseData);
merged.set(tempResponseData, responseData.length);
responseData = merged;
}
if (responseDataSize <= responseSize) {
// 受信が終わったら終了
socket.end();
}
});
});
socket.on("timeout", function () {
socket.destroy();
reject("timeout");
});
socket.on("error", function (error) {
socket.destroy();
reject(error);
});
socket.on("close", function () {
resolve(responseData);
});
});
}
}
アナウンス部分の変更
REST にアナウンス部分を下記に書き換えます。
送信するノードは、ノード間通信ポートが開いているノードであればどれでも良いです。
ソケット通信なのでhttp等の情報は不要です(httpと違ってトランスポート層なので...)。
/** ノードホスト */
const NODE = "symbol02.harvestasya.com";
// Peerノードにアナウンス
const symbolCatapult = new SymbolCatapult(NODE);
await symbolCatapult.announceTransaction(transferTx.serialize());