SPSP / STREAM Theory - A single money packet. ILP over HTTP

I have a question for the ILP gurus.

The spec indicates that once a client app receives back the query for the shared_secret and destination_account they are good to go to open a connection to the /pay endpoint and begin “sending money” using STREAM.

I am of the understanding that therefore I can open a socket connection to the /pay endpoint and send an ILP Prepare Packet WITH the payload of a STREAM Packet, which in turn contains the frame data.

The stream packet data in my example contains only two frames:

    // Load up the STREAM packet
    let sequence = 1;
    let mut frames = vec![Frame::StreamMoney(StreamMoneyFrame {
        stream_id: 1,
        shares: 1,
        frames.push(Frame::ConnectionNewAddress(ConnectionNewAddressFrame {
            source_account: source_account.clone(),
    let stream_packet = StreamPacketBuilder {
        ilp_packet_type: PacketType::Prepare,
        // TODO enforce min exchange rate
        prepare_amount: amount,
        frames: &frames,

Technically, it seems I can just at the very least treat this exchange in a conventional request / response if I plan to only send one packet?

Is this correct?

To clarify, as I test my theory, it is not working, hence the clarification request.

I am wrapping up that StreamPacket and sending it in an ILP PreparePacket.

let data = stream_packet.into_encrypted(shared_secret.as_bytes());
            let execution_condition = generate_condition(shared_secret.as_bytes(), &data);
            let prepare = PrepareBuilder {
                destination: destination_account.clone(),
                execution_condition: &execution_condition,
                expires_at: SystemTime::now() + Duration::from_secs(30),
                // TODO don't copy the data
                data: &data[..],

You are correct about the encapsulation of the packets (frames go in STREAM packets, which are encrypted using a key derived from the shared_secret that the SPSP endpoint returns, and the STREAM packet is included in the data of an ILP packet).

Regarding the /pay endpoint on the interledger.rs node, if you want to have the node forward a fully formed ILP packet you’ll need to send it to /ilp. /pay expects a JSON body with just the receiver and source_amount, and then the node will create the ILP packets and send them automatically.

Ok thank-you for that.

I noticed that the destination returned from the query is like : local.jazzy.rQbNPNDhHS2UsWYcXLCZ6O1C5Aoh_PyxNf4dZgtMhCE

The spec states the server may add segment information… yep just like that! BUT where is that used, it must hold in memory since the underlying store does not persist that.

It is not clear to me which packet routing strategy makes use of that and when I need to use it as THE address and not just local.jazzy.

You need to use local.jazzy.rQbNPNDhHS2UsWYcXLCZ6O1C5Aoh_PyxNf4dZgtMhCE in order for the STREAM server to fulfill the packet.

That implementation of the STREAM/SPSP server deterministically generates the shared_secret from a master secret and a public nonce. That nonce is included in the ILP address and is required for the receiver to be able to rederive the secrets needed to decrypt the data and generate the fulfillment.

Ok bonus question, does the /ilp endpoint know what to do with that address or will it only work in /pay ?

If it has a route for local.jazzy, it’ll be able to route local.jazzy.rQbNPNDhHS2UsWYcXLCZ6O1C5Aoh_PyxNf4dZgtMhCE

1 Like