-
Notifications
You must be signed in to change notification settings - Fork 2
Interledger over BTP
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.
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 |
-
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.
-
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.
-
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:
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.
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.
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.
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.
From now on, nodes are required to implement the following functions:
- Interledger payments with best-effort destination amounts
- Type-12 Interledger payment packets, Type-13 Fulfillments, and Type-14 Rejections, as specified in https://github.com/interledger/rfcs/blob/03c9bb21461dcc0b8e663f5702af21381310e01f/asn1/InterledgerProtocol.asn
- Payments, Fulfillments, and Rejections, sent inside BTP Message calls
- Payment channel updates sent in BTP Transfer calls
- BTP Bearer auth, as described in https://github.com/interledger/rfcs/pull/372
- The newer version of the info protocol, see https://github.com/interledgerjs/ilp-plugin-xrp-asym-client/blob/v1.1.0/index.js#L115-L119
- Always (instead of optionally attach data to fulfillment like in https://github.com/interledgerjs/ilp-plugin-payment-channel-framework/pull/59. Note that this doesn't mean that the Interledger protocol enforces this, and the transfer still counts as completed even if your peer does not pass on the fulfillment-attached data, or changes it while passing it on.
- The 'paychan' protocol was split into 'channel', 'fund_channel', 'channel_signature', 'last_claim' which travel on BTP Message calls (see https://github.com/interledgerjs/ilp-plugin-xrp-asym-server/blob/v1.0.3/index.js#L249-L252), and 'claim', which travels on BTP Transfer calls.
- IL-DCP on top of the 'ilp' protocol.
- (only needed for leaf nodes) psk2 on top of the 'ilp' protocol.
- (only needed for leaf nodes) http-ilp and payment pointers as ways to set up psk2 payments
Nodes are no longer required to implement the following functions:
- The Vouch protocol and on-ledger escrow
- ILQP
- Interledger payments with fixed destination amounts
- Type-1 and Type-10 Interledger payment packets, Type-9 Fulfillments and Type-8 Rejections, as specified in https://github.com/interledger/rfcs/blob/03c9bb21461dcc0b8e663f5702af21381310e01f/asn1/LegacyInterledgerProtocol.asn
- Payments, Fulfillments, and Rejections sent inside BTP Prepare, Fulfill, and Reject calls
- Payment channel updates sent in BTP Response calls (when responding to a fulfillment)
- The previous version of the info protocol
- The balance protocol
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.
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):
- 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 ofReject
was removed - The
rejectionReason
field ofError
was replaced bycode
,name
,triggeredAt
anddata
, see https://github.com/interledger/rfcs/pull/291/files#diff-2c87a4d4e57dd9430f40e0fe2b4d295aR58 - For
Message
andResponse
, 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)'
- Never send
Ack
, instead sendResponse
with an empty protocol data array. Conversely, when receiving aResponse
with no protocol data, interpret it as anAck
. - Only send
Error
to report an error at the btp level; to report an error at the protocol level, sendResponse
and put the error inside the correct protocol data entry. For instance, to report that a quote request failed, even though theMessage
call containing it was successfully unpacked, send aResponse
with protocolData: { protocolName: 'ilp', contentType: applicationOctetString, data: }`. - In a
Reject
, put therejectionReason
in the appropriate protocolData entry (probably 'ilp').
- 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.
- Decided: ILP errors now travel in the
ilp
sub-protocol, instead of in therejectionReason
field. ILP errors are to be formatted as described in IL-RFC-3, draft 2. ILP payment packets and ILQP request /response packets remain unchanged. - Clarified during dev phase: https://github.com/interledger/rfcs/pull/204
- Optionally, attach data to fulfillment like in https://github.com/interledgerjs/ilp-plugin-payment-channel-framework/pull/59. This should be the same data as what the receiver attached when they fulfilled their incoming transfer, although if it's not, then the payment is still due solely because the correct fulfillment was presented.
- 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.
- Decided: The 8 bytes returned are an
Int64
, not aUInt64
, so that negative balance can be represented
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 adata
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.
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.
- To be discussed. Basically, we will freeze it to a version of the master branch of https://github.com/interledgerjs/ilp-plugin-payment-channel-framework once it's implemented and tested there.
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 aResponse
or anError
back from Bob to Alice. No payment is involved. - A
Prepare
from Alice immediately triggers anAck
from Bob, and will be reciprocated later with either aFulfill
or aReject
from Bob. - A
Fulfill
from Bob either triggers anAck
from Alice (in which case, the balance is adjusted in Bob's favor, by the amount mentioned in the corresponding Prepare), or anError
. - A
Reject
from Bob either triggers anAck
from Alice, or anError
. - A response call (
Response
,Error
, andAck
) has the samerequestId
as the request call (Message
,Prepare
,Fulfill
, orReject
) that triggered it, and travels in the opposite direction (e.g. Alice requests, Bob responds). A call is considered a repeat if itsrequestId
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
andReject
, 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 viarequestId
), andPrepare
call - result-reporting request call (linked viatransactionId
). - 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
orPrepare
request call. One important such protocol is of course"ilp"
, as defined in interledger rfcs version8b65d63
. - The other protocols which version 1 of the testnet-of-testnets uses on top of BTP are
"info"
,"balance"
,"ccp"
,"vouch"
, and"paychan"
.
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).
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.
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.
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.
The "ccp"
protocol defines two packets:
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.
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.
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).
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:
- ledger (always
'XRP'
) - conditional (always
false
) - biDirectional (always
false
) - publicKey (see https://github.com/ripple/ilp-plugin-xrp-paychan/blob/cec6cb8/src/lib/outgoing-channel.js#L59)
- claimMessage (see https://github.com/ripple/ilp-plugin-xrp-paychan/blob/cec6cb8/src/util/encode.js)
- signature (hex string, see https://github.com/ripple/ilp-plugin-xrp-paychan/blob/cec6cb8/src/lib/outgoing-channel.js#L119-L122)
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
.