最終更新:
編集

Checkの送信

Checkの送信は、指定受取人にあなたからの支払いを引き出す許可を与えることに似ています。このプロセスの結果、受取人が後で現金化できるレジャーのCheckオブジェクトが作成されます。

多くの場合、CheckではなくPaymentが送信されます。これは、Paymentでは1つのステップで受取人に直接送金できるためです。ただし、指定受取人がDepositAuthを使用している場合はPaymentを直接送信できないため、代替手段としてCheckが適切です。

このチュートリアルでは、架空の会社BoxSend SG(XRP LedgerアドレスはrBXsgNkPcDN2runsvWmwxk3Lh97zdgo9za)が架空の暗号資産コンサルタント会社Grand Payments(XRP LedgerアドレスはrGPnRH1EBpHeTF2QG8DCAgM7z5pb75LAis)に、コンサルティング料を支払う例を取り上げます。Grand PaymentsはXRPでの支払いを望んでいますが、税務処理と規制対応を簡素化するため、明示的に承認した支払いのみを受け入れます。

XRP Ledgerの外部でGrand PaymentsはBoxSend SGに請求書(IDは46060241FABCF692D4D934BA2A6C4427CD4279083E38C77CBE642243E43BE291)を送り、Grand PaymentsのXRP Ledgerアドレス(rGPnRH1EBpHeTF2QG8DCAgM7z5pb75LAis)宛てに100 XRPのCheckを送信するよう要求します。

前提条件

このチュートリアルでCheckを送信するには、以下が必要です。

  • Checkの送信元である資金供給のあるアカウントのアドレスシークレットキー
    • XRP Ledger Test Net Faucetを使用して、10,000 Test Net XRPを保有する資金供給のあるアドレスおよびシークレットを取得できます。
  • Checkを受領する資金供給のあるアカウントのアドレス
  • トランザクションに安全に署名できる手段
  • クライアントライブラリまたはHTTPライブラリ、WebSocketライブラリなど。

1. CheckCreateトランザクションの準備

Checkの額と、Checkを現金化できる当事者を決定します。CheckCreateトランザクションのフィールドの値を決定します。以下のフィールドは必要最小限のフィールドです。その他のフィールドはオプションまたは署名時に自動入力できるフィールドです。

フィールド説明
TransactionType文字列このフィールドには文字列CheckCreateを使用します。
Account文字列(アドレス)Checkを作成する送金元のアドレス。(あなたのアドレスです。)
Destination文字列(アドレス)Checkを換金できる指定受取人のアドレス。
SendMax文字列またはオブジェクト(額)Checkが現金化されるときに送金元から引き出される最大額。XRPの場合、XRPのdrop数を示す文字列を使用します。トークンの場合、currencyissuer、およびvalue フィールドを含むオブジェクトを使用します。詳細は、通貨額の指定をご覧ください。受取人がXRP以外の通貨で正確な額のCheckを換金できるようにし、かつ送金手数料を含めるには、送金手数料分の追加パーセンテージを必ず指定してください。(たとえば受取人が送金手数料2%でCheckをイシュアーからの100 CADに現金化できるようにするには、SendMaxをイシュアーからの102 CADに設定する必要があります。)

CheckCreateトランザクションの準備の例

以下の例は、BoxSend SG(rBXsgNkPcDN2runsvWmwxk3Lh97zdgo9za)がGrand Payments(rGPnRH1EBpHeTF2QG8DCAgM7z5pb75LAis)宛てに作成した100 XRPのCheckです。追加(オプション)のメタデータとして、BoxSend SGはGrand Paymentsの請求書のIDを追加しています。これによりGrand PaymentsはこのCheckがどの請求書に対する支払いかを確認できます。

'use strict'
const RippleAPI = require('ripple-lib').RippleAPI

// This example connects to a public Test Net server
const api = new RippleAPI({server: 'wss://s.altnet.rippletest.net:51233'})
api.connect().then(() => {
  console.log('Connected')

  const sender = 'rBXsgNkPcDN2runsvWmwxk3Lh97zdgo9za'
  const receiver = 'rGPnRH1EBpHeTF2QG8DCAgM7z5pb75LAis'
  const options = {
    // Allow up to 60 ledger versions (~5 min) instead of the default 3 versions
    // before this transaction fails permanently.
    "maxLedgerVersionOffset": 60
  }
  return api.prepareCheckCreate(sender, {
    "destination": receiver,
    "sendMax": {
      "currency": "XRP",
      "value": "100" // RippleAPI uses decimal XRP, not integer drops
    },
    "invoiceID": "46060241FABCF692D4D934BA2A6C4427CD4279083E38C77CBE642243E43BE291"
  }, options)

}).then(prepared => {
  console.log("txJSON:", prepared.txJSON);

// Disconnect and return
}).then(() => {
  api.disconnect().then(() => {
    console.log('Disconnected')
    process.exit()
  })
}).catch(console.error)


// Example output:
//
// Connected
// txJSON: {"Account":"rBXsgNkPcDN2runsvWmwxk3Lh97zdgo9za",
//  "TransactionType":"CheckCreate",
//  "Destination":"rGPnRH1EBpHeTF2QG8DCAgM7z5pb75LAis",
//  "SendMax":"100000000",
//  "Flags":2147483648,
//  "InvoiceID": "46060241FABCF692D4D934BA2A6C4427CD4279083E38C77CBE642243E43BE291",
//  "LastLedgerSequence":7835917,"Fee":"12","Sequence":2}
// Disconnected

2. CheckCreateトランザクションへの署名

トランザクションに署名する最も安全な方法は、クライアントライブラリを使用してローカルで署名することです。signメソッドを使用してトランザクションに署名することもできますが、その場合は信頼できる暗号化された接続を使用するか、ローカル接続を使用して、自分が管理しているサーバに対してのみに行うようにしてください。

いずれの場合も、後のために、署名したトランザクションの識別用ハッシュを書き留めておいてください。

リクエストの例

'use strict'
const RippleAPI = require('ripple-lib').RippleAPI

// Can sign offline if the txJSON has all required fields
const api = new RippleAPI()

const txJSON = '{"Account":"rBXsgNkPcDN2runsvWmwxk3Lh97zdgo9za", \
  "TransactionType":"CheckCreate", \
  "Destination":"rGPnRH1EBpHeTF2QG8DCAgM7z5pb75LAis", \
  "SendMax":"100000000", \
  "Flags":2147483648, \
  "LastLedgerSequence":7835923, \
  "Fee":"12", \
  "Sequence":2}'

// Be careful where you store your real secret.
const secret = 's████████████████████████████'

const signed = api.sign(txJSON, secret)

console.log("tx_blob is:", signed.signedTransaction)
console.log("tx hash is:", signed.id)

レスポンスの例

tx_blob is: 12001022800000002400000002201B0077911368400000000000000C694000000005F5E100732103B6FCD7FAC4F665FE92415DD6E8450AD90F7D6B3D45A6CFCF2E359045FF4BB400744630440220181FE2F945EBEE632966D5FB03114611E3047ACD155AA1BDB9DF8545C7A2431502201E873A4B0D177AB250AF790CE80621E16F141506CF507586038FC4A8E95887358114735FF88E5269C80CD7F7AF10530DAB840BBF6FDF8314A8B6B9FF3246856CADC4A0106198C066EA1F9C39
tx hash is: C0B27D20669BAB837B3CDF4B8148B988F17CE1EF8EDF48C806AE9BF69E16F441

3.署名済みトランザクションの送信

前のステップで署名したトランザクションblobをrippledサーバに送信します。これはrippledサーバを実行していなくても安全にできます。レスポンスには仮の結果が含まれ、それはtesSUCCESSであるべきですが、この結果は通常は最終的なものではありませんキューに入れられたトランザクションは通常、次のオープンレジャーのバージョンに含まれるからです(通常、送信から約10秒後となります)。

ヒント: 仮の結果が tefMAX_LEDGER であった場合、そのトランザクションのLastLedgerSequenceパラメータが現在のレジャー番号よりも低いため、そのトランザクションが失敗しています。これは、トランザクション情報を準備してから送信するまでに、予想されるレジャーのバージョン数よりも長くかかった場合に起こります。このような場合は、LastLedgerSequenceの値を大きくしてステップ1からやり直してください。

リクエストの例

'use strict'
const RippleAPI = require('ripple-lib').RippleAPI

// This example connects to a public Test Net server
const api = new RippleAPI({server: 'wss://s.altnet.rippletest.net:51233'})
api.connect().then(() => {
  console.log('Connected')

  const tx_blob = "12001022800000002400000002201B0077911368400000000000000"+
    "C694000000005F5E100732103B6FCD7FAC4F665FE92415DD6E8450AD90F7D6B3D45A6"+
    "CFCF2E359045FF4BB400744630440220181FE2F945EBEE632966D5FB03114611E3047"+
    "ACD155AA1BDB9DF8545C7A2431502201E873A4B0D177AB250AF790CE80621E16F1415"+
    "06CF507586038FC4A8E95887358114735FF88E5269C80CD7F7AF10530DAB840BBF6FD"+
    "F8314A8B6B9FF3246856CADC4A0106198C066EA1F9C39"

  return api.submit(tx_blob)
}).then(response => {
  console.log("Preliminary transaction result:", response.resultCode)

// Disconnect and return
}).then(() => {
  api.disconnect().then(() => {
    console.log('Disconnected')
    process.exit()
  })
}).catch(console.error)

レスポンスの例

Connected
Preliminary transaction result: tesSUCCESS
Disconnected

4.検証の待機

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

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

5.最終結果の確認

トランザクションのステータスを確認するには、CheckCreateトランザクションの識別用ハッシュを指定したtxメソッドを使用します。トランザクションメタデータで、トランザクションが成功したことを示す"TransactionResult": "tesSUCCESS"フィールドを探し、またこの結果が最終結果であることを示す"validated": trueフィールドを結果で探します。

トランザクションのメタデータで、LedgerEntryType"Check"CreatedNodeオブジェクトを探します。これは、トランザクションによりCheckレジャーオブジェクトが作成されたことを示します。このオブジェクトのLedgerIndex がCheckのIDです。以下の例ではCheckのIDは84C61BE9B39B2C4A2267F67504404F1EC76678806C1B901EA781D1E3B4CE0CD9です。

リクエストの例

'use strict'
const RippleAPI = require('ripple-lib').RippleAPI
const decodeAddress = require('ripple-address-codec').decodeAddress;
const createHash = require('crypto').createHash;

// This example connects to a public Test Net server
const api = new RippleAPI({server: 'wss://s.altnet.rippletest.net:51233'})
api.connect().then(() => {
  console.log('Connected')

  const tx_hash = "C0B27D20669BAB837B3CDF4B8148B988F17CE1EF8EDF48C806AE9BF69E16F441"

  return api.getTransaction(tx_hash)
}).then(response => {
  console.log("Final transaction result:", response)

  // Re-calculate checkID to work around issue ripple-lib#876
  const checkIDhasher = createHash('sha512')
  checkIDhasher.update(Buffer.from('0043', 'hex'))
  checkIDhasher.update(new Buffer(decodeAddress(response.address)))
  const seqBuf = Buffer.alloc(4)
  seqBuf.writeUInt32BE(response.sequence, 0)
  checkIDhasher.update(seqBuf)
  const checkID = checkIDhasher.digest('hex').slice(0,64).toUpperCase()
  console.log("Calculated checkID:", checkID)

// Disconnect and return
}).then(() => {
  api.disconnect().then(() => {
    console.log('Disconnected')
    process.exit()
  })
}).catch(console.error)

レスポンスの例

Connected
Final transaction result: { type: 'checkCreate',
  address: 'rBXsgNkPcDN2runsvWmwxk3Lh97zdgo9za',
  sequence: 2,
  id: 'C0B27D20669BAB837B3CDF4B8148B988F17CE1EF8EDF48C806AE9BF69E16F441',
  specification:
   { destination: 'rGPnRH1EBpHeTF2QG8DCAgM7z5pb75LAis',
     sendMax: { currency: 'XRP', value: '100' } },
  outcome:
   { result: 'tesSUCCESS',
     timestamp: '2018-03-27T20:47:40.000Z',
     fee: '0.000012',
     balanceChanges: { rBXsgNkPcDN2runsvWmwxk3Lh97zdgo9za: [Array] },
     orderbookChanges: {},
     ledgerVersion: 7835887,
     indexInLedger: 0 } }
Calculated checkID: CEA5F0BD7B2B5C85A70AE735E4CE722C43C86410A79AB87C11938AA13A11DBF9
Disconnected