LoginSignup
10
3

Blockchain Symbol と NEM の転送トランザクションを発行するコード見比べてみる

Last updated at Posted at 2024-01-27

はじめに

何か違うのか両方のチェーンの転送トランザクションを投げるコードを書いて比べてみました。

Faced生成

SymbolとNEMかの指定の違いだけで、殆ど同じですね。

Symbol

// faced生成
const facade = new symbolSdk.facade.SymbolFacade("testnet");

NEM

// facade生成
const facade = new symbolSdk.facade.NemFacade("testnet");

アカウント生成

こちらも、SymbolとNEMかの指定の違いだけで、またまたほぼ同じです。

SymbolとNEMの秘密鍵長は同じなので、流用できます。やっていいのかどうかは分かりませんが…
なお、仕組みが違うので同じ秘密鍵から生成してもアドレス、公開鍵は異なります。

Symbol

// 秘密鍵からアリスアカウント生成
const aliceKeyPair = new symbolSdk.symbol.KeyPair(
  new symbolSdk.PrivateKey(ALICE_PRIVATE_KEY)
);
// 公開鍵からボブアドレス生成
const bobPublicKey = new symbolSdk.symbol.PublicKey(
  Uint8Array.from(Buffer.from(BOB_PUBLIC_KEY, "hex"))
);
const bobAddress: Address = facade.network.publicKeyToAddress(bobPublicKey);

NEM

// 秘密鍵からアリスアカウント生成
const aliceKeyPair = new symbolSdk.nem.KeyPair(
  new symbolSdk.PrivateKey(ALICE_PRIVATE_KEY)
);
// 公開鍵からボブアドレス生成
const bobPublicKey = new symbolSdk.nem.PublicKey(
  Uint8Array.from(Buffer.from(BOB_PUBLIC_KEY, "hex"))
);
const bobAddress: Address = facade.network.publicKeyToAddress(bobPublicKey);

ネットワーク時間

ネットワーク時間は実時間よりズレているので、上限ギリギリの時間をセットする場合は、ノードからネットワーク時間を取得します。
ただ、NEMはトランザクション作るのに現在ネットワーク時刻が必須です。
Symbolはdeadlineの設定で必要としていますが、未来の時間を入力するので実時間で問題ないです(ギリギリだとアウトですけど…)。

今回は書き方を合わせるためにSymbolもネットワーク時間を取得しています。

Symbol

// ネットワーク時間取得
const networkTimeRes = await fetch(new URL("/node/time", NODE), {
  method: "GET",
  headers: { "Content-Type": "application/json" },
});
const nodeTimeJson = await networkTimeRes.json();
const nodeTime = BigInt(nodeTimeJson.communicationTimestamps.receiveTimestamp);
const symbolNetworkTime = new symbolSdk.symbol.NetworkTimestamp(nodeTime);

NEM

// ネットワーク時間取得
const networkTimeRes = await fetch(new URL("/time-sync/network-time", NODE), {
  method: "GET",
  headers: { "Content-Type": "application/json" },
});
const nodeTimeJson = await networkTimeRes.json();
const nodeTime = BigInt(nodeTimeJson.receiveTimeStamp);
const nemNetworkTime = new symbolSdk.nem.NetworkTimestamp(nodeTime);

メッセージ作成

NEMがクラスやら定数があってすっきり書けて、Symbolはごちゃってますが、やってることは同じです。1024Byteの先頭1byteに制御用のコードが付与され、その後にメッセージ本文が続きます。

Symbol

// 平文メッセージ作成
const messageData = new Uint8Array([
  0x00,
  ...new TextEncoder().encode("はじめてのSymbolトランザクション"),
]);

NEM

// 平文メッセージ作成
const nemMessage = new symbolSdk.nem.Message();
nemMessage.messageType = symbolSdk.nem.MessageType.PLAIN;
nemMessage.message = new TextEncoder().encode("はじめてのNemトランザクション");

トランザクション生成

だいたいパラメータは同じですが、NEMのみtimestampがあります。あと、NEMのtimestamp、deadlineはNumber型で、SymbolはBigInt型です。NEMはミリ秒ないと思っておけば大丈夫です。

手数料の計算は異なります。Symbolはトランザクションの容量から算出できます。NEMは計算式が分からなかったので、足りるだろうと思われる値を固定で入れてます。

NEMの転送トランザクションv1はXEMのみ扱えるので、モザイクの指定はありません。

Symbol

// トランザクション生成
const transferTx = facade.transactionFactory.create({
  type: "transfer_transaction_v1",
  signerPublicKey: aliceKeyPair.publicKey,
  deadline: symbolNetworkTime.addHours(2).timestamp,
  recipientAddress: bobAddress.toString(),
  mosaics: [{ mosaicId: 0x72c0212e67a08bcen, amount: 100000n }],
  message: messageData,
}) as TransferTransactionV1;
transferTx.fee = new symbolSdk.symbol.Amount(BigInt(transferTx.size * 100));

NEM

// トランザクション生成
const transferTx = facade.transactionFactory.create({
  type: "transfer_transaction_v1",
  signerPublicKey: aliceKeyPair.publicKey,
  timestamp: Number(nemNetworkTime.timestamp / 1000n),
  deadline: Number(nemNetworkTime.addHours(2).timestamp / 1000n),
  recipientAddress: bobAddress,
  amount: 100000n,
  message: nemMessage,
  fee: 500000n,
}) as TransferTransactionV1;

署名

SymbolとNEMかの指定の違いだけで、同じですね。

Symbol

// 署名
const transferTxSignature = facade.signTransaction(aliceKeyPair, transferTx);
const transferTxPayloadJson =
  symbolSdk.symbol.SymbolTransactionFactory.attachSignature(
    transferTx,
    transferTxSignature
  );
const transferTxHash = facade.hashTransaction(transferTx);
console.log(`transferTxHash: ${transferTxHash}`);

NEM

// 署名
const transferTxSignature = facade.signTransaction(aliceKeyPair, transferTx);
const transferTxPayloadJson =
  symbolSdk.nem.NemTransactionFactory.attachSignature(
    transferTx,
    transferTxSignature
  );
const transferTxHash = facade.hashTransaction(transferTx);
console.log(`transferTxHash: ${transferTxHash}`);

アナウンス

RESTにペイロードJsonを投げるだけですね。
ただ、メソッドがSymbolがPUT、NEMがPOSTとなっています。

Symbol

// RESTにアナウンス
const announceResponse = await fetch(new URL("/transactions", NODE), {
  method: "PUT",
  headers: { "Content-Type": "application/json" },
  body: transferTxPayloadJson,
});
const announceResponseJson = await announceResponse.json();
console.log(announceResponseJson);

NEM

// RESTにアナウンス
const announceResponse = await fetch(new URL("/transaction/announce", NODE), {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: transferTxPayloadJson,
});
const announceResponseJson = await announceResponse.json();
console.log(announceResponseJson);

全体のコード

Symbol

import symbolSdk from "symbol-sdk";
import {
  Address,
  TransferTransactionV1,
} from "symbol-sdk/ts/src/symbol/models.js";

/** APIノードURL */
const NODE = "http://sym-test-01.opening-line.jp:3000";

/** アリス秘密鍵 */
const ALICE_PRIVATE_KEY =
  "****************************************************************";

/** ボブ公開鍵 */
const BOB_PUBLIC_KEY =
  "94EC711522B4B32A1B6A6ED61D86D1E3EE11AFB9B912A17F8983EED3808819FD";

// faced生成
const facade = new symbolSdk.facade.SymbolFacade("testnet");

// 秘密鍵からアリスアカウント生成
const aliceKeyPair = new symbolSdk.symbol.KeyPair(
  new symbolSdk.PrivateKey(ALICE_PRIVATE_KEY)
);

// 公開鍵からボブアドレス生成
const bobPublicKey = new symbolSdk.symbol.PublicKey(
  Uint8Array.from(Buffer.from(BOB_PUBLIC_KEY, "hex"))
);
const bobAddress: Address = facade.network.publicKeyToAddress(bobPublicKey);

// ネットワーク時間取得
const networkTimeRes = await fetch(new URL("/node/time", NODE), {
  method: "GET",
  headers: { "Content-Type": "application/json" },
});
const nodeTimeJson = await networkTimeRes.json();
const nodeTime = BigInt(nodeTimeJson.communicationTimestamps.receiveTimestamp);
const symbolNetworkTime = new symbolSdk.symbol.NetworkTimestamp(nodeTime);

// 平文メッセージ作成
const messageData = new Uint8Array([
  0x00,
  ...new TextEncoder().encode("はじめてのSymbolトランザクション"),
]);

// トランザクション生成
const transferTx = facade.transactionFactory.create({
  type: "transfer_transaction_v1",
  signerPublicKey: aliceKeyPair.publicKey,
  deadline: symbolNetworkTime.addHours(2).timestamp,
  recipientAddress: bobAddress.toString(),
  mosaics: [{ mosaicId: 0x72c0212e67a08bcen, amount: 100000n }],
  message: messageData,
}) as TransferTransactionV1;
transferTx.fee = new symbolSdk.symbol.Amount(BigInt(transferTx.size * 100));

// 署名
const transferTxSignature = facade.signTransaction(aliceKeyPair, transferTx);
const transferTxPayloadJson =
  symbolSdk.symbol.SymbolTransactionFactory.attachSignature(
    transferTx,
    transferTxSignature
  );
const transferTxHash = facade.hashTransaction(transferTx);
console.log(`transferTxHash: ${transferTxHash}`);

// RESTにアナウンス
const announceResponse = await fetch(new URL("/transactions", NODE), {
  method: "PUT",
  headers: { "Content-Type": "application/json" },
  body: transferTxPayloadJson,
});
const announceResponseJson = await announceResponse.json();
console.log(announceResponseJson);

NEM

import symbolSdk from "symbol-sdk";
import {
  Address,
  TransferTransactionV1,
} from "symbol-sdk/ts/src/nem/models.js";

/** APIノードURL */
const NODE = "http://hugetestalice.nem.ninja:7890";

/** アリス秘密鍵 */
const ALICE_PRIVATE_KEY =
  "****************************************************************";

/** ボブ公開鍵 */
const BOB_PUBLIC_KEY =
  "6048C62CDCEB5D80E8B4C637B9F149F6FA71FB4A227E1C16AF273549B09DCB1E";

// facade生成
const facade = new symbolSdk.facade.NemFacade("testnet");

// 秘密鍵からアリスアカウント生成
const aliceKeyPair = new symbolSdk.nem.KeyPair(
  new symbolSdk.PrivateKey(ALICE_PRIVATE_KEY)
);

// 公開鍵からボブアドレス生成
const bobPublicKey = new symbolSdk.nem.PublicKey(
  Uint8Array.from(Buffer.from(BOB_PUBLIC_KEY, "hex"))
);
const bobAddress: Address = facade.network.publicKeyToAddress(bobPublicKey);

// ネットワーク時間取得
const networkTimeRes = await fetch(new URL("/time-sync/network-time", NODE), {
  method: "GET",
  headers: { "Content-Type": "application/json" },
});
const nodeTimeJson = await networkTimeRes.json();
const nodeTime = BigInt(nodeTimeJson.receiveTimeStamp);
const nemNetworkTime = new symbolSdk.nem.NetworkTimestamp(nodeTime);

// 平文メッセージ作成
const nemMessage = new symbolSdk.nem.Message();
nemMessage.messageType = symbolSdk.nem.MessageType.PLAIN;
nemMessage.message = new TextEncoder().encode("はじめてのNemトランザクション");

// トランザクション生成
const transferTx = facade.transactionFactory.create({
  type: "transfer_transaction_v1",
  signerPublicKey: aliceKeyPair.publicKey,
  timestamp: Number(nemNetworkTime.timestamp / 1000n),
  deadline: Number(nemNetworkTime.addHours(2).timestamp / 1000n),
  recipientAddress: bobAddress,
  amount: 100000n,
  message: nemMessage,
  fee: 500000n,
}) as TransferTransactionV1;

// 署名
const transferTxSignature = facade.signTransaction(aliceKeyPair, transferTx);
const transferTxPayloadJson =
  symbolSdk.nem.NemTransactionFactory.attachSignature(
    transferTx,
    transferTxSignature
  );
const transferTxHash = facade.hashTransaction(transferTx);
console.log(`transferTxHash: ${transferTxHash}`);

// RESTにアナウンス
const announceResponse = await fetch(new URL("/transaction/announce", NODE), {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: transferTxPayloadJson,
});
const announceResponseJson = await announceResponse.json();
console.log(announceResponseJson);

さいごに

ほぼ、同じ書き方でトランザクション投げれる。すごい!!

10
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
3