初めまして。
CryptoGamesというブロックチェーンゲーム企業でエンジニアをしている cardene(かるでね) です!
ERCについてひたすらまとめたり、スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。
以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください!
https://twitter.com/cardene777
https://chaldene.net/
https://qiita.com/cardene
https://zenn.dev/heku
https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58
https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4
また、これからも情報を見逃したくないという方はぜひ以下の購読ボタンを教えてください。
更新があった時、登録しているメールアドレス宛に通知が飛ぶようになります。
今回はXRP LEDGERについての第6回の記事になります。
第1回~第5回の記事は以下になります。
https://cardene.substack.com/p/xrp-ledger-introduction
https://cardene.substack.com/p/xrp-ledger-usecase
https://cardene.substack.com/p/xrp-ledger-concept-network
https://cardene.substack.com/p/xrp-ledger-consensus
https://cardene.substack.com/p/xrp-ledger-ledger
XRP LEDGERのコンセプトの1つである「トランザクション」についてまとめていきます。
この記事は以下の公式ドキュメントを参考にまとめていきます。
https://xrpl.org/docs/concepts/
トランザクション
XRP Ledgerでは、トランザクションだけがレジャーを変更できます。
トランザクションは、以下の条件を全て満たした場合にのみ確定します。
署名されている
送信されている
コンセンサスプロセスに従って検証済みのレジャーバージョンに受け入れられている
一部のレジャールールでは、署名や送信は行われないものの、コンセンサスによって受け入れられる必要がある疑似トランザクションも生成されます。
失敗したトランザクションも、スパム防止のためのトランザクション手数料としてXRPの残高を変更するため、レジャーに含まれます。
XRP Ledgerのトランザクションは、送金に限らず以下のような用途にも使用されます。
暗号鍵のローテーション
その他の設定の管理
XRP Ledgerの分散型取引所での取引
トランザクションの識別
署名済みのトランザクションには、それぞれ固有のhashがあり、トランザクションを識別するために使用されます。
トランザクションを送信すると、サーバーはレスポンスでハッシュを提供します。
また、account_tx
コマンドを使用して、アカウントのトランザクション履歴からトランザクションを検索することもできます。
トランザクションハッシュは「支払い証明」として使用できます。
誰でもハッシュを使ってトランザクションを検索し、その最終ステータスを確認できるためです。
注意
XRP Ledgerの全履歴の中で、トランザクションハッシュが一意であるという規則の例外があります。
初期の2つのSetFee
疑似トランザクションは全く同じフィールドを持っていたため、同じハッシュ1C15FEA3E1D50F96B6598607FC773FF1F6E0125F30160144BE0C5CBC52F5151B
になりました。
疑似トランザクションとは、通常のトランザクションとは異なり、署名や送信は行われませんが、コンセンサスによって承認される必要があるものです。
SetFee
は、トランザクション手数料を設定するための疑似トランザクションです。
これらのトランザクションの1つ目はレジャー3715073
に、2つ目はレジャー3721729
に現れています。
新しいSetFee
疑似トランザクションにはLedgerSequence
フィールドが含まれているため、これにより一意性が保証されています。
Claimコスト
失敗したトランザクションに対してもトランザクションコストがかかるのには理由があります。
シーケンス番号の連続性の維持
失敗したトランザクションの後に送信されたトランザクションでは、シーケンス番号を振り直す必要がありません。
失敗したトランザクションをレジャーに組み込むことで、そのトランザクションのシーケンス番号が使用され、期待されるシーケンスが維持されます。
ネットワーク負荷の増大に対する防御
トランザクションをネットワーク全体に伝搬することで、ネットワーク負荷が増加します。
コストを課すことで、攻撃者が失敗したトランザクションを使ってネットワークを悪用することを難しくします。
現実世界での影響は軽微
トランザクションコストは少額であるため、大量のトランザクションを送信しない限り、金額としてそこまで大きくなりません。
トランザクションの承認
XRP Ledgerにおいて、トランザクションが特定のアクションを実行する権限を持つことは、デジタル署名によって証明されます。
署名されたトランザクションのみがネットワークに送信され、検証済みのレジャーに含まれることができます。
署名されたトランザクションは不変であり、その内容を変更することはできず、署名は他のトランザクションには有効ではありません。
トランザクションは、以下のいずれかの種類の署名によって承認されます。
送信元アドレスに数学的に関連付けられたマスター秘密鍵からの単一の署名
AccountSet
トランザクションを使用して、マスターキーペアを無効化または有効化できます。
アドレスに関連付けられた通常の秘密鍵と一致する単一の署名
SetRegularKey
トランザクションを使用して、通常のキーペアを追加、削除、または置き換えることができます。
アドレスが所有する署名者のリストと一致するマルチシグネチャ
SignerListSet
トランザクションを使用して、署名者のリストを追加、削除、または置き換えることができます。
以下の例外を除き、どの署名タイプでもあらゆる種類のトランザクションを承認できます。
マスター公開鍵を無効化できるのはマスター秘密鍵のみです。
凍結する能力を永続的に放棄できるのはマスター秘密鍵のみです。
トランザクションの署名と送信
XRP Ledgerにトランザクションを送信するには、以下のようないくつかのステップがあります。
JSONフォーマットで未署名のトランザクションを作成します。
1つ以上の署名を使用して、トランザクションを承認します。
トランザクションをXRP Ledgerサーバー(通常は
rippled
インスタンス)に送信します。トランザクションが適切に形成されている場合、サーバーは暫定的にそのトランザクションを現在のレジャーバージョンに適用し、ピアツーピアネットワークの他のメンバーにトランザクションを中継します。
コンセンサスプロセスにより、次の検証済みレジャーにどの暫定トランザクションが含まれるかが決定されます。
サーバーはそれらのトランザクションを標準的な順序で前のレジャーに適用し、結果を共有します。
十分な数の信頼できるバリデーターが全く同じレジャーを作成した場合、そのレジャーは検証済みであると宣言され、そのレジャー内のトランザクションの結果は不変となります。
JSONで表現された未署名のPaymentトランザクションの例
{
"TransactionType" : "Payment",
"Account" : "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Destination" : "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"Amount" : {
"currency" : "USD",
"value" : "1",
"issuer" : "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"
},
"Fee": "12",
"Flags": 2147483648,
"Sequence": 2,
}
XRP Ledgerは、トランザクションオブジェクトが送信元アドレス(Account
フィールド)によって承認されている場合にのみ、トランザクションを中継および実行します。
署名済みトランザクション BLOB の例
トランザクションに署名すると、「ブロブ」と呼ばれるバイナリデータの塊が生成されます。
これはネットワークに送信可能なものです。
以下は、署名済みのブロブとして表現された同じトランザクションを、WebSocket APIで送信している例です。
{
"id": 2,
"command": "submit",
"tx_blob" : "120000240000000461D4838D7EA4C6800000000000000000000000000055534400000000004B4E9C06F24296074F7BC48F92A97916C6DC5EA968400000000000000F732103AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB74483046022100982064CDD3F052D22788DB30B52EEA8956A32A51375E72274E417328EBA31E480221008F522C9DB4B0F31E695AA013843958A10DE8F6BA7D6759BEE645F71A7EB240BE81144B4E9C06F24296074F7BC48F92A97916C6DC5EA983143E9D4A2B8AA0780F682D136F7A56D6724EF53754"
}
この例では、tx_blob
フィールドに署名済みのトランザクションデータが含まれています。
このデータは、トランザクションの内容とそれに対する署名を表すバイナリデータです。
id
フィールドは、このリクエストを識別するための任意の値です。command
フィールドは、このリクエストが署名済みトランザクションを送信するためのものであることを示しています。
署名済みトランザクションデータ(ブロブ)は、トランザクションの内容とそれに対する署名をバイナリ形式で表現したものです。
これにより、トランザクションが改ざんされていないこと、および送信者が正当な署名者であることが保証されます。
このように、署名済みトランザクションブロブを生成し、それをXRP Ledgerネットワークに送信することで、トランザクションを実行することができます。
メタデータ付き実行トランザクションの例
トランザクションが実行された後、XRP Ledgerはメタデータを追加して、トランザクションの最終的な結果とトランザクションによる変更を示します。
例えばtx
コマンドを使用して、トランザクションのステータスを確認することができます。
tx
コマンドのレスポンス例。
{
"id": 6,
"status": "success",
"type": "response",
"result": {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Amount": {
"currency": "USD",
"issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"value": "1"
},
"Destination": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"Fee": "10",
"Flags": 2147483648,
"Sequence": 2,
"SigningPubKey": "03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB",
"TransactionType": "Payment",
"TxnSignature": "3045022100D64A32A506B86E880480CCB846EFA3F9665C9B11FDCA35D7124F53C486CC1D0402206EC8663308D91C928D1FDA498C3A2F8DD105211B9D90F4ECFD75172BAE733340",
"date": 455224610,
"hash": "33EA42FC7A06F062A7B843AF4DC7C0AB00D6644DFDF4C5D354A87C035813D321",
"inLedger": 7013674,
"ledger_index": 7013674,
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"FinalFields": {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Balance": "99999980",
"Flags": 0,
"OwnerCount": 0,
"Sequence": 3
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8",
"PreviousFields": {
"Balance": "99999990",
"Sequence": 2
},
"PreviousTxnID": "7BF105CFE4EFE78ADB63FE4E03A851440551FE189FD4B51CAAD9279C9F534F0E",
"PreviousTxnLgrSeq": 6979192
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "2"
},
"Flags": 65536,
"HighLimit": {
"currency": "USD",
"issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"value": "0"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "USD",
"issuer": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"value": "100"
},
"LowNode": "0000000000000000"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "96D2F43BA7AE7193EC59E5E7DDB26A9D786AB1F7C580E030E7D2FF5233DA01E9",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "1"
}
},
"PreviousTxnID": "7BF105CFE4EFE78ADB63FE4E03A851440551FE189FD4B51CAAD9279C9F534F0E",
"PreviousTxnLgrSeq": 6979192
}
}
],
"TransactionIndex": 0,
"TransactionResult": "tesSUCCESS"
},
"validated": true
}
}
このレスポンスには、実行されたトランザクションの詳細が含まれています。
Account
トランザクションの送信元アカウントのアドレス。
Amount
送金された金額とその通貨。
Destination
送金先のアカウントのアドレス。
Fee
トランザクションの手数料。
Sequence
トランザクションのシーケンス番号。
meta
トランザクションの結果とその影響を受けたレジャーエントリーの詳細。
AffectedNodes
トランザクションによって変更されたレジャーエントリーの詳細。
TransactionResult
トランザクションの結果(この例では "tesSUCCESS")。
validated
トランザクションが検証済みのレジャーに含まれているかどうか。
このように、tx
コマンドを使用することで、実行されたトランザクションの詳細とその結果を確認することができます。
手数料
Rippleを含むいかなる単一の主体も、ネットワークへのアクセスに対して手数料を要求することができません。
ただし、XRP Ledgerのルールには、レジャーの悪用を防ぐための中立的な手数料など、いくつかのタイプの手数料が含まれています。
これらの中立的な手数料は、誰にも支払われることはありません。
また、ユーザーが相互に手数料を徴収するためのオプションの方法が、XRP Ledger内外にいくつか存在します。
レジャーでの手数料
XRP Ledgerには、大きく分けて2種類の手数料があります。
中立的な手数料
トランザクションコスト(トランザクション手数料)
トランザクションを送信するために破棄される少量のXRP。
ネットワークの負荷に応じて変動し、ピアツーピアネットワークをスパムから保護する。
準備金要件
アカウントが保有しなければならない最小限のXRP。
アカウントがレジャーで所有するオブジェクトの数に応じて増加する。
ユーザーが不注意または悪意を持ってレジャーのサイズを増大させることを防ぐ。
オプションの手数料
送金手数料
発行者が、発行した通貨をXRP Ledger内の他のアドレスに送付する時に発生できるパーセンテージ手数料。
オプションであり、発行者が自由に設定できる。
トラストラインクオリティ
アカウントが、トラストライン上の残高を額面価値よりも高く、または低く評価することを可能にする設定。
手数料が発生するようなシチュエーションがある。
XRPには適用されない(XRPはトラストラインに紐付けられていないため)。
中立的な手数料は、XRP Ledgerの健全性を維持し、悪用を防ぐために設計されています。
一方、オプションの手数料は、ユーザー間での柔軟な取引を可能にするものです。
これらの手数料は、XRP Ledgerを安全かつ効率的に運用するために重要な役割を果たしています。
レジャー外での手数料
XRP Ledgerには組み込まれている手数料以外にも、レジャーに関連する手数料を請求する方法があります。
例えば、金融機関は以下のようなサービスに対して手数料を請求することが一般的です。
XRP Ledgerに送金する時の手数料
XRP Ledgerから引き出す時の手数料
また、ビジネスにおいては、以下のようなサービスに対しても手数料を請求することができます。
クライアントアプリケーションへのアクセス
XRP Ledger以外のアカウントのメンテナンス
交換サービス(特にXRP Ledgerの分散型取引所ではなく、プライベートマーケットでXRPを購入する場合)
その他多数のサービス
金融機関と取引を行う前に、手数料体系を必ず確認するようにしてください。
信頼性の高いトランザクションの送信
XRP Ledgerを使用する金融機関やその他のサービスは、ベストプラクティスを使用して、トランザクションが検証可能かつ迅速な方法で検証または拒否されるようにする必要があります。
トランザクションは、信頼できるrippled
サーバーに送信する必要があります。
ベストプラクティスにより、アプリケーションは以下のことを実現しながら、XRP Ledgerにトランザクションを送信することができます。
冪等性
トランザクションは一度だけ、または全く処理されないようにする。
検証可能性
アプリケーションは、トランザクションの最終結果を判断できる。
ベストプラクティスを実装しないアプリケーションは、以下のようなエラーが発生する可能性があります。
不注意でトランザクションが実行されないことがある。
暫定的なトランザクション結果を最終的な不変の結果と間違える。
以前レジャーに適用されたトランザクションの信頼できる結果を見つけられない。
これらのタイプのエラーは、深刻な問題につながる可能性があります。
例えば、以前の支払いトランザクションが成功したことを見つけられないアプリケーションは、誤って別のトランザクションを送信し、元の支払いを重複させる可能性があります。
背景
XRP Ledgerは、コンセンサスと検証のプロセスを通じて、トランザクションがレジャーに適用される順序を決定します。
正しい形式のトランザクションは通常数秒で検証または拒否されますが、グローバルトランザクションコストの変動によっては、迅速に処理されない場合があります。
トランザクションコストが指定された額よりも増加した場合、トランザクションは次の検証済みレジャーに含まれません。
コストが下がれば後のレジャーに含まれる可能性がありますが、有効期限が指定されていない場合いつレジャーに追加されるかは不明です。
XRP Ledgerでは、トランザクションは以下のようなプロセスを経てレジャーに適用されます。
アカウント所有者がトランザクションを作成し、署名します。
所有者がトランザクション候補としてネットワークに送信します。
形式が正しくないトランザクションは即座に拒否されます。
形式が正しいトランザクションは、暫定的な結果が後で変更される可能性があります。
コンセンサスと検証を通じ、トランザクションがレジャーに適用されます。
検証されたレジャーにトランザクションが含まれ、結果が不変となります。
APIやアプリケーションは、トランザクションを含むレジャーが検証されるまで、ステータスを繰り返し確認する必要があります。
トランザクションの適用には、最後に検証されたレジャーが使用されます。
コンセンサスと検証のプロセスにより、新しいトランザクションが正規の順序で適用され、新しい検証済みレジャーが生成されます。
トランザクションの暫定的な実行順序は、検証済みレジャーの最終的な実行順序とは異なる場合があるため、暫定的な結果と最終結果が異なることがあります。
LastLedgerSequenceは、トランザクションをXRP Ledgerに送信する時に指定できるオプションのパラメーターです。
これは、トランザクションが特定のレジャーバージョンまでに検証されるべきであることをXRP Ledgerに指示します。
LastLedgerSequenceを設定することで、トランザクションが長期間未確認のままになることを防ぎ、予測可能な方法で迅速に検証または拒否されるようにできます。自動プロセスでは、最後に検証されたレジャーインデックスよりも4つ大きい値を使用するのが一般的です。
トランザクションを送信する時には、LastLedgerSequenceを明示的に指定する必要があり、これによりトランザクションが意図したタイミングで処理されることが保証されます。
ベストプラクティス
以下は、トランザクションの提出とその結果の決定に関する推奨フローをまとめたものです。
信頼できるトランザクションの提出
トランザクションを信頼性の高い方法で送信するために、アプリケーションは以下のベストプラクティスに従う必要があります。
送信
トランザクションをネットワークに送信し、暫定的な結果を受け取ります。
確認
検証済みレジャーを確認し、正式な結果を判断します。
これらの手続きは別々に実装する必要があります。アプリケーションは、最終的で検証済みの結果に基づいて処理を行うために、トランザクション結果を確認することが重要です。
送信
トランザクションを送信する時は、電源障害やネットワーク障害に備えて、送信前にトランザクションの詳細を保持しておく必要があります。
送信プロセスは以下の手順で行います。
トランザクションを生成して署名し、
LastLedgerSequence
パラメーターを指定します。トランザクションハッシュ、
LastLedgerSequence
、送信者の情報、最新の検証済みレジャーインデックス、必要なアプリケーション固有のデータを保存します。トランザクションを送信します。
再起動時には、保持された値を使用してトランザクションのステータスを確認することができます。
確認
アプリケーションは、送信したトランザクションのステータスを、ハッシュを使って確認したり、APIから通知を受け取ったりすることで確認できます。
しかし、ネットワーク障害や電源障害などによって中断が発生した場合には、再起動時に信頼性の高い方法でトランザクションのステータスを確認する必要があります。
再起動時や最新の検証済みレジャーの確認時には、保持されたトランザクションごとに以下の手順を実行します。
トランザクションのハッシュを使用して結果を照会します。
結果が検証済みレジャーに現れた場合、最終結果を保存し、成功したトランザクションに基づいて処理を行います。
失敗した場合は、必要に応じて新しいLastLedgerSequence
とFee
でトランザクションを再送信します。LastLedgerSequence
が最新の検証済みレジャーよりも大きい場合、結果はまだ確定していないため、さらにレジャーが検証されるのを待ちます。サーバーに連続したレジャー履歴がある場合、送信者のアカウントシーケンスとトランザクションシーケンスを比較して、別のトランザクションが最終結果を持っているかどうかを確認します。
サーバーに連続したレジャー履歴がない場合、トランザクションの最終結果が得られないため、連続したレジャー履歴を取得するのを待ちます。
失敗のケース
トランザクションの失敗には2つのケースがあります。
トランザクションがレジャーに含まれ、XRPコストは消却されたが、他には何も起こらなかった場合。
原因は流動性の欠如、不適切なパス、その他の状況が考えられます。
同様のトランザクションを即座に再試行しても同じ結果になる可能性が高いです。トランザクションがレジャーに含まれず、何も起こらなかった場合。
原因はトランザクションコストの不足、早すぎるLastLedgerSequence
、不安定なネットワーク接続などが考えられます。LastLedgerSequence
やFee
を調整して再送信すれば成功する可能性があります。
予期しない状態として、同じSequence
番号の別のトランザクションがレジャーに含まれている場合があります。
この場合、原因を特定し適切な対処が必要であり、考えられる原因と対処方法は以下の通りです。
前回のトランザクションが予想と異なるハッシュで検証済みレジャーに含まれていた場合、異なるハッシュとトランザクションの最終結果を保存し、通常の活動を再開します。
トランザクションがキャンセルされ、置換トランザクションが処理された場合、両方の最終結果を保存し、欠落や置換されたトランザクションがないか確認してから通常の活動を再開します。
アクティブ/パッシブフェイルオーバー構成で、複数のシステムが同時にアクティブになった場合、1つのシステムのみがアクティブであることを確認し、検証済みレジャーに含まれていたすべてのトランザクションの最終結果を記録してから、通常の活動を再開します。
秘密鍵が不正に使用された場合、可能であればキーペアをローテーションし、他のトランザクションを確認します。
レジャーのギャップ
サーバーに連続したレジャー履歴がない場合、トランザクションの確定結果が得られない可能性があります。rippled
サーバーにリソースの余裕がある場合、不足しているレジャーバージョンを自動的に取得しますが、設定された履歴期間よりも古いレジャーは取得されません。
ledger_request
メソッドを使用して、サーバーに履歴レジャーバージョンの取得をリクエストできますが、設定された履歴範囲外のレジャーバージョンからのトランザクション結果は検索できない場合があります。
別の方法として、Rippleの完全履歴サーバーなど、必要なレジャー履歴を持つ別のrippled
サーバーを使用してトランザクションのステータスを検索することもできます。
ただし、不正なサーバーがトランザクションのステータスや結果について偽の情報を提供する可能性があるため、信頼できるサーバーのみを使用します。
安全な署名
XRP Ledgerにトランザクションを送信するには、秘密鍵のセキュリティを確保しながらデジタル署名を行う必要があります。
他の人があなたの秘密鍵にアクセスできる場合、資金が盗まれたり消却されたりする可能性があります。
トランザクションに安全に署名できる環境を設定するためのオプションには以下のようなものがあります。
rippled
をローカルまたは同じLAN内で実行する。ローカル署名を行えるクライアントライブラリを使用する。
XRP Ledgerの署名に対応した専用の署名デバイスを使用する。
信頼できるリモート
rippled
マシンに接続するために安全なVPNを使用する。
ネットワークにトランザクションを送信していない場合は、信頼できる公開サーバーを使用して着信トランザクションの監視やその他のネットワークアクティビティの読み取りを安全に行うことができます。
安全でない構成
秘密鍵の秘匿性を維持することは非常に重要です。
外部のソースから秘密鍵にアクセスできる構成は危険で、不正使用者によってすべてのXRPが盗まれる可能性があります。
秘密鍵は、プレーンテキストではなく暗号化された形式で保存し、メールで送信したり人の目に触れるところで入力したりしてはいけません。
セキュリティと利便性のバランスはアドレスの保有額によって変わるため、さまざまな目的に合わせて異なるセキュリティ構成の複数のアドレスを使用することが推奨されます。
rippledをローカルで実行する
この構成では、トランザクションを生成するマシンでrippled
を実行します。
秘密鍵はマシンから出ていかないため、マシンへのアクセス権がない人は秘密鍵にアクセスできません。
手順は以下の通りです。
rippled
をインストールし、マシンがシステム要件を満たしていることを確認します。トランザクションに署名する時は、
localhost
または127.0.0.1
のサーバーに接続し、シングル署名にはsignメソッド、マルチシグにはsign_forメソッドを使用します。サーバーを稼働状態に保ち、ネットワークと同期されるようにします。
注意点として、署名にコマンドラインAPIを使用するとセキュリティが弱くなる可能性があります。
また、トランザクションを送信していないときにrippled
サーバーをオフにすることは可能ですが、再起動時にネットワークとの同期に時間がかかります。同じLANでrippled
を実行する
この構成では、トランザクションを生成するマシンと同じプライベートLAN内の専用マシンでrippled
サーバーを実行します。
これにより、中程度のスペックの複数のマシンでトランザクションの指示を組み立てながら、rippled
を実行する専用マシンを使用できます。
手順は以下の通りです。
rippled
サーバーをLAN内のwssおよびhttps接続を受け入れるように設定します。証明書ピンニングを使用する場合は自己署名証明書、社内や既知の認証局が署名した証明書、またはLet's Encryptなどの無料の証明書を使用できます。
マシンのセキュリティ保護に関する業界標準のプラクティスに従い、ファイアウォール、ウイルス対策、適切なユーザー権限を使用します。
この構成は、自己所有のデータセンターやサーバールームがある場合に魅力的な選択肢です。
ローカル署名付きのクライアントライブラリを使用
この構成では、使用しているプログラミング言語で、組み込みの署名機能を持つクライアントライブラリを使用します。
署名ライブラリのセキュリティベストプラクティス
使用する署名ライブラリが、署名アルゴリズムを適切かつ安全に実装していることを確認してください。
例えば、ライブラリがデフォルトのECDSAアルゴリズムを使用している場合、RFC6979で説明されているように、決定論的ノンスを使用する必要があります。
クライアントライブラリを最新のStableバージョンに更新しておき、セキュリティ強化のために、Vaultなどの管理ツールから秘密鍵を読み込むことができます。
ローカル署名の例
以下は、さまざまな言語とライブラリを使用してトランザクション命令をローカルで署名する方法の例です。
JavaScript / TypeScript
xrpl.js
Python
xrpl-py
Java
xrpl4j
例えば、JavaScriptのxrpl.js
ライブラリを使用したセキュアなオフライン署名のサンプルコードは以下のようになります。
// xrpl.jsライブラリを使用したセキュアなオフライン署名のサンプルコード
const xrpl = require('xrpl')
// 環境変数からシード値を読み込む
const my_wallet = xrpl.Wallet.fromSeed(process.env['MY_SEED'])
// オフライン署名では、アドレスの次のシーケンス番号を知る必要がある
// または、シーケンス番号の代わりにチケットを使用することもできる
// これは、複数の署名が必要で、トランザクションを順不同で処理したい場合に便利
// 詳細は https://xrpl.org/tickets.html を参照
let my_seq = 21404872
// トランザクションに署名する前に、*すべての*必須フィールドを提供する
const txJSON = {
"Account": my_wallet.address,
"TransactionType":"Payment",
"Destination":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Amount":"13000000",
"Flags":2147483648,
"LastLedgerSequence":7835923, // オプションだが推奨
"Fee":"13",
"Sequence": my_seq
}
const signed = my_wallet.sign(txJSON)
console.log("tx_blob is:", signed.tx_blob)
console.log("tx hash is:", signed.hash)
このようにクライアントライブラリを使用することで、安全にローカルでトランザクションに署名することができます。
ライブラリのセキュリティベストプラクティスに従い、最新の安定版に更新しておくことが重要です。
専用の署名デバイスを使用
Ledger Nano Sなどの専用の署名デバイスを販売している企業もあります。
これらのデバイスは、秘密鍵をデバイスから出さずに署名ができます。
リモートリップルサーバーで安全なVPNを使用
この構成では、暗号化されたVPNを使用して、コロケーション施設や遠隔のデータセンターなどにリモートでホストされているrippled
サーバーに接続します。
送信元タグと宛先タグ
送信元タグと宛先タグは、XRP Ledgerの支払い機能で使用される32ビット符号なし整数で、多目的アドレスからの支払いや多目的アドレスへの支払いの特定の目的を示すことができます。
これらのタグは、レジャー外のシステムが支払いをどのように処理すべきかについての情報を提供するだけで、レジャー上の直接的な機能は持ちません。
宛先タグは支払いの受取人または宛先を示し、送信元タグは支払いの送信者または送信元を示します。
返金する場合は、受領した支払いの送信元タグを返金支払いの宛先タグとして使用する必要があります。
ホストされたアカウントでは、顧客に別のインターフェースを使用してXRP Ledgerアドレスからトランザクションを送受信する機能を提供し、通常は顧客ごとに送信元タグと宛先タグを使用します。
Xアドレスは、従来のアドレスとタグを組み合わせて1つのアドレスにエンコードしたもので、顧客にとって管理が簡単になります。
XRP Ledgerでは、他の分散型台帳とは異なり、支払いを受け取るためには入金され有効化済みのアカウントが必要です。
顧客ごとに異なるアドレスを使用するアプローチは、ネットワークのリソースを無駄に消費し、大きなコストがかかります。
送信元タグと宛先タグは、入金と支払いを個別の顧客にマッピングするより軽量な方法を提供します。
ビジネスでは、これらのタグを以下のような目的で使用できます。
顧客アカウントへの直接マッピング
返金された支払いの支払元へのマッピング
期限切れの見積もりへの支払いのマッピング
使い捨てタグの提供
プライバシーを保護しながらタグの重複を防ぐために、利用可能なタグの範囲を目的ごとに分割し、予測不可能な順序で割り当てることが推奨されます。
具体的には、暗号ハッシュ関数と剰余演算を使用して、タグを関連するセクションのサイズにマッピングし、新しいタグを使う前に古いタグとの衝突をチェックします。
番号順にタグを割り当てると、顧客のプライバシーが損なわれる可能性があります。
複数の顧客口座への支払いを受け取る可能性があるアドレスでは、宛先タグなしで支払いを受け取ることは問題になります。
この問題を減らすために、RequireDest
設定を有効にすることで、ユーザーがタグを設定し忘れた場合にXRP Ledgerが支払いを拒否するようにできます。
トランザクションコスト
XRP LedgerをスパムやDoS攻撃から保護するために、各トランザクションは少量のXRPを破棄しなければなりません。
トランザクションコストは、ネットワークの負荷に応じて増加するように設計されており、ネットワークに過負荷をかけることが非常に高価になります。
すべてのトランザクションは、トランザクションコストを支払うためにどれだけのXRPを破棄するかを指定する必要があります。
現在のトランザクションコスト
現在、ネットワークが標準的なトランザクションに要求する最小トランザクションコストは0.00001 XRP
(10 drop
)です。
通常よりも負荷が高い場合、このコストは増加することがあります。
また、rippled
に現在のトランザクションコストを問い合わせることもできます。
特別なトランザクションコスト
一部のトランザクションには、異なるトランザクションコストがあります。
リファレンストランザクション(ほとんどのトランザクション)
10 drop
キーリセットトランザクション
0
マルチ署名トランザクション
10 drop
×(1 + 提供された署名の数)
履行付きEscrowFinishトランザクション
10 drop
×(33 +(履行サイズ(バイト)÷ 16))
AccountDeleteトランザクション
2,000,000 drop
トランザクションコストの受益者
トランザクションコストはどの当事者にも支払われません。
XRPは取り消し不能な形で破棄されます。
ロードコストとオープンレジャーコスト
FeeEscalation Amendment
が有効な場合、トランザクションコストには負荷ベーストランザクションコストとオープンレジャーコストの2つのしきい値があります。
これによりトランザクションは以下の3つのカテゴリに分けられます。
負荷ベーストランザクションコストによって拒否されるトランザクション。
現在のオープンレジャーに組み入れられるトランザクション。
後のレジャーバージョンのキューに入れられるトランザクション。
各rippled
サーバーには、現在の負荷に基づいてコストしきい値が保持されています。
トランザクションのFee
値がサーバーの現在の負荷ベーストランザクションコストより低い場合、そのサーバーはトランザクションの適用も中継もしません。
オープンレジャーコストは、オープンレジャー内のトランザクション数がソフトリミットと等しくなるまでは最低トランザクションコストと同じですが、それを超えると急激に増加します。
オープンレジャーコストの水準は、絶対的なトランザクションコストではなく標準的なトランザクションコストに比例しています。
rippled
が、サーバーのローカル負荷コストは満たすがオープンレジャーコストは満たさないトランザクションを受け取った場合、サーバーはそのトランザクションが後のレジャーに含まれる可能性を判断し、可能性が高ければトランザクションキューに追加して中継し、そうでなければ破棄します。
リファレンストランザクションコスト
負荷スケーリング前のトランザクションコストという観点からは、最も安価な(無料ではない)トランザクションを指します。
ほとんどのトランザクションのコストは、Reference
トランザクションと同じです。
一部のトランザクション(マルチシグトランザクションなど)では、このコストの数倍が必要になります。
手数料レベル
トランザクションの最少コストと実際のコストとの相対的な差を表します。
オープンレジャーコストは絶対的なコストではなく手数料レベルで評価されます。
Referenceトランザクション(ほとんどのトランザクション)
最少コスト
1024 drop
、手数料レベルで512倍のコスト
4つの署名を持つマルチシグトランザクション
最少コスト
50240 drop
、手数料レベルで512倍のコスト
Key Resetトランザクション
最少コスト0 (事実上無限)、手数料レベルなし(事実上無限)
32バイトのプリイメージ付きのEscrowFinishトランザクション
最少コスト3500、手数料レベルで256倍のコスト
トランザクションコストの問い合わせ
トランザクションコストの問い合わせには、以下の2つの方法があります。
server_infoコマンド (人を対象とする)
validated_ledger.base_fee_xrp
で前のレジャー時点のスケーリングされていない最低XRPコストを10進数XRP形式で取得できます。
実際のコストを算出するには、base_fee_xrp
にload_factor
(サーバーの現在の負荷レベル)を掛けます。
現在のトランザクションコスト
XRP = base_fee_xrp × load_factor
server_stateコマンド (マシンを対象とする)
rippled
の内部負荷計算の内容をそのまま表示します。
有効負荷率はload_base
に対するload_factor
の割合です。validated_ledger.base_fee
がXRPの最低トランザクションコスト(drop単位)です。
現在のトランザクションコスト
drop = (base_fee × load_factor) ÷ load_base
この設計により、rippled
では整数のみでトランザクションコストを計算でき、サーバー負荷の微調整も可能です。FeeEscalation Amendment
が有効の場合は、fee
メソッドでオープンレジャーコストを確認できます。
トランザクションコストの指定
署名されたすべてのトランザクションには、Fee
フィールドにトランザクションコストを含める必要があります。
XRP Ledgerはトランザクションを署名されたとおりに実行するため、指定したとおりのXRPがすべてのトランザクションで消却されます。
トランザクションに署名する前に、現在の負荷ベースのトランザクションコストを調べることが推奨されます。
トランザクションコストが高い場合は低下するまで待ったり、少し高めのトランザクションコストを指定できます。
オンラインでトランザクションに署名する場合は、Fee
フィールドを省略し、rippled
またはクライアントライブラリが現在の要件に基づいて自動的に入力することができます。
ただし、この方法には以下の欠点と制限事項があります。
ネットワークのトランザクションコストが上昇した場合、トランザクションが承認されない可能性がある。
署名するトランザクションの
Fee
フィールドの正確な値が事前にわからない。オフラインのマシンから現在のトランザクションコストを調べることができない。
マルチシグの場合、トランザクションコストの自動指定ができない。
トランザクションコストと失敗したトランザクション
トランザクションコストの目的はXRP Ledgerピアツーピアネットワークを過度な負荷から保護することであるため、トランザクションが成功するかどうかにかかわらず、ネットワークに分散されるすべてのトランザクションにコストが適用されます。
ただし、トランザクションコストは、トランザクションが実際に検証済みレジャーに含められた場合にのみ、送信者のXRP残高から差し引かれます。
rippled
サーバーは、失敗したトランザクションをtec
ステータスコードとともにレジャーに含めようとします。
トランザクションの失敗が確定である場合、ネットワークへの中継は行われず、誰のXRP残高にも影響しません。
送信側アカウントにXRPトランザクションコストを支払うのに十分なXRP残高がない場合、rippled
サーバーはエラーコードterINSUF_FEE_B
でトランザクションを拒否します。
トランザクションがすでにネットワークに配信されている場合は、結果コードtecINSUFF_FEE
が発生し、アカウントからは可能な限りすべてのXRPが支払われます。
Key Resetトランザクション
特殊なケースとして、アカウントのlsfPasswordSpent
フラグが無効な限り、そのアカウントはトランザクションコスト0
でSetRegularKey
トランザクションを送信できます。
このトランザクションにはアカウントのマスターキーペアによる署名が必要で、送信するとlsfPasswordSpent
フラグが有効になります。
この機能は、レギュラーキーが危害を受けた場合に、使用可能なXRPの有無にかかわらずアカウントを復元できるように設計されています。
lsfPasswordSpent
フラグは、マスターキーペアによって署名されたSetRegularKey
トランザクションを送信すると有効になり、アカウントでXRPの支払いが受け入れられると再び無効になります。
FeeEscalation Amendment
が有効な場合、rippled
は他のトランザクションよりもKey Reset
トランザクションを優先します。
トランザクションコストの変更
XRP Ledgerは、XRPの長期的な価値変動に備え、最低トランザクションコストを変更する仕組みを備えています。
変更にはすべて、コンセンサスプロセスによる承認が必要です。
トランザクションキュー
トランザクションキューの役割
rippled
サーバーでは、各レジャーに設定された目標トランザクション数に基づき、オープンレジャーコストを適用しています。
レジャーのサイズがこの目標を超える場合、トランザクションコストが自動的に上昇します。
このシステムにより、コストが高くなったトランザクションは、支払い不能となりキューへと移され、次のレジャーでの処理を待ちます。
コンセンサスプロセスとの統合
トランザクションキューはコンセンサスプロセスにおいて中心的な役割を担います。
コンセンサスラウンド1
バリデータは次のレジャーバージョンに含めるトランザクションセットを提案します。
未提案のトランザクションは候補のキューに保持されます。
コンセンサスラウンド2
バリデータが提案からトランザクションを削除する時、これらはキューに戻されます。
コンセンサスラウンドN
十分な合意が得られるまで、バリデータ間での交渉が続けられます。
検証
複数のサーバが同一のレジャーを構築し、そのレジャーが正しいと検証します。
次の提案の作成
バリデータはキューに入っているトランザクションを使って次のレジャーの提案を準備します。
キューへの追加
提案レジャーが既にいっぱいの場合、新たなトランザクションは後続のレジャー用キューに追加されます。
このプロセスは、新しいトランザクションが継続的に受け入れられる一方で、前のレジャーのコンセンサスプロセスが進行中に次の提案の準備が進められるため、並行して実行されます。
この連続した動作により、システムは効率的かつ迅速にトランザクションを処理できるように設計されています。
トランザクションキューの制約事項
トランザクションがキューに入るための条件は、以下のように定められています。
トランザクションの形式と承認
トランザクションは適切な形式であり、有効な署名が必要です。
AccountTxnIDの制約
AccountTxnID
フィールドが指定されているトランザクションはキューに追加できません。
トランザクションの数制限
1つの送信側アドレスからは、同時に最大10個のトランザクションがキューに入れられます。
XRP保有量
送信者はキュー内の全トランザクションのトランザクションコストをカバーするだけのXRPを持っている必要があり、これにはアカウントの基本準備金(現時点で
10 XRP
)も含まれます。
LastLedgerSequenceフィールド
指定されている場合、その値は現在のレジャーインデックス+2以上でなければなりません。
シーケンスの依存関係
あるトランザクションが承認される方法に依存している場合、そのアドレスからの他のトランザクションは後続でなければキューに入れられません。
手数料の平均化
キューに複数のトランザクションが存在する場合、新しいトランザクションは既存のトランザクションをオープンレジャーに押し出すために十分な手数料を含む必要があります。
これにより、送信側アドレスは必要な全トランザクションコストをカバーできるだけのXRPを保持していなければなりません。
キュー内の順序
トランザクションは、支払われたトランザクションコストの高さに基づいてキュー内でランク付けされます。
このランクは、絶対的なXRPコストではなく、関連するトランザクションタイプの最小コストに対する相対値で決定されます。
同額のトランザクションコストを持つトランザクションが複数ある場合は、受け取った順にランク付けされます。
また、同一送信者のトランザクションはシーケンス番号によって順にソートされます。
これらのプロセスと制約により、rippled
サーバはトランザクションを効率的かつ公平に処理し、ネットワークの安定性と信頼性を保つことを目指しています。
最後に
今回はXRP LEDGERのコンセプトの1つである「トランザクション」についてまとめました。
これからも他のブロックチェーンやサービスについてもまとめていきます。
情報を見逃したくないという方はぜひ以下の購読ボタンを教えてください。
更新があった時、登録しているメールアドレス宛に通知が飛ぶようになります。
また、拡散もしてくれると嬉しいです🙇♂️