Skip to content

Interledger over BTP

Michiel de Jong edited this page Jan 31, 2018 · 15 revisions

This wiki-page defines versions 17Q3 and 17Q4 of the Interledger testnet, from the point of view of messages exchanged on the wire. It does not describe the part of the interaction which goes via one of the blockchain testnets, only the direct node-to-node communication.

For the description of the testnet-18Q1 version of Interledger, see https://github.com/interledger/interledger/wiki/18Q1.

Versioning

Currently, '17Q3' is the only live version, and '17Q4' will be implemented in Amundsen soon. Testnet nodes can then choose which of the two they prefer to use. Each version is available for a year (dev phase + live phase), and a new version will be added each quarter (on 1 January, 1 April, 1 July, and 1 October), to create overlapping version updates. So the release schedule looks as follows:

Version 2017-Q3 2017-Q4 2018-Q1 2018-Q2 2018-Q3 2018-Q4
17Q3 dev 1) live live live
17Q4 spec 2) dev live live live
18Q1 spec dev live live live
18Q2 spec dev live live
18Q3 spec dev live
18Q4 spec dev
19Q1 spec
  1. Version 17Q3 'spec' was extended from 1 July to 28 August so that BTP could be included, and 'dev' phase was cut short from 1 October to 18 September, to make place for BTP/1.0.

  2. Version 17Q4 'spec' was cut short from 1 October to 18 September to speed up the availability of a BTP/1.0 end-point at Amundsen, so 'dev' phase of this version will be slightly longer.

  3. This versioning schedule was adapted from the testnet's original six-months update schedule, because of https://github.com/michielbdejong/ilp-node/issues/46

The three phases of a version are 'spec', 'dev', and 'live', and this is what they mean:

spec phase

Changes to BTP and to protocols on top of BTP are discussed in https://github.com/interledger/rfcs. Whatever is in master at the end of the phase, will be used on this wiki page, as the result of the spec phase. For instance, for version 17Q3, BTP version 8b65d63 from the rfcs repo (then still called CommonLedgerProtocol) was used.

dev phase

Once the spec has been frozen, subprotocols can still be added and built out for another three months. If ambiguities are discovered in the spec, they are clarified during the dev phase. Amundsen's endpoint for the version that's in dev phase will be updated often, and Software versions that use only this new spec version can already be published during this phase.

live phase

Testnet nodes have six months to update their live deployed testnet nodes to a software version that supports the new spec version. If bugs are found during this phase, they are fixed, even if they represent a breaking change. This means it's useful to know both the spec version and the software product and version of your peer's end-point, so that you can support connecting to buggy software. If ambiguities in the spec are discovered, then the spec is adapted to whatever was implemented.

Sub-protocols

Although the BTP spec does not change during the dev phase, sub-protocols like auth, ilp, info, balance, etcetrera can be introduced at any time during the dev phase, and can be expanded at any time (adding new call types to one), but breaking changes in any sub-protocols always need to be postponed until the next version.

Changes between versions 17Q4 and 18Q1

From now on, nodes are required to implement the following functions:

Nodes are no longer required to implement the following functions:

Furthermore, note that the BTP-level balance was effectively inverted; the Prepare call that existed in 17Q3 and 17Q4 would decrease the sender's "balance", and increase their "unsecuredAmount". In 18Q1, that call is deprecated, as is the concept of BTP-level balance. There still is a concept of "unsecuredAmount", but the newly introduced Transfer call does not increase it. Instead, the Interledger payments that now travel inside BTP Message calls increase the sender's unsecuredAmount, and the BTP Transfer call can instead be used to decrease one's unsecuredAmount.

Changes between versions 17Q3 and 17Q4

This document describes version 17Q3 of the Interledger Testnet Stack. Version 17Q4 differs from version 17Q3 in the following ways (note this list is still subject to change, as not all sub-protocols that existed in 17Q3 have been added to 17Q4 yet):

Changes to BTP ASN.1

  • The Ack call type was removed and the remaining call type id's were decreased by 1 (from 2..7 to 1..6)
  • The rejectionReason field of Reject was removed
  • The rejectionReason field of Error was replaced by code, name, triggeredAt and data, see https://github.com/interledger/rfcs/pull/291/files#diff-2c87a4d4e57dd9430f40e0fe2b4d295aR58
  • For Message and Response, protocolData was wrapped in a SEQUENCE (this does not affect the OER encoding)
  • The name 'Common Ledger Protocol (CLP)' was changed to 'Bilateral Transfer Protocol (BTP)'

Changes to BTP usage

  • Never send Ack, instead send Response with an empty protocol data array. Conversely, when receiving a Response with no protocol data, interpret it as an Ack.
  • Only send Error to report an error at the btp level; to report an error at the protocol level, send Response and put the error inside the correct protocol data entry. For instance, to report that a quote request failed, even though the Message call containing it was successfully unpacked, send a Response with protocolData: { protocolName: 'ilp', contentType: applicationOctetString, data: }`.
  • In a Reject, put the rejectionReason in the appropriate protocolData entry (probably 'ilp').

Changes to Setup

  • Three new sub-protocols, 'auth', 'auth_username', and 'auth_token' are added, as described in https://github.com/interledger/rfcs/pull/300, and they replace the NAME and TOKEN from the WebSocket URL. Note that the bytes in the NAME and TOKEN are therefore no longer constrained to URL-allowed ASCII characters, but they are still restricted to UTF-8.
  • The /NAME/TOKEN suffix of the WebSocket URL is removed.
  • Servers may put restrictions on the NAME a client chooses, and close the connection if those restrictions are not met. In particular, it can use a whitelist, or require approval from the server admin before the server accepts a new client.
  • Servers may put restrictions on the TOKEN a client chooses, and close the connection if those restrictions are not met. The TOKEN can be derivable from the NAME by some process (e.g. a WebFinger lookup), it can be hard-coded in configuration, it can be agreed out-of-band, etc.

Changes to ilp sub-protocol

Changes to info sub-protocol

  • Decided: response to a type 1 request is now directly the ILP address with a MIME_TEXT_PLAIN_UTF8 contentType, no longer that address prepended with 0x02 and MIME_APPLICATION_OCTET_STREAM contentType. This page previously incorrectly reported that we decided not to change the info protocol, even though consensus about this change had already been reached.

Changes to balance sub-protocol

  • Decided: The 8 bytes returned are an Int64, not a UInt64, so that negative balance can be represented

Changes to vouch sub-protocol

To be decided, but probably the vouch sub-protocol will be deprecated in favor of the new custom sub-protocol, in the following way:

  • all calls will be of contentType MIME_APPLICATION_JSON, and their content should be the JSON representation of an object containing a method and a data field, and no other fields.
  • for the 'vouch for' call, the method will be 'vouch_for', and the data will be the { address: <theAddress> }
  • for the 'reach me at' call, the method will be 'reach_me_at', and the data will be the { address: <theAddress> }
  • for the 'rollback' call, to be decided once we have implemented and tested this in https://github.com/interledgerjs/amundsen.

Changes to ccp sub-protocol

To be decided, but probably the ccp sub-protocol will be deprecated in favor of the new custom sub-protocol, to be used with contentType MIME_APPLICATION_JSON, and exactly like currently implemented in https://github.com/interledgerjs/ilp-connector version 21.1.10. See an example of the custom field in https://github.com/interledger/rfcs/blob/master/0021-plugin-rpc-api/0021-plugin-rpc-api.md#send-request-routing. This is for the 'routes' packet, but the 'request full table' packet gets deprecated, as it will now piggy-back on route broadcasts, like implemented in https://github.com/interledgerjs/ilp-connector/commit/7e4189de27640e02de336331ff6c4f6a617a6fed.

Changes to paychan sub-protocol

BTP

The Bilateral Transfer Protocol (BTP), in its current form, is a simple binary protocol which supports unpaid and conditionally paid requests. It was originally called the 'Common Ledger Protocol', and evolved from the PluginVirtual RPC protocol, which itself was derived from the [Ledger Plugin Interface(https://github.com/interledger/rfcs/blob/7a5eb38fd38136eb58f7002644a2d028b1e3fab6/0004-ledger-plugin-interface/0004-ledger-plugin-interface.md). BTP requests can be requests for information, or requests for action. The payment is denominated in abstract "ledger base units", and it's up to higher-level protocols to peg those to a well-known unit of value, or otherwise add semantics to them. The Common Ledger Protocol defines a number of calls (4 request calls and 3 response calls), and its rules are as follows:

  • A Message from Alice to Bob triggers either a Response or an Error back from Bob to Alice. No payment is involved.
  • A Prepare from Alice immediately triggers an Ack from Bob, and will be reciprocated later with either a Fulfill or a Reject from Bob.
  • A Fulfill from Bob either triggers an Ack from Alice (in which case, the balance is adjusted in Bob's favor, by the amount mentioned in the corresponding Prepare), or an Error.
  • A Reject from Bob either triggers an Ack from Alice, or an Error.
  • A response call (Response, Error, and Ack) has the same requestId as the request call (Message, Prepare, Fulfill, or Reject) that triggered it, and travels in the opposite direction (e.g. Alice requests, Bob responds). A call is considered a repeat if its requestId is the same as a previous call in the same direction. All calls can be repeated indefinitely (idempotency of sending a call), as long all fields contain the same data.
  • Two of the three request calls, Fulfill and Reject, can be thought of as 'result-reporting request calls', and they are "requests" in the sense that they request the other party to update the state of a transaction. At the same time, they "report the result" of the higher-level request linked to that transaction. This means that BTP has two levels of back-and-forth linking: request call - response call (linked via requestId), and Prepare call - result-reporting request call (linked via transactionId).
  • The exact format is BTP as defined in interledger rfcs version 8b65d63, encoded in OER, transmitted over WebSockets. The reason to use WebSockets instead of TLS sockets is that WebSockets implement message frames (data comes in readily sliced into messages, rather than as a continuous slur of unsliced data). A secondary reason is that it also aligns better with what certain client-side web clients like Butterfly may use when implementing BTP, since TLS is not directly accessible from in-browser JavaScript code.
  • On top of BTP, higher-level protocol data defines what information or action is requested in a Message or Prepare request call. One important such protocol is of course "ilp", as defined in interledger rfcs version 8b65d63.
  • The other protocols which version 1 of the testnet-of-testnets uses on top of BTP are "info", "balance", "ccp", "vouch", and "paychan".

Setup

For two nodes in the testnet-of-testnets to become connected, 'master' and 'slave' roles need to be assigned. Generally, when a node newly enters the network, the pre-existing (listed) node is automatically assigned the 'master' role, hosting a WebSocket server, and the newcomer plays the 'slave' role, connecting with a WebSocket client. If two already connected nodes want to add a direct peering relationship, they need to pick who will be the master and who will be the slave [pro tip: use Chwazi for this. ;) ].

The 'slave' node needs to somehow obtain a BTP WebSocket BASE_URL (starting with wss:// and without / at the end), spec version, and (optionally, for adapting to known bugs) software version, from the 'master' node. The master node should be listening for secure WebSocket connections on that hostname and port, and then slave should do the following:

  • Pick a NAME. This can be any name that's not in use yet, and should be permitted as a segment in an ILP address, so that's a-z, A-Z, 0-9, _, ~, - (case sensitive).
  • Pick a TOKEN. This should be 64 bits (8 bytes) represented in upper case hex string (16 characters 0-9, A-F).
  • Now connect to ${BASE_URL}/${NAME}/${TOKEN}.

The WebSocket URL is unique and secret (thanks to the TOKEN), and the slave should only connect to it with one client at a time. The master should not allow other simultaneous connections with the same NAME, especially if the TOKEN does not match the initial one. The balance of the BTP connection starts at zero. The way to value of one ledger unit is fully controlled and determined by the master, as is the ledger prefix and the account names on there of both the master itself, and the slave. This effectively means the master is always right in case of disputes, and the slave takes a risk when depositing balance into the BTP trustline (payment-to-self), or fulfilling payments on there that arrive from elsewhere.

Both nodes can now announce their on-ledger addresses which the other node can use to initiate Interledger payments directly from a blockchain wallet (see Vouch protocol below).

ilp

The "ilp" sub-protocol can be used to attach Interledger payments to PREPARE calls, as described in IL-RFC-3, draft 2, and to attach ILQP requests to MESSAGE calls / ILQP response to RESPONSE calls, as described in IL-RFC 8, draft 2.

Info

The "info" protocol defines four packets: first of all, 'get info' (which travels inside a BTP Message from slave to master) and 'get info response' (which travels inside a BTP Response from master to slave). The 'get info request' packet is a single byte with a value of 0x01. The 'get info response' packet is of variable length, the first byte having value 0x02, and the remaining bytes encoding the slave's primary ILP address as an ascii string. The slave's primary ILP address will generally be made up of the master's primary ILP address, followed by a ., followed by the slave's NAME. Packet type 0x03 is for 'get info full' which invites a response of contentType applicationJson(2), and with contents equal to the LedgerInfo object defined in LPI draft 5.

Balance

The "balance" protocol defines two packets: 'get balance request' (which travels inside a BTP Message from slave to master) and 'get balance response' (which travels inside a BTP Response from master to slave). The 'get balance request' packet is a single byte with a value of 0x01. The 'get balance response' packet is 10 byte, the first of which has value 0x02, the second has value 0x08, and the remaining 8 represent the balance as a big-endian unsigned 64-bit integer.

ccp (route broadcasts)

The "ccp" protocol defines two packets:

Routes

A 'routes' packet starts with a byte of value 0, followed by a relaxed variation on the ilp-connector@7e4189d routing update, encoded as a length-prefixed utf-8 JSON document. Example of the JSON part of a routing update:

{
  "new_routes": [ {
    "destination_ledger": "g.ledger.",
    "points": "AAAAAAAAAAAAAAAAAAAAAP////////////////////8="
  } ],
  "unreachable_through_me": [
    "g.some-deprecated-ledger."
  ]
}

The length prefix is defined in section 8.6 of the OER spec. A BTP Message containing a 'routes' packet in its 'ccp' protocolData entry can be responded to with an Ack or an Error. Note that 'routes' packets can also appear in a BTP Response, namely when responding to a 'request full table' request (see below).

Note that, although it is originally the master node who assigns an Interledger address (ledger prefix + account) to the slave, route broadcasts allow the slave to announce their own preferred Interledger address. Even when the other node responds with an Ack, there is no guarantee that the routes that were announce will become routable, as this depends not only on the direct peer, but also on other nodes in the network.

Request full table

The 'request full table' packet consist of one byte, with value 1, and can only travel on a BTP request. It expects a BTP response carrying a 'routes' packet.

Vouch

The vouch protocol is used to allow a node to initiate an Interledger payment directly from a blockchain wallet, without going through the BTP trustline. To vouch for your on-chain wallets, send a 'vouch for' sequence, which is an OER sequence containing the byte 0x01 followed by the wallet's Interledger address as a utf-8 variable-length octetstring.

The 'reach me at' sequence is the same, but with a first byte of 0x02, and it indicates you invite your peer to try to send Interledger payments on top of on-ledger transfers to that address (after they vouched for their own on-ledger wallet address).

If an Interledger payment is attached to a conditional on-ledger transfer from that wallet to your peer's wallet on the same blockchain, your peer will be more likely to try to forward it. If the payment fails, the peer will adjust the balance of the trustline with a rollback penalty. When this happens, the peer will send an OER sequence containing the byte 0x03 ('roll back') followed by the source wallet's Interledger address as a utf-8 variable-length octetstring, followed by the execution condition of the rolled-back transfer as a UInt256, followed by a UInt64 amount. The trustline balance is then silently updated in favor of the master by that amount.

Note that this violates the design of BTP because this message would use the 'unconditional paid request' with a negative amount, if that request type would exist in BTP, but the current trustline balance can still be determined at any time by adding up all fulfilled BTP transfers in both directions, and all vouch protocol rollbacks in both directions.

Also note that the amount in the 'roll back' sequence can be determined by the peer (the fulfilling party), and there is no way to announce this amount in a quote request response. The node who sent the failed payment (the preparing party) can respond to a 'roll back' message with either Ack or Error, and it is ultimately the master who authoritatively keeps track of the balance. Future versions of the testnet-of-testnets may include machine-assisted dispute resolution, which would effectively give both the slave and the master a say in what the current trustline balance is, thus removing one of the two ways in which slaves differ from masters (the other difference being WebSocket server vs. WebSocket client).

Paychan

With the "paychan" side-protocol you can send a payment channel claim to your peer. Like the 'roll back' sequence from the "vouch" protocol, this is sent on top of a Message BTP packet, even though it affects the BTP balance. The first byte of a "paychan" sequence is 0 for the 'send claim' call type, the rest is a length-prefixed octetstring containing a utf-8 JSON document, followed by a UInt64 amount. If the message is sent by the master, or the master responds to it with an Ack, the trustline balance is silently updated by that amount in favor of the sender. The JSON document represents an object with the following fields:

The signature should be correct, the channel needs to exist on the XRP testnet, and the amount of the claim should not exceed the channel maximum. If the slave sends a claim to the master, and these criteria are not met, the master should respond with an Error instead of with an Ack.