ILP(Interledger)书信教程

备注:下文只做简单的翻译,没有做校验

书信教程

在本教程中,您将学习Interledger的一些基本概念以及使ILP成为可应用于任何支付网络的协议的一些标准。

在教程中,我们将运行一个非常基本的在线服务,销售...信件,并写一个客户来支付他们。

每次您访问该网站时,您都会收到一封来自A - Z的随机信件。但是,如果您没有支付费用,则会提示您先付款,并且只能在收到您的信后才能收到您的信件。

为了使我们的信得到付款,我们还将实施一个简单的客户来付款。

本教程使用XRP Testnet作为基础支付网络,但如果存在工作分类帐插件,则可以使用任何支付网络。查看GitHub上正在开发的现有插件

开始之前你需要什么:

  • 一台带互联网连接的电脑
  • 安装了NodeJS版本7或更高版本
  • 命令行终端的基本知识
  • 基本的JavaScript知识
  • (可选)安装了git

你会学到什么:

  • 关于有条件支付和散列时间锁定协议
  • 关于ILP地址
  • 关于Ledger插件接口(LPI)以及如何配置和使用插件
  • 如何构建一个接受Interledger支付的服务
  • 如何构建发送Interledger付款的基本付款客户端
  • (奖金)如何构建一个客户端代理,根据需要自动支付请求

注意:在执行本教程时,有时会按照说明操作导致出现错误。这可能是故意的。请继续阅读以了解错误存在的原因以及如何解决该错误。

背景

为了说明基本的ILP概念,我们将建立一个简单的在线服务,销售字母表中的字母并通过ILP接受付款。我们没有将这个想法申请专利,所以如果你部署这个并成为即时的百万富翁,请大度慷慨。

要做到这一点,我们需要一个由商店运行服务器,还有一个由客户运行的客户

服务器

服务器将执行两个功能:

  1. 它将托管一个HTTP服务,客户可以根据每个请求获取一个新的随机字母。
  2. 它将监听与即将购买的信件相关的即将到来的ILP付款,完成这些付款以接受付款,并向付款人提供可用来索取信件的代币。

一旦我们完成了教程,服务器将执行以下步骤:

  1. 连接到一个帐户并监控它收到的付款
  2. 启动Web服务器以接受信件请求
  3. 处理请求,存储结果,并向客户提供如何为请求付款和获得结果的详细信息
  4. 处理与先前请求相关的收款,并向付款人提供使用付款证明令牌再次执行请求所需的数据
  5. 处理包含验证付款标记的请求,并返回最初生成的结果

客户

客户将执行ILP付款并输出退回的履行。这可以兑换成原始请求中要求的信件。

一旦我们完成了教程,客户端将执行以下步骤:

  1. 连接到从中发送付款的帐户
  2. 使用原信件请求回复中提供的地址,金额和条件开始付款
  3. 完成付款并返回付款证明标记以及如何使用它来完成原始请求的说明

第1步:获取代码

本教程的代码有两种形式:

  1. 如果你遵循这个教程,你将会从一些非常基本的代码开始,并且会在我们去的时候充实它。
  2. 如果您希望从完成的代码开始,并简单地通过步骤,那么您也可以这样做。

首先让我们得到所有的代码,或者使用Git或者直接下载并解压缩它。

混帐

git clone https://github.com/interledger/tutorials

下载

ZIP档案

假设您已经克隆了存储库,或者解压缩了下载,请在项目的根目录下打开一个终端窗口并切换到letter-shop目录。

您应该看到以下文件:

README.md
completed/
index.md
package-lock.json
package.json
pay.js
plugins.js
proxy.js
shop.js

大多数这些文件只是脚手架,等着你完成我们去。如果您想要完整的文件,以便您可以继续,请切换到complete子目录。

第2步:运行服务器

我们需要做的第一件事是安装我们的依赖关系并启动我们的商店服务器。

我们使用npm进行安装(这可能需要几分钟时间来下载所有依赖项):

npm install

如果您在此处遇到错误,请确保已node安装。

然后我们尝试并启动我们的商店服务器:

node shop.js

但是,我们遇到了麻烦......请继续阅读。

Interledger插件

我们的商店以及我们将在本教程中构建的许多其他组件使用分类帐插件。插件是与特定分类帐上的特定帐户进行对话的一段代码。

由于Interledger将潜在的截然不同的分类账连接在一起,我们需要一个抽象层来隐藏分类账的细节,但是暴露了通过分类账发送和接收资金所需的接口。这是Interledger插件的用途。

Ledger插件公开了一个通用接口(Ledger插件接口),因此无论您的应用程序连接到哪个分类帐,它发送和接收付款的方式都是相同的。

注意:在我们阅读本教程时,我们将使用Ledger插件接口的不同功能。您可以在IL-RFC 0004草案8中找到参考文档

我们已将所有插件配置放入一个名为的文件中plugins.js我们使用的默认插件是XRP Escrow插件,它允许我们连接到XRP Testnet Ledger。

XRP中介插件是围绕一个包装RippleLib,并暴露总帐插件接口(LPI)。在代码中,您会看到'ilp-plugin-xrp-escrow'插件正在配置秘密,帐户,服务器和前缀。前三个值来自XRP Testnet龙头

在您最喜爱的文本编辑器中编辑此文件,并按照代码注释中的说明操作:

  1. 为商店和客户获取不同的XRP Testnet凭证。
  2. 使用这些新凭证配置XRP分类帐插件

重要提示:即使您使用的是完整的教程代码,您也需要对其进行设置,以便您使用自己的测试帐户。

现在尝试再次运行您的服务器。

$ node shop.js

你应该看到像这样的东西:

== Starting the shop server ==

在里面shop.js你会看到我们有一个你配置的分类账插件的实例plugins.js

const plugin = require('./plugins.js').xrp.Shop()

现在我们需要用这个插件做点什么将文字替换// Do something...为以下内容:

console.log(` 1. Connecting to an account to accept payments...`)

plugin.connect().then(function () {
  // Get ledger and account information from the plugin
  const ledgerInfo = plugin.getInfo()
  const account = plugin.getAccount()

  console.log(`    - Connected to ledger: ${ledgerInfo.prefix}`)
  console.log(`    -- Account: ${account}`)
  console.log(`    -- Currency: ${ledgerInfo.currencyCode}`)
  console.log(`    -- CurrencyScale: ${ledgerInfo.currencyScale}`)

  // Convert our cost (10) into the right format given the ledger scale
  const normalizedCost = cost / Math.pow(10, parseInt(ledgerInfo.currencyScale))

  console.log(` 2. Starting web server to accept requests...`)
  console.log(`    - Charging ${normalizedCost} ${ledgerInfo.currencyCode}`)

  // Handle incoming web requests...

  // Handle incoming transfers...

})

停止服务器并使用新代码重新启动它。现在它将使用分类帐插件连接到XRP Testnet分类帐。当它连接时(通常几秒钟),您会在控制台中看到一些记录,提供有关分类帐的信息。

我们可以从插件中获取关于分类账和它使用getInfo()getAccount()方法连接到的账户的大量信息

注:分类帐的货币为XRP,货币等级为6.这意味着要发送1个XRP到此分类帐,我们必须发送1000000(100万滴)的Interledger付款。

Interledger在64位无符号整数上标准化数量。

注意我们如何规范我们服务的成本(10滴),以便能够在XRP(0.00001 XRP)中进行表达以用于显示目的。

重要提示:规模(和精确度)可能是任何金融协议中令人困惑的一个方面,您最好理解您正在使用的分类帐,并确保在您向您发送付款并向用户显示金额时,您将获得您的分摊比例精确的权利。我们决定避免一些复杂性,只使用规模并要求分类账总是以零的精度表示其货币。这使我们只能在协议中使用整数(而不是小数),所以所有的数字都被规范化为这种形式。如果您有兴趣,您可以在项目问题列表中找到详细讨论的决定背后有很多其他技术原因。

现在我们已经连接了插件,并且我们有关于我们将接受付款的帐户所需的信息。

 恭喜,我们的商店正在执行我们的5步计划的第1步!

我们的下一个工作是启动将托管我们的Letter Shop服务的Web服务器。

每当客户访问该网站时,我们都会尝试在URL中查找证明付款标记。如果令牌在那里,我们会尝试兑换它并给客户他们的信。如果没有,我们将生成信件和支付证明令牌并将其存储以备后用。

最后,我们推导出付款条件(稍后更详细地解释),我们可以向客户发送付款条件。这在Interledger中具有重要作用,但对于我们的商店来说,协调付款和原始请求也很有用。

将文字替换// Handle incoming web requests...为以下内容:

  // Handle incoming web requests
  http.createServer(function (req, res) {
    // Browsers are irritiating and often probe for a favicon, just ignore
    if (req.url.startsWith(`/favicon.ico`)) {
      res.statusCode = 404
      res.end()
      return
    }

    console.log(`    - Incoming request to: ${req.url}`)
    const requestUrl = url.parse(req.url)

    if (requestUrl.path === `/`) {
      // Request for a letter with no attached fulfillment

      // Respond with a 402 HTTP Status Code (Payment Required)
      res.statusCode = 402

      // Generate a preimage and its SHA256 hash,
      // which we'll use as the fulfillment and condition, respectively, of the
      // conditional transfer.
      const fulfillment = crypto.randomBytes(32)
      const condition = sha256(fulfillment)

      // Get the letter that we are selling
      const letter = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
        .split('')[(Math.floor(Math.random() * 26))]

      console.log(`    - Generated letter (${letter}) ` +
      `at http://localhost:8000${req.url}${base64url(fulfillment)}`)

      // Store the fulfillment (indexed by condition) to use when we get paid
      fulfillments[base64url(condition)] = fulfillment

      // Store the letter (indexed by the fulfillment) to use when the customer
      // requests it
      letters[base64url(fulfillment)] = letter

      console.log(`    - Waiting for payment...`)

      res.setHeader(`Pay`, `${cost} ${account} ${base64url(condition)}`)

      res.end(`Please send an Interledger payment of` +
          ` ${normalizedCost} ${ledgerInfo.currencyCode} to ${account}` +
          ` using the condition ${base64url(condition)}\n` +
        `> node ./pay.js ${account} ${cost} ${base64url(condition)}`)
    } else {
      // Request for a letter with the fulfillment in the path

      // Get fulfillment from the path
      const fulfillmentBase64 = requestUrl.path.substring(1)

      // Lookup the letter we stored previously for this fulfillment
      const letter = letters[fulfillmentBase64]

      if (!letter) {
        // We have no record of a letter that was issued for this fulfillment

        // Respond with a 404 HTTP Status Code (Not Found)
        res.statusCode = 404

        console.log('     - No letter found for fulfillment: ' +
                                                      fulfillmentBase64)

        res.end(`Unrecognized fulfillment.`)
      } else {
        // Provide the customer with their letter
        res.end(`Your letter: ${letter}`)

        console.log(` 5. Providing paid letter to customer ` +
                                 `for fulfillment ${fulfillmentBase64}`)
      }
    }
  }).listen(8000, function () {
    console.log(`    - Listening on http://localhost:8000`)
    console.log(` 3. Visit http://localhost:8000 in your browser ` +
                                                        `to buy a letter`)
  })

这段代码为传入的HTTP请求定义了两个代码路径(三个,但第一个仅仅是忽略favicon请求的黑客)。

第一个路径处理对http:// localhost:8000 /的请求(即URL中没有令牌),并定义逻辑来生成一个新的字母,但将其存储在内存中,直到客户付钱为止。

首先,我们将HTTP响应代码设置为402(需要付款)。这不是必要的,但它是一个有用的惯例。我们稍后会看到这变得更有价值。

然后我们生成向客户请求Interledger付款所需的数据。

// Generate a preimage and its SHA256 hash,
// which we'll use as the fulfillment and condition, respectively, of the
// conditional transfer.
const fulfillment = crypto.randomBytes(32)
const condition = sha256(fulfillment)

第一部分数据是我们在付款交付时向付款人发放的履行情况。第二个是我们给付款人附加付款的条件。

条件只是履行的SHA-256散列,意味着没有人可以从条件中获得满足,但可以快速验证条件是满足的散列。

注:条件和履行是Interledger的一个重要方面,我们将在进行付款时进一步解释。现在可以简单地理解履行是收款人所持有的秘密并且付款人通过付款发送条件是很好的。

在我们的商店中,我们使用履行作为支付凭证令牌,因此我们使用履行作为索引存储我们为客户生成的信函。

代码的其余部分只是将金额,ILP地址和条件返回给客户,以便他们可以为他们的请求付款。

Interledger地址

请注意,我们向客户提供的关键数据是ILP地址,他们必须付款。

ILP地址是任何网络或帐簿上任何帐户的通用地址。它们由标识帐务/网络的前缀,帐户的标识符组成,也可以具有特定于事务的后缀(本教程中未使用)。

前缀是Interledger前缀,就像IP子网一样在这种情况下,test.表示我们正在连接到Interledger testnet-of-testnets。

下一部分,crypto.表明我们将提到一个加密货币的分类帐。最后,xrp.表明这个分类账是XRP testnet分类帐。如果您知道分类帐前缀和帐户,则可以将它们放在一起以获取Interledger地址。

在这种情况下,test.crypto.xrp.当您启动服务器时,我们商店帐户的Interledger地址已输出到控制台。

注意:如果我们需要分类账/网络不可知的支付协议,则使用通用寻址方案至关重要。所有付款都需要一个目的地,虽然许多传统账户有自己的寻址方案(IBAN,PAN,PayPal地址),但没有一个通用方案可用于解决任何付款问题。

要详细了解ILP地址以及传统和新型支付网络上的帐户如何派生,请参阅IL-RFC-15草案1

如果您现在运行服务器,您将看到它完成了我们的5步程​​序的第1步和第2步。

 恭喜,你在成为一个信封男爵方面取得了很大的进步。

现在让我们把自己放在顾客的鞋子里,并试图购买我们的第一封信!

第3步:支付一封信

按照控制台中的指示,打开浏览器窗口并转至http:// localhost:8000。

您应该收到以下信息:

Please send an Interledger payment of 0.00001 XRP to XXXXXXXXXXXXXXXXX using the condition YYYYYYYYYYYYYYYY
> node ./pay.js XXXXXXXXXXXXXXXXX 10 YYYYYYYYYYYYYYYY

正如他们所说,“没有免费的信件这样的东西!”所以现在我们把注意力转向客户,并为这个难以捉摸的角色付出代价。

打开一个新的控制台窗口,并确保您当前的工作目录与您用于运行商店的目录相同。复制店铺帮助提供的命令并将其粘贴到控制台窗口中。

node ./pay.js XXXXXXXXXXXXXXXXX 10 YYYYYYYYYYYYYYYY

如果您已经配置了客户插件,plugins.js那么您可能会看到以下内容,然后完成脚本:

== Starting the payment client ==

就像我们使用服务器一样,我们需要使用我们配置的插件做些什么。所以让我们编辑pay.js// Do something...使用以下代码替换文本,然后再试一次:

console.log(` 1. Connecting to an account to send payments...`)

plugin.connect().then(function () {
  const ledgerInfo = plugin.getInfo()
  const account = plugin.getAccount()
  console.log(`    - Connected to ledger: ${ledgerInfo.prefix}`)
  console.log(`    -- Account: ${account}`)
  console.log(`    -- Currency: ${ledgerInfo.currencyCode}`)
  console.log(`    -- CurrencyScale: ${ledgerInfo.currencyScale}`)

  // Make payment...

  // Listen for fulfillments...

})

您会看到脚本连接到分类帐,并返回与使用自己的帐户凭证将商店连接到同一分类账时返回的类似信息。

但之后,它只是挂起...

代码中的评论可能已将其提供,但我们现在需要付款。将文字替换为// Make payment...

  console.log(` 2. Making payment to ${destinationAddress} ` +
                                        `using condition: ${condition}`)

  // Send the transfer
  plugin.sendTransfer({
    to: destinationAddress,
    amount: destinationAmount,
    executionCondition: condition,
    id: uuid(),
    from: plugin.getAccount(),
    ledger: plugin.getInfo().prefix,
    ilp: base64url(IlpPacket.serializeIlpPayment({
      amount: destinationAmount,
      account: destinationAddress
    })),
    expiresAt: new Date(new Date().getTime() + 1000000).toISOString()
  }).then(function () {
    console.log('    - Transfer prepared, waiting for fulfillment...')
  }, function (err) {
    console.error(err.message)
  })

我们已经分析了destinationAddressdestinationAmountcondition命令行,现在我们正在指导我们的总账插件编写一份关于台账转移。

请注意金额如何以分类帐的比例表示。我们知道这是6,因此我们支付的0.00001 XRP表示为10滴。

另请注意,我们如何设定我们正在准备的转帐到期日,以便如果在此之前未完成转帐,它将会回滚。

有条件支付和哈希时间锁定协议

在这一点上,我们应该深入了解Interledger中传输的工作方式。它们与大多数传统支付网络/分类账的定期转账有点不同,因为它们分两个阶段进行:

  1. 转移是在满足条件或超时过期之前做好的
  2. 转移要么因为条件满足而执行,要么因为过期而回滚

在ILP中,我们为条件和履行定义了一个标准。该条件是一个32字节的SHA-256哈希,并且履行是该哈希的32字节的原始图像。当发件人准备转账时,资金被锁定,等待交付给收款人。锁是条件(散列),关键是满足。但是,为了避免资金被无限期锁定,如果钥匙从不生产,准备好的转帐也会到期。

INFO: A hash is a one-way function. Therefor for condition = sha256hash(fulfillment) there is no function f for which fulfillment = f(condition). The only way to figure out the fulfillment is to guess. Because of the size of a SHA-256 hash (32 bytes) it is estimated that all the computing power in the world would take millions of years to guess its preimage. A pretty safe lock then for a transfer that shouldn’t take more than a few seconds to complete.

我们将这种安排称为转让双方之间的哈希时间协议(HTLA)。您可能遇到过许多基于加密货币的系统(如Lightning)中使用的HTLC或散列时间片合同。HTLAs是一个泛化,没有规定它们是如何执行的(即它可能不是一个写在分类账代码中的合同,就像加密货币分类帐中的合同一样,它可能是法律强制执行的协议,或者仅仅基于相信)。

两方如何在分类账上向另一方转账取决于它们之间的关系以及基础分类账的特征。对于我们的教程,我们使用特定于XRP分类帐的插件,该插件使用该分类帐的分类帐托管功能。这意味着各方能够进行没有预先存在关系的转账。

有关不同类型HTLA的更多信息,请参阅IL-RFC 0022

注意: ILP是用于进行可以遍历多个网络的付款的协议。因此,有必要区分付款(从发件人到收款人)和在组成付款的不同网络/分类账(发件人,中间人和收款人之间)上的一次或多次转账

在这个简单的例子中,仅使用一次转账进行支付,因此目的地和转账金额与付款的目的地和金额相同。你可以从这样一个事实中看到这一点,即相同的值传递给sendTransfer()函数并用于构建ILP包(IlpPacket.serializeIlpPayment()

在稍后的教程中,我们将扩展此示例以跨多个网络进行支付,并演示这些值通常会有所不同。

现在我们已经添加了代码来完成我们的转移,让我们再次运行脚本并为我们的信付款。这次你应该看到一条消息,说我们正在付款,并等待履行。

恭喜,这是为我们的客户完成的第3步。现在切换回您运营商店的终端,并查看付款是否已到达...

它没有,是吗?所以我们来弄清楚为什么。

在分类帐上找到您的转账

以下是XRP分类帐非常具体的部分,但是如果您要调试失败的付款,您可以了解如何执行此操作。如果你宁愿跳到下一节你可以。

让我们来剖析一下底下应该发生什么。您使用该pay.js脚本加载XRP Escrow插件,并为商店提供的地址准备10次丢弃(0.00001 XRP)的托管付款。

我们的付款尚未到达商店,因此我们首先检查XRP Testnet分类账并查看是否已准备好付款。我们将使用由我们连接插件的同一个testnet服务器公开的REST API。

首先,您需要找到您使用的发送帐户地址。查看plugins.js并找到您的客户插件配置的帐户,或查看运行的输出pay.js(您只需要纹波地址,以便修剪ILP分类帐前缀)。

以下内容将安装一个小工具,用于格式化JSON输出以使其更易于阅读:

npm install -g jslint

接下来使用curl将一个API调用POST到XRP Testnet服务器(用您的发送地址替换地址):

curl -X POST -d '{ "method": "account_objects", "params": [{"ledger_index":"validated", "account": "YOUR-SENDING-ADDRESS", "type": "escrow"}]}' https://client.altnet.rippletest.net:51234 | jslint

安装curl超出了本教程的范围,所以如果您还没有安装它,您可以直接跳过或自己想一想。

你应该找回一个JSON对象来显示你的账户发送的所有交易。其中应该是您刚创建的交易。

所以我们的调查显示转移已经在分类帐上创建。我们需要回到我们的店铺服务并找出为什么没有收到。

第4步:接受付款

如果你仔细观察我们shop.js到目前为止的内容,你会注意到一条评论//Handle incoming transfers...这是我们的第一个暗示,我们错过了一些代码。

这是我们对可以通过分类帐插件引发的一些事件的第一次介绍。有一些这些,我们感兴趣的人被称为incoming_prepare无论何时在分类帐上准备转帐并且您的插件帐户是预期收件人时都会引发此事件。

只要yur插件连接到分类帐,它就会开始监听传入的传输,并且应该在每次发生时都引发此事件。

替换//Handle incoming transfers...为以下代码:

  // Handle incoming payments
  plugin.on('incoming_prepare', function (transfer) {
    if (parseInt(transfer.amount) < 10) {
      // Transfer amount is incorrect
      console.log(`    - Payment received for the wrong amount ` +
                                        `(${transfer.amount})... Rejected`)

      const normalizedAmount = transfer.amount /
                            Math.pow(10, parseInt(ledgerInfo.currencyScale))

      plugin.rejectIncomingTransfer(transfer.id, {
        code: 'F04',
        name: 'Insufficient Destination Amount',
        message: `Please send at least 10 ${ledgerInfo.currencyCode},` +
                  `you sent ${normalizedAmount}`,
        triggered_by: plugin.getAccount(),
        triggered_at: new Date().toISOString(),
        forwarded_by: [],
        additional_info: {}
      })
    } else {
      // Lookup fulfillment from condition attached to incoming transfer
      const fulfillment = fulfillments[transfer.executionCondition]

      if (!fulfillment) {
        // We don't have a fulfillment for this condition
        console.log(`    - Payment received with an unknown condition: ` +
                                              `${transfer.executionCondition}`)

        plugin.rejectIncomingTransfer(transfer.id, {
          code: 'F05',
          name: 'Wrong Condition',
          message: `Unable to fulfill the condition:  ` +
                                              `${transfer.executionCondition}`,
          triggered_by: plugin.getAccount(),
          triggered_at: new Date().toISOString(),
          forwarded_by: [],
          additional_info: {}
        })
      }

      console.log(` 4. Accepted payment with condition ` +
                                              `${transfer.executionCondition}.`)
      console.log(`    - Fulfilling transfer on the ledger ` +
                                 `using fulfillment: ${base64url(fulfillment)}`)

      // The ledger will check if the fulfillment is correct and
      // if it was submitted before the transfer's rollback timeout
      plugin.fulfillCondition(transfer.id, base64url(fulfillment))
        .catch(function () {
          console.log(`    - Error fulfilling the transfer`)
        })
      console.log(`    - Payment complete`)
    }
  })

在这段代码中,我们首先检查传入的传输是否为正确的数量,如果不是我们引发ILP错误并调用rejectIncomingTransfer()拒绝传输函数。如果在接受转让之前我们想要评估其他业务规则,我们也可以在这里进行。

如果转移是正确的,我们提取条件并使用它来查找我们当地商店的履约情况。如果我们找不到它,那么我们无法完成转移,因此我们拒绝它。

注:还有其他方式可以将履行情况映射到即将到来的转移,其中包括从履行数据派生履约的技术。对于这种技术的一个例子来看看PSK协议

我们需要执行的最后一步是完成转帐,以便资金从账本托管中发放给我们。我们使用fulfillCondition()插件上的功能来做到这一点

注意:只有两种方法可以将incoming_prepare事件作为接收方处理。拒绝它或履行它。然后,这一行动将支付链逐级降低,导致链中的转移被完成或拒绝。

重新启动商店服务,并完成访问网站并再次付款的过程。您应该看到,服务器的输出现在包括接受和履行付款。

 恭喜,我们的服务器完成了第4步,共5步!

但是,我们的客户仍在等待实现。让我们回到客户端并完成它!

第5步:使用履行获得信

正如你所猜测的那样,我们在客户端也缺少一个事件监听器。返回pay.js并替换// Listen for fulfillments...为以下代码:

  // Handle fulfillments
  plugin.on('outgoing_fulfill', function (transferId, fulfillmentBase64) {
    console.log('    - Transfer executed. Got fulfillment: ' +
                                                      fulfillmentBase64)
    console.log(` 3. Collect your letter at ` +
                           `http://localhost:8000/${fulfillmentBase64}`)
    plugin.disconnect()
    process.exit()
  })

作为转账发起人,我们感兴趣的是收款人提交后从账本中提取的履约情况,因此我们倾听该outgoing_fulfill事件。

在这种情况下,我们需要做的所有事情都是打印到屏幕上,以便用户可以去收集他们的信件。

重新运行你的客户端,几秒钟后,你现在应该得到一个响应。

恭喜!你已经完成了客​​户端。

所以剩下要做的就是得到那封信!返回浏览器并将客户端输出的URL粘贴到地址栏中,然后您将收到您在第一次请求时为您准备的信件。

如果您切换回服务器的终端窗口,您会看到一些额外的输出,显示商店服务器接受了请求,使用履行查找了信函,并返回了这封信。

 恭喜,这是第5步和第5步,你现在也有一个工作服务器。

奖金步骤:信店客户

每次您需要在线支付费用时,将条件从浏览器复制并粘贴到命令行终端非常麻烦,然后在您付款后从终端复制粘贴履行到您的浏览器。

但是我们可以轻松地编写一个脚本来获取http:// localhost:8000,解析主体以查看需要付款,付款,然后为您提取该信函。现在您可以直接从命令行终端购买Letter Shop的信件,而无需切换到您的Web浏览器:

const IlpPacket = require('ilp-packet')
const plugin = require('./plugins.js').xrp.Customer()
const uuid = require('uuid/v4')
const fetch = require('node-fetch')

function base64url (buf) {
  return buf.toString('base64')
    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
}

plugin.connect().then(function () {
  return fetch('http://localhost:8000/')
}).then(function (res) {
  return res.text()
}).then(function (body) {
  const parts = body.split(' ')
  if (parts[0] === 'Please') {
    const destinationAddress = parts[16]
    const destinationAmount = parts[17]
    const condition = parts[18]
    return plugin.sendTransfer({
      id: uuid(),
      from: plugin.getAccount(),
      to: destinationAddress,
      ledger: plugin.getInfo().prefix,
      expiresAt: new Date(new Date().getTime() + 1000000).toISOString(),
      amount: destinationAmount,
      executionCondition: condition,
      ilp: base64url(IlpPacket.serializeIlpPayment({
        account: destinationAddress,
        amount: destinationAmount,
        data: ''
      }))
    })
  }
})

plugin.on('outgoing_fulfill', function (transferId, fulfillmentBase64) {
  fetch('http://localhost:8000/' + fulfillmentBase64).then(function (res) {
    return res.text()
  }).then(function (body) {
    console.log(body)
    return plugin.disconnect()
  }).then(function () {
    process.exit()
  })
})

随着信件商店的运行,您可以在终端上试用这个脚本:

$ node ./client.js
Your letter: B

正如你所看到的,这段代码依赖于文本的确切表述; 它会检查文本是否以'Please'开始,如果是,则只查找页面上的第16,第17和第18个单词。这当然不是一个非常稳健的设计,如果信店的网页设计发生变化,它会变得非常脆弱。因此,如果我们将机器可读信息分隔成一个http响应头部,那就更好了。正如草稿希望bailie-http-payments网络草案中所述,我们可以使用最近提出的格式为此,我们在该res.end之前的字母商店代码中添加一行:

      console.log(`    - Waiting for payment...`)

      res.setHeader("Pay", `interledger-condition ${cost} ${account} ${condition}`)

      res.end(`Please send an Interledger payment of ${normalizedCost} ${ledgerInfo.currencyCode} to ${account} using the condition ${condition}\n` +
              `> node ./pay.js ${account} ${cost} ${condition}`)

当我们的商店响应请求提供“付款”回复的信件时,它将包含如何在“付款”标题中进行付款的详细信息

知道如何解读该头文件的客户可以在不依赖人类可读网页上的特定措辞的情况下处理该头文件并进行支付。

您现在可以更新您的客户端脚本,以便不必分析响应正文中的单词,而是查看响应标头:

plugin.connect().then(function () {
  return fetch('http://localhost:8000/')
}).then(function (res) {
  const parts = res.headers.get('Pay').split(' ')
  if (parts[0] === 'interledger-condition') {
    const destinationAmount = parts[1]
    const destinationAddress = parts[2]
    const condition = parts[3]
    return plugin.sendTransfer({
      // ...

你可以在这里看到完成的client.js脚本这就是本教程的全部内容!

我们学到了什么?

这三个脚本中使用的插件公开了Ledger插件接口(LPI),如IL-RPC-4草案6中所述,并且该脚本使用以下方法和事件:

  • sendTransfer 方法准备转账到同一台账上的其他账户。
  • getInfo 方法来获取插件连接到的分类帐的信息
  • getAccount 方法来获取插件的账户ILP地址
  • rejectIncomingTransfer 如果有人试图支付错误金额或条件无效,该方法会拒绝传入转账
  • fulfillCondition 方法满足传入传输的条件
  • incoming_prepare 当其他人向您发送条件转移时触发事件
  • outgoing_fulfill 当其他人完成您的条件转移时触发事件

下一步是什么?

在下一个名为'http-ilp'的教程中,我们将在这个Pay头文件中添加更多功能下一个教程

另外,如果你阅读上面的段落,你会看到不少新单词; 如果您忘记了其中的一部分,请参阅IL-RFC-19草案1中的术语表作为参考。

你可能感兴趣的:(Ripple,ILP,跨链)