Evan Schwartz

STREAMing Money and Data Over ILP

Originally published on the Interledger Blog

STREAM is the new TCP for Interledger. It makes building Interledger applications easier by splitting payments into packets, sending them over ILP, and automatically reassembling them for you. STREAM, or the Streaming Transport for the Realtime Exchange of Assets and Messages, is designed to be used whenever you want to send streaming micropayments or larger discrete payments. This tutorial will show you how to:

If you want to learn more about the STREAM protocol, check out the RFC.

Stream

Photo by Luke Besley on Unsplash

Install STREAM

You’ll need a local moneyd running and Node.js ≥ v8.10.

You can install the STREAM module in your Node.js project by running:

npm install --save ilp-protocol-stream

Or git clone the repo to run the example from there.

Listening for Connections

STREAM servers listen for incoming connections established over ILP. When you call await createServer({ plugin }) it connects to the given plugin and waits for ILP packets (ilp-plugin is used below to connect to moneyd).

The server uses generateAddressAndSecret() to generate a unique ILP address and shared secret for each client, which will be used to encrypt data and generate fulfillments for ILP packets. STREAM does not handle communicating the address and secret to the client, so this must be done using an external encrypted communication channel (like an HTTPS request/response in the case of SPSP).

const IlpStream = require('ilp-protocol-stream')
const createPlugin = require('ilp-plugin')

const serverPlugin = createPlugin()
const server = await IlpStream.createServer({
  plugin: serverPlugin
})

server.on('connection', (connection) => {
  console.log('server got connection')
  
  connection.on('stream', (stream) => {
    console.log(`server got a new stream: ${stream.id}`)

    // Set the maximum amount of money this stream can receive
    stream.setReceiveMax(10000)

    // Handle incoming money
    stream.on('money', (amount) => {
      console.log(`server stream ${stream.id} got incoming payment for: ${amount}`)
    })

    // Handle incoming data
    stream.on('data', (chunk) => {
      console.log(`server stream ${stream.id} got data: ${chunk.toString('utf8')}`)
    })
  })
})

// These would need to be passed from the server to the client using
// some encrypted communication channel (not provided by STREAM)
const { destinationAccount, sharedSecret } = server.generateAddressAndSecret()
console.log(`server generated ILP address (${destinationAccount}) and shared secret (${sharedSecret.toString('hex')}) for client`)

Server side (see here for the full example)

If you notice the connection.on('stream'... line, that’s included because each connection can include many separate streams of money and data. Streams are used as a flexible encapsulation for payments and messages (you can read more about this here).

Connecting to a Server

Clients connect using createConnection once they have the ILP address and shared secret from the server.

const clientPlugin = createPlugin()
const clientConn = await IlpStream.createConnection({
  plugin: clientPlugin,
  destinationAccount,
  sharedSecret
})

// Streams are automatically given ids (client-initiated ones are odd, server-initiated are even)
const streamA = clientConn.createStream()
const streamB = clientConn.createStream()

console.log(`sending data to server on stream ${streamA.id}`)
streamA.write('hello there!')

console.log(`sending data to server on stream ${streamB.id}`)
streamB.write('hello there!')

console.log(`sending money to server on stream ${streamA.id}`)
await streamA.sendTotal(100)
console.log('sent 100 units')

console.log(`sending money to server on stream ${streamB.id}`)
await streamB.sendTotal(200)
console.log('sent 200 units')

Client side (see here for the full example)

The streams returned by connection.createStream expose the Node.js Stream API for sending data. When you want to send money, you call sendTotal with the total amount you want to send on that stream (so in this example, 200 units will be sent, not 300).

Running the two examples together (see the full script here), you’ll see:

ilp-protocol-stream git:master ❯ node example.js                                                                                                                                                                                                             ⏎
server generated ILP address (test.amundsen.bmp.btp18q1xrp.tPcfTiM2_-stOLrODMIQbFX7BLln60TC8qy2BefdDsQ.local.X22YdPW64RhBKHCHYYjVDnpz7x83I76gbWq6_JY4Ngs.PNOMQrElaGRlc5VK2uKIFSug) and shared secret (e945dd40aecfe9191e83e8dce2ab81d1fe4760c9ef5a0f71b227adc9308ca173) for client
server got connection
sending data to server on stream 1
sending data to server on stream 3
sending money to server on stream 1
server got a new stream: 1
server got a new stream: 3
server stream 1 got incoming payment for: 100
server stream 1 got data: hello there!
server stream 3 got data: hello there!
sent 100 units
sending money to server on stream 3
server stream 3 got incoming payment for: 200
sent 200 units

Multiplexed Bidirectional Streams

As you can see above, STREAM connections consist of any number of streams. This is inspired by QUIC, a newer alternative to TCP, that multiplexes multiple logical streams of data over a single connection between the client and server. The lightweight stream abstraction can either be used for ongoing interactions or to frame larger requests or responses.

In addition to multiplexing, STREAM also provides for sending money or data in both directions. A client can send money to the server, or vice versa. An example of a server sending money to the client would be something like the gift card server that Ben Sharafian came up with.

Sending Data Over ILP

STREAM can be used to send messages directly through ILP, rather than over the internet. Now, why would you want to do that? For some applications, it may be easier to keep messages together with the payment (like a memo). There are also some crazier, more interesting possibilities that come from sending data over ILP, but we’ll explore those further in a future post.

If you have any questions about STREAM or want to use it in an application, feel free to ask questions on Gitter or one of our community calls!

#interledger