9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

トランザクション - Blockchain Symbol

Last updated at Posted at 2024-02-06

前準備

変更しない値を一括で記述しておきます。アカウントはコードで作成したものでもウォレットで作成した物、どちらでもかまいません。

SymConst.ts
import sdk from 'symbol-sdk';

export class SymConst {
  /** RestGatewayURL */
  static readonly REST_GATEWAY_URL = 'https://sym-test-01.opening-line.jp:3001';
  /** ネットワーク識別 */
  static readonly NETWORK_IDENTIFIER = sdk.symbol.Network.TESTNET.name;
  /** カレントモザイクID */
  static readonly CURRENCY_MOSAIC_ID = BigInt('0x72C0212E67A08BCE');
  /** アリス秘密鍵 */
  static readonly ALICE_PRIVATE_KEY =
    'F10B0***********************************************************';
  /** ボブ秘密鍵 */
  static readonly BOB_PRIVATE_KEY =
    '6EBF9***********************************************************';
  /** ボブ公開鍵 */
  static readonly BOB_PUBLIC_KEY =
    '94EC711522B4B32A1B6A6ED61D86D1E3EE11AFB9B912A17F8983EED3808819FD';
  /** キャロル公開鍵 */
  static readonly CAROL_PUBLIC_KEY =
    '249B8ADE64EFF216D43BB655EF41DC1D7B8DDF96BD655749FFAF78BE3ACE7D77';
}

トランザクション

簡単なトランスファートランザクションの発行です。

アカウント生成

アリスからボブへの転送を考えているので、アリスは秘密鍵からアカウント生成します。ボブは公開鍵からのアドレスを生成します。

  // faced生成
  const facade = new sdk.facade.SymbolFacade(SymConst.NETWORK_IDENTIFIER);

  // 送信者:アリス
  // 秘密鍵からアリスのキーペア生成
  const aliceKeyPair = new sdk.symbol.KeyPair(
    new sdk.PrivateKey(SymConst.ALICE_PRIVATE_KEY)
  );

  // 受信者:ボブ
  // 文字列公開鍵からPublicKey生成
  const bobPublicKey = new sdk.symbol.PublicKey(
    Uint8Array.from(Buffer.from(SymConst.BOB_PUBLIC_KEY, 'hex'))
  );
  // ボブのアドレスを取得
  const bobAddress = new sdk.symbol.Address(
    facade.network.publicKeyToAddress(bobPublicKey)
  );

トランザクションの生成

デッドラインは、承認され無かった場合の待ち時間で、未承認のまま期限が過ぎると取り消されます。最大6時間となっています。
転送モザイクは、配列になっていて複数モザイクを同時に転送できます。一度に転送できる上限があるのかは分かりませんが、1アカウントにつき1,000モザイクまでしか持てないので、1,000が上限になってくるかと思います。
メッセージは、平文(メッセージタイプ:0x00)で転送します。メッセージタイプの1バイトを含む1024バイトまで格納できます。

  // デッドライン設定
  // 現在日時をSymbolTimeに変換
  const networkTimestamp = new sdk.symbol.NetworkTimestamp(
    facade.network.fromDatetime(new Date())
  );
  const deadlineTimestamp = networkTimestamp.addHours(2).timestamp;

  // 転送モザイク設定
  const sendMosaics = [
    { mosaicId: SymConst.CURRENCY_MOSAIC_ID, amount: 1_000000n },
  ];

  // 平文メッセージ
  const messageData = new Uint8Array([
    0x00,
    ...new TextEncoder().encode('Hello, Symbol!!'),
  ]);

上記の値をセットして転送トランザクションを生成します。最後にトランザクション発行の手数料を設定しています。トランザクションサイズに100を掛ければ、大抵のノードが承認します。細かい手数料は/network/fees/transactionを参照してください。

  // 転送トランザクション生成
  const transferTx = facade.transactionFactory.create({
    type: 'transfer_transaction_v1',
    signerPublicKey: aliceKeyPair.publicKey,
    recipientAddress: bobAddress,
    deadline: deadlineTimestamp,
    mosaics: sendMosaics,
    message: messageData,
  });
  transferTx.fee = new sdk.symbol.Amount(BigInt(transferTx.size * 100)); // 手数料

署名

アリスのキーペアで署名を生成して、トランザクションと合わせます。トランザクションハッシュもこのタイミングで取得出来ます。今回、特に用途はないです。

  // アリス署名
  const transferTxSignature = facade.signTransaction(aliceKeyPair, transferTx);
  const transferTxPayloadJson =
    sdk.symbol.SymbolTransactionFactory.attachSignature(
      transferTx,
      transferTxSignature
    );
  const transferTxHash = facade.hashTransaction(transferTx);

アナウンス

RESTにアナウンスします。一応レスポンスはありますが、「ノードに送信できたよ!」っていうレスポンスなので、ノード側でリジェクトされている可能性もあります。別途確認のコードを追加した方が良いです(今回は省略します)。アナウンス先のノードを接続先にしたウォレットを立ち上げておくと、エラーの場合、通知が出るのでテストする際は便利です。

  // RESTにアナウンス
  const transactionsResponse = await fetch(
    new URL('/transactions', SymConst.REST_GATEWAY_URL),
    {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: transferTxPayloadJson,
    }
  );

  // アナウンス結果を表示
  const transactionsResponseJson = await transactionsResponse.json();
  console.log(`restResponse      : ${transactionsResponseJson.message}`);

コード例

04_transaction/transferTx.ts
import sdk from 'symbol-sdk';
import { SymConst } from '../SymConst.js';

try {
  // faced生成
  const facade = new sdk.facade.SymbolFacade(SymConst.NETWORK_IDENTIFIER);

  // 送信者:アリス
  // 秘密鍵からアリスのキーペア生成
  const aliceKeyPair = new sdk.symbol.KeyPair(
    new sdk.PrivateKey(SymConst.ALICE_PRIVATE_KEY)
  );

  // 受信者:ボブ
  // 文字列公開鍵からPublicKey生成
  const bobPublicKey = new sdk.symbol.PublicKey(
    Uint8Array.from(Buffer.from(SymConst.BOB_PUBLIC_KEY, 'hex'))
  );
  // ボブのアドレスを取得
  const bobAddress = new sdk.symbol.Address(
    facade.network.publicKeyToAddress(bobPublicKey)
  );

  // デッドライン設定
  // 現在日時をSymbolTimeに変換
  const networkTimestamp = new sdk.symbol.NetworkTimestamp(
    facade.network.fromDatetime(new Date())
  );
  const deadlineTimestamp = networkTimestamp.addHours(2).timestamp;

  // 転送モザイク設定
  const sendMosaics = [
    { mosaicId: SymConst.CURRENCY_MOSAIC_ID, amount: 1_000000n },
  ];

  // 平文メッセージ
  const messageData = new Uint8Array([
    0x00,
    ...new TextEncoder().encode('Hello, Symbol!!'),
  ]);

  // 転送トランザクション生成
  const transferTx = facade.transactionFactory.create({
    type: 'transfer_transaction_v1',
    signerPublicKey: aliceKeyPair.publicKey,
    recipientAddress: bobAddress,
    deadline: deadlineTimestamp,
    mosaics: sendMosaics,
    message: messageData,
  });
  transferTx.fee = new sdk.symbol.Amount(BigInt(transferTx.size * 100)); // 手数料

  // 未署名トランザクションを表示
  console.log(
    `unsignedTx        : ${Buffer.from(transferTx.serialize())
      .toString('hex')
      .toUpperCase()}`
  );

  // アリス署名
  const transferTxSignature = facade.signTransaction(aliceKeyPair, transferTx);
  const transferTxPayloadJson =
    sdk.symbol.SymbolTransactionFactory.attachSignature(
      transferTx,
      transferTxSignature
    );
  const transferTxHash = facade.hashTransaction(transferTx);

  // 署名トランザクションを表示
  console.log(
    `signedTx          : ${Buffer.from(transferTx.serialize())
      .toString('hex')
      .toUpperCase()}`
  );
  // トランザクションハッシュを表示
  console.log(`txHash            : ${transferTxHash}`);

  // RESTにアナウンス
  const transactionsResponse = await fetch(
    new URL('/transactions', SymConst.REST_GATEWAY_URL),
    {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: transferTxPayloadJson,
    }
  );

  // アナウンス結果を表示
  const transactionsResponseJson = await transactionsResponse.json();
  console.log(`restResponse      : ${transactionsResponseJson.message}`);
} catch (e) {
  console.error(e);
}

メッセージを暗号化

メッセージを暗号化することも出来ます。暗号化用のクラスが用意されていて、アリスの秘密鍵とボブの公開鍵で暗号化。自動的にメッセージタイプ0x01が付与されます。ただ、v2の二重16進数化バグがあるので、バグが修正されたv3で暗号化した文章は現在のウォレットで復号できません。

const message = "Hello Symbol!!";
const aliceMsgEncoder = new symbolSdk.symbol.MessageEncoder(aliceKeyPair);
const messageData = aliceMsgEncoder.encode(
  bobPublicKey,
  new TextEncoder().encode(message)
);

メッセージにバイナリデータを格納

決まりはないのですが、メッセージタイプ:0xFFをバイナリデータとして扱います。メッセージタイプは特に決まりが無く、各アプリでどう扱うかに任せられます。

const messageData = new Uint8Array([
  0xff,
  ...new TextEncoder().encode("Hello, Symbol!!"),
]);

FireShot Capture 132 - Symbol Block Explorer - testnet.symbol.fyi.jpg

スクリーンショット 2024-01-14 152154.png

以下は、Zero埋めした1023バイトをメッセージに入れたトランザクションです。

9
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
9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?