はじめに
何か違うのか両方のチェーンの転送トランザクションを投げるコードを書いて比べてみました。
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);
さいごに
ほぼ、同じ書き方でトランザクション投げれる。すごい!!