条件に基づくEscrowの送信

1.条件とフルフィルメントの生成

XRP Ledger EscrowにはPREIMAGE-SHA-256 Crypto-Conditions が必要です。条件とフルフィルメントを適切なフォーマットで計算するには、five-bells-condition などのCrypto-conditionsライブラリを使用する必要があります。フルフィルメントについては、以下のフルフィルメントを生成するためのメソッドのいずれかを使用することが推奨されます。

  • 暗号論的に安全な乱数ソースを使用して、32バイト以上のランダムバイトを生成します。
  • Interledger ProtocolのPSK仕様 に従い、ILPパケットのHMAC-SHA-256をフルフィルメントとして使用します。

ランダムなフルフィルメントと条件のJavaScriptコードの例:

const cc = require('five-bells-condition')
const crypto = require('crypto')

const preimageData = crypto.randomBytes(32)
const myFulfillment = new cc.PreimageSha256()
myFulfillment.setPreimage(preimageData)

const condition = myFulfillment.getConditionBinary().toString('hex').toUpperCase()
console.log('Condition:', condition)
// (Random hexadecimal, 72 chars in length)

// keep secret until you want to finish executing the held payment:
const fulfillment = myFulfillment.serializeBinary().toString('hex').toUpperCase()
console.log('Fulfillment:', fulfillment)
// (Random hexadecimal, 78 chars in length)

後で使用できるように条件とフルフィルメントを保存します。保留中の支払いの実行が完了するまでは、フルフィルメントを公開しないでください。フルフィルメントを知っていれば誰でもEscrowを終了でき、保留中の資金を指定された送金先にリリースできます。

2.リリース時刻または取消し時刻の計算

条件付きEscrowトランザクションには、CancelAfterフィールドとFinishAfterフィールドのいずれか、または両方が含まれている必要があります。CancelAfterフィールドを使用すると、指定の時刻までに条件を満たすことができなかった場合に送金元へXRPを返金できます。FinishAfterフィールドに指定される時刻より前の時間は、正しいフルフィルメントが送信されてもEscrowを実行できません。いずれのフィールドでも、将来の時刻を指定する必要があります。

CancelAfterの時刻を24時間先に設定する例:

const rippleOffset = 946684800
const CancelAfter = Math.floor(Date.now() / 1000) + (24*60*60) - rippleOffset
console.log(CancelAfter)
// Example:556927412

警告: XRP Ledgerでは、時刻をRippleエポック(2000-01-01T00:00:00Z)以降の経過秒数として指定する必要があります。CancelAfterまたはFinishAfterフィールドで、UNIX時刻を同等のRipple時刻に変換せずに使用すると、ロック解除時刻が30年先に設定されることになります。

3.EscrowCreateトランザクションの送信

EscrowCreateトランザクション署名して送信します。トランザクションのConditionフィールドを、保留中の支払いがリリースされる時刻に設定します。Destinationを受取人に設定します。受取人と送金元のアドレスは同じでもかまいません。前の手順で算出したCancelAfterまたはFinishAfterの時刻も指定します。Amountを、EscrowするXRP、drop単位の合計額に設定します。

Caution: Never submit a secret key to a server you do not control. Do not send a secret key unencrypted over the network.

要求:

{
  "id": 1,
  "command": "submit",
  "secret": "s████████████████████████████",
  "tx_json": {
    "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
    "TransactionType": "EscrowCreate",
    "Amount": "100000",
    "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
    "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
    "CancelAfter": 556927412
  }
}

応答:

{
  "id": 1,
  "status": "success",
  "type": "response",
  "result": {
    "engine_result": "tesSUCCESS",
    "engine_result_code": 0,
    "engine_result_message": "The transaction was applied. Only final in a validated ledger.",
    "tx_blob": "120001228000000024000000052024213209B46140000000000186A068400000000000000A732103E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61744730450221008AC8BDC2151D5EF956197F0E6E89A4F49DEADC1AC38367870E444B1EA8D88D97022075E31427B455DFF87F0F22B849C71FC3987A91C19D63B6D0242E808347EC8A8F701127A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD81012081149A2AA667E1517EFA8A6B552AB2EDB859A99F26B283144B4E9C06F24296074F7BC48F92A97916C6DC5EA9",
    "tx_json": {
      "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
      "Amount": "100000",
      "CancelAfter": 556927412,
      "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
      "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
      "Fee": "10",
      "Flags": 2147483648,
      "Sequence": 5,
      "SigningPubKey": "03E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61",
      "TransactionType": "EscrowCreate",
      "TxnSignature": "30450221008AC8BDC2151D5EF956197F0E6E89A4F49DEADC1AC38367870E444B1EA8D88D97022075E31427B455DFF87F0F22B849C71FC3987A91C19D63B6D0242E808347EC8A8F",
      "hash": "E22D1F6EB006CAD35E0DBD3B4F3748427055E4C143EBE95AA6603823AEEAD324"
    }
  }
}

4.検証の待機

本番環境のネットワークやRipple Test Netでは、レジャーが自動的に閉鎖するまでに4~7秒かかる場合があります。

スタンドアロンモードでrippledを実行している場合は、ledger_acceptメソッドを使用してレジャーを手動で閉鎖します。

5.Escrowが作成されたことの確認

トランザクションの識別用ハッシュを指定したtxメソッドを使用して、トランザクションの最終ステータスを確認します。特に、Escrowレジャーオブジェクトが作成されたことを示すCreatedNodeをトランザクションメタデータで探します。

要求:

{
  "id": 3,
  "command": "tx",
  "transaction": "E22D1F6EB006CAD35E0DBD3B4F3748427055E4C143EBE95AA6603823AEEAD324"
}

応答:

{
  "id": 3,
  "status": "success",
  "type": "response",
  "result": {
    "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
    "Amount": "100000",
    "CancelAfter": 556927412,
    "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
    "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
    "Fee": "10",
    "Flags": 2147483648,
    "Sequence": 5,
    "SigningPubKey": "03E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61",
    "TransactionType": "EscrowCreate",
    "TxnSignature": "30450221008AC8BDC2151D5EF956197F0E6E89A4F49DEADC1AC38367870E444B1EA8D88D97022075E31427B455DFF87F0F22B849C71FC3987A91C19D63B6D0242E808347EC8A8F",
    "date": 556841101,
    "hash": "E22D1F6EB006CAD35E0DBD3B4F3748427055E4C143EBE95AA6603823AEEAD324",
    "inLedger": 1772019,
    "ledger_index": 1772019,
    "meta": {
      "AffectedNodes": [
        {
          "ModifiedNode": {
            "LedgerEntryType": "AccountRoot",
            "LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8",
            "PreviousTxnID": "52C4F626FE6F33699B6BE8ADF362836DDCE9B0B1294BFAA15D65D61501350BE6",
            "PreviousTxnLgrSeq": 1771204
          }
        },
        {
          "ModifiedNode": {
            "FinalFields": {
              "Flags": 0,
              "Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
              "RootIndex": "4B4EBB6D8563075813D47491CC325865DFD3DC2E94889F0F39D59D9C059DD81F"
            },
            "LedgerEntryType": "DirectoryNode",
            "LedgerIndex": "4B4EBB6D8563075813D47491CC325865DFD3DC2E94889F0F39D59D9C059DD81F"
          }
        },
        {
          "ModifiedNode": {
            "FinalFields": {
              "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
              "Balance": "9999798970",
              "Flags": 0,
              "OwnerCount": 1,
              "Sequence": 6
            },
            "LedgerEntryType": "AccountRoot",
            "LedgerIndex": "5F3B7107F4B524367A173A2B0EAB66E8CC4D2178C1B0C0528CB2F73A8B6BF254",
            "PreviousFields": {
              "Balance": "9999898980",
              "OwnerCount": 0,
              "Sequence": 5
            },
            "PreviousTxnID": "52C4F626FE6F33699B6BE8ADF362836DDCE9B0B1294BFAA15D65D61501350BE6",
            "PreviousTxnLgrSeq": 1771204
          }
        },
        {
          "CreatedNode": {
            "LedgerEntryType": "Escrow",
            "LedgerIndex": "E2CF730A31FD419382350C9DBD8DB7CD775BA5AA9B97A9BE9AB07304AA217A75",
            "NewFields": {
              "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
              "Amount": "100000",
              "CancelAfter": 556927412,
              "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
              "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"
            }
          }
        }
      ],
      "TransactionIndex": 0,
      "TransactionResult": "tesSUCCESS"
    },
    "validated": true
  }
}

6.EscrowFinishトランザクションの送信

FinishAfterの時刻が経過した後で資金のリリースを実行するEscrowFinishトランザクション署名して送信します。トランザクションのOwnerフィールドにEscrowCreateトランザクションのAccountアドレスを設定し、OfferSequence にEscrowCreateトランザクションのSequence番号を設定します。ConditionフィールドとFulfillmentフィールドに、ステップ1で生成した条件値とフルフィルメント値をそれぞれ16進数で設定します。フルフィルメントのサイズ(バイト数)に基づいてFeeトランザクションコスト)の値を設定します。条件付きEscrowFinishでは、少なくとも330 drop(XRP)と、フルフィルメントのサイズで16バイトごとに10 dropが必要です。

注記: EscrowCreateトランザクションにFinishAfterフィールドが含まれている場合、Escrowの条件として正しいフルフィルメントを指定しても、この時刻よりも前の時点ではこのトランザクションを実行できません。前に閉鎖されたレジャーの閉鎖時刻がFinishAfterの時刻よりも前である場合、EscrowFinishトランザクションは結果コードtecNO_PERMISSIONで失敗します。

Escrowが有効期限切れの場合は、Escrowの取消しだけが可能です。

Caution: Never submit a secret key to a server you do not control. Do not send a secret key unencrypted over the network.

{
  "id": 4,
  "command": "submit",
  "secret": "s████████████████████████████",
  "tx_json": {
    "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
    "TransactionType": "EscrowFinish",
    "Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
    "OfferSequence": 5,
    "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
    "Fulfillment": "A0228020D280D1A02BAD0D2EBC0528B92E9BF37AC3E2530832C2C52620307135156F1048",
    "Fee": "500"
  }
}

応答:

{
  "id": 4,
  "status": "success",
  "type": "response",
  "result": {
    "engine_result": "tesSUCCESS",
    "engine_result_code": 0,
    "engine_result_message": "The transaction was applied. Only final in a validated ledger.",
    "tx_blob": "120002228000000024000000062019000000056840000000000001F4732103E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B617446304402207DE4EA9C8655E75BA01F96345B3F62074313EB42C15D9C4871E30F02202D2BA50220070E52AD308A31AC71E33BA342F31B68D1D1B2A7A3A3ED6E8552CA3DCF14FBB2701024A0228020D280D1A02BAD0D2EBC0528B92E9BF37AC3E2530832C2C52620307135156F1048701127A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD81012081149A2AA667E1517EFA8A6B552AB2EDB859A99F26B282149A2AA667E1517EFA8A6B552AB2EDB859A99F26B2",
    "tx_json": {
      "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
      "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
      "Fee": "500",
      "Flags": 2147483648,
      "Fulfillment": "A0228020D280D1A02BAD0D2EBC0528B92E9BF37AC3E2530832C2C52620307135156F1048",
      "OfferSequence": 5,
      "Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
      "Sequence": 6,
      "SigningPubKey": "03E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61",
      "TransactionType": "EscrowFinish",
      "TxnSignature": "304402207DE4EA9C8655E75BA01F96345B3F62074313EB42C15D9C4871E30F02202D2BA50220070E52AD308A31AC71E33BA342F31B68D1D1B2A7A3A3ED6E8552CA3DCF14FBB2",
      "hash": "0E88368CAFC69A722ED829FAE6E2DD3575AE9C192691E60B5ACDF706E219B2BF"
    }
  }
}

トランザクションの識別用hash値をメモしておきます。これにより、検証済みレジャーバージョンに記録されるときにその最終ステータスを確認できます。

7.検証の待機

本番環境のネットワークやRipple Test Netでは、レジャーが自動的に閉鎖するまでに4~7秒かかる場合があります。

スタンドアロンモードでrippledを実行している場合は、ledger_acceptメソッドを使用してレジャーを手動で閉鎖します。

8.最終結果の確認

EscrowFinishトランザクションの識別用ハッシュを指定したtxメソッドを使用して、トランザクションの最終ステータスを確認します。特にトランザクションメタデータ内で、エスクローに預託された支払いの送金先のModifiedNode(タイプがAccountRoot)を確認します。オブジェクトのFinalFieldsに、BalanceフィールドのXRP返金額の増分が表示されている必要があります。

要求:

{
  "id": 20,
  "command": "tx",
  "transaction": "0E88368CAFC69A722ED829FAE6E2DD3575AE9C192691E60B5ACDF706E219B2BF"
}

応答:

{
  "id": 20,
  "status": "success",
  "type": "response",
  "result": {
    "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
    "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
    "Fee": "500",
    "Flags": 2147483648,
    "Fulfillment": "A0228020D280D1A02BAD0D2EBC0528B92E9BF37AC3E2530832C2C52620307135156F1048",
    "OfferSequence": 2,
    "Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
    "Sequence": 4,
    "SigningPubKey": "03E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61",
    "TransactionType": "EscrowFinish",
    "TxnSignature": "3045022100925FEBE21C2E57F81C472A4E5869CAB1D0164C472A46532F39F6F9F7ED6846D002202CF9D9063ADC4CC0ADF4C4692B7EE165C5D124CAA855649389E245D993F41D4D",
    "date": 556838610,
    "hash": "0E88368CAFC69A722ED829FAE6E2DD3575AE9C192691E60B5ACDF706E219B2BF",
    "inLedger": 1771204,
    "ledger_index": 1771204,
    "meta": {
      "AffectedNodes": [
        {
          "ModifiedNode": {
            "FinalFields": {
              "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
              "Balance": "400100000",
              "Flags": 0,
              "OwnerCount": 0,
              "Sequence": 1
            },
            "LedgerEntryType": "AccountRoot",
            "LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8",
            "PreviousFields": {
              "Balance": "400000000"
            },
            "PreviousTxnID": "795CBC8AFAAB9DC7BD9944C7FAEABF9BB0802A84520BC649213AD6A2C3256C95",
            "PreviousTxnLgrSeq": 1770775
          }
        },
        {
          "ModifiedNode": {
            "FinalFields": {
              "Flags": 0,
              "Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
              "RootIndex": "4B4EBB6D8563075813D47491CC325865DFD3DC2E94889F0F39D59D9C059DD81F"
            },
            "LedgerEntryType": "DirectoryNode",
            "LedgerIndex": "4B4EBB6D8563075813D47491CC325865DFD3DC2E94889F0F39D59D9C059DD81F"
          }
        },
        {
          "ModifiedNode": {
            "FinalFields": {
              "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
              "Balance": "9999898980",
              "Flags": 0,
              "OwnerCount": 0,
              "Sequence": 5
            },
            "LedgerEntryType": "AccountRoot",
            "LedgerIndex": "5F3B7107F4B524367A173A2B0EAB66E8CC4D2178C1B0C0528CB2F73A8B6BF254",
            "PreviousFields": {
              "Balance": "9999899480",
              "OwnerCount": 1,
              "Sequence": 4
            },
            "PreviousTxnID": "5C2A1E7B209A7404D3722A010D331A8C1C853109A47DDF620DE5E3D59F026581",
            "PreviousTxnLgrSeq": 1771042
          }
        },
        {
          "DeletedNode": {
            "FinalFields": {
              "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
              "Amount": "100000",
              "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
              "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
              "FinishAfter": 556838185,
              "Flags": 0,
              "OwnerNode": "0000000000000000",
              "PreviousTxnID": "795CBC8AFAAB9DC7BD9944C7FAEABF9BB0802A84520BC649213AD6A2C3256C95",
              "PreviousTxnLgrSeq": 1770775
            },
            "LedgerEntryType": "Escrow",
            "LedgerIndex": "DC524D17B3F650E7A215B332F418E54AE59B0DFC5392E74958B0037AFDFE8C8D"
          }
        }
      ],
      "TransactionIndex": 1,
      "TransactionResult": "tesSUCCESS"
    },
    "validated": true
  }
}