メインコンテンツまでスキップ

5. トランザクション

5.1 トランザクションとは

5.1.1 トランザクションの基本概念

ブロックチェーンにおける「トランザクション」とは、ネットワークに記録される操作や処理の単位を指します。
銀行における「振込」や「引き落とし」が処理の単位であるのと同様に、ブロックチェーン上では「アカウントからアカウントへの送金」や 「モザイクの発行」といった操作がトランザクションです。

トランザクションは、署名された後にネットワークへアナウンスされ、ノードに伝播し、最終的にブロックに取り込まれることで台帳に記録されます。
これにより、誰でも検証可能な形で「いつ」「誰が」「何をしたか」が保証されます。

5.1.2 Symbol ブロックチェーンにおける役割

Symbol ブロックチェーンでは、すべての状態変化はトランザクションによって実現されます。

  • アカウント間の XYM 送金
  • 新しいモザイク(トークン)の発行
  • ネームスペースの登録
  • マルチシグアカウントの設定
  • アグリゲートトランザクションによる複数操作の一括実行

これらはすべて「トランザクション」という形式で表現され、ブロックチェーンに取り込まれることで有効になります。
つまり、トランザクションは ブロックチェーンを動かす最小単位の命令 だと捉えることができます。

5.1.3 トランザクションの種類(概要)

Symbol では多様な用途に対応するために、複数の種類のトランザクションが用意されています。代表的なものを以下に挙げます。

  • 転送トランザクション
    アカウント間で XYM やモザイクを送信するトランザクション。メッセージも付与可能。

  • アグリゲートトランザクション
    複数のトランザクションを一つにまとめて実行する仕組み。複数署名者による共同署名も可能。

  • モザイク関連トランザクション
    新規モザイクの作成、供給量の変更、制約(譲渡制限など)の設定を行う。

  • ネームスペース関連トランザクション
    ネームスペースの登録や更新を行う。モザイクやアカウントにフレンドリーな名前を付与できる。

  • アカウント関連トランザクション
    マルチシグ設定、キーリンク、アカウント設定変更など。

これらのトランザクションが組み合わさることで、Symbol ブロックチェーン上で多様なアプリケーションを構築することができます。

5.2 トランザクションのライフサイクル

トランザクションは作成されてからブロックに記録されるまで、いくつかの段階を経ます。
この流れを理解しておくことは、アプリケーション開発において「いつ送金が完了したとみなせるか」 「どのタイミングでエラー処理を行うか」を判断する上で重要です。

5.2.1 ライフサイクルの流れ

  1. 作成
    アプリケーションやウォレットで、送金や操作の内容を指定してトランザクションを生成します。
    例: 送信先アドレス、送信量、手数料、メッセージなどを組み込む。

  2. 署名
    作成したトランザクションを秘密鍵で署名します。これにより、

    • 送信者本人が承認したこと
    • 改ざんされていないこと

    が保証されます。

  3. アナウンス
    署名済みトランザクションをノードに送信します。SDK を使えば REST API 経由で簡単に行えます。

  4. ネットワーク伝播
    受け取ったノードは、P2P ネットワークを通じて他のノードへトランザクションを転送します。
    この段階ではまだブロックに取り込まれていないため「未承認」状態です。

  5. ブロック取り込み
    ノードの収集したトランザクションがバリデーションを通過すると、次に生成されるブロックに格納されます。
    ブロックに含まれると「承認済み」となり、さらに最終化が進むことで「確定」します。

5.2.2 トランザクションのステータス

トランザクションには以下のステータスがあります。

  • Unconfirmed(未承認)
    アナウンスされ、ネットワークに伝播しているが、まだブロックに含まれていない状態。

  • Confirmed(承認済み)
    ブロックに含まれ、チェーンに記録された状態。通常はここで「処理が完了」とみなします。

  • Finalized(確定)
    ファイナリティが到達し、チェーン分岐の影響を受けない状態。高額取引や重要な処理ではこの状態を待つことがあります。

  • Failed(失敗)
    手数料不足や制約違反などにより取り消された場合。REST API の status エンドポイントで確認できます。

5.2.3 ノード間での伝播の仕組み

Symbol のネットワークは P2P 方式で構築されています。

  1. クライアントがトランザクションをノード A にアナウンスする。
  2. ノード A は署名や手数料、アカウント残高などを検証する。
  3. 問題がなければ、ノード A はトランザクションを保持し、他のピアノードへ転送する。
  4. ネットワーク全体にトランザクションが行き渡り、次のブロックで承認される。

この仕組みにより、トランザクションは分散的かつ効率的に処理されます。

5.3 転送トランザクション

転送トランザクションは最も基本的なトランザクションです。XYM やモザイクを他のアカウントに送金するときに使用します。

Symbol SDK では、以下の項目を指定してトランザクションを作成します:

  • 送信者
  • 宛先アドレス
  • 送信するモザイク
  • メッセージ(任意)
  • 手数料

メッセージには文字列や暗号化されたメッセージを添付できます。 手数料はトランザクションのサイズに応じて自動計算されますが、手動設定も可能です。

以下は、アカウント間で送金を行うコード例です:

import {
descriptors,
generateMosaicAliasId,
models,
SymbolFacade,
SymbolTransactionFactory,
} from 'symbol-sdk/symbol';
import { KeyStore } from '../KeyStore';

const keyStore = new KeyStore('testnet');

const facade = new SymbolFacade('testnet');

// 署名アカウント(送信者)
const aliceAccount = facade.createAccount(keyStore.AlicePrivateKey);
// 受信アカウント
const bobPublicAccount = facade.createPublicAccount(keyStore.BobPublicKey);

// 転送モザイク設定
const mosaicId = generateMosaicAliasId('symbol.xym');
const mosaics = [
new descriptors.UnresolvedMosaicDescriptor(
new models.UnresolvedMosaicId(mosaicId),
new models.Amount(10_000000n)
),
];

// 平文メッセージ
const message = new TextEncoder().encode('\0Hello, Symbol!!');

// 転送トランザクションディスクリプタ生成
const transferTxDescriptor = new descriptors.TransferTransactionV1Descriptor(
bobPublicAccount.address,
mosaics,
message
);

// 転送トランザクション生成
const transferTx = facade.createTransactionFromTypedDescriptor(
transferTxDescriptor,
aliceAccount.publicKey,
100, // 手数料係数
60 * 60 * 2 // 有効期限(秒)
);

console.log(transferTx.toJson());

このように、転送トランザクションは必要な情報を指定することで、 比較的シンプルなコードで作成できます。 次は、このトランザクションに署名してネットワークへアナウンスしましょう。

5.4 署名とアナウンス

作成したトランザクションは、まだネットワークで有効ではありません。有効化するには以下の 2 つのステップが必要です:

5.4.1 署名

署名は、送信者本人がトランザクションを承認したことを証明する重要な処理です。秘密鍵を使用して署名することで:

  • 送信者本人による承認を証明
  • トランザクションの改ざんを防止
  • 誰でも送信者と内容を検証可能
// アリス署名
const sig = aliceAccount.signTransaction(transferTx);
const payloadJsonString = SymbolTransactionFactory.attachSignature(transferTx, sig);

console.log('txHash:', facade.hashTransaction(transferTx).toJson());
console.log('payload:', payloadJsonString);
console.log(transferTx.toJson());