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

Add STREAM receipts #570

Merged
merged 20 commits into from
Apr 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: The Simple Payment Setup Protocol (SPSP)
draft: 9
draft: 10
---
# Simple Payment Setup Protocol (SPSP)

Expand Down Expand Up @@ -62,14 +62,26 @@ Host: example.com
Accept: application/spsp4+json, application/spsp+json
```

##### Request Headers to Support STREAM Receipts

The request MAY contain at least the following headers in order to pre-share [STREAM Receipt](../proposals/0000-stream-receipts.md) details between the SPSP Server and [receipt verifier](../proposals/0000-stream-receipts.md#conventions-and-definitions):

| Header | Description |
|:----------------|:-----------------------------------------------------------|
| `Receipt-Nonce` | A base64-encoded Receipt Nonce. |
| `Receipt-Secret` | A base64-encoded Receipt Secret. |

The SPSP Client MAY be provided with an SPSP Endpoint belonging to the receipt verifier, which would add the receipt headers and proxy the query to the SPSP Server.
wilsonianb marked this conversation as resolved.
Show resolved Hide resolved

#### Response
``` http
HTTP/1.1 200 OK
Content-Type: application/spsp4+json

{
"destination_account": "example.ilpdemo.red.bob",
"shared_secret": "6jR5iNIVRvqeasJeCty6C+YB5X9FhSOUPCL/5nha5Vs="
"shared_secret": "6jR5iNIVRvqeasJeCty6C+YB5X9FhSOUPCL/5nha5Vs=",
"receipts_enabled": true
}
```
More information about the parameters can be found in section [Response Body](#response-body).
Expand Down Expand Up @@ -100,6 +112,7 @@ The response body is a JSON object that includes basic account details necessary
|---|---|---|
| `destination_account` | [ILP Address](../0015-ilp-addresses/0015-ilp-addresses.md) | ILP Address of the SPSP Server. In case of push payments, this is the receiver. In case of pull payments, this is the sender. |
| `shared_secret` | 32 bytes, [base64 (base64url) encoded](https://en.wikipedia.org/wiki/Base64) (including padding) | The shared secret to be used by this specific HTTP client in the [STREAM](../0029-stream/0029-stream.md). Should be shared only by the server and this specific HTTP client, and should therefore be different in each query response. Even though clients SHOULD accept base64url encoded secrets, base64 encoded secrets are recommended. |
| `receipts_enabled` | Boolean | _(OPTIONAL)_ If `true`, the SPSP server will issue STREAM Receipts in the STREAM connection. If `false` or omitted, the server will not issue STREAM Receipts. |

##### Errors

Expand Down
17 changes: 16 additions & 1 deletion 0029-stream/0029-stream.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: STREAM - A Multiplexed Money and Data Transport for ILP
draft: 8
draft: 9
---

# STREAM: A Multiplexed Money and Data Transport for ILP
Expand Down Expand Up @@ -55,6 +55,7 @@ This document specifies the STREAM Interledger Transport protocol, which provide
- [5.3.12. `StreamMaxData` Frame](#5312-streammaxdata-frame)
- [5.3.13. `StreamDataBlocked` Frame](#5313-streamdatablocked-frame)
- [5.3.14. `ConnectionAssetDetails` Frame](#5314-connectionassetdetails-frame)
- [5.3.15. `StreamReceipt` Frame](#5315-streamreceipt-frame)
- [5.4. Error Codes](#54-error-codes)
- [6. Condition and Fulfillment Generation](#6-condition-and-fulfillment-generation)
- [6.1. Unfulfillable Condition](#61-unfulfillable-condition)
Expand Down Expand Up @@ -158,6 +159,8 @@ A server MUST communicate the following values to a client using an **authentica

To avoid storing a 32 byte secret for each connection, a server MAY deterministically generate the shared secret for each connection from a single server secret and a nonce appended to the ILP Address given to a particular client, for example by using an HMAC.

For each new connection, a server MAY be provided with a pre-shared 32 byte Receipt Secret (to generate [STREAM receipts](../proposals/0000-stream-receipts.md)) and a 16 byte Receipt Nonce (to include in those receipts). To avoid storing this data for each connection, a Server MAY deterministically append this data to the ILP Address used for a Connection. If doing so, the server MUST encrypt the Receipt Secret.

### 4.2. Matching Packets to Connections

Incoming packets can either be associated with an existing connection, or, for servers, potentially create a new connection. Endpoints MAY append extra segments to the ILP addresses assigned to them by their upstream connectors to help direct incoming packets.
Expand Down Expand Up @@ -192,6 +195,10 @@ Client streams MUST be odd-numbered starting with 1 and server-initiated streams

Money can be sent for a given stream by sending an ILP Prepare packet with a non-zero `amount` and a `StreamMoney` frame in the STREAM packet to indicate which stream the money is for. A single ILP Prepare can carry value destined for multiple streams and the `shares` field in each of the `StreamMoney` frames indicates what portion of the Prepare amount should go to each stream.

The receiver SHOULD include `StreamReceipt` frames in the ILP Fulfill packet indicating the total amount of money received in each stream, unless a Receipt Secret and Receipt Nonce were not pre-shared with the receiver.

To use [STREAM receipts](../proposals/0000-stream-receipts.md), the Receipt Secret and Receipt Nonce are pre-shared between the receiver and [receipt verifier](../proposals/0000-stream-receipts.md#conventions-and-definitions). Receipts are generated by the receiver and passed to the sender, who may submit the receipts directly or indirectly to the verifier. This allows the verifier to confirm the payment, as only the receiver and the verifier know the Receipt Secret.

#### 4.4.3. Sending Data

Data can be sent for a given stream by sending an ILP Prepare packet with a `StreamData` frame in the STREAM packet. A single ILP Prepare can carry data destined for multiple streams.
Expand Down Expand Up @@ -313,6 +320,7 @@ The frame types are as follows and each is described in greater detail below:
| `0x14` | Stream Data |
| `0x15` | Stream Data Max |
| `0x16` | Stream Data Blocked |
| `0x17` | Stream Receipt |

#### 5.3.1. `ConnectionClose` Frame

Expand Down Expand Up @@ -436,6 +444,13 @@ In other words, if a sender resends data (e.g. because a packet was lost), it MU

Asset details exposed by this frame MUST NOT change during the lifetime of a Connection.

#### 5.3.15. `StreamReceipt` Frame

| Field | Type | Description |
|---|---|---|
| Stream ID | VarUInt | Identifier of the stream this frame refers to. |
| Receipt | VarOctetString | Length-prefixed [STREAM Receipt](../proposals/0000-stream-receipts.md#specification) provided by the receiver as proof of the total amount received on this stream. Note that the stream sender is not expected to decode the receipt itself. |

### 5.4. Error Codes

Error codes are sent in `StreamClose` and `ConnectionClose` frames to indicate what caused the stream or connection to be closed.
Expand Down
17 changes: 17 additions & 0 deletions 0029-stream/test-vectors/StreamPacketFixtures.json
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,23 @@
},
"buffer": "AQwBAAEAAQEWCwF7CP//////////"
},
{
"name": "frame:stream_receipt",
"packet": {
"sequence": "0",
"packetType": 12,
"amount": "0",
"frames": [
{
"type": 23,
"name": "StreamReceipt",
"streamId": "1",
"receipt": "AQAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAfTBIvoCUt67Zy1ZGCP3EOmVFtZzhc85fah8yPnoyL9RMA=="
}
]
},
"buffer": "AQwBAAEAAQEXPQEBOgEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAH0wSL6AlLeu2ctWRgj9xDplRbWc4XPOX2ofMj56Mi/UTA="
},
{
"name": "frame:stream_max_money:receive_max:too_big",
"packet": {
Expand Down
16 changes: 15 additions & 1 deletion asn1/Stream.asn
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ IMPORTS

Address
FROM InterledgerTypes

Receipt
FROM StreamReceipt
;

StreamEncryptionEnvelope ::= SEQUENCE {
Expand Down Expand Up @@ -67,7 +70,8 @@ FrameSet FRAME ::= {
{19 StreamMoneyBlocked} |
{20 StreamData} |
{21 StreamMaxData} |
{22 StreamDataBlocked}
{22 StreamDataBlocked} |
{23 StreamReceipt}
}

StreamFrame ::= SEQUENCE {
Expand Down Expand Up @@ -174,4 +178,14 @@ StreamDataBlocked ::= SEQUENCE {
maxOffset VarUInt
}

StreamReceipt ::= SEQUENCE {
-- Identifier of the stream
streamId VarUInt,

-- Length-prefixed Receipt for verifying the total amount received on this stream
-- The sender is not expected to decode the Receipt fields but must be able to decode the
-- receipt string in the frame, even if it represents an unsupported Receipt version
receipt Receipt
sappenin marked this conversation as resolved.
Show resolved Hide resolved
}

END
45 changes: 45 additions & 0 deletions asn1/StreamReceipt.asn
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
StreamReceipt
DEFINITIONS
AUTOMATIC TAGS ::=
BEGIN

IMPORTS
UInt8,
UInt64,
UInt128,
UInt256
FROM GenericTypes
;

RECEIPT ::= CLASS {
&typeId UInt8 UNIQUE,
&Type
} WITH SYNTAX {&typeId &Type}

ReceiptSet RECEIPT ::= {
{1 ReceiptV1}
}

Receipt ::= SEQUENCE {
type RECEIPT.&typeId ({ReceiptSet}),
-- NOT length-prefixed
data RECEIPT.&Type ({ReceiptSet}{@type})
}

ReceiptV1 ::= SEQUENCE {
-- Identifier of the connection
nonce UInt128,

-- Identifier of the stream
streamId UInt8,

-- Total amount the stream has received so far
totalReceived UInt64,

-- HMAC-SHA256 using a 32 byte Secret
-- The message is the concatenation of the 1 byte type/version (always 1) and all other fields,
-- in the order listed above
hmac UInt256
}

END
93 changes: 93 additions & 0 deletions proposals/0000-stream-receipts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
title: STREAM Receipts
type: proposal
draft: 1
---

# STREAM Receipts
> Proof provided by a [STREAM](../0029-stream/0029-stream.md) receiver of the total amount received on a stream.

## Motivation

Interledger payment verification is necessary for multiple use cases including Web Monetization, where ILP payments must be verified to securely serve exclusive content, and API micropayments.

Standardized receipt functionality at the STREAM layer enables the payment recipient or a third party to verify payment without having access to the corresponding ILP packets.

## Conventions and Definitions

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in BCP 14 [RFC2119](https://tools.ietf.org/html/rfc2119) [RFC8174](https://tools.ietf.org/html/rfc8174) when, and only when, they appear in all capitals, as shown here.

Definitions of terms that are used in this document:

- **Receiver** - The party that is receiving funds over STREAM
- **Verifier** - The party that wishes to verify that ILP payments occurred to the Receiver
- **Sender** - The party that is sending funds over STREAM
- **Receipt Nonce** - A unique nonce used to identify a STREAM connection in a Receipt
- **Receipt Secret** - The key used to generate a Receipt's HMAC
- **Receipt** - A binary object containing a version number, the Receipt Nonce, a Stream ID on the STREAM connection, an integer amount of funds received on a stream, and an HMAC over all those fields

## Overview

In a STREAM connection, a Receipt is generated by the Receiver for every fulfilled ILP packet. Each Receipt on a stream contains a progressively higher amount, representing the total amount received on that stream. So the latest Receipt on a stream replaces any previous Receipts on that stream.

The Verifier SHOULD consider a Receipt valid if:

- The Receipt contents produce an HMAC (using the Receipt Secret as the seed) identical to the Receipt's HMAC field.
- The amount is greater than any other Receipts with that Receipt Nonce and Stream ID.
- The Receipt is not stale.

The Verifier SHOULD determine a length of time starting from when a Receipt Nonce is generated that corresponding Receipts will be considered valid. Mechanisms by which the Verifier can check Receipts' staleness include, but are not limited to:
- persistently storing the Receipt Nonce and a timestamp relative to when the Nonce was generated
- encoding a timestamp in the Receipt Nonce

If keeping a balance of how much the Sender has paid, the Verifier SHOULD store the Receipt amount under the Receipt Nonce + Stream ID. If there is already an amount stored for that Stream ID, the Verifier SHOULD only credit the Sender the difference in Receipt amounts.

Alteratively, the Verifier MAY forego checking the Receipt amount if they are only concerned with whether or not the Sender paid at all, as opposed to how much.

### Flow

This flow occurs for each STREAM connection with Receipts enabled.

1. Verifier MUST generate a unique Receipt Nonce.

2. Verifier MUST communicate the Receipt Nonce and Receipt Secret to the Receiver. The Receipt Secret MUST NOT be communicated to the Sender.

3. Once the STREAM connection is open and the Sender is sending, the Receiver MUST add an additional frame containing a Receipt to each ILP Fulfill packet.

4. Sender MUST give the Receipt directly or indirectly to the Verifier.

5. Verifier MUST verify the Receipt before accepting the Receipt amount as paid.

### Stateless Verifier

The Verifier MAY generate the Receipt Secret by producing an HMAC of the Receipt Nonce using a seed (known only to itself) as the HMAC key. This ensures the Receipt Secret will be unique to this STREAM connection and allows the Verifier to reproduce the Receipt Secret from any Receipts containing the Receipt Nonce.

### Stateless Receiver

Before a STREAM connection is established, the Receiver MAY use the Receipt Nonce and Receipt Secret as parameters to generate connection details (ILP Address and shared secret). The Receiver MAY encode the Receipt Nonce and Receipt Secret in the STREAM server ILP Address to allow the STREAM receiver to avoid persisting state. If doing so, the Receipt Secret MUST be encrypted. The Receipt Nonce MUST be unique each time STREAM parameters are generated, since it MUST be unique globally.

### SPSP

The Verifier MAY communicate the Receipt Nonce and Receipt Secret to the Receiver by proxying the Sender's [SPSP](../0009-simple-payment-setup-protocol/0009-simple-payment-setup-protocol.md) query, as follows:

1. Sender queries the Verifier's payment pointer URL.
2. Verifier MUST add the Receipt Nonce and Receipt Secret to the request in `Receipt-Nonce` and `Receipt-Secret` headers.
3. Verifier MUST pass the query to the Receiver.
4. Receiver MUST include `"receipts_enabled": true` in a successful SPSP response.
5. Verifier MAY return a `409` error code if the response does not include `"receipts_enabled": true`. Otherwise, the Verifier MUST return the SPSP response to the Sender.

### Web Monetization

A [Web Monetization](https://webmonetization.org/specification.html) Sender MUST include each ILP Fulfill packet's Receipt in the corresponding `monetizationprogress` event. This allows the webpage to pass Receipts to the Verifier.

## Specification

A Receipt MUST contain the following fields encoded using [CANONICAL-OER](https://github.com/interledger/rfcs/blob/master/0030-notes-on-oer-encoding/0030-notes-on-oer-encoding.md#canonical-oer) (find more details [here](https://github.com/interledger/rfcs/blob/05ab457b9301b031e1ec954632582a325c4907b4/asn1/README.md)):

| Field | Type | Description |
|---|---|---|
| Version | UInt8 | `1` for this version. |
| Receipt Nonce | UInt128 | A unique nonce pre-shared between the Verifier and the Receiver used to identify the STREAM connection. |
| Stream ID | UInt8 | Identifier of the stream this Receipt refers to. |
| Total Received | UInt64 | Total amount, denominated in the units of the Receiver, that the Receiver has received on this stream thus far. |
| HMAC | UInt256 | HMAC-SHA256 using the 32 byte Receipt Secret, which is pre-shared between the Verifier and the Receiver. The HMAC message is the concatenation of all other Receipt fields, in the order listed above. |