Re: [Ledger] An attempt to clean up the protocol architecture

On 14 August 2017 at 22:03, Evan Schwartz <evan@ripple.com> wrote:

> I think this thread is going to get extremely unwieldy but here goes:
>
> > - All interledger layer messages should be ILP packets (including
> fulfillments) and be capable of carrying higher layer protocol payloads.
>
> Interledger has higher requirements than ILP for the lowest layer,
> specifically because we are carrying money and not just data. One of the
> requirements is being able to transmit a 32-byte fulfillment back along the
> same path that carried the payment originally. If we expect this of the
> lower layer, I don't see a point in putting the fulfillment into an ILP
> packet and transmitting it as Interledger data along the same path. All
> ledger-layer protocols will need to interpret the fulfillment passed in
> their protocol, not the one passed through the Interledger layer.
>

This is not correct. There is no requirement on ledger layer protocols to
transmit or understand the fulfillment. You are looking at this through the
lens of existing implementations from the bottom up instead of starting at
the interledger layer.

The primary function of the condition and fulfillment is as a signed
end-to-end receipt. If the sender agrees a condition with a receiver and
then gets back the valid fulfillment they don't care what happened in the
middle. The receiver has signed a receipt saying they have their money.

The value of using a standard for the receipt and signature is that each
transfer along the way CAN re-use it. One the one hand you can have a
transfer between two peers that have zero trust and the ledger they use
supports conditional payments completely. On the other extreme you can have
two peers that have a full trust and ignore the condition and fulfillment
completely.

The ledger layer protocols carry ILP packets. Payment requests and either
fulfillment or error responses. If a ledger layer protocol wants to use the
condition and fulfillment for their own operations they can extract these
from the ILP packets and use them.


> > - While it may make sense to split the interledger payment and
> interledger quoting protocols into new higher level protocols that seems
> like an unnecessary abstraction. Instead the packet definitions should just
> have some consistency and probably a common base/header.
>
> The current protocols effectively have this header but it isn't separated
> out. There are two fields in the request header: type and destination
> address. There is one field in the response header: type. I don't think it
> makes that much of a big difference to separate these fields if all of the
> fields in that packet need to be interpreted together (for example, you
> can't understand a quote request if you strip off the destination address).
>

I agree that we don't HAVE to explicitly separate them out but I think ti
would make it clearer how the stack is architected if there was a header
that was consistent across all packets. Currently the only thing that is
consitent across all ILP packets is that they are defined int he same file.


>
> > - We should define two base ILP packet types: request and response.
>
> Unless this adds some substantive benefit or new fields I don't think it's
> worth breaking all of the formats we have just to rearrange things.
>

The goal of this exercise is to tease out the best design and ignore the
cost of change until we can compare the results with the current design.


>
> > - ILP is not about ledgers, it is about trustlines between nodes/hosts.
>
> A ledger is any system that tracks accounts and balances. When you use a
> trustline all of your messages still need to go through an accounting
> system (such as the plugin in the JS implementation) and then on to the
> other program logic.
>

As above, this is incorrect. There is no requirement for "all messages to
go through an accounting system".

Since designing the first implementation of 5-bells ledger we have assumed
that passing the ILP packet MUST be done by the ledger because that is how
we implemented it. But that is not true. It is perfectly valid for the
passing of an ILP packet from one peer to another to be simply an exchange
of data.

The receiving peer makes a decision about whether or not to forward the
packet based on the current financial position they have with the sending
peer.

It is convenient if the ledger that records the net positions of the peers
also forwards the messaging and even better if it natively supports
conditional payments and can use the condition and the fulfillment from the
ILP packet for those but that's all it is, convenient.



> In that case, the plugin or whatever is doing the accounting is the
> ledger. Digital value is always tracked in ledgers, so I think it does make
> sense to think of this as the base layer. The reason to abstract the
> functionality you expect from the ledger layer is precisely so you can
> handle it in different ways, depending on what the underlying systems
> provide.
>
> I see 3 ways to think of the layer(s) underpinning ILP:
>
>    1. The "Ledger Layer" provides both messaging capabilities and some
>    type of HTLA
>    <https://github.com/interledger/rfcs/blob/master/0022-hashed-timelock-agreements/0022-hashed-timelock-agreements.md>
>    2. There are separate plugins for messaging and for transfers and when
>    you peer with someone you have to agree on a plugin for each
>    3. We standardize the messaging part and say that all goes over IP and
>    then just have more minimal plugins for the on-ledger settlements
>
> Number 1 is what we have and I think that still makes the most sense.
>

I think you're confusing implementation details and defining of interfaces
with definition of a protocol stack. The only differences between the tree
examples you have above is in implementation.

>From the perspective of the Interledger Protocol the implementation of the
lower layers is not important, that's the whole point of layering. By
forcing important aspects of ILP like the condition, fulfillment and expiry
down into those layers you muddy the waters and we now have to standardize
those protocols too. Instead we should just be defining the functions they
must provide and then leave it up to implementations to provide those
functions.

I know we want to define a standard to bootstrap the system (CLP) but
that's misleading us into thinking it's an essential part of the stack.
It's perfectly valid for two peers to not use CLP and still be part of the
Interledger.

That said, you raise an interesting consideration about the layers below
ILP and actually I think it makes sense to split these.

We keep trying to force messaging through the ledger layer and actually
that's the wrong place to put it if we can split the ledger layer into a
messaging layer and a ledger layer. That way we can stop trying to think of
all HLTAs as ledgers.

A thought, why not use sub-layers as is common in other stacks:

1. Link layer: Layer upon which two peers that have a direct link, or
participate in the same payment network, communicate
2. Transfer/ ledger: Layer on which financial positions between two peers
are recorded

This reflects the already emerging HTLA model and many of our existing
plugins and ledger integrations.
Link Layer: XRP Paychan, Lightning
Ledger Layer: XRP Ledger, Bitcoin

This doesn't prevent us from defining a standard binary protocol that
defines all of the operations for both layers (like CLP) but I see value in
distinguishing between these two.


>
> > - The protocol should differentiate between the operation of preparing
> a transfer on a ledger and the operation of passing an ILP packet from one
> peer to another.
>
> The protocol assumes your conditional transfer is underpinned by some HTLA
> <https://github.com/interledger/rfcs/blob/master/0022-hashed-timelock-agreements/0022-hashed-timelock-agreements.md>.
> It doesn't care whether that's on-ledger or not.
>

What do you mean when you say "the protocol"? In my statement I am
referring to ILP.
My point above being that ILP expects ILP packets to be passed from peer to
peer but has no expectations about transfers.

It's perfectly legal (from an ILP perspective) for two peers to exchange
ILP packets with no transfers. Clearly if a node routes a packet on and has
no incoming transfer it's going to lose money but that's a consideration
for that node. It doesn't affect anyone else in the chain.

ILP doesn't assume anything about transfers at all, let alone conditional
transfers. It provides useful semantics for conditional transfers to be
used by two peers to transact as part of a larger ILP payment.


>
> > - The condition and timeout should be included in the ILP payment
> packet.
>
> I strongly disagree with this. We had this debate a year ago and I was on
> your side but was convinced that this is not a good idea.
>

Yes, I recall this and I'm sorry I didn't push harder on this point.
Unfortunately I think the decision to pull it out of the packet is mostly
driven by how our prototypes were implemented rather than good architecture.

>
> The layer below ILP must be capable of doing conditional transfers based
> on sha256 hashlocks with 32-byte preimages.
>

This is not true and I think it would be useful for us to agree on this as
this seems to be the argument I am coming up against most often. The peers
participating in a transfer that is part of an ILP payment may wish to use
conditional transfers as a way to enforce their agreement but this is not a
requirement of the protocol.

The agreement between any two peers is: "I will pay you X if you can
provide a receipt that Y was paid Z before T".
ILP provides a standard for expressing this agreement so that these can be
chained together BUT it is not a requirement that every agreement in the
chain uses the condition, and fulfillment provided at the ILP layer.


>
> As a result, the original condition and the corresponding preimage MUST be
> expressed in that layer.
>

As I have shown above, this is not true.


> Then the question is whether we should also include it in the packet that
> is forwarded. What ultimately convinced me is the following: All connectors
> MUST ignore the condition if it is in the packet, because they are only
> guaranteed their money back if they use the same condition from the
> incoming transfer they got.
>

Here is where the layering is being corrupted.

All connectors MUST inspect the condition in the ILP packet as part of
their decision to route the packet or not.
When the local transfer module of the connectors stack passes the ILP
packet up to the ILP module it should indicate the properties of the
incoming transfer that carried the packet.
This is essential firstly so that the routing logic in the ILP module can
record the incoming transfer identifier so it is able to use the correct
response id when it passes back the fulfillment or error.
The other properties that the ILP module should look at are the condition
and expiry on the incoming transfer.

If the incoming route uses conditional transfers and these are supposed to
match the condition and expiry in the ILP packet then the ILP module should
compare them and reject the packet if:
a) the conditions don't match OR
b) the expiry is too short

We should still discuss if the expiry should be set by the sender and left
unchanged or used like a TTL and decremented by each node.


> Also, the receiver will need to take out the condition in order to hash
> the packet for PSK or IPR.
>

This is completely normal. Zeroing a checksum field in a header before
calculating the checksum is VERY common precisely because it's long been
accepted that the right place to put that data is in the headers and the
work of zero'ing it out to calculate the checksum (or signature in our
case) is not material.


> So basically, no one wants the condition in there. It feels like it ought
> to be in there, but literally none of the parties want the extra 32 bytes
> in there.
>

"Nobody wants it there" is a terrible reason to abandon the correct design.
The whole purpose of a good architecture is you accept that there may be
cases in future that haven't been considered now so designing just for the
known cases is a bad idea.

Good architecture is not the same as optimization. Taking stuff out (even
when it feels wrong) to save a few bytes is a good sign that it's a bad
idea.


>
> The reason the timeout should not be in there is that there isn't a single
> timeout for the payment. There are multiple separate timeouts for each of
> the bilateral transfers. Those must go in the individual transfers and
> there is no sensible value to put in the Interledger packet.
>

As above, this is somewhat equivalent to the TTL in an IP packet. I'm open
to discussing if it should be a fixed value set by the sender where each
node uses their own value but has the sender-defined value as a reference
or it is actually decremented at each hop.

Either way, this is part of ILP not the ledger layer just like the
condition and fulfillment. It may be used by the ledger layer but that's
implementation specific. It belongs in the ILP packet.

>
>
>

Received on Tuesday, 15 August 2017 10:04:58 UTC