シリアル化フォーマット
このページでは、XRP Ledgerのトランザクションとその他のデータの正規バイナリフォーマットについて説明します。このバイナリフォーマットは、トランザクションの内容のデジタル署名を作成および検証するために必要であり、サーバー間のピアツーピア通信を含む他の用途にも使用されます。通常、rippled
APIは、JSONを使用してクライアントアプリケーションと通信します。ただしJSONは、同じデータをさまざまな同等の方法で表現できるため、デジタル署名を付与するトランザクションをシリアル化するのに適したフォーマットではありません。
トランザクションをJSONまたはその他の表現から正規バイナリフォーマットへシリアル化するプロセスのステップを、以下にまとめます。
- すべての必須フィールドが指定されていること(必須の「自動入力可能」フィールドを含む)を確認します。
トランザクションフォーマットリファレンスに、XRP Ledgerトランザクションの必須フィールドと省略可能なフィールドが定義されています。
注記: SigningPubKey
もこのステップで指定する必要があります。署名の際に、署名用に指定された秘密鍵からこのキーを導出できます。
-
各フィールドのデータを「内部」バイナリフォーマットに変換します。
-
フィールドを正規順序でソートします。
-
各フィールドの前にフィールドIDを付加します。
-
フィールド(プレフィクスを含む)をソート順に連結します。
その結果、ECDSA(secp256k1楕円曲線を使用)やEd25519などの既知の署名アルゴリズムを使用して署名できるバイナリブロブが1つ作成されます。XRP Ledgerのために、適切なプレフィクス(シングル署名の場合は0x53545800
、マルチ署名の場合は0x534D5400
)を使用してデータをハッシュ化する必要があります。署名後に、指定されているTxnSignature
フィールドを使用してトランザクションを再度シリアル化する必要があります。
注記: XRP Ledgerでは、レジャーオブジェクトや処理済みのトランザクションなど他のタイプのデータを表す場合にも同じシリアル化フォーマットが使用されます。ただし、署名されるトランザクションに追加するのに適切なフィールドは限られています。(たとえば署名自体が指定されているTxnSignature
フィールドは、署名するバイナリブロブに含まれていてはなりません。)このように、「署名」フィールドとされてオブジェクトに署名するときにオブジェクトに含まれるフィールドもあれば、「非署名」とされてオブジェクトに含まれないフィールドもあります。
例
署名済みトランザクションと未署名のトランザクションはいずれも、JSONフォーマットとバイナリフォーマットの両方で表すことができます。同じ署名済みトランザクションのJSONフォーマットとバイナリフォーマットの例を以下に示します。
JSON:
{
"Account": "rMBzp8CgpE441cp5PVyA9rpVV7oT8hP3ys",
"Expiration": 595640108,
"Fee": "10",
"Flags": 524288,
"OfferSequence": 1752791,
"Sequence": 1752792,
"SigningPubKey": "03EE83BB432547885C219634A1BC407A9DB0474145D69737D09CCDC63E1DEE7FE3",
"TakerGets": "15000000000",
"TakerPays": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "7072.8"
},
"TransactionType": "OfferCreate",
"TxnSignature": "30440220143759437C04F7B61F012563AFE90D8DAFC46E86035E1D965A9CED282C97D4CE02204CFD241E86F17E011298FC1A39B63386C74306A5DE047E213B0F29EFA4571C2C",
"hash": "73734B611DDA23D3F5F62E20A173B78AB8406AC5015094DA53F53D39B9EDB06C"
}
バイナリ(16進数として表現):
120007220008000024001ABED82A2380BF2C2019001ABED764D55920AC9391400000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D165400000037E11D60068400000000000000A732103EE83BB432547885C219634A1BC407A9DB0474145D69737D09CCDC63E1DEE7FE3744630440220143759437C04F7B61F012563AFE90D8DAFC46E86035E1D965A9CED282C97D4CE02204CFD241E86F17E011298FC1A39B63386C74306A5DE047E213B0F29EFA4571C2C8114DD76483FACDEE26E60D8A586BB58D09F27045C46
サンプルコード
ここで説明するシリアル化プロセスは複数の場所にさまざまなプログラミング言語で実装されています。
- C++:
rippled
コードベース - JavaScript:
ripple-binary-codec
パッケージ - Python 3: このリポジトリのコードサンプルセクション
これらのすべての実装には、一般利用が可能なオープンソースライセンスが提供されているので、学習のためにドキュメントと合わせて使用するだけでなく、必要に応じてコードをインポート、使用、または変更することができます。
内部フォーマット
各フィールドには「内部」バイナリフォーマットがあります。このフォーマットは、rippled
ソースコードで署名時に(およびその他のほとんどの場合に)そのフィールドを表示するのに使用されます。すべてのフィールドの内部フォーマットは、SField.cpp
のソースコードに定義されています。(このフィールドには、トランザクションフィールド以外のフィールドも含まれています。)トランザクションフォーマットリファレンスにも、すべてのトランザクションフィールドの内部フォーマットが記載されています。
たとえばFlags
共通トランザクションフィールドはUInt32(32ビット符号なし整数)になります。
定義ファイル
以下のJSONファイルには、XRP Ledgerデータをそのバイナリフォーマットにシリアル化し、バイナリからシリアル化解除するのに必要な重要な定数が定義されています。
https://github.com/ripple/ripple-binary-codec/blob/master/src/enums/definitions.json
この定義ファイルの最上位フィールドの定義を以下の表に示します。
フィールド | 内容 |
---|---|
TYPES |
フィールドIDの作成と正規順序でのフィールドのソートのためのデータタイプからその「タイプコード」へのマップ。1未満のコードは実際のデータには含まれません。10000を超えるコードは、他のオブジェクト内部ではシリアル化できない「トランザクション」などの特殊な「上位」オブジェクトタイプを表します。各タイプのシリアル化方法についての詳細は、タイプリストを参照してください。 |
LEDGER_ENTRY_TYPES |
レジャーオブジェクトから対応するデータタイプへのマップ。これはレジャー状態データと、処理されたトランザクションのメタデータの「affected nodes」セクションに含まれます。 |
FIELDS |
トランザクション、レジャーオブジェクト、あるいはその他のデータに含まれる可能性があるすべてのフィールドを表すタプルからなるソート済み配列。各タプルの1番目のメンバーはフィールドの文字列名であり、2番目のメンバーはそのフィールドのプロパティーが含まれているオブジェクトです。(これらのフィールドの定義については、以下の「フィールドプロパティー」の表を参照してください。) |
TRANSACTION_RESULTS |
トランザクション結果コードから対応する数値へのマップ。レジャーに含まれない結果タイプにはマイナスの値が含まれています。tesSUCCESS に数値0が含まれています。tec クラスコードは、レジャーに含まれている失敗を示しています。 |
TRANSACTION_TYPES |
トランザクションのタイプから対応する数値へのマップ。 |
署名と送信のためにトランザクションをシリアル化するという目的から、FIELDS
、TYPES
、およびTRANSACTION_TYPES
フィールドが必要です。
FIELDS
配列のフィールド定義オブジェクトには以下のフィールドが含まれています。
フィールド | 型 | 内容 |
---|---|---|
nth |
数値 | このフィールドのフィールドコード。このコードは、フィールドIDの作成時と、同一データタイプの他のフィールドとのソート時に使用されます。 |
isVLEncoded |
ブール値 | true の場合、このフィールドには長さプレフィクスが付加されています。 |
isSerialized |
ブール値 | true の場合、このフィールドはシリアル化バイナリデータにエンコードされる必要があります。このフィールドがfalse の場合、一般にフィールドは保管されず、オンデマンドで再作成されます。 |
isSigningField |
ブール値 | true の場合、署名のためにトランザクションを準備する際にこのフィールドをシリアル化する必要があります。false の場合、このフィールドは署名対象データから省略する必要があります。(これはトランザクションに含まれていない可能性があります。) |
type |
文字列 | このフィールドの内部データタイプ。これは、このフィールドのタイプコードを示すTYPES マップのキーにマップします。 |
フィールドID
フィールドのタイプコードとフィールドコードを結合すると、フィールドの一意のIDになります。このIDは、最終的なシリアル化ブロブでこのフィールドの前に付加されます。フィールドIDのサイズは、タイプコードとその結合対象のフィールドコードに応じて1~3バイトとなります。以下の表を参照してください。
タイプコード < 16 | タイプコード >= 16 | |
---|---|---|
フィールドコード < 16 | ||
フィールドコード >= 16 |
デコードの際には、1番目のバイトのどのビットがゼロであるかによって、フィールドIDのバイト数を把握できます。これは、上記の表の例に対応しています。
上位4ビットがゼロ以外である | 上位4ビットがゼロである | |
---|---|---|
下位4ビットがゼロ以外である | 1バイト: 上位4ビットがタイプを定義し、下位4ビットがフィールドを定義します。 | 2バイト: 1番目のバイトの下位4ビットがフィールドを定義し、次のバイトがタイプを定義します。 |
下位4ビットがゼロである | 2バイト: 1番目のバイトの上位4ビットがタイプを定義し、1番目のバイトの下位4ビットは0になります。次のバイトがフィールドを定義します。 | 3バイト: 1番目のバイトは0x00、2番目のバイトはタイプを定義します。3番目のバイトはフィールドを定義します。 |
注意: フィールドIDは、フィールドのソートに使用される2つの要素で構成されますが、シリアル化されたフィールドID自体に基づいてソートを実行しないでください。これは、フィールドIDのバイト構造によってソート順序が変わるためです。
長さプレフィクスを付加する
一部の可変長フィールドの前には、長さインディケーターが付加されています。Blob
フィールド(任意のバイナリデータを含む)がこれに該当します。長さプレフィクスが付加されているタイプのリストについては、タイプリストの表を参照してください。
注記: 一部のタイプの可変長フィールドには、長さプレフィクスが付加されません。このようなタイプでは、他の方法で内容の終わりが示されます。
長さプレフィクスはフィールドの長さを示す1~3バイトで構成され、タイププレフィクスと内容の間に挿入されます。
-
フィールドに0~192バイトのデータが含まれている場合、1番目のバイトは内容の長さを示し、長さバイトの直後にそのバイト数のデータが続きます。
-
フィールドに193~12480バイトのデータが含まれている場合、最初の2バイトは以下の式で算出されるフィールドの長さを示します。
193 + ((byte1 - 193) * 256) + byte2
-
フィールドに12481~918744バイトのデータが含まれている場合、最初の3バイトは以下の式で算出されるフィールドの長さを示します。
12481 + ((byte1 - 241) * 65536) + (byte2 * 256) + byte3
-
長さプレフィクスが付加されているフィールドに格納できる最大データは918744バイトです。
デコード時に、1番目の長さバイトの値から、追加の長さバイト(0、1、または2)が存在するかどうかを把握できます。
- 1番目の長さバイトの値が192以下の場合、これは唯一の長さバイトであり、フィールドの内容の長さはこのバイトが示すバイト数です。
- 1番目の長さバイトの値が193~240の場合、2つの長さバイトがあります。
- 1番目の長さバイトの値が241~254の場合、3つの長さバイトがあります。
フィールドの正規順序
トランザクションのすべてのフィールドは、まずフィールドのタイプ(特に各タイプに割り当てられている数値の「タイプコード」)に基づいて特定の順序でソートされ、次にフィールド自体(「フィールドコード」)に基づいてソートされます。(たとえば、姓がフィールドのタイプ、名前がフィールド自体とすると、姓で最初にソートし、次に名でソートすることになります。)
タイプコード
各フィールドタイプには任意のタイプコードが含まれており、番号が小さいコードから最初にソートされます。これらのコードはSField.h
で定義されています。
たとえば UInt32のタイプコードが2である ので、すべてのUInt32フィールドは、すべてのAmountフィールド(タイプコード6) よりも前に位置します。
定義ファイルには、TYPES
マップの各タイプのタイプコードがリストされています。
フィールドコード
各フィールドにはフィールドコードが含まれています。フィールドコードは、同じタイプのフィールドをソートするときに使用され、番号が小さいコードが最初になるようにソートされます。これらのフィールドはSField.cpp
で定義されています。
たとえばPaymentトランザクションのAccount
フィールドのソートコードが1である 場合、このフィールドはDestination
フィールド(ソートコードが3である フィールド)よりも前に位置します。
フィールドコードは異なるフィールドタイプのフィールドで再利用されますが、同じタイプのフィールドに同じフィールドコードが含まれることはありません。タイプコードとフィールドコードを組み合わせると、フィールドの一意のフィールドIDになります。
タイプリスト
トランザクションの指示には、以下のタイプのフィールドを指定できます。
タイプ名 | タイプコード | ビット長 | 長さプレフィクスを付加する? | 説明 |
---|---|---|---|---|
AccountID | 8 | 160 | はい | アカウントの一意のID。 |
Amount | 6 | 64または384 | いいえ | XRPまたは発行済み通貨の額。フィールドの長さは、XRPの場合は64ビット、発行済み通貨の場合は384ビット(64+160+160)です。 |
Blob | 7 | 可変 | はい | 任意のバイナリデータ。このようなフィールドの中で重要なフィールドとして、TxnSignature (トランザクションを承認する署名)があります。 |
Hash128 | 4 | 128 | いいえ | 128ビットの任意のバイナリ値。該当する唯一のフィールドはEmailHash です。これは、Gravatar を取得する目的でアカウント所有者のメールのMD-5ハッシュを保管するフィールドです。 |
Hash160 | 17 | 160 | いいえ | 160ビットの任意のバイナリ値。これにより通貨コードまたはイシュアーが定義されます。 |
Hash256 | 5 | 256 | いいえ | 256ビットの任意のバイナリ値。これは通常、トランザクション、レジャーバージョン、またはレジャーデータオブジェクトの「SHA-512ハーフ」ハッシュを表します。 |
PathSet | 18 | 可変 | いいえ | 複数通貨間ペイメントの有効なペイメントパスのセット。 |
STArray | 15 | 可変 | いいえ | 可変数のメンバーからなる配列。フィールドによってタイプが異なる場合があります。この例として、memosやマルチ署名で使用される署名者のリストがあります。 |
STObject | 14 | 可変 | いいえ | 1つ以上のネストされたフィールドを含むオブジェクト。 |
UInt8 | 16 | 8 | いいえ | 8ビットの符号なし整数。 |
UInt16 | 1 | 16 | いいえ | 16ビットの符号なし整数。TransactionType は、このタイプの特殊なフィールドで、特定の文字列から整数値へのマッピングを含みます。 |
UInt32 | 2 | 32 | いいえ | 32ビットの符号なし整数。このタイプの例として、すべてのトランザクションのFlags フィールドとSequence フィールドがあります。 |
上記のフィールドタイプの他に、レジャーオブジェクトやトランザクションメタデータなどのコンテキストでは以下のタイプが含まれることがあります。
タイプ名 | タイプコード | 長さプレフィクスを付加する? | 説明 |
---|---|---|---|
Transaction | 10001 | いいえ | トランザクション全体を含む「上位」タイプ。 |
LedgerEntry | 10002 | いいえ | レジャーオブジェクト全体を含む「上位」タイプ。 |
Validation | 10003 | いいえ | ピアツーピア通信でコンセンサスプロセスの検証投票を表すために使用される「上位」タイプ。 |
Metadata | 10004 | いいえ | 1つのトランザクションのメタデータを含む「上位」タイプ。 |
UInt64 | 3 | いいえ | 64ビットの符号なし整数。このタイプはトランザクションの指示には含まれませんが、さまざまなレジャーオブジェクトでこのタイプのフィールドが使用されます。 |
Vector256 | 19 | はい | このタイプはトランザクションの指示には含まれませんが、AmendmentレジャーオブジェクトのAmendments フィールドでは、現在有効なAmendmentを示すためにこのタイプが使用されます。 |
AccountIDフィールド
このタイプのフィールドには、XRP Ledgerアカウントの160ビットのIDが含まれています。JSONではこれらのフィールドはbase58 XRP Ledger「アドレス」および追加のチェックサムデータとして表示されます。このため、スペルミスが有効なアドレスとなることがありません。(このエンコードは「Base58Check」とも呼ばれ、誤ったアドレスへの送金を防止します。)これらのフィールドのバイナリフォーマットにはチェックサムデータは含まれておらず、またアドレスのbase58エンコードで使用される0x00
「タイププレフィクス」も含まれていません。(ただし、バイナリフォーマットは主に署名済みトランザクションに使用されるため、署名済みトランザクションを転記する際にスペルミスなどのエラーが発生すると署名が無効となり、送金できなくなります。)
スタンドアロンフィールドとして表示されるAccountID(Account
やDestination
など)の長さは固定長の160ビットですが、長さプレフィクスが付加されます。その結果、これらのフィールドの長さインディケーターは常に0x14
バイトになります。特殊フィールドの子として示されるAccountID(Amount issuer
、PathSet account
など)では長さプレフィクスは付加 されません 。
Amountフィールド
「Amount」タイプは、通貨(XRPまたは発行済み通貨)の額を表す特殊なフィールドタイプです。このタイプは2つのサブタイプで構成されます。
- XRP
XRPは64ビット符号なし整数(ビッグエンディアンオーダー)としてシリアル化されます。ただし、XRPであることを示すため最上位ビットが常に0であり、プラスの値であることを示す最上位から2番目のビットは1
となります。XRPの最大額(1017 drop)には57ビットが必要であるため、XRPのシリアル化フォーマットを計算するには、標準の64ビット符号なし整数をとり、0x4000000000000000
のビットOR演算を行います。
- 発行済み通貨
発行済み通貨は以下の3つのセグメントで構成され、セグメントの順序は以下のとおりです。
- 内部通貨フォーマットの額を示す64ビット。1番目のビットは、これがXRPではないことを示す
1
です。 - 通貨コードを示す160ビット。標準APIでは、標準通貨コードフォーマットを使用して「USD」などの3文字のコードが160ビットのコードに変換されますが、160ビットのカスタムコードも使用できます。
- イシュアーのアカウントIDを示す160ビット。(関連項目: アカウントアドレスエンコード)
1番目のビットに基づいて2つのサブタイプのいずれに該当するかを確認できます。0
の場合はXRP、1
の場合は発行済み通貨です。
以下の図に、XRPの額と発行済み通貨の額のシリアル化フォーマットを示します。
配列フィールド
一部のトランザクションフィールド(SignerListSetトランザクションのSignerEntries
やMemos
など)はオブジェクトの配列です(「STArray」タイプと呼ばれます)。
配列には、さまざまなオブジェクトフィールドがそのネイティブバイナリフォーマットで特定の順序で含まれています。JSONでは、各配列メンバーが1つのフィールド(メンバーオブジェクトフィールドの名前)を含むJSON「ラッパー」オブジェクトです。そのフィールドの値は(「内部」)オブジェクト自体です。
バイナリフォーマットでは、配列の各メンバーにはフィールドIDプレフィクス(ラッパーオブジェクトの単一キーに基づく)と内容(オブジェクトとしてシリアル化された内部オブジェクトからなる)が含まれています。配列の終わりをマークするため、アイテムにフィールドID 0xf1
(配列のタイプコードとフィールドコード1)を付加し、内容は指定しません。
以下の例は、配列のシリアル化フォーマットを示します(SignerEntries
フィールド)。
ブロブフィールド
ブロブタイプは、任意のデータを持つ長さプレフィクスが付加されているフィールドです。このタイプを使用する2種類の一般的なフィールドとして、SigningPubKey
とTxnSignature
があります。これらのフィールドにはそれぞれ、トランザクションの実行を承認する公開鍵と署名が含まれています。
両方のフィールドにはこれ以上の内容構造がないため、フィールドIDと長さプレフィクスの後に可変長エンコードで示される正確なバイト数で構成されます。
ハッシュフィールド
XRP LedgerのハッシュタイプにはHash128、Hash160、Hash256があります。これらのフィールドには特定のビット数のバイナリデータが含まれており、それらのデータはハッシュ演算の結果を表す場合とそうでない場合があります。
これらのフィールドは、長さインディケーターを使用せずに、ビッグエンディアンバイトオーダーで特定数のビットとしてシリアル化されます。
オブジェクトフィールド
トランザクションフィールドの一部(SignerListSetトランザクションのSignerEntry
やMemos
配列のMemo
など)はオブジェクトです(「STObject」タイプと呼ばれます)。オブジェクトのシリアル化は配列のシリアル化に似ていますが、唯一異なる点としてオブジェクトフィールド内ではオブジェクトのメンバーを正規順序に従って配置する必要がある点があげられます。配列フィールドではすでに順序が明示的に設定されています。
オブジェクトフィールドの正規フィールド順序は、すべての最上位フィールドの正規フィールド順序と同じですが、オブジェクトのメンバーはオブジェクト内でソートする必要があります。最終メンバーの後には、「オブジェクトの終わり」を示すフィールドID(0xe1
)があり、このフィールドには内容がありません。
以下の例は、オブジェクトのシリアル化フォーマットを示します(Memos
配列内の1つのMemo
オブジェクト)。
PathSetフィールド
複数通貨間PaymentトランザクションのPaths
フィールドは、JSONで配列からなる配列として表される「PathSet」です。使用されるパスについての詳細は、パスを参照してください。
PathSetは、1~6の個別パスとして順序どおりにシリアル化されます[ソース] 。それぞれの完全なパスの後には、パスの後に続く内容を示すバイトが配置されます。
0xff
は別のパスが続くことを示します。0x00
はPathSetの終わりを示します。
各パスには1~8のパスステップがこの順序で含まれています[ソース] 。各ステップはタイプを示すバイトで始まり、その後にパスステップを記述する1つ以上のフィールドが続きます。タイプは、ビット単位のフラグを使用してそのパスステップに含まれるフィールドを示します。(たとえば値が0x30
の場合、通貨とイシュアーの両方が変更されます。)複数のフィールドが含まれている場合、フィールドは常に特定の順序で配置されます。
以下の表に、有効なフィールドと、タイプバイトでフィールドを示すために設定されるビット単位のフラグを示します。
タイプフラグ | 含まれるフィールド | フィールドタイプ | ビットサイズ | 順序 |
---|---|---|---|---|
0x01 |
account |
AccountID | 160ビット | 1番目 |
0x10 |
currency |
通貨コード | 160ビット | 2番目 |
0x20 |
issuer |
AccountID | 160ビット | 3番目 |
いくつかの組み合わせは無効です。詳細は、パスの仕様を参照してください。
account
フィールドとissuer
フィールドのAccountIDには、長さプレフィクスは付加 されていません 。currency
がXRPの場合、通貨コードは160ビットのゼロとして表されます。
各ステップの直後にはパス上の次のステップが続きます。前述したように、パスの最終ステップの後には0xff
(別のパスが続く場合)または0x00
(これが最終パスの終わりの場合)が続きます。
以下の例は、PathSetのシリアル化フォーマットを示します。
UIntフィールド
XRP Ledgerには符号なし整数タイプUInt8、UInt16、UInt32、UInt64があります。これらのタイプはすべて、指定されたビット数の標準ビッグエンディアンバイナリー符号なし整数です。
JSONオブジェクトにこれらのフィールドが含まれている場合、ほとんどはデフォルトでJSONの数値として表されます。例外として、UInt64は文字列として表されます。これは、一部のJSONデコーダーがこれらの整数を64ビットの「倍精度」浮動小数点数として表現しようとするためです。64ビットの「倍精度」浮動小数点数では、すべてのUInt64値を完全な精度で表現することができません。
もう1つの特殊なケースとしてTransactionType
フィールドがあります。JSONではこのフィールドは便宜上、トランザクションタイプの名前の文字列として表現されますが、バイナリではこのフィールドはUInt16です。定義ファイル内のTRANSACTION_TYPES
オブジェクトにより、これらの文字列が特定の数値にマップされます。