【Jaguar's Blog 9】トランザクションを理解する(転送編)
この記事は2024年1月4日にNEM/Symgolのコア開発者Jaguar氏によって投稿された記事「Understanding TXes - Transfer」をChatGPTを用いて翻訳したものです。
Symbolの転送トランザクションは、モザイクやメッセージを1つのアカウントから別のアカウントに送信するために使用されます。これらは単独のトランザクションであるか、またはアグリゲートトランザクションに埋め込まれている可能性があります。トランザクションのより深い理解を得るためには、Symbol Technical Referenceの第6章を読むことがお勧めです。
この記事の目的には、転送トランザクションの構成要素を検討し、そのデザインに関する意思決定について学びます。完全なスキーマはこちらで確認できます。さあ、始めましょう!
静的プロパティ
TRANSACTION_VERSION = make_const(uint8, 1)
TRANSACTION_TYPE = make_const(TransactionType, TRANSFER)
この記事で取り上げられている転送トランザクションのバージョンは1です。TRANSFERトランザクションのタイプは0x4154で、エンティティタイプ(0x1 << 14)、コード(0x1 << 8)、ファシリティコード(0x54)です。
レイアウト
# recipient address
recipient_address = UnresolvedAddress
recipient_addressは、トランザクションに含まれるモザイクやメッセージを受け取るアカウントのアドレスです。これはUnresolvedAddressタイプであることに注意してください。これは、アドレスがアドレスのエイリアスである可能性があり、処理の前にSymbolクライアントによって解決される必要があることを示しています。慣習的に、addressで終わるフィールドはアドレスタイプです。
# size of attached message
message_size = uint16
message_sizeは添付メッセージのサイズ(バイト単位)です。この値がゼロの場合、メッセージは存在しません。これは16ビットの符号なし整数であるため、構造体で許容される最大値は2^16です。通常、これはconfig-node.propertiesのmaxMessageSizeという構成プロパティによってさらに制限されます。mainnetの場合、この設定の値は1024です。慣習的に、sizeで終わるフィールドはバイト単位のサイズです。
# number of attached mosaics
mosaics_count = uint8
mosaics_countは添付されたモザイクの数です。この値がゼロの場合、モザイクは存在しません。これは8ビットの符号なし整数であるため、構造体で許容される最大値は2^8です。慣習的に、countで終わるフィールドはアイテムの数です。
# reserved padding to align mosaics on 8-byte boundary
transfer_transaction_body_reserved_1 = make_reserved(uint8, 0)
# reserved padding to align mosaics on 8-byte boundary
transfer_transaction_body_reserved_2 = make_reserved(uint32, 0)
mosaics_countの後には、送信者によってゼロにされる必要のある5つの予約済みバイトがあります(8ビットと32ビットのプレースホルダーが1つずつ)。これらは将来の使用のために予約されており、将来的に何かに使用される可能性があります。これらが全く必要ないのか疑問に思うかもしれませんね。では学んでみましょう!
CPUレベルでの最良のパフォーマンスを得るためには、整数はサイズの倍数であるバイト境界から始まる必要があります。例えば、8バイトの整数は8の倍数で始まるオフセットを持っていなければならず、4バイトの整数は4の倍数で始まるオフセットを持っていなければなりません。独立したトランザクションヘッダーと埋め込まれたトランザクションヘッダーの両方が8の倍数のサイズで終わります。したがって、各個別のトランザクションのためのカスタムトランザクションペイロードは8バイトの境界から始まります。
前述のフィールドのサイズを合計すると、recipient_address(24)、message_size(2)、mosaics_count(1)で、合計が27になります。それに予約済みバイト(5)を加えると、32になり、これは8の倍数です。その結果、次のフィールドは8バイトの整数になります(実際にそのようになっています)。
# attached mosaics
@sort_key(mosaic_id)
mosaics = array(UnresolvedMosaic, mosaics_count)
mosaicsは、送信者から受信者に転送されるモザイクの配列です。この配列にはゼロ個以上のモザイクが含まれる可能性があります。ただし、mosaics_countと同じ数のモザイクを含まなければなりません。sort_key属性は、mosaicsがmosaic_idフィールドでソートされている必要があることを示しています。
なぜゼロのモザイクが許可されているのでしょうか?
これにより、送信者はトークンを転送せずにメッセージを受信者に送信することができます。これはメッセージングシステムやメッセージベースのNFTを構築するために使用できます。
なぜ複数のモザイクが許可されているのでしょうか?
これにより、送信者は異なる形式の支払いからなる単一の購入を行うことができます。例えば、送信者は購入のためにYENトークンとSTORE_CREDITトークンを転送することができます。これは複数の転送トランザクションを集約トランザクションで使用することでも達成できますが、単一の転送トランザクションを使用する場合の方が常に安価です(合計トランザクションサイズが小さくなるため)。
UnresolvedMosaicは、(未解決の)mosaic_idとamountから構成される構造体です:
# A quantity of a certain mosaic, specified either through a MosaicId or an alias.
struct UnresolvedMosaic
# Unresolved mosaic identifier.
mosaic_id = UnresolvedMosaicId
# Mosaic amount.
amount = Amount
mosaic_idとamountはいずれも8バイトの符号なし値であり、したがって、UnresolvedMosaicのサイズは16バイトです。mosaic_idはUnresolvedMosaicIdタイプであることに注意してください。これは、mosaic idがモザイクidのエイリアスである可能性があり、処理の前にSymbolクライアントによって解決される必要があることを示しています。
# attached message
message = array(uint8, message_size)
messageは、送信者から受信者に送信されるゼロ個以上のデータバイトから構成されるバイト配列です。メッセージデータは非構造化であり、Symbolの合意プロトコルによって検証されません。これは正確にmessage_sizeバイトの長さでなければなりません。messageがmosaicsの後に来ていることに注意してください。もしこれらのフィールドが逆だった場合、mosaicsが常に8バイトの境界で始まるようにするためにmessageとmosaicsの間にパディングが必要でした。対照的に、現在の順序付けはそのようなパディングが不要になるため、よりコンパクトです。
なぜメッセージが転送トランザクションの一部なのでしょうか?
支払いと一緒にメッセージを送信することは一般的です。たとえば、メッセージフィールドは請求書を保存するために使用できます。それにもかかわらず、メッセージは任意です。もしmessage_typeがなければ、mosaicsの適切な配置を可能にするためにそのバイトが(予約されて)まだ必要でしょう。トランザクション手数料はトランザクション(バイト)サイズに対応しているため、メッセージが指定されていない場合は追加のコストはありません。
なぜメッセージデータは検証されないのでしょうか?
これにより、アプリケーション開発者は自分たちが望むものを何でも保存できる最大限の柔軟性を得ることができます。NEMとは異なり、メッセージタイプ(平文または暗号化)は存在しません。暗号化は純粋にアプリケーションレベルの概念です。実際、この柔軟性により、異なるアプリケーションが必要に応じて異なる種類の暗号化を使用することができます。