Bidirectional streams of money over ILP
Work in Progress -- Nothing is finished and everything could change!
See the issues for features under development.
- Clone the repo
npm install
npm run build
DEBUG=ilp-protocol* node example.js
Duplex payment streams over ILP (and PSK)
Home Page: https://github.com/interledger/rfcs/issues/388
Bidirectional streams of money over ILP
Work in Progress -- Nothing is finished and everything could change!
See the issues for features under development.
npm install
npm run build
DEBUG=ilp-protocol* node example.js
Right now they are communicated as UInt64s, but VarInts would take up fewer bytes when the amount is small and allow larger numbers so maybe that would be better?
@adrianhopebailie raised the point that we may want to separate the generation of the destinationAccount
and sharedSecret
from the creation of the socket object.
The two reasons I could see for doing this would be:
The downsides of doing this would be:
Right now, each side communicates a destinationAccount
and sharedSecret
to the other side. We could remove one of the fields from the protocol messages if the client side could listen using the same sharedSecret
as the server.
This would require a function to be added to the PSK2 module that allows you to specify an address, secret, and handler rather than just having it generate the address and corresponding secret.
It is possible for the two sides to set their limits such that at least one of them is going to end up unhappy. It might be better to try to determine if this is going to be the case before sending any money, to error out before money is wasted.
Should there be a way to call pause
on the socket, which would let the other party know that we're not going to send or receive any more until the user unpauses the stream?
https://github.com/emschwartz/ilp-protocol-paystream/blob/master/index.ts#L156
This line will cause problems if the minBalance starts negative; the first chunk (at which the balance is 0) causes the minBalance to become 0. This means to receive a negative amount you need to set enableRefunds to true.
If you don't raise the minimum each time you get a chunk in, the other person can reverse the flow of funds and get a refund. We probably should have an option to enableRefunds
that is disabled by default (so the limit will automatically go up every time you get a chunk in).
Instead of using the min/max balance, we could make the API a little more similar to normal sockets (like @adrianhopebailie suggests in #19) by having methods like send
and receive
on the socket object.
Use Case | Min/Max Balance API | Alternative |
---|---|---|
Sending | client.setMinAndMaxBalance(-1000) |
client.add(1000); client.flush() (or client.send(1000) ) |
Requesting | server.setMinBalance(1000) |
server.receive(1000) |
Making funds available to be requested | client.setMinBalance(-1000) |
client.add(1000) |
Enabling refunds | (not modifying the min balance as money is received) | server.receive(1000); server.on('chunk', (amount) => client.add(amount)) |
You would add and remove money from the socket, instead of setting the range you want your balance to end up in. When you call receive
it would reduce the socket's balance
by that amount if it's already got that much or it would request more from the sender and wait until it has reached that amount.
Thoughts? Would that be easier or harder to work with?
The simple logic would be to say that one socket corresponds to one invoice. But if an invoice is persistent and several people can pay to it, there should be no reason why there cannot be several sockets open and paying off one invoice.
It's not clear to me how best to manage the balance to accomplish this. Right now I'm just closing the channel when the invoice is filled, but that feels a little bit hacky. Another option would be to change the balance of all sockets related to an invoice whenever a packet comes in, but then it creates a ton of messages.
Currently, in an SPSP server I create a new receiver socket on every single query. After a while they close if there is no activity, but it seems like an SPSP endpoint could be overwhelmed by queries if it keeps the sockets around for a while. Also if the sender begins to send but then stops, the socket does not appear to time out.
When I call setMinAndMaxBalance(10000)
on the receiving side, and pass a minimum balance of -Infinity
to the sending side, 100081
ends up making it to the receiver. The sender is using USD, and the receiver is using JPY. The source amount sent is 937
at an exchange rate of 106.81
.
It appears as though the receiving amount is impossible to hit exactly with these parameters, and if the receiver were to send the 81 micro-yen back, it would be too small to get one micro-dollar. Although I didn't notice anywhere where the receiver attempted to send the excess back.
What should be done in this case?
Currently, the data that goes across the wire for every chunk includes a spot for the destinationAccount
. At the moment this is only used on the first message the client sends to the server, and is left as an empty string (encoded as a single 0-byte) for every subsequent message.
We should probably make a separate message type that has the destinationAccount
field.
One question related to this is whether that message should be thought of as a "connect" message, or whether you could send that later, for example if you want to change the ILP address the other party should be sending to.
Right now it defaults to Infinity
but @justmoon raised a point in #22 (comment) that it might be better to start at 0.
It should automatically adjust the chunk size based on how much more the other side will accept and the path's MPS.
If you send 1000 and ~1 or 0 units get to the other side, you probably need to quote with a different amount
If the Maximum Payment Size is lower than the total bandwidth, we would want to send multiple packets at the same time. Right now this is unlikely to be the case, but it may be so in the future.
Should there be an option to keep it open but close it by default when it reaches the limit?
Should there be a way to communicate in the protocol that you're going to close the socket?
Suggestion from @sharafian
Might be useful for determining if you want to request a refund, or how much to expect if you do
When the sockets connect, they send one chunk just to figure out the exchange rate. Every subsequent will have the minDestinationAmount
set so that the receiver won't accept the chunk if the exchange rate has gotten worse than the original rate by the given slippage
percentage. The question is what the default slippage should be.
If the default slippage
is 1%, clients and servers will tolerate the exchange rate getting worse by at most 1% before throwing an error and stopping the flow of money. If we set it to 0% by default, that increases the chances that payments will fail because of legitimate exchange rate movements but also changes the incentive for connectors who might maliciously change the exchange rate. If implementations have zero tolerance by default, changing the exchange rate while a payment is in flight will cause user-level errors that a sneaky connector would not want.
Working with the minBalance
and maxBalance
directly can be a bit unintuitive. Should we add methods like sendSourceAmount
that return a Promise that resolves when the amount has been sent, or receiveAmount
that resolves when an amount has been received?
One thing to figure out with such methods is how to deal with the case where the balance is not already at a limit. If the balance
is 0 and the minBalance
is 100 and you call receiveAmount(50)
, should it wait until the balance is 50 or 150? Also, what happens if you call one of these methods again before the first call has resolved? Should it add to (or subtract from) the limit and wait until the updated limit is reached?
Should this protocol be called something other than "paystream" or "payment stream"?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.