Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggested changes to protocol stack and CLP #270

Closed

Conversation

adrianhopebailie
Copy link
Collaborator

@adrianhopebailie adrianhopebailie commented Aug 10, 2017

EDITED: Replaced this PR with a fresh revision based on some discussion with @emschwartz, @sharafian and @michielbdejong

This proposal is a variation of @emschwartz Common Ledger Protocol proposal. It is based on the following assumptions.

This is a protocol for host-to-host communication

The protocol will be used between Interledger hosts (also called nodes, peers, connectors, senders, receivers). Therefor the protocol fits into the protocol stack below the internetworking layer (also called the Interledger layer).

The protocol will be used to transport Interledger packets between hosts as part of a longer end-to-end transport (called InterledgerMessages in this proposal) but it may also be used for direct exchanges between hosts (called LocalMessages in this proposal).

The protocol supports transfers and messages

It supports two types of exchange between hosts, an exchange of data and an exchange of value.

An exchange of data is just a message from one host to another whereas an exchange of value is a transfer.

ILP is built on request/response semantics

ILP differs from IP in that an ILP packet does not carry a source address in it's header. This is because an ILP packet that carries a response to a request MUST travel the same route as the original request.

The result is that ILP has request/response semantics in the internetworking layer.

As such it's important that the protocol define one or more valid response packets for each request packet.

This is pretty obvious for the ILQP packets. For ILP this proposal suggests a new packet that encapsulates a fulfillment. This is the accepted response to a payment request.

ILP packets should carry a payload

The Interledger layer is an internetworking layer so it should accommodate encapsulation of higher-level protocol payloads in all Interledger packets.

Both asynchronous and synchronous messaging should be supported

It's up to each pair of hosts to determine how they want to communicate at the host-to-host layer and the protocol should accommodate both patterns.

To achieve this it is important to differentiate between a logical request (e.g. a transfer request) from the actual messaging semantics (a request is the first message in a request/response pair).

As an example, the CLP could be implemented using an async pattern where a quote request/response is sent as follows:

Host A --------------- Host B

--- Request(QuoteRequest) --->
<------  Response(Ack)  ------
<--- Request(QuoteResponse) --
-------- Response(Ack) ------>

... or in a sync pattern as follows

Host A --------------- Host B

--- Request(QuoteRequest) --->
<-- Response(QuoteResponse) --

The condition and fulfillment should be carried at the ILP layer

This is the most controversial change proposed but I think it is justified. Both of these data elements are end-to-end considerations (not host-to-host).

Interledger is different from the Internet in that the lower layers (like CLP) may have a dependency on this data. BUT, this is not core to the protocol itself. It is possible to have a chain of transfers where only some or none of the transfers even look at the conditions and fulfillments.

I think we should look at an Interledger payment as a chain of transfers on trustline as illustrated below:

Trustlines

When a transfer is done between two hosts it is that specific host-to-host arrangement that determines if the condition and fulfillment are relevant. This should not affect the protocol architecture.

@michielbdejong
Copy link
Contributor

CLP has either local messages or ILP messages/transfers which always encapsulate an ILP packet (so fulfillments should also be packets)

I think the way we have it in #271 now is cleaner, as it basically says all messages/transfers may have protocol data, where protocolId 1 is for InterledgerPackets, which may again contain InterledgerProtocolPayments (I think we should try to avoid the ambiguous term ILP packet)

All packets should carry higher level protocol payload

The way it works now in #271 is that for instance a Fulfill doesn't have higher level protocol payload, because it only acts at the ledger level. Is that an issue?

Changed CLP to have some internal layering to separate logical messages from on-the-wire messages

In #271 there is now the transferId and the requestId. Ben and I discussed this and decided to remove the requestId = 0 thing for the reasons your explained, but didn't go as far as adding a correlationId to responses so that they can have their own requestId, as you also proposed. I did like that idea and wouldn't mind either way. But I don't really want to spend much more time discussing those details, given how much time we already spent on CLP in the last two weeks, the testnet is now really behind schedule and I feel it would be better if I try to start implementing now.

Changed the ILP packet to have a common header and always carry a payload

I'm not in favor of changing the InterledgerPacket, I think it works well the way it is defined now. It may be a bit arbitrary that it lists three different Interledger-level protocols (Payment, Quoting, Errors), but at the same time it packs the call id's of those three protocols into one byte, so that means CLP just needs 1 byte to say Interledger, and the next byte immediately determines what the request or error is within the next protocol.

Added condition to payment packet as it is an end-to-end value.

Although that would make sense when you only think of CLP trustlines, this is a bit of a footgun when used on top of a ledger, for instance, if a payment request comes to you over XRP, then what you care about is finding the condition of that conditional XRP payment, that's what gets you paid. Also, I think we should consider the InterledgerProtocolPayment format as already frozen.

Trimmed down ILQP packets as they now inherit a lot from the common header

Same reasoning as above - let's not move things back and forth in packets that were already defined.

@adrianhopebailie
Copy link
Collaborator Author

I think we should consider the InterledgerProtocolPayment format as already frozen.

Everyone keeps saying this and I don't understand why. Either we want the best design or we don't care. Whats the rush?

There is a false sense of urgency to freeze the design start implementing and yet there is also no willingness to fully discuss the details.

I am also keen to freeze for an extended period and focus on implementation but then we need to be 100% sure there are no outstanding questions with the design.

@adrianhopebailie
Copy link
Collaborator Author

a Fulfill doesn't have higher level protocol payload, because it only acts at the ledger level. Is that an issue?

I disagree with this strongly. The Fulfill and Condition are not ledger layer concerns they are end-to-end ILP layer concerns.

They are used by SOME trustlines but not all which suggest that the rustline sthat use them should then be able to understand the ILP layer packet and get the condition and fulfillment from there.

Although that would make sense when you only think of CLP trustlines, this is a bit of a footgun when used on top of a ledger,

Truslines are an abstraction of two accounts on a ledger. All links between peers on the Interledger are trustlines but not all trustlines have ledgers underwriting them.

for instance, if a payment request comes to you over XRP, then what you care about is finding the condition of that conditional XRP payment, that's what gets you paid.

So what's the problem? Look in the ILP packet and you'll find it there.

@michielbdejong
Copy link
Contributor

Look in the ILP packet and you'll find it there.

Ah, but if I know that's what you'll do, then I can trick you:

  1. I create an escrow to you on the XRP ledger, under a condition which you don't look at, but carrying a packet which you will forward for me.
  2. You look inside the packet and decide to transfer some of your own money to the destination I chose.
  3. The receiver fulfills the condition of the packet (which is different from the one condition of the XRP escrow) so you have now paid.
  4. You try to claim your XRP but it fails because there are two unrelated conditions in play, and you obtained a fulfillment for only one of them.

So then you paid out, but the incoming transfer still gets rolled back.

@adrianhopebailie
Copy link
Collaborator Author

The receiver fulfills the condition of the packet (which is different from the one condition of the XRP escrow) so you have now paid

Of course I would ensure the condition in the packet is the same as the one used in the incoming transfer...

@sharafian
Copy link

If you ensure the one in the packet is the same as the one in the transfer, then the condition in the packet gives you no new information; it's just a pitfall. Furthermore, you have to take out the condition when you perform an HMAC. There's no case where the condition in the packet is helpful

@dappelt
Copy link

dappelt commented Aug 14, 2017

After having read up on this discussion, I don't agree with putting the condition into the ILP packet. Putting the condition into the ILP packet would require the ledger to understand ILP packets, which would break our abstraction. A ledger should not care about any Interledger functionality; for all he knows, he is the only existing ledger.

Interledger is different from the Internet in that the lower layers (like CLP) may have a dependency on this data. BUT, this is not core to the protocol itself. It is possible to have a chain of transfers where only some or none of the transfers even look at the conditions and fulfillments.

In case there is a chain of transfers and some transfers don't require a condition: Such transfers should store the condition as opaque data next to the ILP packet (i.e. in the memo field using fb-ledger terminology).

@adrianhopebailie
Copy link
Collaborator Author

Putting the condition into the ILP packet would require the ledger to understand ILP packets

This is not true. There is some confusion about how the ledger fits into this architecture. The connection between two peers is NOT through a ledger.

IF two peers connect OVER a ledger then the function that ledger provides is simply to remove the need for the peers to trust each other directly for delivery of a transfer.

The primary role of the condition is to provide a way for a host to proves to its downstream peer that the end-to-end payment it requested, was completed. It has nothing to do with the ledger or ledgers at each hop.

BUT, by standardizing this condition/fulfillment pair it can be used on trustlines where there is a ledger and where the transfer between those peers can be prepared and committed using the same condition/fulfillment on that ledger.

Note: This is not an operation performed by the ledger. This is done by the peers, using the ledger as an agreed tool because: a) it supports conditional transfers using an ILP condition (or derivation thereof, e.g. lightning) and b) they both hold accounts on that ledger.

@emschwartz
Copy link
Member

I think some of this debate stems from confusion between three different ways we think about "ledgers":

  1. (Underlying) Ledger - the system in which "real value" is tracked, such as a blockchain or a bank's ledger
  2. Trustline Plugin - this implements a ledger (a system for tracking accounts and balances) that happens to only have two accounts
  3. Ledger Layer - a combination of HTLAs and messaging that Interledger requires to function

The connection between two peers is NOT through a ledger.

Actually if they are tracking value, transfers are always sent through a ledger. The protocol doesn't care whether this is a "real" or "underlying" ledger, or just a ledger you use with your peer for tracking your bilateral credit relationship. But either way, transfers are sent through a ledger.

The primary role of the condition is to provide a way for a host to proves to its downstream peer that the end-to-end payment it requested, was completed. It has nothing to do with the ledger or ledgers at each hop.

Read the HTLA document. Conditional transfers are a requirement of the Ledger Layer, so it definitely affects each hop.

Note: This is not an operation performed by the ledger.

The condition is enforced by the ledger. It doesn't matter whether that ledger is the one you're using bilaterally to keep a credit balance with your peer or an external system.


@adrianhopebailie my hunch right now is that this is one of the things that's rubbing you the wrong way about this whole protocol stack:

In IP everything is about simple one-way data transfer. The networking layer only needs one function, which is to send data locally. Everything built on IP takes advantage of one function: sending data across an internetworked system.

Interledger is similar in some ways to IP but fundamentally different in others. Interledger is about sending data and value.

When sending value, there are more requirements for the layer underneath ILP. The main functions that you need in order to do ILP are:

  • Preparing a conditional transfer using any type of HTLA
  • Sending a fulfillment to execute a conditional transfer
  • Rejecting an incoming conditional transfer with a certain message
  • Sending data and/or sending a request and getting a response

In IP, everything that's relevant for the Internet layer has to go in the IP packet. Since ILP requires more functions from the underlying layer, Interledger details do not necessarily need to travel together in a single data packet but may be carried alongside Interledger packets in the Ledger Layer protocol.

If we can send some of the Interledger-layer details alongside the ILP packets, the question is whether it is better to do that or better to include the details in the packets themselves. The answer is that it depends on the details.

Certain information, such as the condition and fulfillment, MUST be passed along in the Ledger Layer protocol, because they are part of the instructions to the accounting system. In this case there is the option to also include them in the Interledger packet. At best, this is just some redundant bytes being sent over the wire. At worst, as I wrote here it's actually information that the participants must ignore for safety, so it's redundant and a footgun. In such cases, we should definitely not include information in the packet just because we think it feels nice to have all of the Interledger-related details wrapped up together.

Another example of data that MUST travel alongside the ILP packet but that is relevant for the Interledger layer is the amount of money traveling with the packet. This is expressed in the Ledger Layer protocol, not the Interledger packet, for obvious reasons (the amount and value it represents changes at every step of the way). If you want to understand Interledger, you need to understand that ILP packets are conveyed alongside other fields like the amount, condition, expiry, and possibly other side protocols. But that doesn't mean all of these should be inside the ILP packet. Instead, they are transmitted in the Ledger Layer, required by the Interledger layer, and forwarded (some fields with modification and some without) at each hop. The condition and fulfillment are similar to the amount in that they are fundamentally instructions for the Ledger Layer.

Copy link
Contributor

@michielbdejong michielbdejong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't re-read the discussion, but I still think Adrian is right that it would be cool to define CLP as Interledger-centric, i.e. first put all Interledger-relevant fields into the ILP packet, and then put the rest outside it, instead of the current data structure, namely to put all ledger-relevant in the transfer, and then put the rest in the ILP packet. Obviously, the two approaches differ in where the fields end up that matter for both the ledger transfer and the interledger hop.

I guess the main question is do we want to a) keep things as they are and just get to work, b) change this in the september 18 version, c) keep discussing this and change it in a next version. I'll leave that up to @adrianhopebailie and @emschwartz to discuss this week ;)

@emschwartz
Copy link
Member

@adrianhopebailie can we close this now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants