diff --git a/0001-interledger-architecture/0001-interledger-architecture.md b/0001-interledger-architecture/0001-interledger-architecture.md index 990b8035..d7661281 100644 --- a/0001-interledger-architecture/0001-interledger-architecture.md +++ b/0001-interledger-architecture/0001-interledger-architecture.md @@ -1,252 +1,147 @@ +--- +title: Interledger Architecture +draft: 3 +--- # Interledger Architecture -This document outlines the Interledger architecture and explains how the different layers relate to each other. +Interledger provides for secure payments across multiple assets on different ledgers. The architecture consists of a conceptual model for interledger payments, a mechanism for securing payments, and a suite of protocols that implement this design. -The Interledger architecture is heavily inspired by the Internet architecture described in [RFC 1122](https://tools.ietf.org/html/rfc1122), [RFC 1123](https://tools.ietf.org/html/rfc1123) and [RFC 1009](https://tools.ietf.org/html/rfc1009). - -**This is a strawman proposal.** Choices made in this document are merely intended as a starting point. - -## Introduction - -This document is intended to provide an overview over the Interledger architecture and protocol suite. - -## The Interledger Architecture - -All electronic transfers have to be recorded in stateful systems. Otherwise the same instance of an asset could be sent to two different destinations, essentially duplicating the asset. This is also known as a double-spend. We call these stateful systems *ledgers*. - -Ledgers consist of *accounts*. Accounts are individual buckets containing a decimal amount of one type of asset, usually (but not always) associated with an owner. This amount is also called the account's *balance*. Balances can be positive or negative, representing assets or liabilities. - -Assets can be transferred between accounts on the same ledger. We call these events *book transfers* or just *transfers*. - -A transfer of assets across ledgers requires two or more local book transfers. Some system must know the relationship between the two transfers. We call this system a *connector*. The same system may act as both a ledger and a connector. - -The Interledger is a network of independent and diverse ledgers. Each account is part of a particular ledger; the Interledger itself is only conceptual. - -### Architectural Assumptions - -#### Ledgers contain only one type of asset. - -We assume that all assets within one ledger are fungible. Within one ledger transfers can happen directly from sender to recipient and do not require a third party exchanger. - -When a single organization such as a bank has accounts in multiple different assets of different types, we treat each asset as belonging to its own ledger. - -#### Connectors do not keep transfer state information. - -Just as Internet gateways do not keep connection state information, connectors do not keep transfer state information. All state is kept at the ledger level and connectors merely react to and trigger ledger events. - -Note that this does not mean that all connectors are necessarily stateless. They may keep track of their available liquidity or even maintain an internal ledger if they are transacting on behalf of third parties. But we are assuming that they do not keep authoritative state about transfers. - -#### Routing complexity should be in the connectors. - -Routing is a complex and difficult problem, and ought to be performed by the connectors, not the end users of the system. An important objective is to isolate user software from changes caused by the inevitable evolution of the Interledger routing architecture. - -#### The system must tolerate wide ledger variation. - -A basic objective of the Interledger design is to tolerate a wide range of ledger characteristics --- e.g. throughput, latency, fees, access restrictions, reliability, decentralization. In addition it must support all types of different asset characteristics --- e.g. divisibility, fungibility, interest/demurrage, issuance. - -### Interledger Protocol Suite - -The Interledger architecture is separated into four layers: - -![Interledger architecture layers](assets/interledger-architecture-layers.png) - - -#### Application Layer - -The application layer is the top layer of the Interledger protocol suite. Protocols on this layer are responsible for negotiating the key properties of a payment: +The [Interledger Protocol (ILP)](#interledger-layer) is the core of the Interledger protocol suite. -* Source Account -* Destination Account -* Amount -* Condition +Colloquially, the whole Interledger stack is sometimes referred to as "ILP". Technically, however, the Interledger Protocol is only one layer in the stack. -Once these parameters are decided, the application layer protocol will instantiate a transport layer protocol to initiate the payment. - -Application layer protocols include open protocols such as the Open Web Payment Scheme (OWPS) and proprietary protocols such as the Ripple Payment Services Protocol (RPSP). - -#### Transport Layer - -The transport layer provides end-to-end transaction services for applications. There are three primary transport layer protocols at the moment: - -* Optimistic Transport Protocol (OTP) -* Universal Transport Protocol (UTP) -* Atomic Transport Protocol (ATP) - -OTP is a trivial transport protocol which simply transfers funds without the protection of escrow. Hence it is only suitable for very small microtransactions where efficiency matters more than reliability. - -UTP uses escrowed transfers to guarantee that no funds leave the sender's account unless the transaction was successfully completed. It is suitable for transactions of any size and should be considered the default choice. - -ATP uses escrowed transfers and a set of pre-agreed, trusted notaries in order to guarantee atomicity across multiple ledgers. It is relatively expensive to set up, but can be used to reduce the need for reconciliation. - -#### Interledger Layer - -All Interledger transport protocols use the Interledger Protocol (ILP) to communicate with connectors about transfer requests. This may include requesting a quote or requesting a transfer on another ledger. - -The Interledger layer defines a standard way to refer to ledgers, accounts and amounts. This is used in routing as well as to try and make quotes comparable. - -#### Ledger Layer - -In order to facilitate transfers between accounts, ledgers must implement some API or protocol. We call this the ledger layer. There is a wide variety of ledger layer protocols, corresponding to the many different types of ledger. - -## Ledger Layer - -### Introduction - -Ledger protocols are responsible for executing the individual transfers that constitute an Interledger transaction. They are also used by connectors to communicate with each other. Ledger layer protocols can differ widely depending on the type of ledger. For example, a central ledger will likely use a very different protocol than a blockchain. - -### Requirements - -#### Minimal Support - -For minimal Interledger support, the ledger MUST have the ability for funds to be transferred from one account to another. This enables OTP transactions to pass through this ledger. It also allows third parties to act as escrow providers in order to enable UTP and ATP transactions on the ledger. - -#### Basic Support - -For basic Interledger support, a ledger MUST fulfill the requirements for minimal support and also the following: - -The ledger MUST provide cryptographic escrow. Escrow means that transfers can have four states: - -![enter image description here](https://lh3.googleusercontent.com/-QHsehrPTXgM/Vu_zs1dLWPI/AAAAAAAAEys/zchHKJj_MQs/s0/Transfer%252520States.png "Transfer States") - -* Proposed -- Nothing has happened yet. -* Prepared -- Funds are held in escrow. -* Executed -- The transfer has completed. -* Rejected -- The transfer has been cancaled (and funds returned to the sender.) - -**Hint:** Escrow is the financial equivalent of a [two-phase commit](http://foldoc.org/two-phase%20commit). - - -The ledger MUST be able to verify a simple SHA-256 hashlock cryptographic escrow condition in order to automatically trigger the release of the escrow. It must also allow a timeout to automatically roll back an escrowed transfer. +The Interledger architecture is heavily inspired by the Internet architecture described in [RFC 1122](https://tools.ietf.org/html/rfc1122), [RFC 1123](https://tools.ietf.org/html/rfc1123) and [RFC 1009](https://tools.ietf.org/html/rfc1009). -The ledger MUST support attaching a short message or *memo* to each transfer. +## Interledger Model -#### Full Support +![Interledger Model: Sender, Connectors, Receiver](../shared/graphs/interledger-model.svg) -The ledger MUST fulfill the requirements for basic support and also the following: +### Connectors -It MUST support all cryptographic condition types with the status "recommended". +A *connector* is a system that provides the service of forwarding Interledger packets towards the destination provided in the packet. Packets are forwarded across connections (called *accounts*) between senders, connectors and receivers. -It MUST support memos to be added for the credited and debited accounts separately and SHOULD support fairly large memo sizes. +Interledger packets describe amounts of money, which may be settled individually or in aggregate through various kinds of external payment systems collectively referred to as *ledgers*. Ledgers can include blockchains, but also banks, peer-to-peer payment schemes, automated clearing house (ACH), mobile money institutions and even central-bank operated real-time gross settlement (RTGS) systems among many other types. Some ledgers support very efficient settlement, while others support only slower forms. In general, the faster and cheaper the ledger, the more often an account is settled. See [IL-RFC 22](../0022-hashed-timelock-agreements/0022-hashed-timelock-agreements.md) for more information on different ledger types and settlement strategies. Settlement does not occur between the sender and receiver directly. Each connector settles only with its direct neighbors. -It MUST support a way to discover connectors connected to the ledger. +Connectors may generate revenue from Interledger payments by reducing the amount on each packet slightly before forwarding. Connectors may also convert an incoming packet denominated in one currency or asset type to an outgoing packet denominated in another currency or asset type. -It MUST support a way to look up a fulfillment by condition hash. It SHOULD automatically reject new transfers (that have not been prepared yet) that have an execution condition for which the ledger already knows the fulfillment. This aids in [UTP error recovery](#error-recovery). +Connectors accept some risk, which they must consider when deciding their business model. See [IL-RFC 18](../0018-connector-risk-mitigations/0018-connector-risk-mitigations.md) for more details on the risks and their mitigations. -### Example Protocols +A single Interledger packet may traverse any number of connectors on its way to the destination. For efficiency reasons, each connector seeks to forward the packet on the shortest known path to the destination. See [IL-RFC 10](../0010-connector-to-connector-protocol/0010-connector-to-connector-protocol.md) for more information on routing in Interledger. -#### Simple Ledger Protocol (SLP) +### Senders -Simple Ledger Protocol (SLP) is a RESTful, JSON-based protocol that was developed specifically to provide the minimum functionality required for full Interledger support. +A *sender* is a system which sends Interledger packets in order to: -A reference implementation of a ledger using SLP can be found [here](https://github.com/interledger/five-bells-ledger). +1. Transfer value from itself to a receiver or +2. Exchange value from one type to another. -#### Blockchain Protocols (e.g. Bitcoin) +When transferring value, the sender will first request payment details from the receiver, including the receiver's ILP address. Once it has those details, it will start sending Interledger packets using this ILP address as the destination. A large payment may be split into many smaller pieces and reassembled by the receiver. For more information on the overall payment process, see the section [Interledger Protocol Suite](#interledger-protocol-suite). -Blockchains are distributed, peer-to-peer systems that provide consensus over a single shared state. Any blockchain that supports escrowed funds transfers is in principle capable of acting as a ledger connected to the Interledger. +A sender may also exchange assets by acting as both sender and receiver. In this case, it would send one asset and receive another, effectively exchanging one for the other. -Bitcoin for instance supports multiple credits and debits as well as SHA-256 hashlocked escrow transfers which means it can participate in OTP/ILP and UTP/ILP Interledger transactions. +Senders typically choose a connector and send all their traffic through this connector. If they are at any point dissatisfied with their connector, they may switch to a different one. Sophisticated senders may operate a connector of their own and maintain relationships with multiple connectors to minimize costs and maximize availability. -#### Legacy Protocols (e.g. ACH, ISO 20022) +### Receivers -Legacy protocols often do not provide escrow functionality. In this case, the protocol can either be upgraded, or a highly trusted participant (e.g. a bank) can act as an escrow provider. +A *receiver* is a system which accepts incoming Interledger packets and provides a cryptographic receipt for each packet which is returned to the sender along the same path. For more information on the cryptographic exchange, see [Payment Flow](#payment-flow). -#### Proprietary Wallet Protocols (e.g. PayPal API) +**Interledger ensures that senders' funds are safe throughout an multi-hop payment and cannot be stolen by faulty or malicious intermediaries (see [Interledger Security](#interledger-security)).** -There are large numbers of proprietary ledgers out there. This includes web-based and mobile wallets. These types of systems can usually be extended with cryptographic escrow functionality by their operator in order to connect them to the Interledger. +### The Interledger -#### Other Proprietary Protocols (e.g. Skype API) +The "Interledger protocol suite" can be used among any network of peered connectors, be it public or private. There is no single network that all parties must connect to to use these protocols. -Some proprietary protocols intentionally do not provide general ledger functionality. A common example are stored-value systems, such as gift cards, loyalty points or pre-paid accounts. Such systems can still be connected to the Interledger in a limited capacity. For example a pre-paid account ledger could be set up to act as a receiving ledger, but not as a sending or intermediate ledger. +"The Interledger" is the name of a public instance of the Interledger protocol suite which seeks to provide a coherent global financial infrastructure. Ideally, anyone connected to the Interledger should be able to transact with anyone else, each using their currency and connector of choice. -By creating two classes of users -- resellers and end users -- and only allowing transfers if the sender is a reseller and the recipient is a user, merchants can create Interledger-capable ledgers which behave like stored-value systems. Such systems allow deposits, but do not allow withdrawals. +## Interledger Security -## Interledger Layer +**Interledger uses *conditional transfers* to secure payments across multiple hops and even through untrusted connectors.** Everyone only needs to trust their direct peers, no matter how many connectors are involved in forwarding a given packet. Connectors take some risk, but this risk can be managed and is primarily based upon the connector's chosen peers. -### Introduction +**Hint:** Conditional transfers or *authorization holds* are the financial equivalent of a [two-phase commit](http://foldoc.org/two-phase%20commit). -The Interledger Protocol (ILP) ensures that different connectors are interoperable and can work together to route transactions. +### Interledger Protocol Flow -### Protocols +The sender first sends an Interledger `prepare` packet. Each connector may either forward the `prepare` packet, or, in the case of an error, return a `reject` packet. If the `prepare` packet reaches the receiver, they may respond with either a `fulfill` or `reject` packet, depending on whether they wish to accept the Interledger payment. In either case, the resulting packet is passed along the same path back to the sender. -#### Interledger Protocol (ILP) + Each `prepare` packet contains a *cryptographic condition* and *timeout*. If a connector receives and forwards the `fulfill` packet before the timeout, the `prepare` packet is considered fulfilled, creating an obligation for the peer that sent the packet to settle with the connector. If the timeout is reached before the `fulfill` packet is received, the connector will consider the `prepare` packet expired and no obligation is created. -When initiating an Interledger transaction, the sender will make a transfer to a connector using their local Ledger layer protocol. Within this transfer, the sender will include an [ILP Packet](../0003-interledger-protocol/) which tells the receiving connector the final destination, the amount to be transferred and -- if applicable -- the condition. +Inspired by the [Lightning Network](http://lightning.network), Interledger uses the digest of the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) hash function as the condition for `prepare` packets. The `fulfill` packet contains a valid 32-byte preimage for the hash specified in the `prepare` packet. Connectors are responsible for validating fulfillments. [Transport Layer](#transport-layer) protocols are used by the sender and receiver to generate the condition for a particular packet. -Note that the exact method of transmitting this data packet is dependent on the ledger layer protocol. Typically, it will be included in the transfer in a memo field. However, some ledgers may specify a different method for transporting the ILP packet. +Settlement may either be based solely on trust between the connector and its peers, or be supported with functionality on the settlement ledger. -#### Interledger Quoting Protocol (ILQP) +As the `prepare` packet makes its way to the receiver, connectors only reserve the appropriate amount of funds, they do not settle it yet. No obligation is agreed yet between the peers, so none can be lost if a connector fails or attempts to redirect the packet. -Before an Interledger transfer takes place, the sender will request quotes from connectors which are connected to the same ledger. These quote requests happen via the [Interledger Quoting Protocol (ILQP)](../0008-interledger-quoting-protocol/). +When the *receiver* receives the `prepare` packet, they return the `fulfill` packet to claim their funds. Each connector uses the same `fulfill` packet to claim their funds respectively from the party (sender or connector) that sent them the `prepare` packet. -Senders MAY cache quotes and send repeated transfers through the same connector. +When forwarding a `prepare` packet from the sender-side account to the receiver-side account, each connector will reduce the timeout by a fixed amount. This provides a fixed time window to pass back the `fulfill` packet from the receiver-side account to the sender-side account later. A connector must have a fair chance to pass the `fulfill` packet back before the timeout on the sender-side account is reached, even if the `fulfill` packet was received on the receiver-side account at the last possible moment. Otherwise, it would be obligated to pay out on the receiver-side account with no obligation on their counterparty on the sender-side account to pay them back. -#### Interledger Control Protocol (ILCP) +For more details on the flow, see the [ILPv4 specification](../0027-interledger-protocol-4/0027-interledger-protocol-4.md). -The Interledger Control Protocol (ILCP) is a protocol used by connectors to exchange routing information and communicate payment errors. +**Note:** Interledger only supports Universal mode as described in the whitepaper. Atomic mode can be used by adjacent subsets of participants in an Interledger payment if desired, but this is not part of the standard. -**TODO**: add link +### Connector Risk and Mitigation -## Transport Layer +Interledger connectors accept some risk in exchange for the revenue they generate from facilitating payments. In the Interledger payment flow, connectors incur outgoing obligations on the receiver-side account, before they become entitled to incoming obligations on the sender-side account. After each connector receives a `fulfill` packet, they have a window of time to deliver the `fulfill` packet to their counterparty on the sender-side account. Connectors that fail to deliver the `fulfill` packet in time may lose money. -### Introduction +If some connectors in the path of an Interledger packet receive the `fulfill` packet in time and others don't, the receiver will know the packet was received but the sender will not know. Usually, a single Interledger packet is part of a larger transaction or payment stream, so the sender may find out what happened when they receive the response for the next packet. Senders MAY also retry packets that expire. The exact behavior of senders and receivers with respect to retries is defined by the transport layer. For an example, see [IL-RFC 16](../0016-pre-shared-key/0016-pre-shared-key.md) for a description of the Pre-Shared Key (PSK) transport layer protocol. -Transport layer protocols are responsible for coordinating the different transfers that make up an Interledger transaction. The safety guarantees afforded to the participants of a transaction vary depending on the type of transport protocol used. +Failing to deliver the `fulfill` packet in time is the main risk connectors face and there are a number of additional strategies connectors should employ to mitigate and manage this risk. For more details, see [IL-RFC 18](../0018-connector-risk-mitigations/0018-connector-risk-mitigations.md). -### Protocols -#### Optimistic Transport Protocol (OTP) +## Interledger Protocol Suite -The Optimistic Transport Protocol (OTP) is the trivial case of a transport protocol. It simply makes a transfer to the next connector. The connector may or may not make the requested transfer and the sender is out the money either way. +The Interledger stack is separated into four layers: -For most cases, OTP will not be appropriate. However, it may make sense in the case of recurring microtransactions where a few lost transactions are not a big deal. +![Interledger Protocol Suite](../shared/graphs/protocol-suite.svg) -**TODO:** Need to figure out how OTP can detect dropped transfers and adjust the route accordingly. +### Application Layer -#### Universal Transport Protocol (UTP) +The application layer is the top layer of the Interledger protocol suite. Protocols on this layer are responsible for: -The Universal Transport Protocol (UTP) is the standard and recommended transport protocol. It sets up a cascading chain of escrowed transfers in order to ensure delivery of funds. +1. Destination account discovery +2. Destination amount negotiation +3. Transport protocol selection and communication of associated details, such as the shared secret or condition +4. Additional details to be communicated in ILP packet data -The protocol flow is described in the [Interledger whitepaper](https://interledger.org/interledger.pdf). +An example of an application layer protocol is the [Simple Payment Setup Protocol (SPSP)](../0009-simple-payment-setup-protocol/0009-simple-payment-setup-protocol.md). +SPSP uses Webfinger ([RFC 7033](https://tools.ietf.org/html/rfc7033)), an HTTPS-based protocol for communicating the destination ILP address and related details, and uses Pre-Shared Key (PSK) as the transport layer protocol. -##### Error Recovery +### Transport Layer -A UTP execution chain can be interrupted if a connector fails to deliver the condition fulfillment. In that case the sender will think that the transaction failed and retry it. While retrying, some connector will try to create a prepared transfer on a ledger which already knows the corresponding fulfillment, which will cause the request to fail. The connector will notice this and simply pass back the fulfillment without paying anything. +Transport layer protocols are end-to-end protocols used by the senders and receivers of Interledger payments to determine the payment condition and other details. The guarantees afforded to the sender vary depending on the type of transport protocol used. -In other words, the failed connector will lose money and some other connector will win money, but from the sender and recipient's point of view everything has executed normally. +There are currently three transport layer protocols: -#### Atomic Transport Protocol (ATP) +* [Loopback Transport (LT)](../0029-loopback-transport/0029-loopback-transport.md) + In the Loopback Transport protocol, the sender temporarily opens a direct ILPv4 connection to the receiver, identified by a loopback address. The sender then sends Interledger payments, via other routes, to her own loopback address "at" the receiver. During the prepare phase, Interledger payments travel from the sender, via a number of connectors, to the receiver, and loop back to the sender (hence the name), who is the sole possessor of the fulfillment. The sender has full control over the accept/reject decision for each Interledger payment sent. -The Atomic Transport Protocol (ATP) is the most conservative, but also most complex of the standard Interledger transport protocols. In addition to the ledgers and connectors, it also involves a set of impartial notaries, which must be agreed upon by the sender, recipient and connectors involved in the transaction. + **LT is the simplest transport layer protocol of the three.** -Since each party chooses their own set of notaries to trust, there may not be any overlap and ATP could not be used in that case. Notaries may be selected using an automatic process or preselected by a group with standing agreements among the participants. +* [Pre-Shared Key (PSK)](../0016-pre-shared-key/0016-pre-shared-key.md) -The protocol flow is described in the [Interledger whitepaper](https://interledger.org/interledger.pdf). + In Pre-Shared Key (PSK) protocol, a direct communication channel between sender and receiver is not required; once a shared secret has been established, it can be used to generate the payment condition, authenticate the ILP packet, and encrypt application data. Using PSK, the sender is guaranteed that fulfillment of their transfer indicates the receiver got the payment, provided that no one aside from the sender and receiver have the secret and the sender did not submit the fulfillment. -##### As a Sub-Protocol + **PSK achieves lower latency than LT.** -ATP can be used as a sub-protocol for part of a UTP transaction. This happens when a connector decides to create an ATP transfer as the next hop. It takes on both the risk of being unable to pass on the receipt and the risk of the notaries failing. This makes sense to do if the next connector quotes a better price for an ATP transfer than for a UTP transfer. +* [Interledger Payment Request (IPR)](../0011-interledger-payment-request/0011-interledger-payment-request.md) -Similarly, an ATP transaction can turn into a UTP transaction when a connector decides to use UTP for the next hop. It takes on the risk of not being able to pass on the receipt to the notaries. This makes sense if ATP is not available. + In the Interledger Payment Request (IPR) protocol, the receiver generates the payment details and condition. The receiver does not share the secret used to generate the condition and fulfillment with the sender or anyone else, but the sender must ask the receiver to generate and share a condition before sending each payment. IPR is primarily useful for building non-repudiable application layer protocols, in which the sender's possession of the fulfillment proves to third parties that the sender has paid the receiver for a specific obligation. -## Application Layer +### Interledger Layer -### Introduction +The Interledger layer is responsible for forwarding Interledger packets between the sender and receiver. There is only one protocol on this layer: the Interledger Protocol (ILP). -Application layer protocols deal with the exchange of payment details and associated negotiation. +The [Interledger Protocol (ILP)](../0003-interledger-protocol/0003-interledger-protocol.md) is the core of the Interledger stack and defines standard address and packet formats that instruct connectors where to forward a packet. It also defines the [protocol flow](#interledger-protocol-flow) described above. -### Simple Payment Setup Protocol (SPSP) +[Interledger Addresses](../0015-ilp-addresses/0015-ilp-addresses.md) provide a universal way to address senders, receivers and connectors. Interledger addresses are hierarchical, dot-separated strings where the left-most segment is most significant. An example address might look like: +`g.us.acmebank.acmecorp.sales.199` or `g.crypto.bitcoin.1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2`. -The Simple Payment Setup Protocol (SPSP) is an application layer protocol for negotiating payment details. SPSP handles account and amount discovery, condition creation, quoting and setup. SPSP uses Webfinger ([RFC 7033](https://tools.ietf.org/html/rfc7033)) and an HTTP-based protocol for querying account and amount details, [ILQP](#interledger-quoting-protocol-ilqp) for quoting, and [UTP](#universal-transport-protocol-utp) for payment execution. +When initiating an Interledger payment, the sender sends an ILP `prepare` packet to the connector. The packet is a binary message that includes the destination account, amount, condition, timeout, and transport-layer data for the receiver. The packet is relayed by connectors. -The protocol is described in [IL-RFC 9](../0009-simple-payment-setup-protocol/). +### Ledger Layer -### Defining Other Application Layer Protocols +In order to move packets from one party (sender, receiver or connector) to another, the parties need a facility for data transmission and, optionally, money transmission for settlement. These facilities may be provided by different protocols belonging to the so-called ledger layer. Which protocol is used between two parties is up to those parties to choose. -Creators of other application layer protocols should consider the following: +See [IL-RFC 17](../0017-ledger-requirements/0017-ledger-requirements.md) for a full description of the ledger layer requirements. -1. Account discovery -2. Amount and condition communication -3. Additional details communicated in memo -4. Condition types supported or required -5. Transport protocol -6. Incoming payment validation (amount, condition, etc.) +Most implementations of Interledger use a plugin architecture to abstract the differences between different ledger layer protocols. For an example of this, see [IL-RFC 4](../0004-ledger-plugin-interface/0004-ledger-plugin-interface.md), which defines the interface for the Javascript implementation. diff --git a/0001-interledger-architecture/assets/interledger-architecture-layers.png b/0001-interledger-architecture/assets/interledger-architecture-layers.png deleted file mode 100644 index eaedc2d8..00000000 Binary files a/0001-interledger-architecture/assets/interledger-architecture-layers.png and /dev/null differ diff --git a/0001-interledger-architecture/assets/interledger-architecture-layers.xml b/0001-interledger-architecture/assets/interledger-architecture-layers.xml deleted file mode 100644 index e64ef679..00000000 --- a/0001-interledger-architecture/assets/interledger-architecture-layers.xml +++ /dev/null @@ -1 +0,0 @@ -7Zjfk5owEMf/Gh47A8Tj9FGttjdjp9fhOn2OECFzgTAxntq//jaYFWg82wetNy34IHx382s/C1nwyLTYfVK0yr/IlAkv9NOdRz56YRgMyAD+jLI/KMPAPwiZ4ql1aoSY/2RWRLcNT9m646ilFJpXXTGRZckS3dGoUnLbdVtJ0R21ohmO2AhxQoWr/uCpzg9qNLDTM/pnxrPcjkx8a1jS5DlTclPa4byQrOrjYC4odmX91zlN5bYlkRmEVUkJHZuzYjdlwoQWo3ZoN3/Depy2YqWd2vkGYDINXqjY2JXHi0c7Nb3HaNQLYqZJ4JHJNueaxRVNjHUL/EHLdSGsmarE8gwIXK61ks9sKoVUdWdkWh9gWXEhWvq8PkDPFE05zL9tC83PtJGlntOCC5NXMS0Bc63hiAO4dmNgw/LClGaYosdgQw4zWTCt9uBirSHytOl75LVtkoFgMuStRECN2vzLjl03EODEcjjNhLhMHuPLQfH/FSgY67/B5N5h8vWpv08cJAFet5gEeO9c/D4ZLaMkCkf+cJmMUrb8MHIgPZSaKcHSjCkHFiwRJtMm0kVQyhI8O9G3EhU8K+FSsJXpwYSLw9YxtnLB09QMcjIBJHivRP3Iz8GPQYNJN21+xXQK5QXQIQEkd/+H5FC7KDnc9lvoFj2131ODwuOW1HD6DbUnBUutpNI9uPO3G2b8bcC5Zd+4qgREVXNZ9ujOoru7KTn7XtXe40zBDmPBUv2Hxbe+KnGrkshFNkCfq1clwZ2D7HtfO7Zq9XdROwaR+0jsKUGwh++Kkvse5oWRMHvLuqKwc0WZOfdmoTeceOM5GmGgjv2/f5e+w0fi2+/SV/u84UAd9lCvAvXUV6sLUYXL5iNlbWt9CCazVw== \ No newline at end of file diff --git a/0002-crypto-conditions/0002-crypto-conditions.md b/0002-crypto-conditions/0002-crypto-conditions.md index 2d6b4be7..9d9490a5 100644 --- a/0002-crypto-conditions/0002-crypto-conditions.md +++ b/0002-crypto-conditions/0002-crypto-conditions.md @@ -1,798 +1,4 @@ --- -coding: utf-8 - -title: Crypto-Conditions -docname: draft-thomas-crypto-conditions-01 -category: std - -pi: [toc, sortrefs, symrefs, comments] -smart_quotes: off - -area: security -author: - - - ins: S. Thomas - name: Stefan Thomas - org: Ripple - street: 300 Montgomery Street - city: San Francisco - region: CA - code: 94104 - country: US - phone: ----------------- - email: stefan@ripple.com - uri: https://www.ripple.com - - - - ins: R. Reginelli - name: Rome Reginelli - org: Ripple - street: 300 Montgomery Street - city: San Francisco - region: CA - code: 94104 - country: US - phone: ----------------- - email: rome@ripple.com - uri: https://www.ripple.com - - - - ins: A. Hope-Bailie - name: Adrian Hope-Bailie - org: Ripple - street: 300 Montgomery Street - city: San Francisco - region: CA - code: 94104 - country: US - phone: ----------------- - email: adrian@ripple.com - uri: https://www.ripple.com - -normative: - RFC3447: - RFC4648: - I-D.draft-irtf-cfrg-eddsa-04: - itu.X680.2015: - title: "Information technology – Abstract Syntax Notation One (ASN.1): Specification of basic notation" - target: https://www.itu.int/rec/T-REC-X.680-201508-I/ - author: - organization: International Telecommunications Union - date: 2015-08 - itu.X696.2015: - title: "Information technology – ASN.1 encoding rules: Specification of Octet Encoding Rules (OER)" - target: http://handle.itu.int/11.1002/1000/12487 - author: - organization: International Telecommunications Union - date: 2015-08 - seriesInfo: - name: "ITU-T" - value: "Recommendation X.696" - -informative: - RFC2119: - RFC3110: - RFC4871: - LARGE-RSA-EXPONENTS: - title: Imperial Violet - Very large RSA public exponents (17 Mar 2012) - target: https://www.imperialviolet.org/2012/03/17/rsados.html - author: - fullname: Adam Langley - date: 2012-03-17 - USING-RSA-EXPONENT-OF-65537: - title: Cryptography - StackExchange - Impacts of not using RSA exponent of 65537 - target: https://crypto.stackexchange.com/questions/3110/impacts-of-not-using-rsa-exponent-of-65537 - author: - fullname: http://crypto.stackexchange.com/users/555/fgrieu - date: 2014-11-18 - KEYLENGTH-RECOMMENDATION: - title: BlueKrypt - Cryptographic Key Length Recommendation - target: https://www.keylength.com/en/compare/ - author: - fullname: Damien Giry - date: 2015-09-17 - NIST-KEYMANAGEMENT: - title: NIST - Recommendation for Key Management - Part 1 - General (Revision 3) - target: http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57_part1_rev3_general.pdf - date: 2012-07 - author: - - fullname: Elaine Barker - - fullname: William Barker - - fullname: William Burr - - fullname: William Polk - - fullname: Miles Smid - OPENSSL-X509-CERT-EXAMPLES: - title: OpenSSL - X509 certificate examples for testing and verification - target: http://fm4dd.com/openssl/certexamples.htm - author: - fullname: FM4DD - date: 2012-07 - ---- note_Feedback - -This specification is a part of the [Interledger Protocol](https://interledger.org/) work. Feedback related to this specification should be sent to . - ---- abstract - -Crypto-conditions provide a mechanism to describe a signed message such that multiple actors in a distributed system can all verify the same signed message and agree on whether it matches the description. This provides a useful primitive for event-based systems that are distributed on the Internet since we can describe events in a standard deterministic manner (represented by signed messages) and therefore define generic authenticated event handlers. - ---- middle - -# Introduction -This specification describes a message format for crypto-conditions and fulfillments, with binary and string-based encodings for each. - -Crypto-conditions are **distributable event descriptions**. This means crypto-conditions say how to recognize a message without saying exactly what the message is. You can transmit a crypto-condition freely, but you cannot forge the message it describes. For convenience, we hash the description so that the crypto-condition can be a fixed size. - -Fulfillments are **cryptographically verifiable messages** that prove an event occurred. If you transmit a fulfillment, then everyone who has the condition can agree that the condition has been met. - -In the Interledger protocol, crypto-conditions and fulfillments provide irrepudiable proof that a transfer occurred in one ledger, as messages that can be easily shared with other ledgers. This allows ledgers to escrow funds or hold a transfer conditionally, then execute the transfer automatically when the ledger sees the fulfillment of the stated condition. - -Crypto-conditions may also be useful in other contexts where a system needs to make a decision based on predefined criteria, such as smart contracts. - -## Terminology -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119][]. - -Within this specification, the term "condition" refers to the hash of a description of a signed message. The hash function must be preimage-resistant. - -The term "fulfillment" refers to a description of a signed message and a signed message that matches the description. We hash the description and compare that to the condition, and also compare the signed message to the description. If the message matches the description and the hash of the description matches the condition, we say that the fulfillment fulfills the condition. - -In the simplest case, the fulfillment can be a preimage that hashes to the condition, in which case the preimage is both the description and the message. - - -## Features -Crypto-conditions are a standard format for expressing conditions and fulfillments. The format supports multiple algorithms, including different hash functions and cryptographic signing schemes. Crypto-conditions can be nested in multiple levels, with each level possibly having multiple signatures. - -This format has been designed so that it can be expanded. For example, you can add new cryptographic signature schemes or hash functions. This is important because advances in cryptography frequently render old algorithms insecure or invent newer, more effective algorithms. - -The [Bitmask](#bitmask) of a crypto-condition indicates which algorithms it uses, so a compliant implementation can know whether it supports the functionality required to interpret the crypto-condition. - -### Multi-Algorithm -The crypto-condition format contains a [Bitmask](#bitmask) that specifies which hash function and signing scheme to use. Any message format for a condition or a fulfillment contains such a bitmask. - -Implementations MAY state their supported algorithms by providing a bitmask in the same format. To verify that a given implementation can verify a fulfillment for a given condition, you compare the bitmasks. If all bits set in the condition's bitmask are also set in the implementation's bitmask, then the implementation can verify the condition's fulfillment. - -### Multi-Signature -Crypto-conditions can abstract away many of the details of multi-sign. When a party provides a condition, other parties can treat it opaquely and do not need to know about its internal structure. That allows parties to define arbitrary multi-signature setups without breaking compatibility. - -Protocol designers can use crypto-conditions as a drop-in replacement for public key signature algorithms and add multi-signature support to their protocols without adding any additional complexity. - -### Multi-Level -Crypto-conditions elegantly support weighted multi-signatures and multi-level signatures. A threshold condition has a number of weighted subconditions, and a target threshold. Each subcondition can be a signature or another threshold condition. This provides flexibility in forming complex conditions. - -For example, consider a threshold condition that consists of two subconditions, one each from Agnes and Bruce. Agnes's condition can be a signature condition while Bruce's condition is a threshold condition, requiring both Claude and Dan to sign for him. - -Weighted signatures allow more complex relationships than simple M-of-N signing. For example, a weighted condition can support an arrangement of subconditions such as, "Either Ron, Adi, and Leonard must approve; or Clifford must approve." - - - -# Format - -## Binary Encoding - -A description of crypto-conditions is provided in this document using Abstract Syntax Notation One (ASN.1) as defined in [ITU.X680.2015](#itu.X680.2015). Implementations of this spec MUST support encoding and decoding using Octet Encoding Rules (OER) as defined in [ITU.X696.2015](#itu.X696.2015). - -## String Types - -Crypto-conditions use the following types within string encoding: - -BASE10 -: Variable-length integer encoded as a base-10 (decimal) number. Implementations MUST reject encoded values that are too large for them to parse. Implementations MUST be tested for overflows. - -BASE16 -: Variable-length integer encoded as a base-16 (hexadecimal) number. Implementations MUST reject encoded values that are too large for them to parse. Implementations MUST be tested for overflows. No leading zeros. - -BASE64URL -: Base64-URL encoding. See [RFC4648](#RFC4648), Section 5. - -## Bitmask -Any system that accepts crypto-conditions must be able to state its supported algorithms. It must be possible to verify that all algorithms used in a certain condition are indeed supported even if the fulfillment is not available yet. Therefore, all conditions and fulfillments contain a bitmask to express the required features. Implementations provide a bitmask of features they support. - -Each bit represents a different suite of features. Each type of crypto-condition depends on one or more feature suites. If an implementation supports all feature suites that a certain type depends on, the implementation MUST support that condition type. The list of known types and feature suites is the IANA-maintained [Crypto-Condition Type Registry](#crypto-conditions-type-registry) . - -To save space, the bitmask is encoded as a variable-length integer. - -## Condition {#condition-format} -Below are the string and binary encoding formats for a condition. - -### String Format {#string-condition-format} - -Conditions are ASCII encoded as: - - "cc:" BASE16(type) ":" BASE16(featureBitmask) ":" - BASE64URL(fingerprint) ":" BASE10(maxFulfillmentLength) - - -### Binary Format {#binary-condition-format} - -Conditions are binary encoded as: - - Condition ::= SEQUENCE { - type ConditionType, - featureBitmask INTEGER (0..MAX), - fingerprint OCTET STRING, - maxFulfillmentLength INTEGER (0..MAX) - } - - ConditionType ::= INTEGER { - preimageSha256(0), - rsaSha256(1), - prefixSha256(2), - thresholdSha256(3), - ed25519(4) - } (0..65535) - -### Fields {#condition-format-fields} - -type -: is the numeric type identifier representing the condition type. - -featureBitmask -: is an unsigned integer encoding the set of feature suites an implementation must support in order to be able to successfully parse the fulfillment to this condition. This is the boolean OR of the featureBitmask values of the top-level condition type and all subcondition types, recursively. - -fingerprint -: is an octet string uniquely representing the condition with respect to other conditions of the same type. Implementations which index conditions MUST use the entire string or binary encoded condition as the key, not just the fingerprint - as different conditions of different types may have the same fingerprint. The length and contents of the fingerprint are defined by the condition type. For most condition types, the fingerprint is a cryptographically secure hash of the data which defines the condition, such as a public key. This is encoded as a variable length octet string as different condition types may use different functions to produce the fingerprint which may therefore have different lengths. While it would be possible to determine the expected length of the fingerprint based on the type it is useful to be able to decode a condition even if the type is not recognized. - -maxFulfillmentLength -: is the maximum length of the fulfillment payload that can fulfill this condition, in bytes. The payload size is measured unencoded. (The size of the payload is larger in BASE64URL format.) When a crypto-condition is submitted to an implementation, this implementation MUST verify that it will be able to process a fulfillment with a payload of size maxFulfillmentLength. - -### Example Condition - -An example condition in string format: - - cc:0:3:dB-8fb14MdO75Brp_Pvh4d7ganckilrRl13RS_UmrXA:66 - -The example has the following attributes: - -| Field | Value | Description | -|----------------------|-------|----------------------------------------------| -| preface | `cc` | Constant. Indicates this is a condition. | -| type | `0` | Type 0 is [PREIMAGE-SHA-256][]. | -| featuresBitmask | `3` | Boolean-OR combination of feature suites SHA-256 (feature bit 0x01) and PREIMAGE (feature bit 0x02). | -| fingerprint | `dB-8fb14MdO75Brp_Pvh4d7ganckilrRl13RS_UmrXA` | The hash of the fulfillment for this condition. | -| maxFulfillmentLength | `66` | The fulfillment payload is 66 bytes long, before being BASE64URL-encoded. | - - -## Fulfillment {#fulfillment-format} -Below are the string and binary encoding formats for a fulfillment. - -### String Format {#string-fulfillment-format} - -Fulfillments are ASCII encoded as: - - "cf:" BASE16(type) ":" BASE64URL(payload) - -### Binary Format {#binary-fulfillment-format} - -Fulfillments are binary encoded as: - - Fulfillment ::= SEQUENCE { - type ConditionType, - payload OCTET STRING - } - -### Fields {#fulfillment-format-fields} - -type -: is the numeric type identifier representing the condition type. For some condition types the fulfillment will contain further subfulfillments, however the type field always represents the outermost, or top-level, type. - -payload -: The payload is an octet string whose internal format is defined by each of the types. - -### Example Fulfillment - -The following is an example fulfillment in string format, for the [example condition](#example-condition): - - cf:0:VGhlIG9ubHkgYmFzaXMgZm9yIGdvb2QgU29jaWV0eSBpcyB1bmxpbWl0ZWQgY3JlZGl0LuKAlE9zY2FyIFdpbGRl - - -The example has the following attributes: - -| Field | Value | Description | -|----------------------|-------|----------------------------------------------| -| preface | `cf` | Constant. Indicates this is a fulfillment. | -| type | `0` | Type 0 is [PREIMAGE-SHA-256][]. | -| payload | `VGhlIG...pbGRl` | The BASE64URL-encoded SHA-256 preimage of the condition, since this is a PREIMAGE-SHA-256 type fulfillment. In this case, it is an arbitrary string. | - - - - -# Feature Suites {#feature-suites} -This specification defines a starting set of feature suites necessary to support the [Condition Types][] also defined in this specification. Future versions of this spec MAY introduce new feature suites and condition types, which SHALL be registered in the IANA maintained [Crypto-Condition Type Registry](#crypto-conditions-type-registry). - -Support for a condition type MUST depend on one or more feature suites. However, all new condition types MUST depend on at least one of the new feature suites. This ensures that all previously created implementations correctly recognize that they do not support the new type. - -Feature suites are chosen such that they represent reasonable clusters of functionality. For instance, it is reasonable to require that an implementation which supports SHA-256 in one context MUST support it in all contexts, since it already needed to implement the algorithm. - -An implementation which supports a certain set of feature suites MUST accept all condition types which depend only on that set or any subset of feature suites. - -| Suite Name | Feature Bit | Feature Bit (BASE16) | Summary | -|------------|-----|------|---------------------------------| -| SHA-256 | 2^0 | 0x01 | The SHA-256 hashing algorithm. | -| PREIMAGE | 2^1 | 0x02 | The functionality of comparing a hash to a preimage. | -| PREFIX | 2^2 | 0x04 | The functionality of prefixing the fulfillment with a prefix before generating the condition. | -| THRESHOLD | 2^3 | 0x08 | The functionality of composing a condition out of several weighted subconditions. | -| RSA-PSS | 2^4 | 0x10 | The RSA-PSS signature algorithm. | -| ED25519 | 2^5 | 0x20 | The ED25519 signature algorithm. | - -## SHA-256 {#sha-256-feature-suite} - -The SHA-256 feature suite provides the SHA-256 hash function. SHA-256 is a cryptographic hash function published by the US National Institute of Standards and Technology that produces 256 bits of output. This feature suite is assigned the feature bit 2^0 = 0x01. - -## PREIMAGE {#preimage-feature-suite} -The PREIMAGE feature suite provides conditions that use a preimage as a one-time signature. This feature suite is assigned the feature bit 2^1 = 0x02. - -The fingerprint of a preimage condition is the hash of an arbitrary value. The payload of a preimage fulfillment is the hashed arbitrary value before hashing, also known as the preimage. Conditions that use this preimage MUST also rely on a cryptographically secure hashing algorithm. Since cryptographically secure hashing functions are preimage-resistant, only the original creator of a preimage condition can produce the preimage, as long as it contains a large amount of random entropy. - -## PREFIX {#prefix-feature-suite} -The PREFIX feature suite provides conditions that prepend a fixed message to a subcondition. This feature suite is assigned the feature bit 2^2 = 0x04. - -A prefix condition prepends the message to be validated with a constant string before passing it on to the subcondition's validation function. - -## THRESHOLD {#threshold-feature-suite} -The THRESHOLD feature suite provides conditions that have several weighted subconditions and a threshold number. This feature suite is assigned the feature bit 2^3 = 0x08. - -Threshold conditions provide flexible multi-signing, such as requiring "M-of-N" subconditions be fulfilled. Subconditions can also be weighted so that one subcondition can count multiple times towards meeting the threshold. - -## RSA-PSS {#rsa-pss-feature-suite} -The RSA-PSS feature suite provides the RSS-PSA signature algorithm. RSA-PSS is a signature algorithm based on the RSA cryptosystem, which relates to the problem of factoring the product of two large prime numbers. This feature suite is assigned the feature bit 2^4 = 0x10. - -## ED25519 {#ed25519-feature-suite} -The ED25519 feature suite provides the Ed25519 signature algorithm. Ed25519 is a signature algorithm based on the EdDSA signing scheme and the compact elliptic curve known as Ed25519. This feature suite is assigned the feature bit 2^5 = 0x20. - - - - -# Condition Types -The following condition types are defined in this version of the specification. Future versions of this spec MAY introduce new feature suites and condition types, which SHALL be registered in the IANA maintained [Crypto-Condition Type Registry](#crypto-conditions-type-registry). - -## PREIMAGE-SHA-256 {#preimage-sha-256-condition-type} -PREIMAGE-SHA-256 is assigned the type ID 0. It relies on the SHA-256 and PREIMAGE feature suites which corresponds to a feature bitmask of 0x03. - -This type of condition is also called a "hashlock". By creating a hash of a difficult-to-guess 256-bit random or pseudo-random integer it is possible to create a condition which the creator can trivially fulfill by publishing the random value. However, for anyone else, the condition is cryptographically hard to fulfill, because they would have to find a preimage for the given condition hash. - -Implementations MUST ignore any input message when validating a PREIMAGE-SHA-256 fulfillment. - -### Condition {#preimage-sha-256-condition-type-condition} -The fingerprint of a PREIMAGE-SHA-256 condition is the SHA-256 hash of the preimage. - -### Fulfillment {#preimage-sha-256-condition-type-fulfillment} -The fulfillment payload of a PREIMAGE-SHA-256 condition is the preimage. - -### Example {#preimage-sha-256-example} - -Example condition: - - cc:0:3:dB-8fb14MdO75Brp_Pvh4d7ganckilrRl13RS_UmrXA:66 - -Example fulfillment: - - cf:0:VGhlIG9ubHkgYmFzaXMgZm9yIGdvb2QgU29jaWV0eSBpcyB1bmxpbWl0ZWQgY3JlZGl0LuKAlE9zY2FyIFdpbGRl - - -## PREFIX-SHA-256 {#prefix-sha-256-condition-type} -PREFIX-SHA-256 is assigned the type ID 1. It relies on the SHA-256 and PREFIX feature suites which corresponds to a feature bitmask of 0x05. - -Prefix conditions provide a way to effective narrow the scope of other conditions. A condition can be used as the fingerprint of a public key to sign an arbitrary message. By creating a prefix subcondition we can narrow the scope from signing an arbitrary message to signing a message with a specific prefix. - -When a prefix fulfillment is validated against a message, it will prepend the prefix to the provided message and will use the result as the message to validate against the subfulfillment. - -### Condition {#prefix-sha-256-condition-type-condition} -The fingerprint of a PREFIX-SHA-256 condition is the SHA-256 digest of the fingerprint contents given below: - - PrefixSha256FingerprintContents ::= SEQUENCE { - prefix OCTET STRING, - condition Condition - } - - -prefix -: is an arbitrary octet string which will be prepended to the message during validation. - -condition -: is the subcondition which the subfulfillment must match. - - -### Fulfillment {#prefix-sha-256-condition-type-fulfillment} - - PrefixSha256FulfillmentPayload ::= SEQUENCE { - prefix OCTET STRING, - subfulfillment Fulfillment - } - - -prefix -: is an arbitrary octet string which will be prepended to the message during validation. - -subfulfillment -: is the fulfilled subcondition. - -### Example {#prefix-sha-256-example} - -Example condition: - - cc:1:25:7myveZs3EaZMMuez-3kq6u69BDNYMYRMi_VF9yIuFLc:102 - -Example fulfillment: - - cf:1:DUhlbGxvIFdvcmxkISAABGDsFyuTrV5WO_STLHDhJFA0w1Rn7y79TWTr-BloNGfiv7YikfrZQy-PKYucSkiV2-KT9v_aGmja3wzN719HoMchKl_qPNqXo_TAPqny6Kwc7IalHUUhJ6vboJ0bbzMcBwo - - -## THRESHOLD-SHA-256 {#threshold-sha-256-condition-type} -THRESHOLD-SHA-256 is assigned the type ID 2. It relies on the SHA-256 and THRESHOLD feature suites which corresponds to a feature bitmask of 0x09. - -### Condition {#threshold-sha-256-condition-type-condition} -The fingerprint of a THRESHOLD-SHA-256 condition is the SHA-256 digest of the fingerprint contents given below: - - ThresholdSha256FingerprintContents ::= SEQUENCE { - threshold INTEGER (0..4294967295), - subconditions SEQUENCE OF ThresholdSubcondition - } - - ThresholdSubcondition ::= SEQUENCE { - weight INTEGER (0..4294967295), - condition Condition - } - -The list of conditions is sorted first based on length, shortest first. Elements of the same length are sorted in lexicographic (big-endian) order, smallest first. - -threshold -: threshold MUST be an integer in the range 1 ... 2^32 - 1. In order to fulfill a threshold condition, the weights of the provided fulfillments MUST be greater than or equal to the threshold. - -subconditions -: is the set of subconditions, each provided as a tuple of weight and condition. - -weight -: is the numeric weight of this subcondition, i.e. how many times it counts against the threshold. - -condition -: is the subcondition. - - -### Fulfillment {#threshold-sha-256-condition-type-fulfillment} - - ThresholdSha256FulfillmentPayload ::= SEQUENCE { - threshold INTEGER (0..4294967295), - subfulfillments SEQUENCE OF ThresholdSubfulfillment - } - - ThresholdSubfulfillment ::= SEQUENCE { - weight INTEGER (0..4294967295) DEFAULT 1, - condition Condition OPTIONAL, - fulfillment Fulfillment OPTIONAL - } - - -threshold -: is a number and MUST be an integer in the range 1 ... 2^32 - 1. In order to fulfill a threshold condition, the weights of the provided fulfillments MUST be greater than or equal to the threshold. - -subfulfillments -: is the set of subconditions and subfulfillments, each provided as a tuple of weight and condition or fulfillment. - -weight -: is the numeric weight of this subcondition, i.e. how many times it counts against the threshold. It MUST be an integer in the range 1 ... 2^32 - 1. - -condition -: is the subcondition if this subcondition is not fulfilled. - -fulfillment -: is the subfulfillment if this subcondition is fulfilled. - -### Example {#threshold-sha-256-example} - -Example condition: - - cc:2:2b:mJUaGKCuF5n-3tfXM2U81VYtHbX-N8MP6kz8R-ASwNQ:146 - - -Example fulfillment: - - cf:2:AQEBAgEBAwAAAAABAQAnAAQBICDsFyuTrV5WO_STLHDhJFA0w1Rn7y79TWTr-BloNGfivwFg - - -## RSA-SHA-256 {#rsa-sha-256-condition-type} -RSA-SHA-256 is assigned the type ID 3. It relies on the SHA-256 and RSA-PSS feature suites which corresponds to a feature bitmask of 0x11. - -The signature algorithm used is RSASSA-PSS as defined in PKCS#1 v2.2. [RFC3447](#RFC3447) - -Implementations MUST NOT use the default RSASSA-PSS-params. Implementations MUST use the SHA-256 hash algorithm and therefor, the same algorithm in the mask generation algorithm, as recommended in [RFC3447](#RFC3447). Implementations MUST also use a salt length of 32 bytes (equal to the size of the output from the SHA-256 algorithm). Therefore the algorithm identifier will have the following value: - - rSASSA-PSS-Crypto-Conditions-Identifier RSASSA-AlgorithmIdentifier ::= { - algorithm id-RSASSA-PSS, - parameters RSASSA-PSS-params : { - hashAlgorithm sha256, - maskGenAlgorithm mgf1SHA256, - saltLength 32, - trailerField trailerFieldBC - } - } - - sha256 HashAlgorithm ::= { - algorithm id-sha256, - parameters NULL - } - - mgf1SHA256 MaskGenAlgorithm ::= { - algorithm id-mgf1, - parameters HashAlgorithm : sha256 - } - -### Condition {#rsa-sha-256-condition-type-condition} -The fingerprint of a RSA-SHA-256 condition is the SHA-256 digest of the fingerprint contents given below: - - RsaSha256FingerprintContents ::= SEQUENCE { - modulus OCTET STRING (SIZE(128..512)) - } - - -modulus -: is an octet string representing the RSA public modulus in big-endian byte order. The first byte of the modulus MUST NOT be zero. - -: The corresponding public exponent e is assumed to be 65537 as recommended in [RFC4871](#RFC4871) . Very large exponents can be a DoS vector [LARGE-RSA-EXPONENTS](#LARGE-RSA-EXPONENTS) and 65537 is the largest Fermat prime, which has some nice properties [USING-RSA-EXPONENT-OF-65537](#USING-RSA-EXPONENT-OF-65537) . - -: Implementations MUST reject moduli smaller than 128 bytes (1017 bits) or greater than 512 bytes (4096 bits.) Large moduli slow down signature verification which can be a denial-of-service vector. DNSSEC also limits the modulus to 4096 bits [RFC3110](#RFC3110) . OpenSSL supports up to 16384 bits [OPENSSL-X509-CERT-EXAMPLES](#OPENSSL-X509-CERT-EXAMPLES) . - - -### Fulfillment {#rsa-sha-256-condition-type-fulfillment} - - RsaSha256FulfillmentPayload ::= SEQUENCE { - modulus OCTET STRING (SIZE(128..512)), - signature OCTET STRING (SIZE(128..512)) - } - - -modulus -: is an octet string representing the RSA public modulus in big-endian byte order. See [rsa-sha-256-condition-type-condition](#rsa-sha-256-condition-type-condition) - -signature -: is an octet string representing the RSA signature. It MUST be encoded in big-endian byte order with the exact same number of octets as the modulus, even if this means adding leading zeros. This ensures that the fulfillment size is constant and known ahead of time. Note that the field is still binary encoded with a length prefix for consistency. - -: Implementations MUST verify that the signature and modulus consist of the same number of octets and that the signature is numerically less than the modulus. - -The message to be signed is provided separately. If no message is provided, the message is assumed to be an octet string of length zero. - -### Implementation {#rsa-sha-256-condition-type-implementation} -The recommended modulus size as of 2016 is 2048 bits [KEYLENGTH-RECOMMENDATION](#KEYLENGTH-RECOMMENDATION) . In the future we anticipate an upgrade to 3072 bits which provides approximately 128 bits of security [NIST-KEYMANAGEMENT](#NIST-KEYMANAGEMENT) (p. 64), about the same level as SHA-256. - -### Example {#rsa-sha-256-example} - -Example condition: - - cc:3:11:Bw-r77AGqSCL0huuMQYj3KW0Jh67Fpayeq9h_4UJctg:260 - -Example fulfillment: - - cf:3:gYCzDnqTh4O6v4NoUP9J4U-H4_ktXEbjP-yj5PCyI1hYCxF2WZX0uO6n-0cSwuHjFvf3dalT0jIhahadmmTdwAcSCkALN_KvwHe2L-ME3nTeahGexAdrUpxPYJawuq1PUz3wFzubgi_YXWX6S--pLY9ST2nLygE2vYDQlcFprsDglYGAjQM0-Z5B-953uQtJ5dXL1D5TWpM0s0eFF0Zty7J2Y3Nb0PqsR5I47a2wYlA7-106vjC8gHFdHVeSR6JksSrhj8YaMWfV0A6qhPz6hq-TqSKCXd4mf3eCpyyFYR_EyH5zXd56sJEU3snWlFbB_bKAW4si_qdfY9dT87YGUp_Grm0 - - - - -## ED25519 {#ed25519-condition-type} -ED25519 is assigned the type ID 4. It relies only on the ED25519 feature suite which corresponds to a bitmask of 0x20. - -The exact algorithm and encodings used for public key and signature are defined in [I-D.irtf-cfrg-eddsa](#I-D.irtf-cfrg-eddsa) as Ed25519. SHA-512 is used as the hashing function. - -Note: This document is not defining the SHA-512 versions of other condition types. In addition, the Ed25519 condition type is already uniquely identified by a corresponding Ed25519 feature suite. Therefore we intentionally do not introduce a SHA-512 feature suite in this document. - -### Condition {#ed25519-condition-type-condition} -The fingerprint of a ED25519 condition is the 32 byte Ed25519 public key. Since the public key is already very small, we do not hash it. - -### Fulfillment {#ed25519-condition-type-fulfillment} - - Ed25519FulfillmentPayload ::= SEQUENCE { - publicKey OCTET STRING (SIZE(32)), - signature OCTET STRING (SIZE(64)) - } - - -publicKey -: is an octet string containing the Ed25519 public key. - -signature -: is an octet string containing the Ed25519 signature. - -### Example - -Example condition: - - cc:4:20:7Bcrk61eVjv0kyxw4SRQNMNUZ-8u_U1k6_gZaDRn4r8:96 - -Example fulfillment: - - cf:4:7Bcrk61eVjv0kyxw4SRQNMNUZ-8u_U1k6_gZaDRn4r-2IpH62UMvjymLnEpIldvik_b_2hpo2t8Mze9fR6DHISpf6jzal6P0wD6p8uisHOyGpR1FISer26CdG28zHAcK - - - - ---- back - -# Security Considerations - -This section to be expanded in a later draft. - -# Test Values - -This section to be expanded in a later draft. For now, see the test cases for the reference implementation: - -# ASN.1 Module {#appendix-c} - - ---- - - CryptoConditions - DEFINITIONS - AUTOMATIC TAGS ::= - BEGIN - - /** - * CONTAINERS - */ - - Condition ::= SEQUENCE { - type ConditionType, - featureBitmask OCTET STRING, - fingerprint OCTET STRING, - maxFulfillmentLength INTEGER (0..MAX) - } - - Fulfillment ::= SEQUENCE { - type ConditionType, - payload OCTET STRING - } - - ConditionType ::= INTEGER { - preimageSha256(0), - rsaSha256(1), - prefixSha256(2), - thresholdSha256(3), - ed25519(4) - } (0..65535) - - /** - * FULFILLMENT PAYLOADS - */ - - -- For preimage conditions, the payload equals the preimage - - PrefixSha256FulfillmentPayload ::= SEQUENCE { - prefix OCTET STRING, - subfulfillment Fulfillment - } - - ThresholdSha256FulfillmentPayload ::= SEQUENCE { - threshold INTEGER (0..4294967295), - subfulfillments SEQUENCE OF ThresholdSubfulfillment - } - - ThresholdSubfulfillment ::= SEQUENCE { - weight INTEGER (0..4294967295) DEFAULT 1, - condition Condition OPTIONAL, - fulfillment Fulfillment OPTIONAL - } - - RsaSha256FulfillmentPayload ::= SEQUENCE { - modulus OCTET STRING (SIZE(128..512)), - signature OCTET STRING (SIZE(128..512)) - } - - Ed25519FulfillmentPayload ::= SEQUENCE { - publicKey OCTET STRING (SIZE(32)), - signature OCTET STRING (SIZE(64)) - } - - /** - * FINGERPRINTS - */ - - -- SHA-256 hash of the fingerprint contents - Sha256Fingerprint ::= OCTET STRING (SIZE(32)) -- digest - - -- 32-byte Ed25519 public key - Ed25519Fingerprint ::= OCTET STRING (SIZE(32)) -- publicKey - - /** - * FINGERPRINT CONTENTS - * - * The content that will be hashed to arrive at the fingerprint. - */ - - -- The preimage type hashes the raw contents of the preimage - - PrefixSha256FingerprintContents ::= SEQUENCE { - prefix OCTET STRING, - condition Condition - } - - ThresholdSha256FingerprintContents ::= SEQUENCE { - threshold INTEGER (0..4294967295), - subconditions SEQUENCE OF ThresholdSubcondition - } - - ThresholdSubcondition ::= SEQUENCE { - weight INTEGER (0..4294967295), - condition Condition - } - - RsaSha256FingerprintContents ::= INTEGER (0..MAX) -- modulus - - /** - * EXAMPLES - */ - - exampleCondition Condition ::= - { - type preimageSha256, - featureBitmask '03'H, - fingerprint ' - E3B0C442 98FC1C14 9AFBF4C8 996FB924 27AE41E4 649B934C A495991B 7852B855 - 'H, - maxFulfillmentLength 2 - } - - exampleFulfillment Fulfillment ::= - { - type preimageSha256, - payload '00'H - } - - exampleRsaSha256FulfillmentPayload RsaSha256FulfillmentPayload ::= - { - modulus ' - B30E7A93 8783BABF 836850FF 49E14F87 E3F92D5C 46E33FEC A3E4F0B2 2358580B - 11765995 F4B8EEA7 FB4712C2 E1E316F7 F775A953 D232216A 169D9A64 DDC00712 - 0A400B37 F2AFC077 B62FE304 DE74DE6A 119EC407 6B529C4F 6096B0BA AD4F533D - F0173B9B 822FD85D 65FA4BEF A92D8F52 4F69CBCA 0136BD80 D095C169 AEC0E095 - 'H, - signature ' - 48E8945E FE007556 D5BF4D5F 249E4808 F7307E29 511D3262 DAEF61D8 8098F9AA - 4A8BC062 3A8C9757 38F65D6B F459D543 F289D73C BC7AF4EA 3A33FBF3 EC444044 - 7911D722 94091E56 1833628E 49A772ED 608DE6C4 4595A91E 3E17D6CF 5EC3B252 - 8D63D2AD D6463989 B12EEC57 7DF64709 60DF6832 A9D84C36 0D1C217A D64C8625 - BDB594FB 0ADA086C DECBBDE5 80D424BF 9746D2F0 C312826D BBB00AD6 8B52C4CB - 7D47156B A35E3A98 1C973863 792CC80D 04A18021 0A524158 65B64B3A 61774B1D - 3975D78A 98B0821E E55CA0F8 6305D425 29E10EB0 15CEFD40 2FB59B2A BB8DEEE5 - 2A6F2447 D2284603 D219CD4E 8CF9CFFD D5498889 C3780B59 DD6A57EF 7D732620 - 'H - } - - exampleEd25519FulfillmentPayload Ed25519FulfillmentPayload ::= - { - publicKey ' - EC172B93 AD5E563B F4932C70 E1245034 C35467EF 2EFD4D64 EBF81968 3467E2BF - 'H, - signature ' - B62291FA D9432F8F 298B9C4A 4895DBE2 93F6FFDA 1A68DADF 0CCDEF5F 47A0C721 - 2A5FEA3C DA97A3F4 C03EA9F2 E8AC1CEC 86A51D45 2127ABDB A09D1B6F 331C070A - 'H - } - - END - - -# IANA Considerations {#appendix-e} - -## Crypto-Condition Type Registry {#crypto-conditions-type-registry} - -The following initial entries should be added to the Crypto-Condition Type registry to be created and maintained at (the suggested URI) -: - -The following feature suite bits are registered: - -| Type Bit | Exp. | Hex | Feature | -|----------|------|------|-----------------| -| 1 | 2^0 | 0x01 | SHA-256 | -| 10 | 2^1 | 0x02 | PREIMAGE | -| 100 | 2^2 | 0x04 | PREFIX | -| 1000 | 2^3 | 0x08 | THRESHOLD | -| 10000 | 2^4 | 0x10 | RSA | -| 100000 | 2^5 | 0x20 | ED25519 | -{: #crypto-condition-feature-suites title="Crypto-Condition Feature Suites"} - -The following types are registered: - -| Type ID | Required Bitmask | Type Name | -|---------|------------------|-------------------| -| 0 | 0x03 | PREIMAGE-SHA-256 | -| 1 | 0x05 | PREFIX-SHA-256 | -| 2 | 0x09 | THRESHOLD-SHA-256 | -| 3 | 0x11 | RSA-SHA-256 | -| 4 | 0x20 | ED25519 | -{: #crypto-condition-types title="Crypto-Condition Types"} +deprecated: true +--- +Spec has been moved to https://github.com/rfcs/crypto-conditions/ \ No newline at end of file diff --git a/0002-crypto-conditions/README.md b/0002-crypto-conditions/README.md index d848d1b5..02a6f7bc 100644 --- a/0002-crypto-conditions/README.md +++ b/0002-crypto-conditions/README.md @@ -1,26 +1,3 @@ # Crypto Conditions -See [here](https://interledger.org/five-bells-condition/spec.html) for the rendered spec. - -## Rendering - -Uses [kramdown-rfc2629](https://github.com/cabo/kramdown-rfc2629/), [xml2rfc](http://xml2rfc.ietf.org/) and [Grunt](http://gruntjs.com/) with [Grunt kramdown_rfc2629 task](https://github.com/hildjj/grunt-kramdown-rfc2629/) - - -From root directory of the repo run: - - npm install - grunt kramdown-rfc2629 - -To watch edits to 0002-crypto-conditions.md and auto-generate output when changes are saved run: - - grunt watch - - -## Files - -* [README.md](README.md) - this file -* [0002-crypto-conditions.md](0002-crypto-conditions.md) - canonical (mostly) markdown format file -* [output/0002-crypto-conditions.xml](output/0002-crypto-conditions.xml) - RFC in XML format -* [output/0002-crypto-conditions.txt](output/0002-crypto-conditions.txt) - RFC in text format -* [output/0002-crypto-conditions.html](output/0002-crypto-conditions.html) - RFC in html format +Spec has been moved to https://github.com/rfcs/crypto-conditions/ \ No newline at end of file diff --git a/0002-crypto-conditions/output/0002-crypto-conditions.html b/0002-crypto-conditions/output/0002-crypto-conditions.html index 7604ae9c..0635fd94 100644 --- a/0002-crypto-conditions/output/0002-crypto-conditions.html +++ b/0002-crypto-conditions/output/0002-crypto-conditions.html @@ -2,1461 +2,14 @@ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> - + - - Crypto-Conditions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Crypto-Conditions - - - - - - - - - - - - - - - - - - - - - - - - - - -
Network Working GroupS. Thomas
Internet-DraftR. Reginelli
Intended status: Standards TrackA. Hope-Bailie
Expires: April 29, 2017Ripple
October 26, 2016
- -

Crypto-Conditions
- draft-thomas-crypto-conditions-01

- -

- Abstract -

-

Crypto-conditions provide a mechanism to describe a signed message such that multiple actors in a distributed system can all verify the same signed message and agree on whether it matches the description. This provides a useful primitive for event-based systems that are distributed on the Internet since we can describe events in a standard deterministic manner (represented by signed messages) and therefore define generic authenticated event handlers.

-

- Feedback -

-

This specification is a part of the Interledger Protocol work. Feedback related to this specification should be sent to public-interledger@w3.org.

-

- Status of This Memo -

-

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

-

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at http://datatracker.ietf.org/drafts/current/.

-

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

-

This Internet-Draft will expire on April 29, 2017.

-

- Copyright Notice -

-

Copyright (c) 2016 IETF Trust and the persons identified as the document authors. All rights reserved.

-

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.

- - -
-

Table of Contents

- - -

1. Introduction

-

This specification describes a message format for crypto-conditions and fulfillments, with binary and string-based encodings for each.

-

Crypto-conditions are distributable event descriptions. This means crypto-conditions say how to recognize a message without saying exactly what the message is. You can transmit a crypto-condition freely, but you cannot forge the message it describes. For convenience, we hash the description so that the crypto-condition can be a fixed size.

-

Fulfillments are cryptographically verifiable messages that prove an event occurred. If you transmit a fulfillment, then everyone who has the condition can agree that the condition has been met.

-

In the Interledger protocol, crypto-conditions and fulfillments provide irrepudiable proof that a transfer occurred in one ledger, as messages that can be easily shared with other ledgers. This allows ledgers to escrow funds or hold a transfer conditionally, then execute the transfer automatically when the ledger sees the fulfillment of the stated condition.

-

Crypto-conditions may also be useful in other contexts where a system needs to make a decision based on predefined criteria, such as smart contracts.

-

1.1. Terminology

-

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119].

-

Within this specification, the term "condition" refers to the hash of a description of a signed message. The hash function must be preimage-resistant.

-

The term "fulfillment" refers to a description of a signed message and a signed message that matches the description. We hash the description and compare that to the condition, and also compare the signed message to the description. If the message matches the description and the hash of the description matches the condition, we say that the fulfillment fulfills the condition.

-

In the simplest case, the fulfillment can be a preimage that hashes to the condition, in which case the preimage is both the description and the message.

-

1.2. Features

-

Crypto-conditions are a standard format for expressing conditions and fulfillments. The format supports multiple algorithms, including different hash functions and cryptographic signing schemes. Crypto-conditions can be nested in multiple levels, with each level possibly having multiple signatures.

-

This format has been designed so that it can be expanded. For example, you can add new cryptographic signature schemes or hash functions. This is important because advances in cryptography frequently render old algorithms insecure or invent newer, more effective algorithms.

-

The Section 2.3 of a crypto-condition indicates which algorithms it uses, so a compliant implementation can know whether it supports the functionality required to interpret the crypto-condition.

-

1.2.1. Multi-Algorithm

-

The crypto-condition format contains a Section 2.3 that specifies which hash function and signing scheme to use. Any message format for a condition or a fulfillment contains such a bitmask.

-

Implementations MAY state their supported algorithms by providing a bitmask in the same format. To verify that a given implementation can verify a fulfillment for a given condition, you compare the bitmasks. If all bits set in the condition's bitmask are also set in the implementation's bitmask, then the implementation can verify the condition's fulfillment.

-

1.2.2. Multi-Signature

-

Crypto-conditions can abstract away many of the details of multi-sign. When a party provides a condition, other parties can treat it opaquely and do not need to know about its internal structure. That allows parties to define arbitrary multi-signature setups without breaking compatibility.

-

Protocol designers can use crypto-conditions as a drop-in replacement for public key signature algorithms and add multi-signature support to their protocols without adding any additional complexity.

-

1.2.3. Multi-Level

-

Crypto-conditions elegantly support weighted multi-signatures and multi-level signatures. A threshold condition has a number of weighted subconditions, and a target threshold. Each subcondition can be a signature or another threshold condition. This provides flexibility in forming complex conditions.

-

For example, consider a threshold condition that consists of two subconditions, one each from Agnes and Bruce. Agnes's condition can be a signature condition while Bruce's condition is a threshold condition, requiring both Claude and Dan to sign for him.

-

Weighted signatures allow more complex relationships than simple M-of-N signing. For example, a weighted condition can support an arrangement of subconditions such as, "Either Ron, Adi, and Leonard must approve; or Clifford must approve."

-

2. Format

-

2.1. Binary Encoding

-

A description of crypto-conditions is provided in this document using Abstract Syntax Notation One (ASN.1) as defined in [itu.X680.2015]. Implementations of this spec MUST support encoding and decoding using Octet Encoding Rules (OER) as defined in [itu.X696.2015].

-

2.2. String Types

-

Crypto-conditions use the following types within string encoding:

-

- -

-
BASE10
-
Variable-length integer encoded as a base-10 (decimal) number. Implementations MUST reject encoded values that are too large for them to parse. Implementations MUST be tested for overflows.
-
BASE16
-
Variable-length integer encoded as a base-16 (hexadecimal) number. Implementations MUST reject encoded values that are too large for them to parse. Implementations MUST be tested for overflows. No leading zeros.
-
BASE64URL
-
Base64-URL encoding. See [RFC4648], Section 5.
-
-

2.3. Bitmask

-

Any system that accepts crypto-conditions must be able to state its supported algorithms. It must be possible to verify that all algorithms used in a certain condition are indeed supported even if the fulfillment is not available yet. Therefore, all conditions and fulfillments contain a bitmask to express the required features. Implementations provide a bitmask of features they support.

-

Each bit represents a different suite of features. Each type of crypto-condition depends on one or more feature suites. If an implementation supports all feature suites that a certain type depends on, the implementation MUST support that condition type. The list of known types and feature suites is the IANA-maintained Crypto-Condition Type Registry [crypto-conditions-type-registry] .

-

To save space, the bitmask is encoded as a variable-length integer.

-

2.4. Condition

-

Below are the string and binary encoding formats for a condition.

-

2.4.1. String Format

-

Conditions are ASCII encoded as:

-
-"cc:" BASE16(type) ":" BASE16(featureBitmask) ":"
-    BASE64URL(fingerprint) ":" BASE10(maxFulfillmentLength)
-
-

2.4.2. Binary Format

-

Conditions are binary encoded as:

-
-Condition ::= SEQUENCE {
-  type ConditionType,
-  featureBitmask INTEGER (0..MAX),
-  fingerprint OCTET STRING,
-  maxFulfillmentLength INTEGER (0..MAX)
-}
-
-ConditionType ::= INTEGER {
-  preimageSha256(0),
-  rsaSha256(1),
-  prefixSha256(2),
-  thresholdSha256(3),
-  ed25519(4)
-} (0..65535)
-
-

2.4.3. Fields

-

- -

-
type
-
is the numeric type identifier representing the condition type.
-
featureBitmask
-
is an unsigned integer encoding the set of feature suites an implementation must support in order to be able to successfully parse the fulfillment to this condition. This is the boolean OR of the featureBitmask values of the top-level condition type and all subcondition types, recursively.
-
fingerprint
-
is an octet string uniquely representing the condition with respect to other conditions of the same type. Implementations which index conditions MUST use the entire string or binary encoded condition as the key, not just the fingerprint - as different conditions of different types may have the same fingerprint. The length and contents of the fingerprint are defined by the condition type. For most condition types, the fingerprint is a cryptographically secure hash of the data which defines the condition, such as a public key. This is encoded as a variable length octet string as different condition types may use different functions to produce the fingerprint which may therefore have different lengths. While it would be possible to determine the expected length of the fingerprint based on the type it is useful to be able to decode a condition even if the type is not recognized.
-
maxFulfillmentLength
-
is the maximum length of the fulfillment payload that can fulfill this condition, in bytes. The payload size is measured unencoded. (The size of the payload is larger in BASE64URL format.) When a crypto-condition is submitted to an implementation, this implementation MUST verify that it will be able to process a fulfillment with a payload of size maxFulfillmentLength.
-
-

2.4.4. Example Condition

-

An example condition in string format:

-
-cc:0:3:dB-8fb14MdO75Brp_Pvh4d7ganckilrRl13RS_UmrXA:66
-
-

The example has the following attributes:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldValueDescription
preface - cc - Constant. Indicates this is a condition.
type - 0 - Type 0 is [PREIMAGE-SHA-256][].
featuresBitmask - 3 - Boolean-OR combination of feature suites SHA-256 (feature bit 0x01) and PREIMAGE (feature bit 0x02).
fingerprint - dB-8fb14MdO75Brp_Pvh4d7ganckilrRl13RS_UmrXA - The hash of the fulfillment for this condition.
maxFulfillmentLength - 66 - The fulfillment payload is 66 bytes long, before being BASE64URL-encoded.
-

2.5. Fulfillment

-

Below are the string and binary encoding formats for a fulfillment.

-

2.5.1. String Format

-

Fulfillments are ASCII encoded as:

-
-"cf:" BASE16(type) ":" BASE64URL(payload)
-
-

2.5.2. Binary Format

-

Fulfillments are binary encoded as:

-
-Fulfillment ::= SEQUENCE {
-  type ConditionType,
-  payload OCTET STRING
-}
-
-

2.5.3. Fields

-

- -

-
type
-
is the numeric type identifier representing the condition type. For some condition types the fulfillment will contain further subfulfillments, however the type field always represents the outermost, or top-level, type.
-
payload
-
The payload is an octet string whose internal format is defined by each of the types.
-
-

2.5.4. Example Fulfillment

-

The following is an example fulfillment in string format, for the example condition [example-condition]:

-
-cf:0:VGhlIG9ubHkgYmFzaXMgZm9yIGdvb2QgU29jaWV0eSBpcyB1bmxpbWl0ZWQgY3JlZGl0LuKAlE9zY2FyIFdpbGRl
-
-

The example has the following attributes:

- - - - - - - - - - - - - - - - - - - - - - - - - -
FieldValueDescription
preface - cf - Constant. Indicates this is a fulfillment.
type - 0 - Type 0 is [PREIMAGE-SHA-256][].
payload - VGhlIG...pbGRl - The BASE64URL-encoded SHA-256 preimage of the condition, since this is a PREIMAGE-SHA-256 type fulfillment. In this case, it is an arbitrary string.
-

3. Feature Suites

-

This specification defines a starting set of feature suites necessary to support the [Condition Types][] also defined in this specification. Future versions of this spec MAY introduce new feature suites and condition types, which SHALL be registered in the IANA maintained Crypto-Condition Type Registry [crypto-conditions-type-registry].

-

Support for a condition type MUST depend on one or more feature suites. However, all new condition types MUST depend on at least one of the new feature suites. This ensures that all previously created implementations correctly recognize that they do not support the new type.

-

Feature suites are chosen such that they represent reasonable clusters of functionality. For instance, it is reasonable to require that an implementation which supports SHA-256 in one context MUST support it in all contexts, since it already needed to implement the algorithm.

-

An implementation which supports a certain set of feature suites MUST accept all condition types which depend only on that set or any subset of feature suites.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Suite NameFeature BitFeature Bit (BASE16)Summary
SHA-2562^00x01The SHA-256 hashing algorithm.
PREIMAGE2^10x02The functionality of comparing a hash to a preimage.
PREFIX2^20x04The functionality of prefixing the fulfillment with a prefix before generating the condition.
THRESHOLD2^30x08The functionality of composing a condition out of several weighted subconditions.
RSA-PSS2^40x10The RSA-PSS signature algorithm.
ED255192^50x20The ED25519 signature algorithm.
-

3.1. SHA-256

-

The SHA-256 feature suite provides the SHA-256 hash function. SHA-256 is a cryptographic hash function published by the US National Institute of Standards and Technology that produces 256 bits of output. This feature suite is assigned the feature bit 2^0 = 0x01.

-

3.2. PREIMAGE

-

The PREIMAGE feature suite provides conditions that use a preimage as a one-time signature. This feature suite is assigned the feature bit 2^1 = 0x02.

-

The fingerprint of a preimage condition is the hash of an arbitrary value. The payload of a preimage fulfillment is the hashed arbitrary value before hashing, also known as the preimage. Conditions that use this preimage MUST also rely on a cryptographically secure hashing algorithm. Since cryptographically secure hashing functions are preimage-resistant, only the original creator of a preimage condition can produce the preimage, as long as it contains a large amount of random entropy.

-

3.3. PREFIX

-

The PREFIX feature suite provides conditions that prepend a fixed message to a subcondition. This feature suite is assigned the feature bit 2^2 = 0x04.

-

A prefix condition prepends the message to be validated with a constant string before passing it on to the subcondition's validation function.

-

3.4. THRESHOLD

-

The THRESHOLD feature suite provides conditions that have several weighted subconditions and a threshold number. This feature suite is assigned the feature bit 2^3 = 0x08.

-

Threshold conditions provide flexible multi-signing, such as requiring "M-of-N" subconditions be fulfilled. Subconditions can also be weighted so that one subcondition can count multiple times towards meeting the threshold.

-

3.5. RSA-PSS

-

The RSA-PSS feature suite provides the RSS-PSA signature algorithm. RSA-PSS is a signature algorithm based on the RSA cryptosystem, which relates to the problem of factoring the product of two large prime numbers. This feature suite is assigned the feature bit 2^4 = 0x10.

-

3.6. ED25519

-

The ED25519 feature suite provides the Ed25519 signature algorithm. Ed25519 is a signature algorithm based on the EdDSA signing scheme and the compact elliptic curve known as Ed25519. This feature suite is assigned the feature bit 2^5 = 0x20.

-

4. Condition Types

-

The following condition types are defined in this version of the specification. Future versions of this spec MAY introduce new feature suites and condition types, which SHALL be registered in the IANA maintained Crypto-Condition Type Registry [crypto-conditions-type-registry].

-

4.1. PREIMAGE-SHA-256

-

PREIMAGE-SHA-256 is assigned the type ID 0. It relies on the SHA-256 and PREIMAGE feature suites which corresponds to a feature bitmask of 0x03.

-

This type of condition is also called a "hashlock". By creating a hash of a difficult-to-guess 256-bit random or pseudo-random integer it is possible to create a condition which the creator can trivially fulfill by publishing the random value. However, for anyone else, the condition is cryptographically hard to fulfill, because they would have to find a preimage for the given condition hash.

-

Implementations MUST ignore any input message when validating a PREIMAGE-SHA-256 fulfillment.

-

4.1.1. Condition

-

The fingerprint of a PREIMAGE-SHA-256 condition is the SHA-256 hash of the preimage.

-

4.1.2. Fulfillment

-

The fulfillment payload of a PREIMAGE-SHA-256 condition is the preimage.

-

4.1.3. Example

-

Example condition:

-
-cc:0:3:dB-8fb14MdO75Brp_Pvh4d7ganckilrRl13RS_UmrXA:66
-
-

Example fulfillment:

-
-cf:0:VGhlIG9ubHkgYmFzaXMgZm9yIGdvb2QgU29jaWV0eSBpcyB1bmxpbWl0ZWQgY3JlZGl0LuKAlE9zY2FyIFdpbGRl
-
-

4.2. PREFIX-SHA-256

-

PREFIX-SHA-256 is assigned the type ID 1. It relies on the SHA-256 and PREFIX feature suites which corresponds to a feature bitmask of 0x05.

-

Prefix conditions provide a way to effective narrow the scope of other conditions. A condition can be used as the fingerprint of a public key to sign an arbitrary message. By creating a prefix subcondition we can narrow the scope from signing an arbitrary message to signing a message with a specific prefix.

-

When a prefix fulfillment is validated against a message, it will prepend the prefix to the provided message and will use the result as the message to validate against the subfulfillment.

-

4.2.1. Condition

-

The fingerprint of a PREFIX-SHA-256 condition is the SHA-256 digest of the fingerprint contents given below:

-
-PrefixSha256FingerprintContents ::= SEQUENCE {
-  prefix OCTET STRING,
-  condition Condition
-}
-
-

- -

-
prefix
-
is an arbitrary octet string which will be prepended to the message during validation.
-
condition
-
is the subcondition which the subfulfillment must match.
-
-

4.2.2. Fulfillment

-
-PrefixSha256FulfillmentPayload ::= SEQUENCE {
-  prefix OCTET STRING,
-  subfulfillment Fulfillment
-}
-
-

- -

-
prefix
-
is an arbitrary octet string which will be prepended to the message during validation.
-
subfulfillment
-
is the fulfilled subcondition.
-
-

4.2.3. Example

-

Example condition:

-
-cc:1:25:7myveZs3EaZMMuez-3kq6u69BDNYMYRMi_VF9yIuFLc:102
-
-

Example fulfillment:

-
-cf:1:DUhlbGxvIFdvcmxkISAABGDsFyuTrV5WO_STLHDhJFA0w1Rn7y79TWTr-BloNGfiv7YikfrZQy-PKYucSkiV2-KT9v_aGmja3wzN719HoMchKl_qPNqXo_TAPqny6Kwc7IalHUUhJ6vboJ0bbzMcBwo
-
-

4.3. THRESHOLD-SHA-256

-

THRESHOLD-SHA-256 is assigned the type ID 2. It relies on the SHA-256 and THRESHOLD feature suites which corresponds to a feature bitmask of 0x09.

-

4.3.1. Condition

-

The fingerprint of a THRESHOLD-SHA-256 condition is the SHA-256 digest of the fingerprint contents given below:

-
-ThresholdSha256FingerprintContents ::= SEQUENCE {
-  threshold INTEGER (0..4294967295),
-  subconditions SEQUENCE OF ThresholdSubcondition
-}
-
-ThresholdSubcondition ::= SEQUENCE {
-  weight INTEGER (0..4294967295),
-  condition Condition
-}
-
-

The list of conditions is sorted first based on length, shortest first. Elements of the same length are sorted in lexicographic (big-endian) order, smallest first.

-

- -

-
threshold
-
threshold MUST be an integer in the range 1 … 2^32 - 1. In order to fulfill a threshold condition, the weights of the provided fulfillments MUST be greater than or equal to the threshold.
-
subconditions
-
is the set of subconditions, each provided as a tuple of weight and condition.
-
weight
-
is the numeric weight of this subcondition, i.e. how many times it counts against the threshold.
-
condition
-
is the subcondition.
-
-

4.3.2. Fulfillment

-
-ThresholdSha256FulfillmentPayload ::= SEQUENCE {
-  threshold INTEGER (0..4294967295),
-  subfulfillments SEQUENCE OF ThresholdSubfulfillment
-}
-
-ThresholdSubfulfillment ::= SEQUENCE {
-  weight INTEGER (0..4294967295) DEFAULT 1,
-  condition Condition OPTIONAL,
-  fulfillment Fulfillment OPTIONAL
-}
-
-

- -

-
threshold
-
is a number and MUST be an integer in the range 1 … 2^32 - 1. In order to fulfill a threshold condition, the weights of the provided fulfillments MUST be greater than or equal to the threshold.
-
subfulfillments
-
is the set of subconditions and subfulfillments, each provided as a tuple of weight and condition or fulfillment.
-
weight
-
is the numeric weight of this subcondition, i.e. how many times it counts against the threshold. It MUST be an integer in the range 1 … 2^32 - 1.
-
condition
-
is the subcondition if this subcondition is not fulfilled.
-
fulfillment
-
is the subfulfillment if this subcondition is fulfilled.
-
-

4.3.3. Example

-

Example condition:

-
-cc:2:2b:mJUaGKCuF5n-3tfXM2U81VYtHbX-N8MP6kz8R-ASwNQ:146
-
-

Example fulfillment:

-
-cf:2:AQEBAgEBAwAAAAABAQAnAAQBICDsFyuTrV5WO_STLHDhJFA0w1Rn7y79TWTr-BloNGfivwFg
-
-

4.4. RSA-SHA-256

-

RSA-SHA-256 is assigned the type ID 3. It relies on the SHA-256 and RSA-PSS feature suites which corresponds to a feature bitmask of 0x11.

-

The signature algorithm used is RSASSA-PSS as defined in PKCS#1 v2.2. [RFC3447]

-

Implementations MUST NOT use the default RSASSA-PSS-params. Implementations MUST use the SHA-256 hash algorithm and therefor, the same algorithm in the mask generation algorithm, as recommended in [RFC3447]. Implementations MUST also use a salt length of 32 bytes (equal to the size of the output from the SHA-256 algorithm). Therefore the algorithm identifier will have the following value:

-
-rSASSA-PSS-Crypto-Conditions-Identifier  RSASSA-AlgorithmIdentifier ::= {
-    algorithm   id-RSASSA-PSS,
-    parameters  RSASSA-PSS-params : {
-        hashAlgorithm       sha256,
-        maskGenAlgorithm    mgf1SHA256,
-        saltLength          32,
-        trailerField        trailerFieldBC
-    }
-}
-   
-sha256 HashAlgorithm ::= {
-    algorithm   id-sha256,
-    parameters  NULL
-}
-
-mgf1SHA256 MaskGenAlgorithm ::= {
-    algorithm   id-mgf1,
-    parameters  HashAlgorithm : sha256
-}
-
-

4.4.1. Condition

-

The fingerprint of a RSA-SHA-256 condition is the SHA-256 digest of the fingerprint contents given below:

-
-RsaSha256FingerprintContents ::= SEQUENCE {
-  modulus OCTET STRING (SIZE(128..512))
-}
-
-

- -

-
modulus
-
is an octet string representing the RSA public modulus in big-endian byte order. The first byte of the modulus MUST NOT be zero.
-
-
The corresponding public exponent e is assumed to be 65537 as recommended in [RFC4871] . Very large exponents can be a DoS vector [LARGE-RSA-EXPONENTS] and 65537 is the largest Fermat prime, which has some nice properties [USING-RSA-EXPONENT-OF-65537] .
-
-
Implementations MUST reject moduli smaller than 128 bytes (1017 bits) or greater than 512 bytes (4096 bits.) Large moduli slow down signature verification which can be a denial-of-service vector. DNSSEC also limits the modulus to 4096 bits [RFC3110] . OpenSSL supports up to 16384 bits [OPENSSL-X509-CERT-EXAMPLES] .
-
-

4.4.2. Fulfillment

-
-RsaSha256FulfillmentPayload ::= SEQUENCE {
-  modulus OCTET STRING (SIZE(128..512)),
-  signature OCTET STRING (SIZE(128..512))
-}
-
-

- -

-
modulus
-
is an octet string representing the RSA public modulus in big-endian byte order. See Section 4.4.1
-
signature
-
is an octet string representing the RSA signature. It MUST be encoded in big-endian byte order with the exact same number of octets as the modulus, even if this means adding leading zeros. This ensures that the fulfillment size is constant and known ahead of time. Note that the field is still binary encoded with a length prefix for consistency.
-
-
Implementations MUST verify that the signature and modulus consist of the same number of octets and that the signature is numerically less than the modulus.
-
-

The message to be signed is provided separately. If no message is provided, the message is assumed to be an octet string of length zero.

-

4.4.3. Implementation

-

The recommended modulus size as of 2016 is 2048 bits [KEYLENGTH-RECOMMENDATION] . In the future we anticipate an upgrade to 3072 bits which provides approximately 128 bits of security [NIST-KEYMANAGEMENT] (p. 64), about the same level as SHA-256.

-

4.4.4. Example

-

Example condition:

-
-cc:3:11:Bw-r77AGqSCL0huuMQYj3KW0Jh67Fpayeq9h_4UJctg:260
-
-

Example fulfillment:

-
-cf:3:gYCzDnqTh4O6v4NoUP9J4U-H4_ktXEbjP-yj5PCyI1hYCxF2WZX0uO6n-0cSwuHjFvf3dalT0jIhahadmmTdwAcSCkALN_KvwHe2L-ME3nTeahGexAdrUpxPYJawuq1PUz3wFzubgi_YXWX6S--pLY9ST2nLygE2vYDQlcFprsDglYGAjQM0-Z5B-953uQtJ5dXL1D5TWpM0s0eFF0Zty7J2Y3Nb0PqsR5I47a2wYlA7-106vjC8gHFdHVeSR6JksSrhj8YaMWfV0A6qhPz6hq-TqSKCXd4mf3eCpyyFYR_EyH5zXd56sJEU3snWlFbB_bKAW4si_qdfY9dT87YGUp_Grm0
-
-

4.5. ED25519

-

ED25519 is assigned the type ID 4. It relies only on the ED25519 feature suite which corresponds to a bitmask of 0x20.

-

The exact algorithm and encodings used for public key and signature are defined in [I-D.irtf-cfrg-eddsa] as Ed25519. SHA-512 is used as the hashing function.

-

Note: This document is not defining the SHA-512 versions of other condition types. In addition, the Ed25519 condition type is already uniquely identified by a corresponding Ed25519 feature suite. Therefore we intentionally do not introduce a SHA-512 feature suite in this document.

-

4.5.1. Condition

-

The fingerprint of a ED25519 condition is the 32 byte Ed25519 public key. Since the public key is already very small, we do not hash it.

-

4.5.2. Fulfillment

-
-Ed25519FulfillmentPayload ::= SEQUENCE {
-  publicKey OCTET STRING (SIZE(32)),
-  signature OCTET STRING (SIZE(64))
-}
-
-

- -

-
publicKey
-
is an octet string containing the Ed25519 public key.
-
signature
-
is an octet string containing the Ed25519 signature.
-
-

4.5.3. Example

-

Example condition:

-
-cc:4:20:7Bcrk61eVjv0kyxw4SRQNMNUZ-8u_U1k6_gZaDRn4r8:96
-
-

Example fulfillment:

-
-cf:4:7Bcrk61eVjv0kyxw4SRQNMNUZ-8u_U1k6_gZaDRn4r-2IpH62UMvjymLnEpIldvik_b_2hpo2t8Mze9fR6DHISpf6jzal6P0wD6p8uisHOyGpR1FISer26CdG28zHAcK
-
-

5. References

-

5.1. Normative References

- - - - - - - - - - - - - - - - - - - - - - - -
- [I-D.irtf-cfrg-eddsa] - Josefsson, S. and I. Liusvaara, "Edwards-curve Digital Signature Algorithm (EdDSA)", Internet-Draft draft-irtf-cfrg-eddsa-04, March 2016.
- [itu.X680.2015] - International Telecommunications Union, "Information technology – Abstract Syntax Notation One (ASN.1): Specification of basic notation", August 2015.
- [itu.X696.2015] - International Telecommunications Union, "Information technology – ASN.1 encoding rules: Specification of Octet Encoding Rules (OER)", August 2015.
- [RFC3447] - Jonsson, J. and B. Kaliski, "Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1", RFC 3447, DOI 10.17487/RFC3447, February 2003.
- [RFC4648] - Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006.
-

5.2. Informative References

- - - , " - , " - , " - , " - - - - - - - - - - - - - , " - -
[KEYLENGTH-RECOMMENDATION]BlueKrypt - Cryptographic Key Length Recommendation", September 2015.
[LARGE-RSA-EXPONENTS]Imperial Violet - Very large RSA public exponents (17 Mar 2012)", March 2012.
[NIST-KEYMANAGEMENT]NIST - Recommendation for Key Management - Part 1 - General (Revision 3)", July 2012.
[OPENSSL-X509-CERT-EXAMPLES]OpenSSL - X509 certificate examples for testing and verification", July 2012.
- [RFC2119] - Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997.
- [RFC3110] - Eastlake 3rd, D., "RSA/SHA-1 SIGs and RSA KEYs in the Domain Name System (DNS)", RFC 3110, DOI 10.17487/RFC3110, May 2001.
- [RFC4871] - Allman, E., Callas, J., Delany, M., Libbey, M., Fenton, J. and M. Thomas, DomainKeys Identified Mail (DKIM) Signatures", RFC 4871, DOI 10.17487/RFC4871, May 2007.
[USING-RSA-EXPONENT-OF-65537]Cryptography - StackExchange - Impacts of not using RSA exponent of 65537", November 2014.
-

Appendix A. Security Considerations

-

This section to be expanded in a later draft.

-

Appendix B. Test Values

-

This section to be expanded in a later draft. For now, see the test cases for the reference implementation: https://github.com/interledger/five-bells-condition/tree/master/test

-

Appendix C. ASN.1 Module

-
---<ASN1.PDU CryptoConditions.Condition, CryptoConditions.Fulfillment>--
-
-CryptoConditions
-DEFINITIONS
-AUTOMATIC TAGS ::=
-BEGIN
-
-/**
-* CONTAINERS
-*/
-
-Condition ::= SEQUENCE {
-type ConditionType,
-featureBitmask OCTET STRING,
-fingerprint OCTET STRING,
-maxFulfillmentLength INTEGER (0..MAX)
-}
-
-Fulfillment ::= SEQUENCE {
-type ConditionType,
-payload OCTET STRING
-}
-
-ConditionType ::= INTEGER {
-preimageSha256(0),
-rsaSha256(1),
-prefixSha256(2),
-thresholdSha256(3),
-ed25519(4)
-} (0..65535)
-
-/**
-* FULFILLMENT PAYLOADS
-*/
-
--- For preimage conditions, the payload equals the preimage
-
-PrefixSha256FulfillmentPayload ::= SEQUENCE {
-prefix OCTET STRING,
-subfulfillment Fulfillment
-}
-
-ThresholdSha256FulfillmentPayload ::= SEQUENCE {
-threshold INTEGER (0..4294967295),
-subfulfillments SEQUENCE OF ThresholdSubfulfillment
-}
-
-ThresholdSubfulfillment ::= SEQUENCE {
-weight INTEGER (0..4294967295) DEFAULT 1,
-condition Condition OPTIONAL,
-fulfillment Fulfillment OPTIONAL
-}
-
-RsaSha256FulfillmentPayload ::= SEQUENCE {
-modulus OCTET STRING (SIZE(128..512)),
-signature OCTET STRING (SIZE(128..512))
-}
-
-Ed25519FulfillmentPayload ::= SEQUENCE {
-publicKey OCTET STRING (SIZE(32)),
-signature OCTET STRING (SIZE(64))
-}
-
-/**
-* FINGERPRINTS
-*/
-
--- SHA-256 hash of the fingerprint contents
-Sha256Fingerprint ::= OCTET STRING (SIZE(32)) -- digest
-
--- 32-byte Ed25519 public key
-Ed25519Fingerprint ::= OCTET STRING (SIZE(32)) -- publicKey
-
-/**
-* FINGERPRINT CONTENTS
-*
-* The content that will be hashed to arrive at the fingerprint.
-*/
-
--- The preimage type hashes the raw contents of the preimage
-
-PrefixSha256FingerprintContents ::= SEQUENCE {
-prefix OCTET STRING,
-condition Condition
-}
-
-ThresholdSha256FingerprintContents ::= SEQUENCE {
-threshold INTEGER (0..4294967295),
-subconditions SEQUENCE OF ThresholdSubcondition
-}
-
-ThresholdSubcondition ::= SEQUENCE {
-weight INTEGER (0..4294967295),
-condition Condition
-}
-
-RsaSha256FingerprintContents ::= INTEGER (0..MAX) -- modulus
-
-/**
-* EXAMPLES
-*/
-
-exampleCondition Condition ::=
-{
-type preimageSha256,
-featureBitmask '03'H,
-fingerprint '
-E3B0C442 98FC1C14 9AFBF4C8 996FB924 27AE41E4 649B934C A495991B 7852B855
-'H,
-maxFulfillmentLength 2
-}
-
-exampleFulfillment Fulfillment ::=
-{
-type preimageSha256,
-payload '00'H
-}
-
-exampleRsaSha256FulfillmentPayload RsaSha256FulfillmentPayload ::=
-{
-modulus '
-B30E7A93 8783BABF 836850FF 49E14F87 E3F92D5C 46E33FEC A3E4F0B2 2358580B
-11765995 F4B8EEA7 FB4712C2 E1E316F7 F775A953 D232216A 169D9A64 DDC00712
-0A400B37 F2AFC077 B62FE304 DE74DE6A 119EC407 6B529C4F 6096B0BA AD4F533D
-F0173B9B 822FD85D 65FA4BEF A92D8F52 4F69CBCA 0136BD80 D095C169 AEC0E095
-'H,
-signature '
-48E8945E FE007556 D5BF4D5F 249E4808 F7307E29 511D3262 DAEF61D8 8098F9AA
-4A8BC062 3A8C9757 38F65D6B F459D543 F289D73C BC7AF4EA 3A33FBF3 EC444044
-7911D722 94091E56 1833628E 49A772ED 608DE6C4 4595A91E 3E17D6CF 5EC3B252
-8D63D2AD D6463989 B12EEC57 7DF64709 60DF6832 A9D84C36 0D1C217A D64C8625
-BDB594FB 0ADA086C DECBBDE5 80D424BF 9746D2F0 C312826D BBB00AD6 8B52C4CB
-7D47156B A35E3A98 1C973863 792CC80D 04A18021 0A524158 65B64B3A 61774B1D
-3975D78A 98B0821E E55CA0F8 6305D425 29E10EB0 15CEFD40 2FB59B2A BB8DEEE5
-2A6F2447 D2284603 D219CD4E 8CF9CFFD D5498889 C3780B59 DD6A57EF 7D732620
-'H
-}
-
-exampleEd25519FulfillmentPayload Ed25519FulfillmentPayload ::=
-{
-publicKey '
-EC172B93 AD5E563B F4932C70 E1245034 C35467EF 2EFD4D64 EBF81968 3467E2BF
-'H,
-signature '
-B62291FA D9432F8F 298B9C4A 4895DBE2 93F6FFDA 1A68DADF 0CCDEF5F 47A0C721
-2A5FEA3C DA97A3F4 C03EA9F2 E8AC1CEC 86A51D45 2127ABDB A09D1B6F 331C070A
-'H
-}
-
-END
-
-

Appendix D. IANA Considerations

-

D.1. Crypto-Condition Type Registry

-

The following initial entries should be added to the Crypto-Condition Type registry to be created and maintained at (the suggested URI) http://www.iana.org/assignments/crypto-condition-types:

-

The following feature suite bits are registered:

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Crypto-Condition Feature Suites
Type BitExp.HexFeature
12^00x01SHA-256
102^10x02PREIMAGE
1002^20x04PREFIX
10002^30x08THRESHOLD
100002^40x10RSA
1000002^50x20ED25519
-

The following types are registered:

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Crypto-Condition Types
Type IDRequired BitmaskType Name
00x03PREIMAGE-SHA-256
10x05PREFIX-SHA-256
20x09THRESHOLD-SHA-256
30x11RSA-SHA-256
40x20ED25519
-

- Authors' Addresses -

-
-
- - Stefan Thomas - - - Ripple - - 300 Montgomery Street - - - San Francisco, - CA - 94104 - - US - - Phone: ----------------- - -EMail: stefan@ripple.com - -URI: https://www.ripple.com - -
-
-
- - Rome Reginelli - - - Ripple - - 300 Montgomery Street - - - San Francisco, - CA - 94104 - - US - - Phone: ----------------- - -EMail: rome@ripple.com - -URI: https://www.ripple.com - -
-
-
- - Adrian Hope-Bailie - - - Ripple - - 300 Montgomery Street - - - San Francisco, - CA - 94104 - - US - - Phone: ----------------- - -EMail: adrian@ripple.com - -URI: https://www.ripple.com - -
-
+

Spec has been moved to https://github.com/rfcs/crypto-conditions/

diff --git a/0002-crypto-conditions/output/0002-crypto-conditions.txt b/0002-crypto-conditions/output/0002-crypto-conditions.txt index fc0b1193..41275244 100644 --- a/0002-crypto-conditions/output/0002-crypto-conditions.txt +++ b/0002-crypto-conditions/output/0002-crypto-conditions.txt @@ -1,1456 +1 @@ - - - - -Network Working Group S. Thomas -Internet-Draft R. Reginelli -Intended status: Standards Track A. Hope-Bailie -Expires: April 29, 2017 Ripple - October 26, 2016 - - - Crypto-Conditions - draft-thomas-crypto-conditions-01 - -Abstract - - Crypto-conditions provide a mechanism to describe a signed message - such that multiple actors in a distributed system can all verify the - same signed message and agree on whether it matches the description. - This provides a useful primitive for event-based systems that are - distributed on the Internet since we can describe events in a - standard deterministic manner (represented by signed messages) and - therefore define generic authenticated event handlers. - -Feedback - - This specification is a part of the Interledger Protocol [1] work. - Feedback related to this specification should be sent to public- - interledger@w3.org [2]. - -Status of This Memo - - This Internet-Draft is submitted in full conformance with the - provisions of BCP 78 and BCP 79. - - Internet-Drafts are working documents of the Internet Engineering - Task Force (IETF). Note that other groups may also distribute - working documents as Internet-Drafts. The list of current Internet- - Drafts is at http://datatracker.ietf.org/drafts/current/. - - Internet-Drafts are draft documents valid for a maximum of six months - and may be updated, replaced, or obsoleted by other documents at any - time. It is inappropriate to use Internet-Drafts as reference - material or to cite them other than as "work in progress." - - This Internet-Draft will expire on April 29, 2017. - -Copyright Notice - - Copyright (c) 2016 IETF Trust and the persons identified as the - document authors. All rights reserved. - - - - -Thomas, et al. Expires April 29, 2017 [Page 1] - -Internet-Draft Crypto-Conditions October 2016 - - - This document is subject to BCP 78 and the IETF Trust's Legal - Provisions Relating to IETF Documents - (http://trustee.ietf.org/license-info) in effect on the date of - publication of this document. Please review these documents - carefully, as they describe your rights and restrictions with respect - to this document. Code Components extracted from this document must - include Simplified BSD License text as described in Section 4.e of - the Trust Legal Provisions and are provided without warranty as - described in the Simplified BSD License. - -Table of Contents - - 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 - 1.1. Terminology . . . . . . . . . . . . . . . . . . . . . . . 4 - 1.2. Features . . . . . . . . . . . . . . . . . . . . . . . . 4 - 1.2.1. Multi-Algorithm . . . . . . . . . . . . . . . . . . . 4 - 1.2.2. Multi-Signature . . . . . . . . . . . . . . . . . . . 5 - 1.2.3. Multi-Level . . . . . . . . . . . . . . . . . . . . . 5 - 2. Format . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 - 2.1. Binary Encoding . . . . . . . . . . . . . . . . . . . . . 5 - 2.2. String Types . . . . . . . . . . . . . . . . . . . . . . 6 - 2.3. Bitmask . . . . . . . . . . . . . . . . . . . . . . . . . 6 - 2.4. Condition . . . . . . . . . . . . . . . . . . . . . . . . 6 - 2.4.1. String Format . . . . . . . . . . . . . . . . . . . . 6 - 2.4.2. Binary Format . . . . . . . . . . . . . . . . . . . . 7 - 2.4.3. Fields . . . . . . . . . . . . . . . . . . . . . . . 7 - 2.4.4. Example Condition . . . . . . . . . . . . . . . . . . 8 - 2.5. Fulfillment . . . . . . . . . . . . . . . . . . . . . . . 9 - 2.5.1. String Format . . . . . . . . . . . . . . . . . . . . 9 - 2.5.2. Binary Format . . . . . . . . . . . . . . . . . . . . 9 - 2.5.3. Fields . . . . . . . . . . . . . . . . . . . . . . . 9 - 2.5.4. Example Fulfillment . . . . . . . . . . . . . . . . . 9 - 3. Feature Suites . . . . . . . . . . . . . . . . . . . . . . . 10 - 3.1. SHA-256 . . . . . . . . . . . . . . . . . . . . . . . . . 11 - 3.2. PREIMAGE . . . . . . . . . . . . . . . . . . . . . . . . 11 - 3.3. PREFIX . . . . . . . . . . . . . . . . . . . . . . . . . 12 - 3.4. THRESHOLD . . . . . . . . . . . . . . . . . . . . . . . . 12 - 3.5. RSA-PSS . . . . . . . . . . . . . . . . . . . . . . . . . 12 - 3.6. ED25519 . . . . . . . . . . . . . . . . . . . . . . . . . 12 - 4. Condition Types . . . . . . . . . . . . . . . . . . . . . . . 12 - 4.1. PREIMAGE-SHA-256 . . . . . . . . . . . . . . . . . . . . 12 - 4.1.1. Condition . . . . . . . . . . . . . . . . . . . . . . 13 - 4.1.2. Fulfillment . . . . . . . . . . . . . . . . . . . . . 13 - 4.1.3. Example . . . . . . . . . . . . . . . . . . . . . . . 13 - 4.2. PREFIX-SHA-256 . . . . . . . . . . . . . . . . . . . . . 13 - 4.2.1. Condition . . . . . . . . . . . . . . . . . . . . . . 14 - 4.2.2. Fulfillment . . . . . . . . . . . . . . . . . . . . . 14 - 4.2.3. Example . . . . . . . . . . . . . . . . . . . . . . . 14 - - - -Thomas, et al. Expires April 29, 2017 [Page 2] - -Internet-Draft Crypto-Conditions October 2016 - - - 4.3. THRESHOLD-SHA-256 . . . . . . . . . . . . . . . . . . . . 14 - 4.3.1. Condition . . . . . . . . . . . . . . . . . . . . . . 14 - 4.3.2. Fulfillment . . . . . . . . . . . . . . . . . . . . . 15 - 4.3.3. Example . . . . . . . . . . . . . . . . . . . . . . . 16 - 4.4. RSA-SHA-256 . . . . . . . . . . . . . . . . . . . . . . . 16 - 4.4.1. Condition . . . . . . . . . . . . . . . . . . . . . . 17 - 4.4.2. Fulfillment . . . . . . . . . . . . . . . . . . . . . 18 - 4.4.3. Implementation . . . . . . . . . . . . . . . . . . . 18 - 4.4.4. Example . . . . . . . . . . . . . . . . . . . . . . . 18 - 4.5. ED25519 . . . . . . . . . . . . . . . . . . . . . . . . . 18 - 4.5.1. Condition . . . . . . . . . . . . . . . . . . . . . . 19 - 4.5.2. Fulfillment . . . . . . . . . . . . . . . . . . . . . 19 - 4.5.3. Example . . . . . . . . . . . . . . . . . . . . . . . 19 - 5. References . . . . . . . . . . . . . . . . . . . . . . . . . 19 - 5.1. Normative References . . . . . . . . . . . . . . . . . . 19 - 5.2. Informative References . . . . . . . . . . . . . . . . . 20 - Appendix A. Security Considerations . . . . . . . . . . . . . . 21 - Appendix B. Test Values . . . . . . . . . . . . . . . . . . . . 21 - Appendix C. ASN.1 Module . . . . . . . . . . . . . . . . . . . . 21 - Appendix D. IANA Considerations . . . . . . . . . . . . . . . . 24 - D.1. Crypto-Condition Type Registry . . . . . . . . . . . . . 24 - Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 25 - -1. Introduction - - This specification describes a message format for crypto-conditions - and fulfillments, with binary and string-based encodings for each. - - Crypto-conditions are *distributable event descriptions*. This means - crypto-conditions say how to recognize a message without saying - exactly what the message is. You can transmit a crypto-condition - freely, but you cannot forge the message it describes. For - convenience, we hash the description so that the crypto-condition can - be a fixed size. - - Fulfillments are *cryptographically verifiable messages* that prove - an event occurred. If you transmit a fulfillment, then everyone who - has the condition can agree that the condition has been met. - - In the Interledger protocol, crypto-conditions and fulfillments - provide irrepudiable proof that a transfer occurred in one ledger, as - messages that can be easily shared with other ledgers. This allows - ledgers to escrow funds or hold a transfer conditionally, then - execute the transfer automatically when the ledger sees the - fulfillment of the stated condition. - - - - - - -Thomas, et al. Expires April 29, 2017 [Page 3] - -Internet-Draft Crypto-Conditions October 2016 - - - Crypto-conditions may also be useful in other contexts where a system - needs to make a decision based on predefined criteria, such as smart - contracts. - -1.1. Terminology - - The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", - "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this - document are to be interpreted as described in [RFC2119]. - - Within this specification, the term "condition" refers to the hash of - a description of a signed message. The hash function must be - preimage-resistant. - - The term "fulfillment" refers to a description of a signed message - and a signed message that matches the description. We hash the - description and compare that to the condition, and also compare the - signed message to the description. If the message matches the - description and the hash of the description matches the condition, we - say that the fulfillment fulfills the condition. - - In the simplest case, the fulfillment can be a preimage that hashes - to the condition, in which case the preimage is both the description - and the message. - -1.2. Features - - Crypto-conditions are a standard format for expressing conditions and - fulfillments. The format supports multiple algorithms, including - different hash functions and cryptographic signing schemes. Crypto- - conditions can be nested in multiple levels, with each level possibly - having multiple signatures. - - This format has been designed so that it can be expanded. For - example, you can add new cryptographic signature schemes or hash - functions. This is important because advances in cryptography - frequently render old algorithms insecure or invent newer, more - effective algorithms. - - The Section 2.3 of a crypto-condition indicates which algorithms it - uses, so a compliant implementation can know whether it supports the - functionality required to interpret the crypto-condition. - -1.2.1. Multi-Algorithm - - The crypto-condition format contains a Section 2.3 that specifies - which hash function and signing scheme to use. Any message format - for a condition or a fulfillment contains such a bitmask. - - - -Thomas, et al. Expires April 29, 2017 [Page 4] - -Internet-Draft Crypto-Conditions October 2016 - - - Implementations MAY state their supported algorithms by providing a - bitmask in the same format. To verify that a given implementation - can verify a fulfillment for a given condition, you compare the - bitmasks. If all bits set in the condition's bitmask are also set in - the implementation's bitmask, then the implementation can verify the - condition's fulfillment. - -1.2.2. Multi-Signature - - Crypto-conditions can abstract away many of the details of multi- - sign. When a party provides a condition, other parties can treat it - opaquely and do not need to know about its internal structure. That - allows parties to define arbitrary multi-signature setups without - breaking compatibility. - - Protocol designers can use crypto-conditions as a drop-in replacement - for public key signature algorithms and add multi-signature support - to their protocols without adding any additional complexity. - -1.2.3. Multi-Level - - Crypto-conditions elegantly support weighted multi-signatures and - multi-level signatures. A threshold condition has a number of - weighted subconditions, and a target threshold. Each subcondition - can be a signature or another threshold condition. This provides - flexibility in forming complex conditions. - - For example, consider a threshold condition that consists of two - subconditions, one each from Agnes and Bruce. Agnes's condition can - be a signature condition while Bruce's condition is a threshold - condition, requiring both Claude and Dan to sign for him. - - Weighted signatures allow more complex relationships than simple - M-of-N signing. For example, a weighted condition can support an - arrangement of subconditions such as, "Either Ron, Adi, and Leonard - must approve; or Clifford must approve." - -2. Format - -2.1. Binary Encoding - - A description of crypto-conditions is provided in this document using - Abstract Syntax Notation One (ASN.1) as defined in [itu.X680.2015]. - Implementations of this spec MUST support encoding and decoding using - Octet Encoding Rules (OER) as defined in [itu.X696.2015]. - - - - - - -Thomas, et al. Expires April 29, 2017 [Page 5] - -Internet-Draft Crypto-Conditions October 2016 - - -2.2. String Types - - Crypto-conditions use the following types within string encoding: - - BASE10 Variable-length integer encoded as a base-10 (decimal) - number. Implementations MUST reject encoded values that are too - large for them to parse. Implementations MUST be tested for - overflows. - - BASE16 Variable-length integer encoded as a base-16 (hexadecimal) - number. Implementations MUST reject encoded values that are too - large for them to parse. Implementations MUST be tested for - overflows. No leading zeros. - - BASE64URL Base64-URL encoding. See [RFC4648], Section 5. - -2.3. Bitmask - - Any system that accepts crypto-conditions must be able to state its - supported algorithms. It must be possible to verify that all - algorithms used in a certain condition are indeed supported even if - the fulfillment is not available yet. Therefore, all conditions and - fulfillments contain a bitmask to express the required features. - Implementations provide a bitmask of features they support. - - Each bit represents a different suite of features. Each type of - crypto-condition depends on one or more feature suites. If an - implementation supports all feature suites that a certain type - depends on, the implementation MUST support that condition type. The - list of known types and feature suites is the IANA-maintained Crypto- - Condition Type Registry (Appendix D.1) . - - To save space, the bitmask is encoded as a variable-length integer. - -2.4. Condition - - Below are the string and binary encoding formats for a condition. - -2.4.1. String Format - - Conditions are ASCII encoded as: - - "cc:" BASE16(type) ":" BASE16(featureBitmask) ":" - BASE64URL(fingerprint) ":" BASE10(maxFulfillmentLength) - - - - - - - -Thomas, et al. Expires April 29, 2017 [Page 6] - -Internet-Draft Crypto-Conditions October 2016 - - -2.4.2. Binary Format - - Conditions are binary encoded as: - - Condition ::= SEQUENCE { - type ConditionType, - featureBitmask INTEGER (0..MAX), - fingerprint OCTET STRING, - maxFulfillmentLength INTEGER (0..MAX) - } - - ConditionType ::= INTEGER { - preimageSha256(0), - rsaSha256(1), - prefixSha256(2), - thresholdSha256(3), - ed25519(4) - } (0..65535) - -2.4.3. Fields - - type is the numeric type identifier representing the condition type. - - featureBitmask is an unsigned integer encoding the set of feature - suites an implementation must support in order to be able to - successfully parse the fulfillment to this condition. This is the - boolean OR of the featureBitmask values of the top-level condition - type and all subcondition types, recursively. - - fingerprint is an octet string uniquely representing the condition - with respect to other conditions of the same type. - Implementations which index conditions MUST use the entire string - or binary encoded condition as the key, not just the fingerprint - - as different conditions of different types may have the same - fingerprint. The length and contents of the fingerprint are - defined by the condition type. For most condition types, the - fingerprint is a cryptographically secure hash of the data which - defines the condition, such as a public key. This is encoded as a - variable length octet string as different condition types may use - different functions to produce the fingerprint which may therefore - have different lengths. While it would be possible to determine - the expected length of the fingerprint based on the type it is - useful to be able to decode a condition even if the type is not - recognized. - - maxFulfillmentLength is the maximum length of the fulfillment - payload that can fulfill this condition, in bytes. The payload - size is measured unencoded. (The size of the payload is larger in - - - -Thomas, et al. Expires April 29, 2017 [Page 7] - -Internet-Draft Crypto-Conditions October 2016 - - - BASE64URL format.) When a crypto-condition is submitted to an - implementation, this implementation MUST verify that it will be - able to process a fulfillment with a payload of size - maxFulfillmentLength. - -2.4.4. Example Condition - - An example condition in string format: - - cc:0:3:dB-8fb14MdO75Brp_Pvh4d7ganckilrRl13RS_UmrXA:66 - - The example has the following attributes: - - +---------------+---------------------------------+-----------------+ - | Field | Value | Description | - +---------------+---------------------------------+-----------------+ - | preface | "cc" | Constant. | - | | | Indicates this | - | | | is a condition. | - | | | | - | type | "0" | Type 0 is | - | | | [PREIMAGE- | - | | | SHA-256][]. | - | | | | - | featuresBitma | "3" | Boolean-OR | - | sk | | combination of | - | | | feature suites | - | | | SHA-256 | - | | | (feature bit | - | | | 0x01) and | - | | | PREIMAGE | - | | | (feature bit | - | | | 0x02). | - | | | | - | fingerprint | "dB-8fb14MdO75Brp_Pvh4d7ganckil | The hash of the | - | | rRl13RS_UmrXA" | fulfillment for | - | | | this condition. | - | | | | - | maxFulfillmen | "66" | The fulfillment | - | tLength | | payload is 66 | - | | | bytes long, | - | | | before being | - | | | BASE64URL- | - | | | encoded. | - +---------------+---------------------------------+-----------------+ - - - - - - -Thomas, et al. Expires April 29, 2017 [Page 8] - -Internet-Draft Crypto-Conditions October 2016 - - -2.5. Fulfillment - - Below are the string and binary encoding formats for a fulfillment. - -2.5.1. String Format - - Fulfillments are ASCII encoded as: - - "cf:" BASE16(type) ":" BASE64URL(payload) - -2.5.2. Binary Format - - Fulfillments are binary encoded as: - - Fulfillment ::= SEQUENCE { - type ConditionType, - payload OCTET STRING - } - -2.5.3. Fields - - type is the numeric type identifier representing the condition type. - For some condition types the fulfillment will contain further - subfulfillments, however the type field always represents the - outermost, or top-level, type. - - payload The payload is an octet string whose internal format is - defined by each of the types. - -2.5.4. Example Fulfillment - - The following is an example fulfillment in string format, for the - example condition (Section 2.4.4): - -cf:0:VGhlIG9ubHkgYmFzaXMgZm9yIGdvb2QgU29jaWV0eSBpcyB1bmxpbWl0ZWQgY3JlZGl0LuKAlE9zY2FyIFdpbGRl - - The example has the following attributes: - - - - - - - - - - - - - - -Thomas, et al. Expires April 29, 2017 [Page 9] - -Internet-Draft Crypto-Conditions October 2016 - - - +---------+------------------+--------------------------------------+ - | Field | Value | Description | - +---------+------------------+--------------------------------------+ - | preface | "cf" | Constant. Indicates this is a | - | | | fulfillment. | - | | | | - | type | "0" | Type 0 is [PREIMAGE-SHA-256][]. | - | | | | - | payload | "VGhlIG...pbGRl" | The BASE64URL-encoded SHA-256 | - | | | preimage of the condition, since | - | | | this is a PREIMAGE-SHA-256 type | - | | | fulfillment. In this case, it is an | - | | | arbitrary string. | - +---------+------------------+--------------------------------------+ - -3. Feature Suites - - This specification defines a starting set of feature suites necessary - to support the [Condition Types][] also defined in this - specification. Future versions of this spec MAY introduce new - feature suites and condition types, which SHALL be registered in the - IANA maintained Crypto-Condition Type Registry (Appendix D.1). - - Support for a condition type MUST depend on one or more feature - suites. However, all new condition types MUST depend on at least one - of the new feature suites. This ensures that all previously created - implementations correctly recognize that they do not support the new - type. - - Feature suites are chosen such that they represent reasonable - clusters of functionality. For instance, it is reasonable to require - that an implementation which supports SHA-256 in one context MUST - support it in all contexts, since it already needed to implement the - algorithm. - - An implementation which supports a certain set of feature suites MUST - accept all condition types which depend only on that set or any - subset of feature suites. - - - - - - - - - - - - - -Thomas, et al. Expires April 29, 2017 [Page 10] - -Internet-Draft Crypto-Conditions October 2016 - - - +-----------+---------+-----------+---------------------------------+ - | Suite | Feature | Feature | Summary | - | Name | Bit | Bit | | - | | | (BASE16) | | - +-----------+---------+-----------+---------------------------------+ - | SHA-256 | 2^0 | 0x01 | The SHA-256 hashing algorithm. | - | | | | | - | PREIMAGE | 2^1 | 0x02 | The functionality of comparing | - | | | | a hash to a preimage. | - | | | | | - | PREFIX | 2^2 | 0x04 | The functionality of prefixing | - | | | | the fulfillment with a prefix | - | | | | before generating the | - | | | | condition. | - | | | | | - | THRESHOLD | 2^3 | 0x08 | The functionality of composing | - | | | | a condition out of several | - | | | | weighted subconditions. | - | | | | | - | RSA-PSS | 2^4 | 0x10 | The RSA-PSS signature | - | | | | algorithm. | - | | | | | - | ED25519 | 2^5 | 0x20 | The ED25519 signature | - | | | | algorithm. | - +-----------+---------+-----------+---------------------------------+ - -3.1. SHA-256 - - The SHA-256 feature suite provides the SHA-256 hash function. - SHA-256 is a cryptographic hash function published by the US National - Institute of Standards and Technology that produces 256 bits of - output. This feature suite is assigned the feature bit 2^0 = 0x01. - -3.2. PREIMAGE - - The PREIMAGE feature suite provides conditions that use a preimage as - a one-time signature. This feature suite is assigned the feature bit - 2^1 = 0x02. - - The fingerprint of a preimage condition is the hash of an arbitrary - value. The payload of a preimage fulfillment is the hashed arbitrary - value before hashing, also known as the preimage. Conditions that - use this preimage MUST also rely on a cryptographically secure - hashing algorithm. Since cryptographically secure hashing functions - are preimage-resistant, only the original creator of a preimage - condition can produce the preimage, as long as it contains a large - amount of random entropy. - - - - -Thomas, et al. Expires April 29, 2017 [Page 11] - -Internet-Draft Crypto-Conditions October 2016 - - -3.3. PREFIX - - The PREFIX feature suite provides conditions that prepend a fixed - message to a subcondition. This feature suite is assigned the - feature bit 2^2 = 0x04. - - A prefix condition prepends the message to be validated with a - constant string before passing it on to the subcondition's validation - function. - -3.4. THRESHOLD - - The THRESHOLD feature suite provides conditions that have several - weighted subconditions and a threshold number. This feature suite is - assigned the feature bit 2^3 = 0x08. - - Threshold conditions provide flexible multi-signing, such as - requiring "M-of-N" subconditions be fulfilled. Subconditions can - also be weighted so that one subcondition can count multiple times - towards meeting the threshold. - -3.5. RSA-PSS - - The RSA-PSS feature suite provides the RSS-PSA signature algorithm. - RSA-PSS is a signature algorithm based on the RSA cryptosystem, which - relates to the problem of factoring the product of two large prime - numbers. This feature suite is assigned the feature bit 2^4 = 0x10. - -3.6. ED25519 - - The ED25519 feature suite provides the Ed25519 signature algorithm. - Ed25519 is a signature algorithm based on the EdDSA signing scheme - and the compact elliptic curve known as Ed25519. This feature suite - is assigned the feature bit 2^5 = 0x20. - -4. Condition Types - - The following condition types are defined in this version of the - specification. Future versions of this spec MAY introduce new - feature suites and condition types, which SHALL be registered in the - IANA maintained Crypto-Condition Type Registry (Appendix D.1). - -4.1. PREIMAGE-SHA-256 - - PREIMAGE-SHA-256 is assigned the type ID 0. It relies on the SHA-256 - and PREIMAGE feature suites which corresponds to a feature bitmask of - 0x03. - - - - -Thomas, et al. Expires April 29, 2017 [Page 12] - -Internet-Draft Crypto-Conditions October 2016 - - - This type of condition is also called a "hashlock". By creating a - hash of a difficult-to-guess 256-bit random or pseudo-random integer - it is possible to create a condition which the creator can trivially - fulfill by publishing the random value. However, for anyone else, - the condition is cryptographically hard to fulfill, because they - would have to find a preimage for the given condition hash. - - Implementations MUST ignore any input message when validating a - PREIMAGE-SHA-256 fulfillment. - -4.1.1. Condition - - The fingerprint of a PREIMAGE-SHA-256 condition is the SHA-256 hash - of the preimage. - -4.1.2. Fulfillment - - The fulfillment payload of a PREIMAGE-SHA-256 condition is the - preimage. - -4.1.3. Example - - Example condition: - - cc:0:3:dB-8fb14MdO75Brp_Pvh4d7ganckilrRl13RS_UmrXA:66 - - Example fulfillment: - -cf:0:VGhlIG9ubHkgYmFzaXMgZm9yIGdvb2QgU29jaWV0eSBpcyB1bmxpbWl0ZWQgY3JlZGl0LuKAlE9zY2FyIFdpbGRl - -4.2. PREFIX-SHA-256 - - PREFIX-SHA-256 is assigned the type ID 1. It relies on the SHA-256 - and PREFIX feature suites which corresponds to a feature bitmask of - 0x05. - - Prefix conditions provide a way to effective narrow the scope of - other conditions. A condition can be used as the fingerprint of a - public key to sign an arbitrary message. By creating a prefix - subcondition we can narrow the scope from signing an arbitrary - message to signing a message with a specific prefix. - - When a prefix fulfillment is validated against a message, it will - prepend the prefix to the provided message and will use the result as - the message to validate against the subfulfillment. - - - - - - -Thomas, et al. Expires April 29, 2017 [Page 13] - -Internet-Draft Crypto-Conditions October 2016 - - -4.2.1. Condition - - The fingerprint of a PREFIX-SHA-256 condition is the SHA-256 digest - of the fingerprint contents given below: - - PrefixSha256FingerprintContents ::= SEQUENCE { - prefix OCTET STRING, - condition Condition - } - - prefix is an arbitrary octet string which will be prepended to the - message during validation. - - condition is the subcondition which the subfulfillment must match. - -4.2.2. Fulfillment - - PrefixSha256FulfillmentPayload ::= SEQUENCE { - prefix OCTET STRING, - subfulfillment Fulfillment - } - - prefix is an arbitrary octet string which will be prepended to the - message during validation. - - subfulfillment is the fulfilled subcondition. - -4.2.3. Example - - Example condition: - - cc:1:25:7myveZs3EaZMMuez-3kq6u69BDNYMYRMi_VF9yIuFLc:102 - - Example fulfillment: - -cf:1:DUhlbGxvIFdvcmxkISAABGDsFyuTrV5WO_STLHDhJFA0w1Rn7y79TWTr-BloNGfiv7YikfrZQy-PKYucSkiV2-KT9v_aGmja3wzN719HoMchKl_qPNqXo_TAPqny6Kwc7IalHUUhJ6vboJ0bbzMcBwo - -4.3. THRESHOLD-SHA-256 - - THRESHOLD-SHA-256 is assigned the type ID 2. It relies on the - SHA-256 and THRESHOLD feature suites which corresponds to a feature - bitmask of 0x09. - -4.3.1. Condition - - The fingerprint of a THRESHOLD-SHA-256 condition is the SHA-256 - digest of the fingerprint contents given below: - - - - -Thomas, et al. Expires April 29, 2017 [Page 14] - -Internet-Draft Crypto-Conditions October 2016 - - - ThresholdSha256FingerprintContents ::= SEQUENCE { - threshold INTEGER (0..4294967295), - subconditions SEQUENCE OF ThresholdSubcondition - } - - ThresholdSubcondition ::= SEQUENCE { - weight INTEGER (0..4294967295), - condition Condition - } - - The list of conditions is sorted first based on length, shortest - first. Elements of the same length are sorted in lexicographic (big- - endian) order, smallest first. - - threshold threshold MUST be an integer in the range 1 ... 2^32 - 1. - In order to fulfill a threshold condition, the weights of the - provided fulfillments MUST be greater than or equal to the - threshold. - - subconditions is the set of subconditions, each provided as a tuple - of weight and condition. - - weight is the numeric weight of this subcondition, i.e. how many - times it counts against the threshold. - - condition is the subcondition. - -4.3.2. Fulfillment - - ThresholdSha256FulfillmentPayload ::= SEQUENCE { - threshold INTEGER (0..4294967295), - subfulfillments SEQUENCE OF ThresholdSubfulfillment - } - - ThresholdSubfulfillment ::= SEQUENCE { - weight INTEGER (0..4294967295) DEFAULT 1, - condition Condition OPTIONAL, - fulfillment Fulfillment OPTIONAL - } - - threshold is a number and MUST be an integer in the range 1 ... 2^32 - - 1. In order to fulfill a threshold condition, the weights of - the provided fulfillments MUST be greater than or equal to the - threshold. - - subfulfillments is the set of subconditions and subfulfillments, - each provided as a tuple of weight and condition or fulfillment. - - - - -Thomas, et al. Expires April 29, 2017 [Page 15] - -Internet-Draft Crypto-Conditions October 2016 - - - weight is the numeric weight of this subcondition, i.e. how many - times it counts against the threshold. It MUST be an integer in - the range 1 ... 2^32 - 1. - - condition is the subcondition if this subcondition is not fulfilled. - - fulfillment is the subfulfillment if this subcondition is fulfilled. - -4.3.3. Example - - Example condition: - - cc:2:2b:mJUaGKCuF5n-3tfXM2U81VYtHbX-N8MP6kz8R-ASwNQ:146 - - Example fulfillment: - -cf:2:AQEBAgEBAwAAAAABAQAnAAQBICDsFyuTrV5WO_STLHDhJFA0w1Rn7y79TWTr-BloNGfivwFg - -4.4. RSA-SHA-256 - - RSA-SHA-256 is assigned the type ID 3. It relies on the SHA-256 and - RSA-PSS feature suites which corresponds to a feature bitmask of - 0x11. - - The signature algorithm used is RSASSA-PSS as defined in PKCS#1 v2.2. - [RFC3447] - - Implementations MUST NOT use the default RSASSA-PSS-params. - Implementations MUST use the SHA-256 hash algorithm and therefor, the - same algorithm in the mask generation algorithm, as recommended in - [RFC3447]. Implementations MUST also use a salt length of 32 bytes - (equal to the size of the output from the SHA-256 algorithm). - Therefore the algorithm identifier will have the following value: - - - - - - - - - - - - - - - - - - -Thomas, et al. Expires April 29, 2017 [Page 16] - -Internet-Draft Crypto-Conditions October 2016 - - -rSASSA-PSS-Crypto-Conditions-Identifier RSASSA-AlgorithmIdentifier ::= { - algorithm id-RSASSA-PSS, - parameters RSASSA-PSS-params : { - hashAlgorithm sha256, - maskGenAlgorithm mgf1SHA256, - saltLength 32, - trailerField trailerFieldBC - } -} - -sha256 HashAlgorithm ::= { - algorithm id-sha256, - parameters NULL -} - -mgf1SHA256 MaskGenAlgorithm ::= { - algorithm id-mgf1, - parameters HashAlgorithm : sha256 -} - -4.4.1. Condition - - The fingerprint of a RSA-SHA-256 condition is the SHA-256 digest of - the fingerprint contents given below: - - RsaSha256FingerprintContents ::= SEQUENCE { - modulus OCTET STRING (SIZE(128..512)) - } - - modulus is an octet string representing the RSA public modulus in - big-endian byte order. The first byte of the modulus MUST NOT be - zero. - - The corresponding public exponent e is assumed to be 65537 as - recommended in [RFC4871] . Very large exponents can be a DoS - vector [LARGE-RSA-EXPONENTS] and 65537 is the largest Fermat - prime, which has some nice properties - [USING-RSA-EXPONENT-OF-65537] . - - Implementations MUST reject moduli smaller than 128 bytes (1017 - bits) or greater than 512 bytes (4096 bits.) Large moduli slow - down signature verification which can be a denial-of-service - vector. DNSSEC also limits the modulus to 4096 bits [RFC3110] . - OpenSSL supports up to 16384 bits [OPENSSL-X509-CERT-EXAMPLES] . - - - - - - - -Thomas, et al. Expires April 29, 2017 [Page 17] - -Internet-Draft Crypto-Conditions October 2016 - - -4.4.2. Fulfillment - - RsaSha256FulfillmentPayload ::= SEQUENCE { - modulus OCTET STRING (SIZE(128..512)), - signature OCTET STRING (SIZE(128..512)) - } - - modulus is an octet string representing the RSA public modulus in - big-endian byte order. See Section 4.4.1 - - signature is an octet string representing the RSA signature. It - MUST be encoded in big-endian byte order with the exact same - number of octets as the modulus, even if this means adding leading - zeros. This ensures that the fulfillment size is constant and - known ahead of time. Note that the field is still binary encoded - with a length prefix for consistency. - - Implementations MUST verify that the signature and modulus consist - of the same number of octets and that the signature is numerically - less than the modulus. - - The message to be signed is provided separately. If no message is - provided, the message is assumed to be an octet string of length - zero. - -4.4.3. Implementation - - The recommended modulus size as of 2016 is 2048 bits - [KEYLENGTH-RECOMMENDATION] . In the future we anticipate an upgrade - to 3072 bits which provides approximately 128 bits of security - [NIST-KEYMANAGEMENT] (p. 64), about the same level as SHA-256. - -4.4.4. Example - - Example condition: - - cc:3:11:Bw-r77AGqSCL0huuMQYj3KW0Jh67Fpayeq9h_4UJctg:260 - - Example fulfillment: - -cf:3:gYCzDnqTh4O6v4NoUP9J4U-H4_ktXEbjP-yj5PCyI1hYCxF2WZX0uO6n-0cSwuHjFvf3dalT0jIhahadmmTdwAcSCkALN_KvwHe2L-ME3nTeahGexAdrUpxPYJawuq1PUz3wFzubgi_YXWX6S--pLY9ST2nLygE2vYDQlcFprsDglYGAjQM0-Z5B-953uQtJ5dXL1D5TWpM0s0eFF0Zty7J2Y3Nb0PqsR5I47a2wYlA7-106vjC8gHFdHVeSR6JksSrhj8YaMWfV0A6qhPz6hq-TqSKCXd4mf3eCpyyFYR_EyH5zXd56sJEU3snWlFbB_bKAW4si_qdfY9dT87YGUp_Grm0 - -4.5. ED25519 - - ED25519 is assigned the type ID 4. It relies only on the ED25519 - feature suite which corresponds to a bitmask of 0x20. - - - - - -Thomas, et al. Expires April 29, 2017 [Page 18] - -Internet-Draft Crypto-Conditions October 2016 - - - The exact algorithm and encodings used for public key and signature - are defined in [I-D.irtf-cfrg-eddsa] as Ed25519. SHA-512 is used as - the hashing function. - - Note: This document is not defining the SHA-512 versions of other - condition types. In addition, the Ed25519 condition type is already - uniquely identified by a corresponding Ed25519 feature suite. - Therefore we intentionally do not introduce a SHA-512 feature suite - in this document. - -4.5.1. Condition - - The fingerprint of a ED25519 condition is the 32 byte Ed25519 public - key. Since the public key is already very small, we do not hash it. - -4.5.2. Fulfillment - - Ed25519FulfillmentPayload ::= SEQUENCE { - publicKey OCTET STRING (SIZE(32)), - signature OCTET STRING (SIZE(64)) - } - - publicKey is an octet string containing the Ed25519 public key. - - signature is an octet string containing the Ed25519 signature. - -4.5.3. Example - - Example condition: - - cc:4:20:7Bcrk61eVjv0kyxw4SRQNMNUZ-8u_U1k6_gZaDRn4r8:96 - - Example fulfillment: - -cf:4:7Bcrk61eVjv0kyxw4SRQNMNUZ-8u_U1k6_gZaDRn4r-2IpH62UMvjymLnEpIldvik_b_2hpo2t8Mze9fR6DHISpf6jzal6P0wD6p8uisHOyGpR1FISer26CdG28zHAcK - -5. References - -5.1. Normative References - - [I-D.irtf-cfrg-eddsa] - Josefsson, S. and I. Liusvaara, "Edwards-curve Digital - Signature Algorithm (EdDSA)", draft-irtf-cfrg-eddsa-04 - (work in progress), March 2016. - - - - - - - -Thomas, et al. Expires April 29, 2017 [Page 19] - -Internet-Draft Crypto-Conditions October 2016 - - - [itu.X680.2015] - International Telecommunications Union, "Information - technology - Abstract Syntax Notation One (ASN.1): - Specification of basic notation", August 2015, - . - - [itu.X696.2015] - International Telecommunications Union, "Information - technology - ASN.1 encoding rules: Specification of Octet - Encoding Rules (OER)", August 2015, - . - - [RFC3447] Jonsson, J. and B. Kaliski, "Public-Key Cryptography - Standards (PKCS) #1: RSA Cryptography Specifications - Version 2.1", RFC 3447, DOI 10.17487/RFC3447, February - 2003, . - - [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data - Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006, - . - -5.2. Informative References - - [KEYLENGTH-RECOMMENDATION] - "BlueKrypt - Cryptographic Key Length Recommendation", - September 2015, . - - [LARGE-RSA-EXPONENTS] - "Imperial Violet - Very large RSA public exponents (17 Mar - 2012)", March 2012, - . - - [NIST-KEYMANAGEMENT] - , , , , and , "NIST - Recommendation for Key Management - - Part 1 - General (Revision 3)", July 2012, - . - - [OPENSSL-X509-CERT-EXAMPLES] - "OpenSSL - X509 certificate examples for testing and - verification", July 2012, - . - - [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate - Requirement Levels", BCP 14, RFC 2119, - DOI 10.17487/RFC2119, March 1997, - . - - - - -Thomas, et al. Expires April 29, 2017 [Page 20] - -Internet-Draft Crypto-Conditions October 2016 - - - [RFC3110] Eastlake 3rd, D., "RSA/SHA-1 SIGs and RSA KEYs in the - Domain Name System (DNS)", RFC 3110, DOI 10.17487/RFC3110, - May 2001, . - - [RFC4871] Allman, E., Callas, J., Delany, M., Libbey, M., Fenton, - J., and M. Thomas, "DomainKeys Identified Mail (DKIM) - Signatures", RFC 4871, DOI 10.17487/RFC4871, May 2007, - . - - [USING-RSA-EXPONENT-OF-65537] - "Cryptography - StackExchange - Impacts of not using RSA - exponent of 65537", November 2014, - . - -Appendix A. Security Considerations - - This section to be expanded in a later draft. - -Appendix B. Test Values - - This section to be expanded in a later draft. For now, see the test - cases for the reference implementation: - https://github.com/interledger/five-bells-condition/tree/master/test - -Appendix C. ASN.1 Module - - ---- - - CryptoConditions - DEFINITIONS - AUTOMATIC TAGS ::= - BEGIN - - /** - * CONTAINERS - */ - - Condition ::= SEQUENCE { - type ConditionType, - featureBitmask OCTET STRING, - fingerprint OCTET STRING, - maxFulfillmentLength INTEGER (0..MAX) - } - - Fulfillment ::= SEQUENCE { - type ConditionType, - payload OCTET STRING - - - -Thomas, et al. Expires April 29, 2017 [Page 21] - -Internet-Draft Crypto-Conditions October 2016 - - - } - - ConditionType ::= INTEGER { - preimageSha256(0), - rsaSha256(1), - prefixSha256(2), - thresholdSha256(3), - ed25519(4) - } (0..65535) - - /** - * FULFILLMENT PAYLOADS - */ - - -- For preimage conditions, the payload equals the preimage - - PrefixSha256FulfillmentPayload ::= SEQUENCE { - prefix OCTET STRING, - subfulfillment Fulfillment - } - - ThresholdSha256FulfillmentPayload ::= SEQUENCE { - threshold INTEGER (0..4294967295), - subfulfillments SEQUENCE OF ThresholdSubfulfillment - } - - ThresholdSubfulfillment ::= SEQUENCE { - weight INTEGER (0..4294967295) DEFAULT 1, - condition Condition OPTIONAL, - fulfillment Fulfillment OPTIONAL - } - - RsaSha256FulfillmentPayload ::= SEQUENCE { - modulus OCTET STRING (SIZE(128..512)), - signature OCTET STRING (SIZE(128..512)) - } - - Ed25519FulfillmentPayload ::= SEQUENCE { - publicKey OCTET STRING (SIZE(32)), - signature OCTET STRING (SIZE(64)) - } - - /** - * FINGERPRINTS - */ - - -- SHA-256 hash of the fingerprint contents - Sha256Fingerprint ::= OCTET STRING (SIZE(32)) -- digest - - - -Thomas, et al. Expires April 29, 2017 [Page 22] - -Internet-Draft Crypto-Conditions October 2016 - - - -- 32-byte Ed25519 public key - Ed25519Fingerprint ::= OCTET STRING (SIZE(32)) -- publicKey - - /** - * FINGERPRINT CONTENTS - * - * The content that will be hashed to arrive at the fingerprint. - */ - - -- The preimage type hashes the raw contents of the preimage - - PrefixSha256FingerprintContents ::= SEQUENCE { - prefix OCTET STRING, - condition Condition - } - - ThresholdSha256FingerprintContents ::= SEQUENCE { - threshold INTEGER (0..4294967295), - subconditions SEQUENCE OF ThresholdSubcondition - } - - ThresholdSubcondition ::= SEQUENCE { - weight INTEGER (0..4294967295), - condition Condition - } - - RsaSha256FingerprintContents ::= INTEGER (0..MAX) -- modulus - - /** - * EXAMPLES - */ - - exampleCondition Condition ::= - { - type preimageSha256, - featureBitmask '03'H, - fingerprint ' - E3B0C442 98FC1C14 9AFBF4C8 996FB924 27AE41E4 649B934C A495991B 7852B855 - 'H, - maxFulfillmentLength 2 - } - - exampleFulfillment Fulfillment ::= - { - type preimageSha256, - payload '00'H - } - - - - -Thomas, et al. Expires April 29, 2017 [Page 23] - -Internet-Draft Crypto-Conditions October 2016 - - - exampleRsaSha256FulfillmentPayload RsaSha256FulfillmentPayload ::= - { - modulus ' - B30E7A93 8783BABF 836850FF 49E14F87 E3F92D5C 46E33FEC A3E4F0B2 2358580B - 11765995 F4B8EEA7 FB4712C2 E1E316F7 F775A953 D232216A 169D9A64 DDC00712 - 0A400B37 F2AFC077 B62FE304 DE74DE6A 119EC407 6B529C4F 6096B0BA AD4F533D - F0173B9B 822FD85D 65FA4BEF A92D8F52 4F69CBCA 0136BD80 D095C169 AEC0E095 - 'H, - signature ' - 48E8945E FE007556 D5BF4D5F 249E4808 F7307E29 511D3262 DAEF61D8 8098F9AA - 4A8BC062 3A8C9757 38F65D6B F459D543 F289D73C BC7AF4EA 3A33FBF3 EC444044 - 7911D722 94091E56 1833628E 49A772ED 608DE6C4 4595A91E 3E17D6CF 5EC3B252 - 8D63D2AD D6463989 B12EEC57 7DF64709 60DF6832 A9D84C36 0D1C217A D64C8625 - BDB594FB 0ADA086C DECBBDE5 80D424BF 9746D2F0 C312826D BBB00AD6 8B52C4CB - 7D47156B A35E3A98 1C973863 792CC80D 04A18021 0A524158 65B64B3A 61774B1D - 3975D78A 98B0821E E55CA0F8 6305D425 29E10EB0 15CEFD40 2FB59B2A BB8DEEE5 - 2A6F2447 D2284603 D219CD4E 8CF9CFFD D5498889 C3780B59 DD6A57EF 7D732620 - 'H - } - - exampleEd25519FulfillmentPayload Ed25519FulfillmentPayload ::= - { - publicKey ' - EC172B93 AD5E563B F4932C70 E1245034 C35467EF 2EFD4D64 EBF81968 3467E2BF - 'H, - signature ' - B62291FA D9432F8F 298B9C4A 4895DBE2 93F6FFDA 1A68DADF 0CCDEF5F 47A0C721 - 2A5FEA3C DA97A3F4 C03EA9F2 E8AC1CEC 86A51D45 2127ABDB A09D1B6F 331C070A - 'H - } - - END - -Appendix D. IANA Considerations - -D.1. Crypto-Condition Type Registry - - The following initial entries should be added to the Crypto-Condition - Type registry to be created and maintained at (the suggested URI) - http://www.iana.org/assignments/crypto-condition-types : - - The following feature suite bits are registered: - - - - - - - - - -Thomas, et al. Expires April 29, 2017 [Page 24] - -Internet-Draft Crypto-Conditions October 2016 - - - +----------+------+------+-----------+ - | Type Bit | Exp. | Hex | Feature | - +----------+------+------+-----------+ - | 1 | 2^0 | 0x01 | SHA-256 | - | | | | | - | 10 | 2^1 | 0x02 | PREIMAGE | - | | | | | - | 100 | 2^2 | 0x04 | PREFIX | - | | | | | - | 1000 | 2^3 | 0x08 | THRESHOLD | - | | | | | - | 10000 | 2^4 | 0x10 | RSA | - | | | | | - | 100000 | 2^5 | 0x20 | ED25519 | - +----------+------+------+-----------+ - - Table 1: Crypto-Condition Feature Suites - - The following types are registered: - - +---------+------------------+-------------------+ - | Type ID | Required Bitmask | Type Name | - +---------+------------------+-------------------+ - | 0 | 0x03 | PREIMAGE-SHA-256 | - | | | | - | 1 | 0x05 | PREFIX-SHA-256 | - | | | | - | 2 | 0x09 | THRESHOLD-SHA-256 | - | | | | - | 3 | 0x11 | RSA-SHA-256 | - | | | | - | 4 | 0x20 | ED25519 | - +---------+------------------+-------------------+ - - Table 2: Crypto-Condition Types - -Authors' Addresses - - Stefan Thomas - Ripple - 300 Montgomery Street - San Francisco, CA 94104 - US - - Phone: ----------------- - Email: stefan@ripple.com - URI: https://www.ripple.com - - - - -Thomas, et al. Expires April 29, 2017 [Page 25] - -Internet-Draft Crypto-Conditions October 2016 - - - Rome Reginelli - Ripple - 300 Montgomery Street - San Francisco, CA 94104 - US - - Phone: ----------------- - Email: rome@ripple.com - URI: https://www.ripple.com - - - Adrian Hope-Bailie - Ripple - 300 Montgomery Street - San Francisco, CA 94104 - US - - Phone: ----------------- - Email: adrian@ripple.com - URI: https://www.ripple.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Thomas, et al. Expires April 29, 2017 [Page 26] +The spec has been moved to https://github.com/rfcs/crypto-conditions/ \ No newline at end of file diff --git a/0002-crypto-conditions/output/0002-crypto-conditions.xml b/0002-crypto-conditions/output/0002-crypto-conditions.xml index 69aeab71..e9604e72 100644 --- a/0002-crypto-conditions/output/0002-crypto-conditions.xml +++ b/0002-crypto-conditions/output/0002-crypto-conditions.xml @@ -1,1030 +1,10 @@ - - - - - - - - - -]> - - - - - - - - - - Crypto-Conditions - - - Ripple -
- - 300 Montgomery Street - San Francisco - CA - 94104 - US - - ----------------- - stefan@ripple.com - https://www.ripple.com -
-
- - Ripple -
- - 300 Montgomery Street - San Francisco - CA - 94104 - US - - ----------------- - rome@ripple.com - https://www.ripple.com -
-
- - Ripple -
- - 300 Montgomery Street - San Francisco - CA - 94104 - US - - ----------------- - adrian@ripple.com - https://www.ripple.com -
-
- - - - security - - - - - - -Crypto-conditions provide a mechanism to describe a signed message such that multiple actors in a distributed system can all verify the same signed message and agree on whether it matches the description. This provides a useful primitive for event-based systems that are distributed on the Internet since we can describe events in a standard deterministic manner (represented by signed messages) and therefore define generic authenticated event handlers. - - - - - - - - - -This specification is a part of the Interledger Protocol work. Feedback related to this specification should be sent to public-interledger@w3.org. - - - - - -
- - - - -
-This specification describes a message format for crypto-conditions and fulfillments, with binary and string-based encodings for each. - -Crypto-conditions are distributable event descriptions. This means crypto-conditions say how to recognize a message without saying exactly what the message is. You can transmit a crypto-condition freely, but you cannot forge the message it describes. For convenience, we hash the description so that the crypto-condition can be a fixed size. - -Fulfillments are cryptographically verifiable messages that prove an event occurred. If you transmit a fulfillment, then everyone who has the condition can agree that the condition has been met. - -In the Interledger protocol, crypto-conditions and fulfillments provide irrepudiable proof that a transfer occurred in one ledger, as messages that can be easily shared with other ledgers. This allows ledgers to escrow funds or hold a transfer conditionally, then execute the transfer automatically when the ledger sees the fulfillment of the stated condition. - -Crypto-conditions may also be useful in other contexts where a system needs to make a decision based on predefined criteria, such as smart contracts. - -
-The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in . - -Within this specification, the term "condition" refers to the hash of a description of a signed message. The hash function must be preimage-resistant. - -The term "fulfillment" refers to a description of a signed message and a signed message that matches the description. We hash the description and compare that to the condition, and also compare the signed message to the description. If the message matches the description and the hash of the description matches the condition, we say that the fulfillment fulfills the condition. - -In the simplest case, the fulfillment can be a preimage that hashes to the condition, in which case the preimage is both the description and the message. - -
-
-Crypto-conditions are a standard format for expressing conditions and fulfillments. The format supports multiple algorithms, including different hash functions and cryptographic signing schemes. Crypto-conditions can be nested in multiple levels, with each level possibly having multiple signatures. - -This format has been designed so that it can be expanded. For example, you can add new cryptographic signature schemes or hash functions. This is important because advances in cryptography frequently render old algorithms insecure or invent newer, more effective algorithms. - -The of a crypto-condition indicates which algorithms it uses, so a compliant implementation can know whether it supports the functionality required to interpret the crypto-condition. - -
-The crypto-condition format contains a that specifies which hash function and signing scheme to use. Any message format for a condition or a fulfillment contains such a bitmask. - -Implementations MAY state their supported algorithms by providing a bitmask in the same format. To verify that a given implementation can verify a fulfillment for a given condition, you compare the bitmasks. If all bits set in the condition's bitmask are also set in the implementation's bitmask, then the implementation can verify the condition's fulfillment. - -
-
-Crypto-conditions can abstract away many of the details of multi-sign. When a party provides a condition, other parties can treat it opaquely and do not need to know about its internal structure. That allows parties to define arbitrary multi-signature setups without breaking compatibility. - -Protocol designers can use crypto-conditions as a drop-in replacement for public key signature algorithms and add multi-signature support to their protocols without adding any additional complexity. - -
-
-Crypto-conditions elegantly support weighted multi-signatures and multi-level signatures. A threshold condition has a number of weighted subconditions, and a target threshold. Each subcondition can be a signature or another threshold condition. This provides flexibility in forming complex conditions. - -For example, consider a threshold condition that consists of two subconditions, one each from Agnes and Bruce. Agnes's condition can be a signature condition while Bruce's condition is a threshold condition, requiring both Claude and Dan to sign for him. - -Weighted signatures allow more complex relationships than simple M-of-N signing. For example, a weighted condition can support an arrangement of subconditions such as, "Either Ron, Adi, and Leonard must approve; or Clifford must approve." - -
-
-
-
- -
- -A description of crypto-conditions is provided in this document using Abstract Syntax Notation One (ASN.1) as defined in . Implementations of this spec MUST support encoding and decoding using Octet Encoding Rules (OER) as defined in . - -
-
- -Crypto-conditions use the following types within string encoding: - - - - Variable-length integer encoded as a base-10 (decimal) number. Implementations MUST reject encoded values that are too large for them to parse. Implementations MUST be tested for overflows. - - Variable-length integer encoded as a base-16 (hexadecimal) number. Implementations MUST reject encoded values that are too large for them to parse. Implementations MUST be tested for overflows. No leading zeros. - - Base64-URL encoding. See , Section 5. - - -
-
-Any system that accepts crypto-conditions must be able to state its supported algorithms. It must be possible to verify that all algorithms used in a certain condition are indeed supported even if the fulfillment is not available yet. Therefore, all conditions and fulfillments contain a bitmask to express the required features. Implementations provide a bitmask of features they support. - -Each bit represents a different suite of features. Each type of crypto-condition depends on one or more feature suites. If an implementation supports all feature suites that a certain type depends on, the implementation MUST support that condition type. The list of known types and feature suites is the IANA-maintained Crypto-Condition Type Registry . - -To save space, the bitmask is encoded as a variable-length integer. - -
-
-Below are the string and binary encoding formats for a condition. - -
- -Conditions are ASCII encoded as: - -
- -
-
- -Conditions are binary encoded as: - -
- -
-
- - - - is the numeric type identifier representing the condition type. - - is an unsigned integer encoding the set of feature suites an implementation must support in order to be able to successfully parse the fulfillment to this condition. This is the boolean OR of the featureBitmask values of the top-level condition type and all subcondition types, recursively. - - is an octet string uniquely representing the condition with respect to other conditions of the same type. Implementations which index conditions MUST use the entire string or binary encoded condition as the key, not just the fingerprint - as different conditions of different types may have the same fingerprint. The length and contents of the fingerprint are defined by the condition type. For most condition types, the fingerprint is a cryptographically secure hash of the data which defines the condition, such as a public key. This is encoded as a variable length octet string as different condition types may use different functions to produce the fingerprint which may therefore have different lengths. While it would be possible to determine the expected length of the fingerprint based on the type it is useful to be able to decode a condition even if the type is not recognized. - - is the maximum length of the fulfillment payload that can fulfill this condition, in bytes. The payload size is measured unencoded. (The size of the payload is larger in BASE64URL format.) When a crypto-condition is submitted to an implementation, this implementation MUST verify that it will be able to process a fulfillment with a payload of size maxFulfillmentLength. - - -
-
- -An example condition in string format: - -
- -The example has the following attributes: - - - Field - Value - Description - preface - cc - Constant. Indicates this is a condition. - type - 0 - Type 0 is [PREIMAGE-SHA-256][]. - featuresBitmask - 3 - Boolean-OR combination of feature suites SHA-256 (feature bit 0x01) and PREIMAGE (feature bit 0x02). - fingerprint - dB-8fb14MdO75Brp_Pvh4d7ganckilrRl13RS_UmrXA - The hash of the fulfillment for this condition. - maxFulfillmentLength - 66 - The fulfillment payload is 66 bytes long, before being BASE64URL-encoded. - - -
-
-
-Below are the string and binary encoding formats for a fulfillment. - -
- -Fulfillments are ASCII encoded as: - -
- -
-
- -Fulfillments are binary encoded as: - -
- -
-
- - - - is the numeric type identifier representing the condition type. For some condition types the fulfillment will contain further subfulfillments, however the type field always represents the outermost, or top-level, type. - - The payload is an octet string whose internal format is defined by each of the types. - - -
-
- -The following is an example fulfillment in string format, for the example condition: - -
- -The example has the following attributes: - - - Field - Value - Description - preface - cf - Constant. Indicates this is a fulfillment. - type - 0 - Type 0 is [PREIMAGE-SHA-256][]. - payload - VGhlIG...pbGRl - The BASE64URL-encoded SHA-256 preimage of the condition, since this is a PREIMAGE-SHA-256 type fulfillment. In this case, it is an arbitrary string. - - -
-
-
-
-This specification defines a starting set of feature suites necessary to support the [Condition Types][] also defined in this specification. Future versions of this spec MAY introduce new feature suites and condition types, which SHALL be registered in the IANA maintained Crypto-Condition Type Registry. - -Support for a condition type MUST depend on one or more feature suites. However, all new condition types MUST depend on at least one of the new feature suites. This ensures that all previously created implementations correctly recognize that they do not support the new type. - -Feature suites are chosen such that they represent reasonable clusters of functionality. For instance, it is reasonable to require that an implementation which supports SHA-256 in one context MUST support it in all contexts, since it already needed to implement the algorithm. - -An implementation which supports a certain set of feature suites MUST accept all condition types which depend only on that set or any subset of feature suites. - - - Suite Name - Feature Bit - Feature Bit (BASE16) - Summary - SHA-256 - 2^0 - 0x01 - The SHA-256 hashing algorithm. - PREIMAGE - 2^1 - 0x02 - The functionality of comparing a hash to a preimage. - PREFIX - 2^2 - 0x04 - The functionality of prefixing the fulfillment with a prefix before generating the condition. - THRESHOLD - 2^3 - 0x08 - The functionality of composing a condition out of several weighted subconditions. - RSA-PSS - 2^4 - 0x10 - The RSA-PSS signature algorithm. - ED25519 - 2^5 - 0x20 - The ED25519 signature algorithm. - - -
- -The SHA-256 feature suite provides the SHA-256 hash function. SHA-256 is a cryptographic hash function published by the US National Institute of Standards and Technology that produces 256 bits of output. This feature suite is assigned the feature bit 2^0 = 0x01. - -
-
-The PREIMAGE feature suite provides conditions that use a preimage as a one-time signature. This feature suite is assigned the feature bit 2^1 = 0x02. - -The fingerprint of a preimage condition is the hash of an arbitrary value. The payload of a preimage fulfillment is the hashed arbitrary value before hashing, also known as the preimage. Conditions that use this preimage MUST also rely on a cryptographically secure hashing algorithm. Since cryptographically secure hashing functions are preimage-resistant, only the original creator of a preimage condition can produce the preimage, as long as it contains a large amount of random entropy. - -
-
-The PREFIX feature suite provides conditions that prepend a fixed message to a subcondition. This feature suite is assigned the feature bit 2^2 = 0x04. - -A prefix condition prepends the message to be validated with a constant string before passing it on to the subcondition's validation function. - -
-
-The THRESHOLD feature suite provides conditions that have several weighted subconditions and a threshold number. This feature suite is assigned the feature bit 2^3 = 0x08. - -Threshold conditions provide flexible multi-signing, such as requiring "M-of-N" subconditions be fulfilled. Subconditions can also be weighted so that one subcondition can count multiple times towards meeting the threshold. - -
-
-The RSA-PSS feature suite provides the RSS-PSA signature algorithm. RSA-PSS is a signature algorithm based on the RSA cryptosystem, which relates to the problem of factoring the product of two large prime numbers. This feature suite is assigned the feature bit 2^4 = 0x10. - -
-
-The ED25519 feature suite provides the Ed25519 signature algorithm. Ed25519 is a signature algorithm based on the EdDSA signing scheme and the compact elliptic curve known as Ed25519. This feature suite is assigned the feature bit 2^5 = 0x20. - -
-
-
-The following condition types are defined in this version of the specification. Future versions of this spec MAY introduce new feature suites and condition types, which SHALL be registered in the IANA maintained Crypto-Condition Type Registry. - -
-PREIMAGE-SHA-256 is assigned the type ID 0. It relies on the SHA-256 and PREIMAGE feature suites which corresponds to a feature bitmask of 0x03. - -This type of condition is also called a "hashlock". By creating a hash of a difficult-to-guess 256-bit random or pseudo-random integer it is possible to create a condition which the creator can trivially fulfill by publishing the random value. However, for anyone else, the condition is cryptographically hard to fulfill, because they would have to find a preimage for the given condition hash. - -Implementations MUST ignore any input message when validating a PREIMAGE-SHA-256 fulfillment. - -
-The fingerprint of a PREIMAGE-SHA-256 condition is the SHA-256 hash of the preimage. - -
-
-The fulfillment payload of a PREIMAGE-SHA-256 condition is the preimage. - -
-
- -Example condition: - -
- -Example fulfillment: - -
- -
-
-
-PREFIX-SHA-256 is assigned the type ID 1. It relies on the SHA-256 and PREFIX feature suites which corresponds to a feature bitmask of 0x05. - -Prefix conditions provide a way to effective narrow the scope of other conditions. A condition can be used as the fingerprint of a public key to sign an arbitrary message. By creating a prefix subcondition we can narrow the scope from signing an arbitrary message to signing a message with a specific prefix. - -When a prefix fulfillment is validated against a message, it will prepend the prefix to the provided message and will use the result as the message to validate against the subfulfillment. - -
-The fingerprint of a PREFIX-SHA-256 condition is the SHA-256 digest of the fingerprint contents given below: - -
- - - - is an arbitrary octet string which will be prepended to the message during validation. - - is the subcondition which the subfulfillment must match. - - -
-
- -
- - - - is an arbitrary octet string which will be prepended to the message during validation. - - is the fulfilled subcondition. - - -
-
- -Example condition: - -
- -Example fulfillment: - -
- -
-
-
-THRESHOLD-SHA-256 is assigned the type ID 2. It relies on the SHA-256 and THRESHOLD feature suites which corresponds to a feature bitmask of 0x09. - -
-The fingerprint of a THRESHOLD-SHA-256 condition is the SHA-256 digest of the fingerprint contents given below: - -
- -The list of conditions is sorted first based on length, shortest first. Elements of the same length are sorted in lexicographic (big-endian) order, smallest first. - - - - threshold MUST be an integer in the range 1 … 2^32 - 1. In order to fulfill a threshold condition, the weights of the provided fulfillments MUST be greater than or equal to the threshold. - - is the set of subconditions, each provided as a tuple of weight and condition. - - is the numeric weight of this subcondition, i.e. how many times it counts against the threshold. - - is the subcondition. - - -
-
- -
- - - - is a number and MUST be an integer in the range 1 … 2^32 - 1. In order to fulfill a threshold condition, the weights of the provided fulfillments MUST be greater than or equal to the threshold. - - is the set of subconditions and subfulfillments, each provided as a tuple of weight and condition or fulfillment. - - is the numeric weight of this subcondition, i.e. how many times it counts against the threshold. It MUST be an integer in the range 1 … 2^32 - 1. - - is the subcondition if this subcondition is not fulfilled. - - is the subfulfillment if this subcondition is fulfilled. - - -
-
- -Example condition: - -
- -Example fulfillment: - -
- -
-
-
-RSA-SHA-256 is assigned the type ID 3. It relies on the SHA-256 and RSA-PSS feature suites which corresponds to a feature bitmask of 0x11. - -The signature algorithm used is RSASSA-PSS as defined in PKCS#1 v2.2. - -Implementations MUST NOT use the default RSASSA-PSS-params. Implementations MUST use the SHA-256 hash algorithm and therefor, the same algorithm in the mask generation algorithm, as recommended in . Implementations MUST also use a salt length of 32 bytes (equal to the size of the output from the SHA-256 algorithm). Therefore the algorithm identifier will have the following value: - -
- -
-The fingerprint of a RSA-SHA-256 condition is the SHA-256 digest of the fingerprint contents given below: - -
- - - - is an octet string representing the RSA public modulus in big-endian byte order. The first byte of the modulus MUST NOT be zero. - The corresponding public exponent e is assumed to be 65537 as recommended in . Very large exponents can be a DoS vector and 65537 is the largest Fermat prime, which has some nice properties . - Implementations MUST reject moduli smaller than 128 bytes (1017 bits) or greater than 512 bytes (4096 bits.) Large moduli slow down signature verification which can be a denial-of-service vector. DNSSEC also limits the modulus to 4096 bits . OpenSSL supports up to 16384 bits . - - -
-
- -
- - - - is an octet string representing the RSA public modulus in big-endian byte order. See - - is an octet string representing the RSA signature. It MUST be encoded in big-endian byte order with the exact same number of octets as the modulus, even if this means adding leading zeros. This ensures that the fulfillment size is constant and known ahead of time. Note that the field is still binary encoded with a length prefix for consistency. - Implementations MUST verify that the signature and modulus consist of the same number of octets and that the signature is numerically less than the modulus. - - -The message to be signed is provided separately. If no message is provided, the message is assumed to be an octet string of length zero. - -
-
-The recommended modulus size as of 2016 is 2048 bits . In the future we anticipate an upgrade to 3072 bits which provides approximately 128 bits of security (p. 64), about the same level as SHA-256. - -
-
- -Example condition: - -
- -Example fulfillment: - -
- -
-
-
-ED25519 is assigned the type ID 4. It relies only on the ED25519 feature suite which corresponds to a bitmask of 0x20. - -The exact algorithm and encodings used for public key and signature are defined in as Ed25519. SHA-512 is used as the hashing function. - -Note: This document is not defining the SHA-512 versions of other condition types. In addition, the Ed25519 condition type is already uniquely identified by a corresponding Ed25519 feature suite. Therefore we intentionally do not introduce a SHA-512 feature suite in this document. - -
-The fingerprint of a ED25519 condition is the 32 byte Ed25519 public key. Since the public key is already very small, we do not hash it. - -
-
- -
- - - - is an octet string containing the Ed25519 public key. - - is an octet string containing the Ed25519 signature. - - -
-
- -Example condition: - -
- -Example fulfillment: - -
- -
-
-
- - -
- - - - - -&RFC3447; -&RFC4648; -&I-D.draft-irtf-cfrg-eddsa-04; - - - Information technology – Abstract Syntax Notation One (ASN.1): Specification of basic notation - - International Telecommunications Union - - - - - - - Information technology – ASN.1 encoding rules: Specification of Octet Encoding Rules (OER) - - International Telecommunications Union - - - - - - - - - - -&RFC2119; -&RFC3110; -&RFC4871; - - - Imperial Violet - Very large RSA public exponents (17 Mar 2012) - - - - - - - - - Cryptography - StackExchange - Impacts of not using RSA exponent of 65537 - - - - - - - - - BlueKrypt - Cryptographic Key Length Recommendation - - - - - - - - - NIST - Recommendation for Key Management - Part 1 - General (Revision 3) - - - - - - - - - - - - - - - - - - - - - OpenSSL - X509 certificate examples for testing and verification - - - - - - - - - - - -
- -This section to be expanded in a later draft. - -
-
- -This section to be expanded in a later draft. For now, see the test cases for the reference implementation: https://github.com/interledger/five-bells-condition/tree/master/test - -
-
- -
-- - -CryptoConditions -DEFINITIONS -AUTOMATIC TAGS ::= -BEGIN - -/** -* CONTAINERS -*/ - -Condition ::= SEQUENCE { -type ConditionType, -featureBitmask OCTET STRING, -fingerprint OCTET STRING, -maxFulfillmentLength INTEGER (0..MAX) -} - -Fulfillment ::= SEQUENCE { -type ConditionType, -payload OCTET STRING -} - -ConditionType ::= INTEGER { -preimageSha256(0), -rsaSha256(1), -prefixSha256(2), -thresholdSha256(3), -ed25519(4) -} (0..65535) - -/** -* FULFILLMENT PAYLOADS -*/ - --- For preimage conditions, the payload equals the preimage - -PrefixSha256FulfillmentPayload ::= SEQUENCE { -prefix OCTET STRING, -subfulfillment Fulfillment -} - -ThresholdSha256FulfillmentPayload ::= SEQUENCE { -threshold INTEGER (0..4294967295), -subfulfillments SEQUENCE OF ThresholdSubfulfillment -} - -ThresholdSubfulfillment ::= SEQUENCE { -weight INTEGER (0..4294967295) DEFAULT 1, -condition Condition OPTIONAL, -fulfillment Fulfillment OPTIONAL -} - -RsaSha256FulfillmentPayload ::= SEQUENCE { -modulus OCTET STRING (SIZE(128..512)), -signature OCTET STRING (SIZE(128..512)) -} - -Ed25519FulfillmentPayload ::= SEQUENCE { -publicKey OCTET STRING (SIZE(32)), -signature OCTET STRING (SIZE(64)) -} - -/** -* FINGERPRINTS -*/ - --- SHA-256 hash of the fingerprint contents -Sha256Fingerprint ::= OCTET STRING (SIZE(32)) -- digest - --- 32-byte Ed25519 public key -Ed25519Fingerprint ::= OCTET STRING (SIZE(32)) -- publicKey - -/** -* FINGERPRINT CONTENTS -* -* The content that will be hashed to arrive at the fingerprint. -*/ - --- The preimage type hashes the raw contents of the preimage - -PrefixSha256FingerprintContents ::= SEQUENCE { -prefix OCTET STRING, -condition Condition -} - -ThresholdSha256FingerprintContents ::= SEQUENCE { -threshold INTEGER (0..4294967295), -subconditions SEQUENCE OF ThresholdSubcondition -} - -ThresholdSubcondition ::= SEQUENCE { -weight INTEGER (0..4294967295), -condition Condition -} - -RsaSha256FingerprintContents ::= INTEGER (0..MAX) -- modulus - -/** -* EXAMPLES -*/ - -exampleCondition Condition ::= -{ -type preimageSha256, -featureBitmask '03'H, -fingerprint ' -E3B0C442 98FC1C14 9AFBF4C8 996FB924 27AE41E4 649B934C A495991B 7852B855 -'H, -maxFulfillmentLength 2 -} - -exampleFulfillment Fulfillment ::= -{ -type preimageSha256, -payload '00'H -} - -exampleRsaSha256FulfillmentPayload RsaSha256FulfillmentPayload ::= -{ -modulus ' -B30E7A93 8783BABF 836850FF 49E14F87 E3F92D5C 46E33FEC A3E4F0B2 2358580B -11765995 F4B8EEA7 FB4712C2 E1E316F7 F775A953 D232216A 169D9A64 DDC00712 -0A400B37 F2AFC077 B62FE304 DE74DE6A 119EC407 6B529C4F 6096B0BA AD4F533D -F0173B9B 822FD85D 65FA4BEF A92D8F52 4F69CBCA 0136BD80 D095C169 AEC0E095 -'H, -signature ' -48E8945E FE007556 D5BF4D5F 249E4808 F7307E29 511D3262 DAEF61D8 8098F9AA -4A8BC062 3A8C9757 38F65D6B F459D543 F289D73C BC7AF4EA 3A33FBF3 EC444044 -7911D722 94091E56 1833628E 49A772ED 608DE6C4 4595A91E 3E17D6CF 5EC3B252 -8D63D2AD D6463989 B12EEC57 7DF64709 60DF6832 A9D84C36 0D1C217A D64C8625 -BDB594FB 0ADA086C DECBBDE5 80D424BF 9746D2F0 C312826D BBB00AD6 8B52C4CB -7D47156B A35E3A98 1C973863 792CC80D 04A18021 0A524158 65B64B3A 61774B1D -3975D78A 98B0821E E55CA0F8 6305D425 29E10EB0 15CEFD40 2FB59B2A BB8DEEE5 -2A6F2447 D2284603 D219CD4E 8CF9CFFD D5498889 C3780B59 DD6A57EF 7D732620 -'H -} - -exampleEd25519FulfillmentPayload Ed25519FulfillmentPayload ::= -{ -publicKey ' -EC172B93 AD5E563B F4932C70 E1245034 C35467EF 2EFD4D64 EBF81968 3467E2BF -'H, -signature ' -B62291FA D9432F8F 298B9C4A 4895DBE2 93F6FFDA 1A68DADF 0CCDEF5F 47A0C721 -2A5FEA3C DA97A3F4 C03EA9F2 E8AC1CEC 86A51D45 2127ABDB A09D1B6F 331C070A -'H -} - -END -]]>
- -
-
- -
- -The following initial entries should be added to the Crypto-Condition Type registry to be created and maintained at (the suggested URI) -http://www.iana.org/assignments/crypto-condition-types: - -The following feature suite bits are registered: - - - Type Bit - Exp. - Hex - Feature - 1 - 2^0 - 0x01 - SHA-256 - 10 - 2^1 - 0x02 - PREIMAGE - 100 - 2^2 - 0x04 - PREFIX - 1000 - 2^3 - 0x08 - THRESHOLD - 10000 - 2^4 - 0x10 - RSA - 100000 - 2^5 - 0x20 - ED25519 - - -The following types are registered: - - - Type ID - Required Bitmask - Type Name - 0 - 0x03 - PREIMAGE-SHA-256 - 1 - 0x05 - PREFIX-SHA-256 - 2 - 0x09 - THRESHOLD-SHA-256 - 3 - 0x11 - RSA-SHA-256 - 4 - 0x20 - ED25519 - - -
-
- - -
-
- + + + Crypto-Conditions + + + +

Spec has been moved to https://github.com/rfcs/crypto-conditions/

+ + \ No newline at end of file diff --git a/0003-interledger-protocol/0003-interledger-protocol.md b/0003-interledger-protocol/0003-interledger-protocol.md index 279d13f6..2ea70b22 100644 --- a/0003-interledger-protocol/0003-interledger-protocol.md +++ b/0003-interledger-protocol/0003-interledger-protocol.md @@ -1,3 +1,7 @@ +--- +title: The Interledger Protocol (ILP) +draft: 9 +--- # Interledger Protocol (ILP) ## Preface @@ -20,7 +24,7 @@ The interledger protocol is intentionally limited in scope to provide the functi This protocol is called on by hosts through higher level protocol modules in an interledger environment. Interledger protocol modules call on local ledger protocols to carry the interledger payment to the next connector or destination account. -For example, a [`Simple Payment Setup Protocol (SPSP)`](../0009-simple-payment-setup-protocol/) module would call the interledger module with the address and other parameters in the interledger packet to send a payment. The interledger module would send a transfer to the next connector or destination account along with the interledger packet and according to the parameters given. The transfer and interledger packet would be received by the next host's interledger module and handled by each each successive connector and finally the destination's SPSP module. +For example, a [`Simple Payment Setup Protocol (SPSP)`](../0009-simple-payment-setup-protocol/0009-simple-payment-setup-protocol.md) module would call the interledger module with the address and other parameters in the interledger packet to send a payment. The interledger module would send a transfer to the next connector or destination account along with the interledger packet and according to the parameters given. The transfer and interledger packet would be received by the next host's interledger module and handled by each successive connector and finally the destination's SPSP module. In the Ripple case, for example, the interledger module would call on a local ledger module which would create a Ripple transaction with the interledger packet attached to transmit to the Ripple Consensus Ledger. The Ripple address would be derived from the interledger address by the local ledger interface and would be the address of some account in the Ripple network, which might belong to a connector to other ledgers. @@ -36,19 +40,7 @@ The interledger protocol treats each interledger payment as an independent entit Interledger payments do not carry a dedicated time-to-live or remaining-hops field. Instead, the amount field acts as an implicit time-to-live: Each time the payment is forwarded, the forwarding connector will take some fee out of the inbound amount. Once a connector recognizes that the inbound amount is worth less (though not necessarily numerically smaller) than the destination amount in the ILP header, it will refuse to forward the payment. -### Definitions - -##### Transfer - Change in ownership of some asset - -##### Ledger - System which records transfers - -##### Connector - System which relays transfers between two ledgers - -##### Payment - An exchange of assets involving one or more transfers on different ledgers +See [IL-RFC 19](../0019-glossary/0019-glossary.md) for definitions of terms like Transfer, Ledger, Connector, and Payment. ## Overview @@ -56,54 +48,13 @@ Interledger payments do not carry a dedicated time-to-live or remaining-hops fie The following diagram illustrates the place of the interledger protocol in the protocol hierarchy: -![Interledger model](../0001-interledger-architecture/assets/interledger-architecture-layers.png) +![Interledger model](../shared/graphs/protocol-suite.svg) The interledger protocol interfaces on one side to the higher level end-to-end protocols and on the other side to the local ledger protocol. In this context a "ledger" may be a small ledger owned by an individual or organization or a large public ledger such as Bitcoin. ### Model of Operation -#### Without Holds ("Optimistic Mode") - -The protocol MAY be used without the security provided by holds -- sometimes referred to as "Optimistic Mode". The model of operation for transmitting funds from one application to another without holds is illustrated by the following scenario: - -We suppose the source and destination have accounts on different ledgers connected by a single connector. - - (1) (11) - Application Application - \ / - (2) (6) (10) - Interledger Module Interledger Module Interledger Module - \ / \ / - (3) (5) (7) (9) - LLI-1 LLI-1 LLI-2 LLI-2 - \ (4) / \ (8) / - Local Ledger 1 Local Ledger 2 - -1. The sending application chooses an amount and calls on its local interledger module to send that amount as a payment and passes the destination address and other parameters as arguments of the call. - -2. The interledger module prepares an ILP packet and attaches the data to it. The interledger module determines a destination account on the local ledger for this interledger address. In this case it is the account of a connector. It passes the chosen amount and the local destination account to the local ledger interface. - -3. The local ledger interface creates a local ledger transfer, then authorizes this transfer on the local ledger. - -4. The ledger executes the transfer and notifies the connector. - -5. The connector host's local ledger interface receives the notification and passes it to the interledger module. - -6. The connector's interledger module extracts the ILP packet from the notification and determines from the interledger address that the payment is to be forwarded to another account in a second ledger. The interledger module converts the amount according to its locally available liquidity and determines the local account on the other ledger corresponding to the destination host. It calls on the local ledger interface for the destination ledger to send the transfer, which includes the same ILP packet. - -7. This local ledger interface creates a local ledger transfer and authorizes it. - -8. The ledger executes the transfer and notifies the destination host. - -9. The destination host's local ledger interface receives the notification and passes it to the interledger module. - -10. The interledger module extracts the ILP packet and determines that the payment is for an application in this host. It passes the transfer data to the application. - -11. The destination application receives the notification of incoming funds and reacts accordingly. - -#### With Holds ("Universal Mode") - -The protocol MAY be used with transfer holds to ensure a sender's funds are delivered to the destination or returned to the sender's account. The model of operation is illustrated with the following example: +The protocol uses transfer holds to ensure a sender's funds are delivered to the destination or returned to the sender's account. The model of operation is illustrated with the following example: (1,21) (11) Application Application @@ -117,11 +68,11 @@ The protocol MAY be used with transfer holds to ensure a sender's funds are deli Local Ledger 1 Local Ledger 2 -1. The sending application uses a higher-level protocol to negotiate the address, an amount, and a cryptographic condition with the destination. It calls on the interledger module to send a payment with these parameters. +1. The sending application uses a higher-level protocol to negotiate the address, an amount, a cryptographic condition, and a timeout with the destination. It calls on the interledger module to send a payment with these parameters. 2. The interledger module prepares the ILP packet, chooses the account to send the local ledger transfer to, and passes them to the local ledger interface. -3. The local ledger interface creates a local ledger transfer, including the crytographic condition, then authorizes this transfer on the local ledger. +3. The local ledger interface creates a local ledger transfer, including the cryptographic condition, then authorizes this transfer on the local ledger. 4. The ledger puts the sender's funds on hold -- it does not transfer the funds to the connector -- and notifies the connector. @@ -129,7 +80,7 @@ The protocol MAY be used with transfer holds to ensure a sender's funds are deli 6. The connector's interledger module extracts the ILP packet and determines that it should forward the payment. The interledger module calls on the destination ledger's local ledger interface to send the second transfer, including the same condition as the sender's transfer. -7. The local ledger interface creates a local ledger transfer, including the crytographic condition, then authorizes this transfer on the local ledger. +7. The local ledger interface creates a local ledger transfer, including the cryptographic condition, then authorizes this transfer on the local ledger. 8. The ledger puts the connector's funds on hold -- it does not transfer the funds to the destination -- and notifies the destination host. @@ -172,91 +123,315 @@ As with the [internet protocol](https://tools.ietf.org/html/rfc791#section-2.3), The interledger module translates interledger addresses to local ledger addresses. Connectors and local ledger interfaces are responsible for translating addresses into interledger routes and local routes, respectively. -Addresses are hierarchically structured strings consisting of segments delimited by the period (`.`) character. In order to distinguish the present address format from future or alternative versions, the protocol prefix `ilp:` MUST be used: +Addresses are hierarchically structured strings consisting of segments delimited by the period (`.`) character. ``` -ilp:us.bank1.bob +g.us.bank1.bob ``` -Care must be taken in mapping interledger addresses to local ledger accounts. Examples of address mappings may be found in "Address Mappings" ((TODO)). +More information about ILP addresses can be found in the [ILP Address Specification](../0015-ilp-addresses/). + +The mapping from addresses to local accounts on a ledger is defined by the ledger protocol. ### Connectors -Connectors implement the interledger protocol to forward payments between ledgers. Connectors also implement the [Connector to Connector Protocol (CCP)](../0010-connector-to-connector-protocol/) to coordinate routing and other interledger control information. +Connectors implement the interledger protocol to forward payments between ledgers and relay errors back along the path. Connectors implement (or include a module that implements) the ledger protocol of the ledgers on which they hold accounts. + +Connectors also implement the [Connector to Connector Protocol (CCP)](../0010-connector-to-connector-protocol/) to coordinate routing and other interledger control information. + +### Errors -![Interledger is an overlay across ledgers](assets/ilp-ledger-relation.png) +Errors may be generated at any point as an Interledger payment is being prepared or by the receiver. Connectors that are notified of an outgoing transfer being rejected MUST reject the corresponding incoming transfer with the same error. + +Connectors SHOULD include their ILP address in the [`forwardedBy`](#forwardedby) field in the error. Connectors SHOULD NOT modify errors +in any other way. + +See below for the [ILP Error Format](#ilp-error-format) and [ILP Error Codes](#ilp-error-codes). ## Specification -### ILP Header Format +### ILP Payment Packet Format + +There are two types of payment packets: delivered and forwarded. A delivered payment specifies the amount that should arrive at the destination, a forwarded payment doesn't. +Note that forwarded payments are still experimental, and their definition may change or get deprecated at any time. + +Here is a summary of the fields in the delivered and forwarded ILP payment packet formats: + +#### Delivered Payment Packet + +| Field | Type | Short Description | +|:--|:--|:--| +| type | UInt8 | Always `1`, indicates that this ILP packet is an ILP Payment Packet (type 1) | +| length | Length Determinant | Indicates how many bytes the rest of the packet has | +| amount | UInt64 | Amount the destination account should receive, denominated in the asset of the destination ledger | +| account | Address | Address corresponding to the destination account | +| data | OCTET STRING | Transport layer data attached to the payment | +| extensions | Length Determinant | Always `0` + +Here's an example: + +| Type | Length, 8+(1+14)+(1+3)+1=28 | Amount (123,000,000) ... | +|:--|:--|:--| +| 1 | 28 | 0 0 0 0 7 84 | + + +| .. Amount (123,000,000) | Length | Address ... ('g.us.') | +|:--|:--|:--| +| 212 192 | 14 | 103 46 117 115 46 | + +| ... Address ('nexus.bo') ... | +|:--| +| 110 101 120 117 115 46 98 111 | + +| ... Address ('b') | length | data | extensions | +|:--|:--|:--|:--| +| 98 | 3 | 4 16 65 | 0 | + +### Forwarded Payment Packet (experimental) + +| Field | Type | Short Description | +|:--|:--|:--| +| type | UInt8 | Always `10`, indicates that this ILP packet is an ILP Forwarded Payment Packet (type 10) | +| length | Length Determinant | Indicates how many bytes the rest of the packet has | +| account | Address | Address corresponding to the destination account | +| data | OCTET STRING | Transport layer data attached to the payment | +| extensions | Length Determinant | Always `0` + +Example: + +| Type | Length, (1+14)+(1+3)+1=20 | Length | Address ... ('g.us.nexus.bo') +|:--|:--|:--|:--| +| 10 | 20 | 14 | 103 46 117 115 46 110 101 120 117 115 46 98 111 | + +| ... Address ('b') | length | data | extensions | +|:--|:--|:--|:--| +| 98 | 3 | 4 16 65 | 0 | + + +Let's look more closely at the three important fields: `amount`, `address`, and `data`. + +#### amount -Here is a summary of the fields in the ILP header format: + UInt64 ::= INTEGER (0..18446744073709551615) + +Amount in discrete units of the receiving ledger's asset type. Note that the amount is counted in terms of the smallest indivisible unit on the receiving ledger. + +#### account + + -- Readable names for special characters that may appear in ILP addresses + hyphen IA5String ::= "-" + period IA5String ::= "." + underscore IA5String ::= "_" + tilde IA5String ::= "~" + + -- A standard interledger address + Address ::= IA5String + (FROM + ( hyphen + | period + | "0".."9" + | "A".."Z" + | underscore + | "a".."z" + | tilde ) + ) + (SIZE (1..1023)) + +[Interledger Address](../0015-ilp-addresses/) of the receiving account. + +#### data + + OCTET STRING (SIZE(0..32767)) + +Arbitrary data that is attached to the payment. The contents are defined by the transport layer protocol. + +### ILP Rejection Format + +Here is a summary of the fields in the ILP error format: | Field | Type | Short Description | |:--|:--|:--| -| version | INTEGER(0..255) | ILP protocol version (currently `1`) | -| destinationAddress | IlpAddress | Address corresponding to the destination account | -| destinationAmount | IlpAmount | Amount the destination account should receive, denominated in the asset of the destination ledger | -| condition | IlpCondition | Execution condition for the transfers -| expiresAt | IlpTimestamp | Maximum expiry time of the last transfer that the recipient will accept | +| code | IA5String | [ILP Error Code](#ilp-error-codes) | +| triggeredBy | Address | ILP address of the entity that originally emitted the error | +| message | UTF8String | Error data provided for debugging purposes | +| data | OCTET STRING | Error data provided for debugging purposes | + +#### code + + IA5String (SIZE(3)) + +Error code. For example, `F00`. See [ILP Error Codes](#ilp-error-codes) for the list of error codes and their meanings. + +#### triggeredBy -#### version + Address - INTEGER(0..255) +[ILP Address](#account) of the entity that originally emitted the error. -The version of the Interledger Protocol being used. This document describes version `1`. +#### message -#### destinationAddress + UTF8String (SIZE(0..8191)) - IlpAddress :== SEQUENCE OF OCTET STRING +Human-readable error message. When emitting a rejection, implementations (receivers or connectors) MAY include additional debug information. When including additional debug information, implementations SHOULD use the format `Error message. key1=value1 key2=value2`, e.g.: + +``` +Invalid destination. destination=example.us.bob +``` + +This field MUST be encoded as UTF-8. + +#### data + + OCTET STRING (SIZE(0..32767)) + +Machine-readable data. The format is defined for each error code. Implementations MUST follow the correct format for the code given in the `code` field. When parsing rejection packets, implementations MUST ignore extra bytes in the `data` field. + +### ILP Error Format + +> **Deprecated:** Use [ILP Rejection Format](#ilp-rejection-format) instead. + +Here is a summary of the fields in the ILP error format: + +| Field | Type | Short Description | +|:--|:--|:--| +| code | IA5String | [ILP Error Code](#ilp-error-codes) | +| name | IA5String | [ILP Error Code Name](#ilp-error-codes) | +| triggeredBy | Address | ILP address of the entity that originally emitted the error | +| forwardedBy | SEQUENCE OF Address | ILP addresses of connectors that relayed the error message | +| triggeredAt | GeneralizedTime | Time when the error was initially emitted | +| data | OCTET STRING | Error data provided for debugging purposes | -Hierarchical routing label. +#### code -#### destinationAmount + IA5String (SIZE(3)) - IlpAmount :== SEQUENCE { mantissa INTEGER, exponent INTEGER(-128..127) } +Error code. For example, `F00`. See [ILP Error Codes](#ilp-error-codes) for the list of error codes and their meanings. -Base 10 encoded amount. +#### name -#### condition + IA5String - IlpCondition :== Condition +Error name. For example, `Bad Request`. See [ILP Error Codes](#ilp-error-codes) for the list of error codes and their meanings. -Crypto-condition in binary format as defined in [draft-thomas-crypto-conditions-00](#draft-thomas-crypto-conditions-00). +Implementations of ILP SHOULD NOT depend on the `name` instead of the `code`. The name is primarily provided as a convenience to facilitate debugging by humans. If the `name` does not match the `code`, the `code` is the definitive identifier of the error. -When processing a transfer carrying a condition a ledger MUST place a hold on the funds. While the funds are on hold, neither the sender nor recipient are able to access them. Upon receiving a condition fulfillment, a ledger MUST transfer the funds to the recipient if the funds are held, the fulfillment is a valid fulfillment of the transfer condition and the transfer has not yet expired. ("Universal Mode") +#### triggeredBy -The condition is an optional field. If no condition is provided, the funds are immediately credited to the recipient of the transfer. ("Optimistic Mode") +[ILP Address](#account) of the entity that originally emitted the error. This MAY be an address prefix if the entity that originally omitted the error is a ledger. -#### expiresAt +#### forwardedBy - IlpExpiry :== GeneralizedTime +[ILP Addresses](#account) of the connectors that relayed the error message. -Ledgers MAY require that all transfers with a condition also carry an expiry timestamp. Ledgers MUST reject transfers that carry an expiry timestamp, but no condition. Ledgers MUST reject transfers whose expiry transfer time has been reached or exceeded and whose condition has not yet been fulfilled. When rejecting a transfer, the ledger MUST lift the hold and make the funds available to the sender again. +#### triggeredAt -### Holds Without Native Ledger Support + GeneralizedTime -Not all ledgers support held transfers. In the case of a ledger that doesn't, the sender and recipient of the local ledger transfer MAY choose a commonly trusted party to carry out the hold functions. There are three options: +Date and time when the error was initially emitted. This MUST be expressed in the UTC + 0 (Z) timezone. -1. The sender MAY trust the receiver. The sender will perform a regular transfer in the first step and the receiver will perform a transfer back if the condition has not been met in time. +#### data -2. The receiver MAY trust the sender. The sender will notify the receiver about the intent to transfer. If the receiver provides a fulfillment for the condition before the expiry date, the sender will perform a regular transfer to the receiver. + OCTET STRING (SIZE(0..8192)) -3. The sender and receiver MAY appoint a mutually trusted third-party which has an account on the local ledger. The sender performs a regular transfer into a neutral third-party account. In the first step, funds are transfered into the account belonging to the neutral third-party. -### Payment Channels +Error data provided primarily for debugging purposes. Systems that emit errors SHOULD include additional explanation or context about the issue. + +Protocols built on top of ILP that define behavior for certain errors SHOULD specify the encoding format of error `data`. + +Unless otherwise specified, `data` SHOULD be encoded as UTF-8. + +### ILP Error Codes + +Inspired by [HTTP Status Codes](https://tools.ietf.org/html/rfc2616#section-10), ILP errors are categorized based on the intended behavior of the caller when they get the given error. + +#### F__ - Final Error + +Final errors indicate that the payment is invalid and should not be retried unless the details are changed. + +| Code | Name | Description | Data Fields | +|---|---|---|---| +| **F00** | **Bad Request** | Generic sender error. | (empty) | +| **F01** | **Invalid Packet** | The ILP packet was syntactically invalid. | (empty) | +| **F02** | **Unreachable** | There was no way to forward the payment, because the destination ILP address was wrong or the connector does not have a route to the destination. | (empty) | +| **F03** | **Invalid Amount** | The amount is invalid, for example it contains more digits of precision than are available on the destination ledger or the amount is greater than the total amount of the given asset in existence. | (empty) | +| **F04** | **Insufficient Destination Amount** | The receiver deemed the amount insufficient, for example you tried to pay a $100 invoice with $10. | (empty) | +| **F05** | **Wrong Condition** | The receiver generated a different condition and cannot fulfill the payment. | (empty) | +| **F06** | **Unexpected Payment** | The receiver was not expecting a payment like this (the memo and destination address don't make sense in that combination, for example if the receiver does not understand the transport protocol used) | (empty) | +| **F07** | **Cannot Receive** | The receiver is unable to accept this payment due to a constraint. For example, the payment would put the receiver above its maximum account balance. | (empty) | +| **F99** | **Application Error** | Reserved for application layer protocols. Applications MAY use names other than `Application Error`. | (empty) | + +#### T__ - Temporary Error + +Temporary errors indicate a failure on the part of the receiver or an intermediary system that is unexpected or likely to be resolved soon. Senders SHOULD retry the same payment again, possibly after a short delay. + +| Code | Name | Description | Data Fields | +|---|---|---|---| +| **T00** | **Internal Error** | A generic unexpected exception. This usually indicates a bug or unhandled error case. | (empty) | +| **T01** | **Ledger Unreachable** | The connector has a route or partial route to the destination but was unable to reach the next ledger. Try again later. | (empty) | +| **T02** | **Ledger Busy** | The ledger is rejecting requests due to overloading. Try again later. | (empty) | +| **T03** | **Connector Busy** | The connector is rejecting requests due to overloading. Try again later. | (empty) | +| **T04** | **Insufficient Liquidity** | The connector would like to fulfill your request, but it doesn't currently have enough money. Try again later. | (empty) | +| **T05** | **Rate Limited** | The sender is sending too many payments and is being rate-limited by a ledger or connector. If a connector gets this error because they are being rate-limited, they SHOULD retry the payment through a different route or respond to the sender with a `T03: Connector Busy` error. | (empty) | +| **T99** | **Application Error** | Reserved for application layer protocols. Applications MAY use names other than `Application Error`. | (empty) | + +#### R__ - Relative Error + +Relative errors indicate that the payment did not have enough of a margin in terms of money or time. However, it is impossible to tell whether the sender did not provide enough error margin or the path suddenly became too slow or illiquid. The sender MAY retry the payment with a larger safety margin. + +| Code | Name | Description | Data Fields | +|---|---|---|---| +| **R00** | **Transfer Timed Out** | The transfer timed out, meaning the next party in the chain did not respond. This could be because you set your timeout too low or because something look longer than it should. The sender MAY try again with a higher expiry, but they SHOULD NOT do this indefinitely or a malicious connector could cause them to tie up their money for an unreasonably long time. | (empty) | +| **R01** | **Insufficient Source Amount** | Either the sender did not send enough money or the exchange rate changed before the payment was prepared. The sender MAY try again with a higher amount, but they SHOULD NOT do this indefinitely or a malicious connector could steal money from them. | (empty) | +| **R02** | **Insufficient Timeout** | The connector could not forward the payment, because the timeout was too low to subtract its safety margin. The sender MAY try again with a higher expiry, but they SHOULD NOT do this indefinitely or a malicious connector could cause them to tie up their money for an unreasonably long time. | (empty) | +| **R99** | **Application Error** | Reserved for application layer protocols. Applications MAY use names other than `Application Error`. | (empty) | + +### ILP Fulfillment Data Format + +This type of ILP packet is attached to a fulfillment and carries additional transport layer information that the sender may use to make further payments. Here is a summary of the fields in the ILP fulfillment data format: + +| Field | Type | Short Description | +|:--|:--|:--| +| data | OCTET STRING | Transport layer fulfillment data | + +#### Example + +Consider a payload of `4 16 65` (payload length is `3`). +The length of the envelope content would then be `1+3+1 = 5`. +The ILP packet type is always `9` for fulfillment data, and extensions is always `0`, so on the wire +(and when passed between software modules like for instance ledger plugins), +the resulting ILP packet would look like this: + +| type | content length | payload length | data | extensions | +|:--|:--|:--|:--|:--| +| 9 | 5 | 3 | 4 16 65 | 0 | + +#### data + + OCTET STRING (SIZE(0..32767)) + +Arbitrary data that is attached to the fulfillment. The contents are defined by the transport layer protocol. ## Appendix A: ASN.1 Module +See [ASN.1 Definitions](../asn1/InterledgerProtocol.asn). + ## Appendix B: IANA Considerations ### Header Type Registry The following initial entries should be added to the Interledger Header Type registry to be created and maintained at (the suggested URI) http://www.iana.org/assignments/interledger-header-types: -| Header Type ID | Description | -|:--|:--| -| 0 | [Interledger Protocol (ILP)](#ilp-header-format) | -| 1 | [Optimistic Transport Protocol (OTP)](../0005-optimistic-transport-protocol/) | -| 2 | [Universal Transport Protocol (UTP)](../0006-universal-transport-protocol/) | -| 3 | [Atomic Transport Protocol (ATP)](../0007-atomic-transport-protocol/) | -| 4 | [Interledger Quoting Protocol (ILQP)](../0008-interledger-quoting-protocol/) | +| Header Type ID | Protocol | Message Type | +|:--|:--|:--| +| 1 | ILP | [IlpPayment](#ilp-payment-packet-format) | +| 2 | [ILQP][] | QuoteLiquidityRequest | +| 3 | [ILQP][] | QuoteLiquidityResponse | +| 4 | [ILQP][] | QuoteBySourceAmountRequest | +| 5 | [ILQP][] | QuoteBySourceAmountResponse | +| 6 | [ILQP][] | QuoteByDestinationAmountRequest | +| 7 | [ILQP][] | QuoteByDestinationAmountResponse | +| 8 | ILP | [IlpError](#ilp-error-format) | +| 9 | ILP | [IlpFulfillmentData](#ilp-fulfillment-data-format) | +| 10 | ILP | [IlpForwardedPaymentData](#ilp-forwarded-payment-packet-experimental) | +| 11 | ILP | [IlpRejectionData](#ilp-rejection-format) | + +[ILQP]: ../0008-interledger-quoting-protocol/ diff --git a/0003-interledger-protocol/assets/ilp-ledger-relation.png b/0003-interledger-protocol/assets/ilp-ledger-relation.png deleted file mode 100644 index 873700b7..00000000 Binary files a/0003-interledger-protocol/assets/ilp-ledger-relation.png and /dev/null differ diff --git a/0003-interledger-protocol/assets/ilp-ledger-relation.xml b/0003-interledger-protocol/assets/ilp-ledger-relation.xml deleted file mode 100644 index 8d4d5a5f..00000000 --- a/0003-interledger-protocol/assets/ilp-ledger-relation.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/0004-ledger-plugin-interface/0004-ledger-plugin-interface.md b/0004-ledger-plugin-interface/0004-ledger-plugin-interface.md index ca9b220e..83ba2b83 100644 --- a/0004-ledger-plugin-interface/0004-ledger-plugin-interface.md +++ b/0004-ledger-plugin-interface/0004-ledger-plugin-interface.md @@ -1,8 +1,14 @@ -# Ledger Plugin Interface +--- +title: The Javascript Ledger Plugin Interface +deprecated: 0024-ledger-plugin-interface-2 +draft: FINAL +--- + +# Javascript Ledger Plugin Interface The Interledger Protocol is a protocol suite for connecting blockchains and other ledgers. -This spec defines a ledger abstraction interface for Interledger clients and connectors to communicate and route payments across different ledger protocols. +This spec defines a Javascript ledger abstraction interface for Interledger clients and connectors to communicate and route payments across different ledger protocols. While the exact methods and events defined here are specific to the Javascript implementation, this may be used as a guide for ledger abstractions in other languages. To send ILP payments through a new ledger, one must implement a ledger plugin that exposes the interface defined below. This can be used with the ILP Client and Connector and should work out of the box. @@ -18,33 +24,38 @@ This spec depends on the [ILP spec](../0003-interledger-protocol/). | | [**connect**](#connect) ( options ) `⇒ Promise.` | | | [**disconnect**](#disconnect) ( ) `⇒ Promise.` | | | [**isConnected**](#isconnected) ( ) `⇒ Boolean` | -| | [**getInfo**](#getinfo) ( ) ⇒ Promise.<[LedgerInfo](#class-ledgerinfo)> | -| | [**getPrefix**](#getprefix) ( ) `⇒ Promise` | -| | [**getAccount**](#getaccount) ( ) `⇒ Promise` | +| | [**getInfo**](#getinfo) ( ) ⇒ [LedgerInfo](#class-ledgerinfo) | +| | [**getAccount**](#getaccount) ( ) `⇒ String` | | | [**getBalance**](#getbalance) ( ) ⇒ Promise.<String> | | | [**getFulfillment**](#getfulfillment) ( transferId ) ⇒ Promise.<String> | -| | [**sendTransfer**](#sendTransfer) ( transfer ) ⇒ Promise.<null> | -| | [**sendMessage**](#sendMessage) ( message ) ⇒ Promise.<null> | -| | [**fulfillCondition**](#fulfillcondition) ( transferId, fulfillment ) ⇒ Promise.<null> | -| | [**rejectIncomingTransfer**](#rejectincomingtransfer) ( transferId, rejectMessage ) ⇒ Promise.<null> | +| | [**sendTransfer**](#sendtransfer) ( transfer ) ⇒ Promise.<null> | +| | [**sendRequest**](#sendrequest) ( message ) ⇒ Promise.<[Message](#class-message)> | +| | [**fulfillCondition**](#fulfillcondition) ( transferId, fulfillment, ilp ) ⇒ Promise.<null> | +| | [**rejectIncomingTransfer**](#rejectincomingtransfer) ( transferId, reason ) ⇒ Promise.<null> | +| | [**registerRequestHandler**](#registerrequesthandler) ( requestHandler ) ⇒ null | +| | [**deregisterRequestHandler**](#deregisterrequesthandler) ( ) ⇒ null | ###### Events | Name | Handler | |:--|:--| -| [**connect**](#event-connect-) | `( ) ⇒` | -| [**disconnect**](#event-disconnect-) | `( ) ⇒` | -| [**error**](#event-error-) | `( ) ⇒` | -| [**incoming_transfer**](#event-*_transfer-) | ( transfer:[IncomingTransfer](#incomingtransfer) ) ⇒ | -| [**incoming_prepare**](#event-*_prepare-) | ( transfer:[IncomingTransfer](#incomingtransfer) ) ⇒ | -| [**incoming_fulfill**](#event-*_fulfill-) | ( transfer:[IncomingTransfer](#incomingtransfer), fulfillment:Buffer ) ⇒ | -| [**incoming_reject**](#event-*_reject-) | ( transfer:[IncomingTransfer](#incomingtransfer), rejectionReason:Buffer ) ⇒ | -| [**incoming_cancel**](#event-*_cancel-) | ( transfer:[IncomingTransfer](#incomingtransfer), cancellationReason:Buffer ) ⇒ | -| [**incoming_message**](#event-*_message-) | ( message:[IncomingMessage](#incomingmessage) ) ⇒ | -| [**outgoing_transfer**](#event-*_transfer-) | ( transfer:[outgoingTransfer](#outgoingtransfer) ) ⇒ | -| [**outgoing_prepare**](#event-*_prepare-) | ( transfer:[outgoingTransfer](#outgoingtransfer) ) ⇒ | -| [**outgoing_fulfill**](#event-*_fulfill-) | ( transfer:[outgoingTransfer](#outgoingtransfer), fulfillment:Buffer ) ⇒ | -| [**outgoing_reject**](#event-*_reject-) | ( transfer:[outgoingTransfer](#outgoingtransfer), rejectionReason:Buffer ) ⇒ | -| [**outgoing_cancel**](#event-*_cancel-) | ( transfer:[outgoingTransfer](#outgoingtransfer), cancellationReason:Buffer ) ⇒ | +| [**connect**](#event-connect) | `( ) ⇒` | +| [**disconnect**](#event-disconnect) | `( ) ⇒` | +| [**error**](#event-error) | `( ) ⇒` | +| [**incoming_transfer**](#event-_transfer) | ( transfer:[IncomingTransfer](#class-transfer) ) ⇒ | +| [**incoming_prepare**](#event-_prepare) | ( transfer:[IncomingTransfer](#class-transfer) ) ⇒ | +| [**incoming_fulfill**](#event-_fulfill) | ( transfer:[IncomingTransfer](#class-transfer), fulfillment:String, ilp:String ) ⇒ | +| [**incoming_reject**](#event-_reject) | ( transfer:[IncomingTransfer](#class-transfer), rejectionReason:[RejectionMessage](#class-rejectionmessage) ) ⇒ | +| [**incoming_cancel**](#event-_cancel) | ( transfer:[IncomingTransfer](#class-transfer), cancellationReason:[RejectionMessage](#class-rejectionmessage) ) ⇒ | +| [**incoming_request**](#event-_request) | ( message:[Message](#class-message) ) ⇒ | +| [**incoming_response**](#event-_response) | ( message:[Message](#class-message) ) ⇒ | +| [**outgoing_transfer**](#event-_transfer) | ( transfer:[outgoingTransfer](#class-transfer) ) ⇒ | +| [**outgoing_prepare**](#event-_prepare) | ( transfer:[outgoingTransfer](#class-transfer) ) ⇒ | +| [**outgoing_fulfill**](#event-_fulfill) | ( transfer:[outgoingTransfer](#class-transfer), fulfillment:String ) ⇒ | +| [**outgoing_reject**](#event-_reject) | ( transfer:[outgoingTransfer](#class-transfer), rejectionReason:[RejectionMessage](#class-rejectionmessage) ) ⇒ | +| [**outgoing_cancel**](#event-_cancel) | ( transfer:[outgoingTransfer](#class-transfer), cancellationReason:[RejectionMessage](#class-rejectionmessage) ) ⇒ | +| [**outgoing_request**](#event-_request) | ( message:[Message](#class-message) ) ⇒ | +| [**outgoing_response**](#event-_response) | ( message:[Message](#class-message) ) ⇒ | +| [**info_change**](#event-info_change) | ( info:[LedgerInfo](#class-ledgerinfo) ) ⇒ | ###### Errors | Name | Description | @@ -58,7 +69,10 @@ This spec depends on the [ILP spec](../0003-interledger-protocol/). | [**AlreadyFulfilledError**]() | A requested transfer has already been fulfilled and cannot be modified | | [**TransferNotConditionalError**]() | A requested transfer is not conditional and cannot be rejected/fulfilled/etc. | | [**NotAcceptedError**]() | An operation has been rejected due to ledger-side logic | +| [**InsufficientBalanceError**]() | An operation has been rejected because the source balance isn't high enough | +| [**AccountNotFoundError**]() | An operation has been rejected because the account does not exist | | [**NoSubscriptionsError**]() | A transfer or message cannot be delivered because there are no active websockets | +| [**RequestHandlerAlreadyRegisteredError**]() | The current request handler callback must be unset before a new one can be registered | ### Instance Management @@ -108,10 +122,10 @@ For a detailed description of these properties, please see [`PluginOptions`](#cl `options` is optional. -Initiate ledger event subscriptions. Once `connect` is called the ledger plugin MUST attempt to subscribe to and report ledger events. Once the connection is established, the ledger plugin should emit the [`connect`](#event-connect-) event. If the connection is lost, the ledger plugin SHOULD emit the [`disconnect`](#event-disconnect-) event. +Initiate ledger event subscriptions. Once `connect` is called the ledger plugin MUST attempt to subscribe to and report ledger events. Once the connection is established, the ledger plugin should emit the [`connect`](#event-connect-) event. If the connection is lost, the ledger plugin SHOULD emit the [`disconnect`](#event-disconnect-) event. The plugin should ensure that the information returned by `getInfo` and `getAccount` is available and cached before emitting the [`connect`](#event-connect-) event. -Throws `InvalidFieldsError` if credentials are missing, and `NotAcceptedError` if credentials are rejected. -Throws `TypeError` if `options.timeout` is passed but is not a `Number`. +Rejects with `InvalidFieldsError` if credentials are missing, and `NotAcceptedError` if credentials are rejected. +Rejects with `TypeError` if `options.timeout` is passed but is not a `Number`. #### disconnect ledgerPlugin.disconnect() ⇒ Promise.<null> @@ -124,43 +138,26 @@ Unsubscribe from ledger events. Query whether the plugin is currently connected. #### getInfo -ledgerPlugin.getInfo() ⇒ Promise.<[LedgerInfo](#class-ledgerinfo)> +ledgerPlugin.getInfo() ⇒ [LedgerInfo](#class-ledgerinfo) -Retrieve some metadata about the ledger. Plugin must be connected, otherwise the promise should reject. +Retrieve some metadata about the ledger. Plugin must be connected, otherwise the function should throw. ###### Example Return Value ```json { - "precision": 10, - "scale": 4, + "prefix": "us.fed.some-bank.", "currencyCode": "USD", - "currencySymbol": "$", - "connectors": [ - { - "id": "http://usd-ledger.example/accounts/chloe", - "name": "chloe", - "connector": "http://usd-eur-connector.example" - } - ] + "currencyScale": 4, + "connectors": [ "us.fed.some-bank.chloe" ] } ``` For a detailed description of these properties, please see [`LedgerInfo`](#class-ledgerinfo). -#### getPrefix -ledgerPlugin.getPrefix() ⇒ Promise.<String> - -Get the ledger plugin's ILP address prefix. This is used to determine whether a given ILP address is local to this ledger plugin and thus can be reached using this plugin's `sendTransfer` method. - -The prefix may be configured, automatically detected, or hard-coded, depending on the ledger. For example, a Bitcoin ledger plugin may have the address hard-coded, while a [`five-bells-ledger`](https://github.com/interledger/five-bells-ledger) would use an API call to get the prefix. - -###### Example Return Value -`us.fed.some-bank` - #### getAccount -ledgerPlugin.getAccount() ⇒ Promise.<String> +ledgerPlugin.getAccount() ⇒ String -Get the ledger plugin's ILP address. This is given to senders to receive transfers to this account. +Get the ledger plugin's ILP address. This is given to senders to receive transfers to this account. Plugin must be connected, otherwise the function should throw. The mapping from the ILP address to the local ledger address is dependent on the ledger / ledger plugin. An ILP address could be the `.`, or a token could be used in place of the actual account name or number. @@ -170,17 +167,19 @@ The mapping from the ILP address to the local ledger address is dependent on the #### getBalance ledgerPlugin.getBalance() ⇒ Promise.<String> -Return a decimal string representing the current balance. Plugin must be connected, otherwise the promise should reject. +Return a (base-ten) integer string (`..., '-3', '-2', '-1', '0', '1', '2', '3', ...`) representing the current balance, in the ledger's base unit. For example, on a ledger with `currencyCode` 'USD' and `currencyScale` 6, +the base unit would be micro-dollars. +A balance of '1230000' should then be interpreted as equivalent to 1.23 US dollars. The maximum and minimum balance are up to the ledger to determine. Plugin must be connected, otherwise the promise should reject. #### getFulfillment ledgerPlugin.getFulfillment( transferId ) ⇒ Promise.<String> Return the fulfillment of a transfer if it has already been executed. -Throws `MissingFulfillmentError` if the transfer exists but is not yet -fulfilled. Throws `TransferNotFoundError` if no conditional transfer is found -with the given ID. Throws `AlreadyRolledBackError` if the transfer has been rolled back -and will not be fulfilled. Throws `TransferNotConditionalError` if transfer is not +Rejects with `MissingFulfillmentError` if the transfer exists but is not yet +fulfilled. Rejects with `TransferNotFoundError` if no conditional transfer is found +with the given ID. Rejects with `AlreadyRolledBackError` if the transfer has been rolled back +and will not be fulfilled. Rejects with `TransferNotConditionalError` if transfer is not conditional. #### Event: `connect` @@ -203,12 +202,12 @@ General event for fatal exceptions. Emitted when the plugin experienced an unexp Note that all transfers will have `transferId`'s to allow the plugin user to correlate actions related to a single transfer. The `transferId` will be the same as the ID used by the underlying ledger wherever possible or applicable. If the ledger does not have transfer IDs, the plugin may generate one and use the `store` passed in to the constructor to persist them. #### sendTransfer -ledgerPlugin.sendTransfer( **transfer**:[OutgoingTransfer](#outgoingtransfer) ) ⇒ Promise.<null> +ledgerPlugin.sendTransfer( **transfer**:[Transfer](#class-transfer) ) ⇒ Promise.<null> Plugin must be connected, otherwise the promise should reject. Initiates a ledger-local transfer. A transfer can contain money and/or information. If there is a problem with the structure or -validity of the transfer, then `sendTransfer` should throw an error in the form of a -rejected promise. If the transfer is accepted by the ledger, however, then +validity of the transfer, then `sendTransfer` should reject with an error. +If the transfer is accepted by the ledger, however, then further errors will be in the form of `"reject"` events. All plugins MUST implement zero-amount transfers, but some ledger plugins MAY @@ -217,92 +216,118 @@ implement zero-amount transfers differently than other transfers. ###### Parameters | Name | Type | Description | |:--|:--|:--| -| transfer | [OutgoingTransfer](#outgoingtransfer) | Properties of the transfer to be created | +| transfer | [Transfer](#class-transfer) | Properties of the transfer to be created | + +When sending transfers, the [id](#id), [amount](#amount) and [to](#to) fields +are required. ###### Returns **`Promise.`** A promise which resolves when the transfer has been submitted (but not necessarily accepted.) -Throws `InvalidFieldsError` if required fields are missing from the transfer or malformed. Throws `DuplicateIdError` if a transfer with -the given ID and different already exists. Throws `NotAcceptedError` if the transfer is rejected by the ledger due to insufficient balance or -a nonexistant destination account. +Rejects with `InvalidFieldsError` if required fields are missing from the transfer or malformed. Rejects with `DuplicateIdError` if a transfer with +the given ID and different already exists. Rejects with `InsufficientBalanceError` if the transfer is rejected due to the source balance being too low. Rejects with `AccountNotFoundError` if the destination account does not exist. Rejects with `NotAcceptedError` if the transfer is otherwise rejected by the ledger. ###### Example ```js p.sendTransfer({ id: 'd86b0299-e2fa-4713-833a-96a6a75271b8', - account: 'example.ledger.connector', + to: 'example.ledger.connector', amount: '10', - data: new Buffer('...', 'base64'), noteToSelf: {}, - executionCondition: 'cc:0:3:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU:0', + executionCondition: '47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU', expiresAt: '2016-05-18T12:00:00.000Z' }) ``` -For a detailed description of these properties, please see [`OutgoingTransfer`](#outgoingtransfer). +For a detailed description of these properties, please see [`Class: Transfer`](#class-transfer). -#### sendMessage -ledgerPlugin.sendMessage( **message**:[OutgoingMessage](#outgoingmessage) ) ⇒ Promise.<null> +#### sendRequest +ledgerPlugin.sendRequest( **message**:[Message](#class-message) ) ⇒ Promise.<[Message](#class-message)> -Plugin must be connected, otherwise the promise should reject. Send a ledger-local message. -If there is a problem with the structure or validity of the message, then `sendMessage` should throw an error in the form of a rejected promise. +Plugin must be connected, otherwise the promise should reject. Sends a ledger-local message. Returns a promise for a response message from the other side. +If there is a problem with the structure or validity of the message, then `sendRequest` should reject with an error. Messaging is used by connectors for [quoting](../0008-interledger-quoting-protocol/) and broadcasting routes. ###### Parameters | Name | Type | Description | |:--|:--|:--| -| message | [OutgoingMessage](#outgoingmessage) | Properties of the message to be created | +| message | [Message](#class-message) | Properties of the message to be created | + +When sending messages, the [to](#messageto) and [ilp](#messageilp) fields are required. ###### Returns -**`Promise.`** A promise which resolves when the message has been submitted (but not necessarily delivered). To ensure delivery, you must build a mechanism for that on top. +**Promise.<[Message](#class-message)>** A promise which resolves when a response message has been received. -Throws `InvalidFieldsError` if required fields are missing from the message or malformed. -Throws `NotAcceptedError` if the message is rejected by the ledger. -Throws `NoSubscriptionsError` if the message cannot be delivered because there is nobody listening to messages addressed to the given account. +Rejects with `InvalidFieldsError` if required fields are missing from the message or malformed. +Rejects with `NotAcceptedError` if the message is rejected by the ledger. +Rejects with `NoSubscriptionsError` if the message cannot be delivered because there is nobody listening to messages addressed to the given account. ###### Example ```js -p.sendMessage({ - ledger: 'example.ledger.', - account: 'example.ledger.connector', - data: { foo: 'bar' } +p.sendRequest({ + to: 'example.ledger.connector', + ilp: '...base64url-encoded data...', + custom: { foo: 'bar' } }) ``` -For a detailed description of these properties, please see [`OutgoingMessage`](#outgoingmessage). +For a detailed description of these properties, please see [`Message`](#class-message). #### fulfillCondition -ledgerPlugin.fulfillCondition( **transferId**:String, **fulfillment**:Buffer ) ⇒ Promise.<null> +ledgerPlugin.fulfillCondition( **transferId**:String, **fulfillment**:String, **ilp**:String ) ⇒ Promise.<null> Submit a fulfillment to a ledger. Plugin must be connected, otherwise the promise should reject. -Throws `InvalidFieldsError` if the fulfillment is malformed. Throws `TransferNotFoundError` if the fulfillment -if no conditional transfer with the given ID exists. Throws `AlreadyRolledBackError` if the transfer has already been -rolled back. Throws `NotAcceptedError` if the fulfillment is formatted correctly, but does not match the condition -of the specified transfer. Throws `TransferNotConditionalError` if transfer is not conditional. +The `fulfillment` is an arbitrary 32-byte buffer and is provided as a base64url-encoded string. + +The `ilp` is an optional ILP packet that will transmitted alongside the fulfillment. It is provided as a base64url-encoded string. + +Rejects with `InvalidFieldsError` if the fulfillment is malformed. Rejects with `TransferNotFoundError` if the fulfillment +if no conditional transfer with the given ID exists. Rejects with `AlreadyRolledBackError` if the transfer has already been +rolled back. Rejects with `NotAcceptedError` if the fulfillment is formatted correctly, but does not match the condition +of the specified transfer. Rejects with `TransferNotConditionalError` if transfer is not conditional. #### rejectIncomingTransfer -ledgerPlugin.rejectIncomingTransfer( **transferId**:String, **rejectMessage**:Buffer ) ⇒ Promise.<null> +ledgerPlugin.rejectIncomingTransfer( **transferId**:String, **reason**:[RejectionMessage](#class-rejectionmessage) ) ⇒ Promise.<null> -Reject an incoming transfer that is held pending the fulfillment of its `executionCondition` before the `expiresAt` time. `rejectMessage` MAY be supplied to provide details on why the transfer was rejected. +Reject an incoming transfer that is held pending the fulfillment of its `executionCondition` before the `expiresAt` time. `reason` MAY be supplied to provide details on why the transfer was rejected. -Throws `TransferNotFoundError` if there is no conditional transfer with the -given ID. Throws `AlreadyFulfilledError` if the specified transfer has already been -fulfilled. Throws `NotAcceptedError` if you are not authorized -to reject the transfer (e.g. if you are the sender). Throws `TransferNotConditionalError` +Rejects with `TransferNotFoundError` if there is no conditional transfer with the +given ID. Rejects with `AlreadyFulfilledError` if the specified transfer has already been +fulfilled. Rejects with `NotAcceptedError` if you are not authorized +to reject the transfer (e.g. if you are the sender). Rejects with `TransferNotConditionalError` if transfer is not conditional. This MAY be used by receivers or connectors to reject incoming funds if they will not fulfill the condition or are unable to forward the payment. Previous hops in an Interledger transfer would have their money returned before the expiry and the sender or previous connectors MAY retry and reroute the transfer through an alternate path. +#### registerRequestHandler +ledgerPlugin.registerRequestHandler( **requestHandler**: ( request: [Message](#class-message) ) ⇒ Promise<[Message](#class-message)> ) ⇒ null + +Set the callback which is used to handle incoming request messages. The callback expects one parameter (the request [Message](#class-message)) and returns a promise for the response [Message](#class-message). + +If a request handler is already set, this method throws a `RequestHandlerAlreadyRegisteredError`. In order to change the request handler, the old handler must first be removed via [`deregisterRequestHandler`](#deregisterRequestHandler). This is to ensure that handler are not overwritten by accident. + +#### deregisterRequestHandler +ledgerPlugin.deregisterRequestHandler( ) ⇒ null + +Removes the currently used request handler. This has the same effect as if [`registerRequestHandler`](#registerrequesthandler) had never been called. + +If not request handler is currently set, this method does nothing. + ### Event: `*_transfer` ledgerPlugin.on('incoming_transfer', ( **transfer**:[Transfer](#class-transfer), ) ⇒ ) +ledgerPlugin.on('outgoing_transfer', + ( + **transfer**:[Transfer](#class-transfer), + ) ⇒ +) Emitted after an outgoing/incoming transfer which does not have a condition is executed on the ledger. @@ -320,10 +345,15 @@ to you. **transfer**:[Transfer](#class-transfer), ) ⇒ ) +ledgerPlugin.on('outgoing_prepare', + ( + **transfer**:[Transfer](#class-transfer), + ) ⇒ +) Emitted when an outgoing/incoming transfer containing a condition is prepared. -Note that the `*_prepare` event **DOES NOT** indicate that money has been transferred. The final status will only be known when either the [*_fulfill](#event-*_fulfill-) or [*_cancel](#event-*_cancel-) events are emitted. +Note that the `*_prepare` event **DOES NOT** indicate that money has been transferred. The final status will only be known when either the [*_fulfill](#event-_fulfill) or [*_cancel](#event-_cancel) events are emitted. The ledger plugin MUST authenticate the source for all incoming transfers, whether they include money or not. @@ -331,14 +361,22 @@ If the event is `outgoing_prepare`, then it means you prepared the transfer. `in a transfer to you. ### Event: `*_fulfill` +ledgerPlugin.on('incoming_fulfill', + ( + **transfer**:[Transfer](#class-transfer), + **fulfillment**:String, + **ilp**:String + ) ⇒ +) ledgerPlugin.on('outgoing_fulfill', ( **transfer**:[Transfer](#class-transfer), - **fulfillment**:Buffer + **fulfillment**:String, + **ilp**:String ) ⇒ ) -Emitted when an outgoing/incoming transfer with a condition is fulfilled. +Emitted when an outgoing/incoming transfer with a condition is fulfilled. The `fulfillment` and `ilp` are provided as a base64url-encoded string. This indicates that funds have been transferred. In order to prevent unexpected incoming funds, a ledger MAY forbid accounts from fulfilling a transfer who are not the transfer's receiver. @@ -347,10 +385,16 @@ If the event is `incoming_fulfill`, then it means you fulfilled the transfer. `o of your outgoing transfer has fulfilled the condition. ### Event: `*_reject` +ledgerPlugin.on('incoming_reject', + ( + **transfer**:[Transfer](#class-transfer), + **reason**:[RejectionMessage](#class-rejectionmessage) + ) ⇒ +) ledgerPlugin.on('outgoing_reject', ( **transfer**:[Transfer](#class-transfer), - **reason**:Buffer + **reason**:[RejectionMessage](#class-rejectionmessage) ) ⇒ ) @@ -363,10 +407,16 @@ If the event is `incoming_reject`, then it means you rejected the transfer. `out the receiver of your outgoing transfer has rejected it. ### Event: `*_cancel` +ledgerPlugin.on('incoming_cancel', + ( + **transfer**:[Transfer](#class-transfer), + **reason**:[RejectionMessage](#class-rejectionmessage) + ) ⇒ +) ledgerPlugin.on('outgoing_cancel', ( **transfer**:[Transfer](#class-transfer), - **reason**:Buffer + **reason**:[RejectionMessage](#class-rejectionmessage) ) ⇒ ) @@ -377,123 +427,151 @@ This will happen on a timeout, triggered by the ledger and not by the receiver. If the event is `incoming_cancel`, an incoming transfer was timed out by the ledger. `outgoing_cancel` means that a transfer you created has timed out. -### Event: `incoming_message` -ledgerPlugin.on('incoming_message', +### Event: `*_request` +ledgerPlugin.on('incoming_request', ( - **message**:[IncomingMessage](#incomingmessage), + **message**:[Message](#class-message), + ) ⇒ +) +ledgerPlugin.on('outgoing_request', + ( + **message**:[Message](#class-message), ) ⇒ ) -Emitted when an incoming message arrives from the ledger. - -## Class: Transfer -class Transfer - -The `Transfer` class is used to describe local ledger transfers. Only -[id](#id), [account](#account), [ledger](#ledger), and [amount](#amount) are required; the other -fields can be left undefined (but not any other false-y value) if unused. +Emitted when an incoming request message arrives from another ledger participant (`incoming_request`) or one is sent (`outgoing_request`). -###### Fields -| Type | Name | Description | -|:--|:--|:--| -| `String` | [id](#id) | UUID used as an external identifier | -| `String` | [account](#account) | ILP Address of the source or destination account | -| `String` | [ledger](#ledger) | ILP Address prefix of the ledger | -| `String` | [amount](#amount) | Decimal transfer amount | -| `Buffer` | [data](#data) | Data packet or memo to be sent with the transfer, starts with an ILP header | -| `Buffer` | [noteToSelf](#notetoself) | Host-provided memo that should be stored with the transfer | -| `String` | [executionCondition](#executioncondition) | Cryptographic hold condition | -| `String` | [expiresAt](#expiresat) | Expiry time of the cryptographic hold | -| `Object` | [custom](#custom) | Object containing ledger plugin specific options | +Hosts MUST NOT use these events to respond to requests. In order to provide responses, provide a request handler via [`registerRequestHandler`](#registerRequestHandler). Note that there can only be one request handler active for a plugin at a time, but an unlimited number of (passive) event listeners. +### Event: `*_response` +ledgerPlugin.on('incoming_response', + ( + **message**:[Message](#class-message), + ) ⇒ +) +ledgerPlugin.on('outgoing_response', + ( + **message**:[Message](#class-message), + ) ⇒ +) -### IncomingTransfer -class IncomingTransfer extends [Transfer](#class-transfer) +Emitted when a response message is sent (`outgoing_response`) or received (`incoming_response`). -`IncomingTransfer` objects describe transfers which are **received** by the ledger plugin. The `account` field refers to the local source account that the transfer originated from. +### Event: `info_change` +ledgerPlugin.on('info_change', + ( + **info**:[LedgerInfo](#class-ledgerinfo) + ) ⇒ +) -See [`Transfer`](#class-transfer) for more information. +Emitted any time the plugin's `LedgerInfo` cache changes. -### OutgoingTransfer -class OutgoingTransfer extends [Transfer](#class-transfer) +## Class: Transfer +class Transfer -`OutgoingTransfer` objects describe transfers which have been **sent** by the ledger plugin. The `account` field refers to the local destination account on the underlying ledger. +The `Transfer` class is used to describe local ledger transfers. Fields can be +left undefined (but not any other false-y value) if unused. -See [`Transfer`](#class-transfer) for more information. +###### Fields +| Type | Name | Description | +|:--|:--|:--| +| `String` | [id](#transferid) | UUID used as an external identifier | +| `String` | [from](#transferfrom) | ILP Address of the source account | +| `String` | [to](#transferto) | ILP Address of the destination account | +| `String` | [ledger](#transferledger) | ILP Address prefix of the ledger | +| `String` | [amount](#transferamount) | Integer transfer amount, in the ledger's base unit | +| `String` | [ilp](#transferilp) | Base64-encoded ILP packet | +| `Object` | [noteToSelf](#transfernotetoself-optional) | (Optional) host-provided memo that should be stored with the transfer | +| `String` | [executionCondition](#transferexecutioncondition) | Cryptographic hold condition | +| `String` | [expiresAt](#transferexpiresat) | Expiry time of the cryptographic hold | +| `Object` | [custom](#transfercustom-optional) | (Optional) object containing ledger plugin specific options | ### Fields -#### id +#### Transfer#id **id**:String External unique identifier used by the host. -For [`OutgoingTransfer`](#outgoingtransfer)s, the ID is chosen by the host. The ledger plugin MAY use a different identifier internally, but MUST fail if the external ID has already been used. In the case of a connector, the ID will be deterministically chosen from the hash of the ledger and transfer IDs of the inbound transfer that triggered this outbound transfer. - -For [`IncomingTransfer`](#incomingtransfer)s, the ID is chosen by the ledger plugin. The ledger plugin MAY use any type of UUID, but MUST ensure that the same transfer is always referred to by the same ID and that IDs are unique per ledger. +The ID is always chosen by the sending host. The ledger plugin MAY use a different identifier internally, but MUST fail if the external ID has already been used. In the case of a connector, the ID will be deterministically chosen from the hash of the ledger and transfer IDs of the inbound transfer that triggered this outbound transfer. Ledger plugins that support scalability (e.g. running multiple instances of a connector using the same settings) MUST ensure that external transfer IDs are unique **globally**, i.e. across all machines and instances. Otherwise a connector could accidentally process two outgoing payments for one incoming payment. -#### account +#### Transfer#account **account**:String The ILP Address of a local account. -#### ledger +**Deprecated:** Use [`from`](#from)/[`to`](#to) instead. + +#### Transfer#from +**from**:String + +The ILP Address of the source or debit account. + +#### Transfer#to +**to**:String + +The ILP Address of the destination or credit account. + +#### Transfer#ledger **ledger**:String ILP Address prefix of the ledger that this transfer is going through on. -#### amount +#### Transfer#amount **amount**:String -A decimal amount, represented as a string. MUST be positive. The supported precision is defined by each ledger plugin and can be queried by the host via [`getInfo`](#getinfo). The ledger plugin MUST throw an `InsufficientPrecisionError` if the given amount exceeds the supported level of precision. +An integer amount, represented as a string of base-ten digits. MUST be `>= 0` and `< 2^64`. -#### data -**data**:Buffer +#### Transfer#ilp +**ilp**:String -A buffer containing the data to be sent. Ledger plugins SHOULD treat this data as opaque, however it will usually start with an [ILP header](../0003-interledger-protocol/) followed by a transport layer header, a [quote request](../0008-interledger-quoting-protocol/) or a custom user-provided data packet. +An [ILP packet](https://interledger.org/rfcs/0003-interledger-protocol/draft-4.html#specification), denoting the payment's final destination. -If the `data` is too large, the ledger plugin MUST throw a `MaximumDataSizeExceededError`. If the `data` is too large only because the `amount` is insufficient, the ledger plugin MUST throw an `InsufficientAmountError`. +If the `ilp` data is too large, the ledger plugin MUST reject with a `MaximumIlpDataSizeExceededError`. -#### noteToSelf -**noteToSelf**:Buffer +#### Transfer#noteToSelf (OPTIONAL) +**noteToSelf**:Object -An optional bytestring containing details the host needs to persist with the transfer in order to be able to react to transfer events like condition fulfillment later. +An optional, arbitrary plain JavaScript object containing details the host needs to persist with the transfer in order to be able to react to transfer events like condition fulfillment later. Ledger plugins MAY attach the `noteToSelf` to the transfer and let the ledger store it. Otherwise it MAY use the [`store`](#store) in order to persist this field. Regardless of the implementation, the ledger plugin MUST ensure that all instances of the transfer carry the same `noteToSelf`, even across different machines. Ledger plugins MUST ensure that the data in the `noteToSelf` either isn't shared with any untrusted party or encrypted before it is shared. -#### executionCondition +#### Transfer#executionCondition **executionCondition**:String -A [cryptographic condition](../0002-crypto-conditions/) used for implementing holds. The underlying ledger MUST hold the transfer until the condition has been fulfilled or the `expiresAt` time has been reached. +A cryptographic challenge used for implementing holds. The underlying ledger MUST hold the transfer until the condition has been fulfilled or the `expiresAt` time has been reached. -Ledger plugins that do not support holds MUST throw an `HoldsNotSupportedError` if this parameter is provided. +Conditions are the base64url-encoded SHA-256 hash of a random, pseudo-random or deterministically generated 32-byte preimage called the fulfillment. -Ledger plugins that do support holds, but do not support the given condition type or bitmask MUST throw a `ExecutionConditionNotSupportedError`. +Ledger plugins that do not support holds MUST reject with an `HoldsNotSupportedError` if this parameter is provided. -#### expiresAt +#### Transfer#expiresAt **expiresAt**:String An ISO 8601 timestamp representing the expiry date for the transfer. -Ledger plugins that do not support holds or do not support expiries MUST throw an `ExpiryNotSupportedError` if this parameter is provided. +Ledger plugins that do not support holds or do not support expiries MUST reject with an `ExpiryNotSupportedError` if this parameter is provided. -#### custom +#### Transfer#custom (OPTIONAL) **custom**:Object -Ledger plugins MAY use this object to accept and/or set additional fields for other features they support. The object MUST be serializable, i.e. only plain JSON types are allowed anywhere in the object or sub-objects. +Optional object that ledger plugins MAY use to accept and/or set additional fields for other features they support. The object MUST be serializable, i.e. only plain JSON types are allowed anywhere in the object or sub-objects. + +If the `custom` data is too large, the ledger plugin MUST reject with a `MaximumCustomDataSizeExceededError`. ###### Example ``` js { id: '94adc29e-26cd-471b-987e-8d41e8773864', account: 'example.ledger.bob', + from: 'example.ledger.bob', + to: 'example.ledger.alice', ledger: 'example.ledger.', amount: '100', - data: /* ... */, noteToSelf: /* ... */, custom: { alternateAccount: 'bob-savings', @@ -510,31 +588,57 @@ The `Message` class is used to describe local ledger message. All fields are req ###### Fields | Type | Name | Description | |:--|:--|:--| -| `String` | account | ILP Address of the source or destination account | -| `String` | ledger | ILP Address prefix of the ledger | -| `Object` | data | Data packet to be sent with the message | +| `String` | [id](#messageid) | Unique message identifier | +| `String` | [from](#messagefrom) | ILP Address of the source account | +| `String` | [to](#messageto) | ILP Address of the destination account | +| `String` | [ledger](#messageledger) | ILP Address prefix of the ledger | +| `String` | [ilp](#messageilp) | (Optional if `custom` is present) base64-encoded ILP packet | +| `Object` | [custom](#messagecustom-optional) | (Optional) object containing ledger plugin specific options | + +#### Message#id +**id**:String + +Unique message identifier chosen by the sending host. Request and response messages contain the same ID. +#### Message#from +**from**:String -### IncomingMessage -class IncomingMessage extends [Message](#class-message) +The ILP Address of the source or debit account. -`IncomingMessage` objects describe messages which are **received** by the ledger plugin. The `account` field refers to the local source account that the message originated from. +#### Message#to +**to**:String -See [`Message`](#class-message) for more information. +The ILP Address of the destination or credit account. -### OutgoingMessage -class OutgoingMessage extends [Message](#class-message) +#### Message#ledger +**to**:String -`OutgoingMessage` objects describe messages which have been **sent** by the ledger plugin. The `account` field refers to the local destination account on the underlying ledger. +The ILP Prefix of the ledger being used to transfer the message. -See [`Message`](#class-message) for more information. +#### Message#ilp +**ilp**:String + +An [ILP packet](https://interledger.org/rfcs/0003-interledger-protocol/draft-4.html#specification), used for communication among ledger participants. +Include either this field, or the `custom` field, or both. + +If the `ilp` data is too large, the ledger plugin MUST reject with a `MaximumIlpDataSizeExceededError`. + +#### Message#custom (OPTIONAL) +**custom**:Object + +An optional, arbitrary plain JavaScript object containing additional custom data to be sent. The object MUST be serializable to JSON. Ledger plugins SHOULD treat this data as opaque. + +If the `custom` data is too large, the ledger plugin MUST reject with a `MaximumCustomDataSizeExceededError`. ###### Example ``` js { account: 'example.ledger.bob', + from: 'example.ledger.alice', + to: 'example.ledger.bob', ledger: 'example.ledger.', - data: { /* ... */ } + ilp: '', + custom: { /* ... */ } } ``` @@ -546,38 +650,60 @@ Metadata describing the ledger. This data is returned by the [`getInfo`](#getinf ###### Fields | Type | Name | Description | |:--|:--|:--| -| `Number` | [precision](#precision) | Total number of digits allowed | -| `Number` | [scale](#scale) | Digits allowed after decimal | -| `String` | [currencyCode](#currencycode) | ISO three-letter currency code | -| `String` | [currencySymbol](#currencysymbol) | UTF8 currency symbol | -| `ConnectorInfo[]` | [connectors](#connectors) | Recommended connectors | +| `String` | [prefix](#ledgerinfoprefix) | The plugin's ILP address prefix | +| `String` | [currencyCode](#ledgerinfocurrencycode) | [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) three-letter currency code | +| `Number` | [currencyScale](#ledgerinfocurrencyscale) | Integer `(..., -2, -1, 0, 1, 2, ...)`, such that one of the ledger's base units equals `10^- ` | +| `String[]` | [connectors](#ledgerinfoconnectors) | ILP addresses of recommended connectors | +| `String` | [minBalance](#ledgerinfominbalance-optional) | Integer String, for instance `"0"`, indicating the minimum balance. Optional, defaults to zero. | +| `String` | [maxBalance](#ledgerinfomaxbalance-optional) | Integer String, for instance `"1000000000000"`, indicating the maximum balance. Optional, defaults to plus infinity. | ### Fields -#### precision -**precision**:Number +#### LedgerInfo#prefix +**prefix**:String -The total number of digits (base 10) of precision allowed by the ledger. +The ledger plugin's ILP address prefix. This is used to determine whether a given ILP address is local to this ledger plugin and thus can be reached using this plugin's `sendTransfer` method. -#### scale -**scale**:Number - -The number of digits allowed after the decimal point. +The prefix may be configured, automatically detected, or hard-coded, depending on the ledger. For example, a Bitcoin ledger plugin may have the address hard-coded, while a [`five-bells-ledger`](https://github.com/interledger/five-bells-ledger) would use an API call to get the prefix. -#### currencyCode +#### LedgerInfo#currencyCode **currencyCode**:String -The ISO 4217 currency code (if any) used by the ledger. +The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code (if any) used by the ledger. A custom all-caps three-letter code, not used by ISO 4217, otherwise. +Ledger administrators who choose a custom currency code MAY request a custom currency symbol for their chosen currency code be listed by software modules that map currency codes to currency symbols, +for instance on node package manager (npm) in the case of JavaScript. +To translate an integer amount or balance from the ledger, the currencyCode by itself is not enough. It has to be used in combination with the currencyScale (below) to determine how many +of the ledger's base units correspond to one currency unit. + +#### LedgerInfo#currencyScale +**currencyScale**:String -#### currencySymbol -**currencySymbol**:String +The order of magnitude to express one full currency unit in ledger's base units. For instance, if the integer values represented on the ledger are to be interpreted as +dollar-cents (for the purpose of settling a user's account balance, for instance), then the ledger's +currencyCode is `USD` and its currencyScale is `2`. -The currency symbol as one or more UTF8 characters. +#### LedgerInfo#connectors +**connectors**:String[] -#### connectors -**connectors**:[ConnectorInfo](#class-connectorinfo)[] +The ILP addresses of recommended connectors. -The list of recommended connectors. +#### LedgerInfo#minBalance (OPTIONAL) +**minBalance**:String + +A minimum balance limits how much the ledger trusts the account holder. +This field is optional; when not present, the minimum balance should be assumed to be `0`. +When a plugin does return a `minBalance` field, it should be an Integer String, measured in the ledger's base unit, +comparable to the `balance` Integer Strings for which the `getBalance` method returns a Promise. +Applications using the plugin can expect transfers to fail if they would make the balance go below the minimum. + +#### LedgerInfo#maxBalance (OPTIONAL) +**maxBalance**:String + +A maximum balance limits how much the account holder trusts the ledger. +This field is optional; when not present, the maximum balance should be assumed to be `+Infinity`. +When a plugin does return a `maxBalance` field, it should be an Integer String, measured in the ledger's base unit, +comparable to the `balance` Integer Strings for which the `getBalance` method returns a Promise. +Applications using the plugin can expect transfers to fail if they would make the balance exceed the maximum. ## Class: PluginOptions class PluginOptions @@ -590,11 +716,11 @@ an underscore, and listed in the table below. ###### Special Fields | Type | Name | Description | |:--|:--|:--| -| `Object` | [_store](#_store) | Persistence layer callbacks | +| `Object` | [_store](#pluginoptions-_store) | Persistence layer callbacks | ### Fields -#### _store +#### PluginOptions#_store **_store**:Object Provides callback hooks to the host's persistence layer. @@ -621,58 +747,34 @@ Method names are based on the popular LevelUP/LevelDOWN packages. } ``` -## Class: ConnectorInfo -class ConnectorInfo - -Data about recommended connectors included in [`LedgerInfo`](#class-ledgerinfo). - -###### Fields -| Type | Name | Description | -|:--|:--|:--| -| `String` | id | Account URI | -| `String` | name | Account name | -| `String` | connector | Connector URI | - -### Fields - -#### id -**id**:String - -The connector's account URI on the ledger. - -#### name -**name**:String - -The connector's account name on the ledger. - -#### connector -**connector**:String - -The URI of the connector. - -###### Example -```js -{ - "id": "http://usd-ledger.example/accounts/chloe", - "name": "chloe", - "connector": "http://usd-eur-connector.example" -} -``` - ## Class: ConnectOptions class ConnectOptions ###### Fields | Type | Name | Description | |:--|:--|:--| -| `Number` | timeout | milliseconds | +| `Number` | [timeout](#connectoptions-timeout) | milliseconds | ### Fields -#### id +#### ConnectOptions#timeout **timeout**:Number The number of milliseconds that the plugin should spend trying to connect before giving up. If falsy, use the plugin's default timeout. If `Infinity`, there is no timeout. + +## Class: RejectionMessage +class RejectionMessage + +###### Fields +| Field | Type | Description | +|:------------------|:------------|:------------| +| `code` | String | Machine-readable error code | +| `name` | String | Human-readable description of the error code | +| `message` | String | Description of the error | +| `triggered_by` | ILP Address or ILP Prefix | ILP address or ledger prefix from which the rejection originates | +| `forwarded_by` | ILP Address | (optional) The address of the last connector to forward the rejection | +| `triggered_at` | GeneralizedTime | (optional) The time the rejection occurred. | +| `additional_info` | Object | Additional details about the error | diff --git a/0005-optimistic-transport-protocol/0005-optimistic-transport-protocol.md b/0005-optimistic-transport-protocol/0005-optimistic-transport-protocol.md index 80d4f2f1..cef34197 100644 --- a/0005-optimistic-transport-protocol/0005-optimistic-transport-protocol.md +++ b/0005-optimistic-transport-protocol/0005-optimistic-transport-protocol.md @@ -1 +1,4 @@ -**What was formerly known as the "Optimistic Transport Protocol" has been merged into the [Interledger Protocol](../0003-interledger-protocol).** +--- +deprecated: true +--- +**What was formerly known as the "Optimistic Transport Protocol" has been removed from the standard Interledger Protocol stack.** diff --git a/0006-universal-transport-protocol/0006-universal-transport-protocol.md b/0006-universal-transport-protocol/0006-universal-transport-protocol.md index 8590b3fb..04f4c32e 100644 --- a/0006-universal-transport-protocol/0006-universal-transport-protocol.md +++ b/0006-universal-transport-protocol/0006-universal-transport-protocol.md @@ -1 +1,4 @@ +--- +deprecated: true +--- **What was formerly known as the "Universal Transport Protocol" has been merged into the [Interledger Protocol](../0003-interledger-protocol).** diff --git a/0007-atomic-transport-protocol/0007-atomic-transport-protocol.md b/0007-atomic-transport-protocol/0007-atomic-transport-protocol.md index c2d5be57..390be688 100644 --- a/0007-atomic-transport-protocol/0007-atomic-transport-protocol.md +++ b/0007-atomic-transport-protocol/0007-atomic-transport-protocol.md @@ -1,3 +1,6 @@ +--- +deprecated: true +--- **What was formerly known as the "Atomic Transport Protocol" has been removed from the standard Interledger Protocol stack.** The Atomic mode outlined in the Interledger whitepaper may be used within segments of an Interledger payment path amongst groups of connectors and ledgers that support the required functionality. In an open system of interconnected ledgers, it seems unlikely that all parties in a payment path would have commonly trusted entities between them that could serve as notaries. The protocols used within specific groups need not be standardized because the failure to select commonly trusted notaries would prevent interoperability anyway. diff --git a/0008-interledger-quoting-protocol/0008-interledger-quoting-protocol.md b/0008-interledger-quoting-protocol/0008-interledger-quoting-protocol.md index 6988b90d..99a5fa34 100644 --- a/0008-interledger-quoting-protocol/0008-interledger-quoting-protocol.md +++ b/0008-interledger-quoting-protocol/0008-interledger-quoting-protocol.md @@ -1,136 +1,115 @@ +--- +title: The Interledger Quoting Protocol (ILQP) +draft: 4 +--- # Interledger Quoting Protocol (ILQP) The Interledger Quoting Protocol is a method of getting quote information from a Connector in preparation for arranging transfers across two ledgers. The quote returned by a Connector is non-binding, but provides a basis for choosing which connectors to use. There are two consumers of the ILQP: sending clients, and other connectors. -The JSON interface for the ILQP is a temporary measure. Eventually, it should be replaced completely by a binary version. - ## Background and Terminology The quoting protocol returns the price of a _hypothetical_ payment through the connector being queried. However, no part of the ILQP initiates or creates an actual payment. All the information in the ILQP is non-binding and advisory. ILQP only calculates and reports the expected cost of a payment. -Accounts in ILQP are identified by their ILP Addresses. For a full explanation of ILP Addresses, see [Proposal: ILP Address Mapping](https://github.com/interledger/rfcs/issues/31) and [Fowarding/Delivery Distinction](https://github.com/interledger/rfcs/issues/77). - - +Accounts ILQP are identified by their ILP Addresses. For a full explanation of ILP Addresses, see [ILP Addresses](../0015-ilp-addresses/0015-ilp-addresses.md). -ILP payments can as simple as two transfers in different ledgers joined by a single connector. In other cases, they may be longer chains with multiple connectors across three or more ledgers. The response of an ILQP request only defines one link in the chain. +ILP payments can be as simple as two transfers in different ledgers joined by a single connector. In other cases, they may be longer chains with multiple connectors across three or more ledgers. The response of an ILQP request combines the exchange rates of all links in the chain into one quote. **Ledgers:** * The **source ledger** is where the sender and the connector both have accounts. -* The **destination ledger** is where the receiver and the connector both have accounts. +* The **destination ledger** is where the receiver and the last connector both have accounts. -The sending and receiving ledger are different ledgers. Otherwise, there should be no reason to use ILQP. +The source and destination ledger are different ledgers. Otherwise, there should be no reason to use ILQP. **Parties:** -* The **sender** is the party being debited at the start of the overall chain. -* The **source** is the party being debited in a single link of the chain. -* The **receiver** is the party being credited at the end of the overall chain. -* The **destination** is the party being credited in a single link of the chain. -* A **connector** facilitates the payment between the source and destination in a single link. In a multiple-hop payment, there are multiple connectors, each of which is the destination of one link and the source of the next. +* The **sender** is the party who would be debited on the source ledger by the hypothetical payment. +* The **receiver** is the party being credited on the destination ledger by the hypothetical payment. +* A **connector** forms the active link between an incoming transfer on one ledger and an outgoing transfer on the next. + +In a multiple-hop payment, there are multiple connectors, each of which creates an outgoing transfer in response to an incoming transfer. If the first connector in the chain has full (cached) pricing information, it will respond to the sender's quote request immmediately. If not, it will forward the quote request along the chain, and then relay the response it gets back. + +> The following description of ILQP documents the current behaviour of nodes on the live Interledger, the majority of which are running [ILP Kit](https://github.com/interledgerjs/ilp-kit) software. ## Get Quote -Quotes are requested with `plugin.sendMessage(message)` and returned via `plugin.on("incoming_message")` (see the Ledger Plugin Interface for more details). -The message formats described here are the `data` properties of an `OutgoingMessage`. - -### Request Message Format - -| Field | Type | Description | -|:---------|:----------------|:------------| -| `method` | String | `"quote_request"` | -| `id` | String | A UUID to match this request to the corresponding response. | -| `data` | QuoteParameters | An Object describing the quote parameters. | - -##### QuoteParameters - -| Field | Type | Description | -|:------------------------------|:------------|:------------| -| `source_address` | ILP Address | Address of the source's account in the source ledger. | -| `destination_address` | ILP Address | Address of the destination's account in the destination ledger. | -| `source_amount` | String | Fixed amount to debit from the source's account. (Required unless `destination_amount` specified.) | -| `destination_amount` | String | Fixed amount to credit to the destination's account. (Required unless `source_amount` specified.) | -| `destination_expiry_duration` | String | (Optional) Number of milliseconds before the transfer in the destination ledger expires. | -| `source_expiry_duration` | String | (Optional) Number of milliseconds before the transfer in the source ledger expires. | -| `slippage` | String | (Optional) Custom slippage to assume for the exchange between ledgers, as a decimal proportion. Lower slippage means the payment is cheaper but more likely to fail due to market movement. | - -The request must specify either `source_amount` or `destination_amount` but not both. - -##### Example request - -```js -{ - "method": "quote_request", - "id": "721e4126-98a1-4974-b35a-8a8f4655f934", - "data": { - "source_amount": "100.25", - "source_address": "example.eur-ledger.alice", - "destination_address": "example.usd-ledger.bob", - "source_expiry_duration": "6000", - "destination_expiry_duration": "5" - } -} -``` - -### Response Message Format (success) - -| Field | Type | Description | -|:---------|:---------------|:------------| -| `method` | String | `"quote_response"` or `"error"` | -| `id` | String | A UUID to match this request to the corresponding `quote_request`. | -| `data` | Quote or Error | An Object describing the quote (when method=`quote_response`) or the error (when method=`error`). | - -##### Quote (method = `"quote_response"`) - -| Field | Type | Description | -|:------------------------------|:------------|:------------| -| `source_connector_account` | ILP Address | The address of the connector's account in the source ledger where it should receive the first transfer. | -| `source_ledger` | ILP Address | The address of the ILP-enabled ledger where the source account is. | -| `source_amount` | String | Decimal number amount of currency the connector's account should receive in the source ledger. | -| `source_expiry_duration` | String | Integer number of milliseconds between when the payment in the source ledger is prepared and when it must be executed. | -| `destination_ledger` | ILP Address | The address of the ILP-enabled ledger where the destination account is. | -| `destination_amount` | String | Decimal number amount of currency that should be received by the destination account in the destination ledger. | -| `destination_expiry_duration` | String | Integer number of milliseconds between when the payment in the destination ledger is prepared and when it must be executed. | - -##### Example response (method = `"quote_response"`) - -```js -{ - "method": "quote_response", - "id": "721e4126-98a1-4974-b35a-8a8f4655f934", - "data": { - "source_connector_account": "mark", - "source_ledger": "example.eur-ledger.", - "source_amount": "100.25", - "source_expiry_duration": "6000", - "destination_ledger": "example.usd-ledger.", - "destination_amount": "105.71", - "destination_expiry_duration": "5000" - } -} -``` - -##### Error (method = `"error"`) - -| Field | Type | Description | -|:----------|:-------|:------------| -| `id` | String | The type of error. | -| `message` | String | Additional details about the nature of the error. | - -##### Example response (method = `"error"`) - -```js -{ - "method": "error", - "id": "721e4126-98a1-4974-b35a-8a8f4655f934", - "data": { - "id": "InvalidBodyError", - "message": "Missing required parameter: source_address" - } -} -``` +Quotes are sent through a request/response mechanism exposed by the ledger plugins or ledger layer. + +A quote request's `ilp` property must be a `QuoteLiquidityRequest`, `QuoteBySourceRequest`, or `QuoteByDestinationRequest`. The response's `ilp` property must be a `QuoteLiquidityResponse`, `QuoteBySourceResponse`, or `QuoteByDestinationResponse` respectively. If an error occurs during quoting, `ilp` will be a [`IlpError`](../0003-interledger-protocol/0003-interledger-protocol.md#ilp-error-format) instead. + +## ILQP Packets + +See [interledgerjs/ilp-packet](https://github.com/interledgerjs/ilp-packet/) for an example implementation of a packet serializer/deserializer. + +### QuoteLiquidityRequest + +| Field | Type | Short Description | +|:--|:--|:--| +| type | Byte | Always `2` | +| length | Length Determinant | One or more bytes, indicating how many bytes follow in the rest of the packet | +| destinationAccount | Length-prefixed String | Address corresponding to the destination account | +| destinationHoldDuration | UInt32 | How much time the receiver needs to fulfill the payment (in milliseconds) | +| extensions | Length Determinant | Always `0` | + +### QuoteLiquidityResponse + +| Field | Type | Short Description | +|:--|:--|:--| +| type | Byte | Always `3` | +| length | Length Determinant | One or more bytes, indicating how many bytes follow in the rest of the packet | +| liquidity | LiquidityCurve | Curve describing the liquidity for the quoted route | +| appliesToPrefix | Length-prefixed String | Common prefix of all addresses for which this liquidity curve applies. | +| sourceHoldDuration | UInt32 | How long the sender should put the money on hold (in milliseconds) | +| expiresAt | GeneralizedTime | Maximum time where the connector expects to be able to honor this liquidity curve. This MUST be expressed in the UTC + 0 (Z) timezone. | +| extensions | Length Determinant | Always `0` | + +`LiquidityCurve` is encoded as a `SEQUENCE OF SEQUENCE { x UInt64, y UInt64 }`. This is a binary format, so for example the curve `[ [0, 0], [10, 265] ]` is equivalent to the base64-encoded string `"AAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAkBAAA="`. + +See [interledgerjs/ilp-routing.LiquidityCurve](https://github.com/interledgerjs/ilp-routing/blob/master/src/lib/liquidity-curve.js) for an example implementation of a LiquidityCurve serializer/deserializer. + +### QuoteBySourceRequest + +| Field | Type | Short Description | +|:--|:--|:--| +| type | Byte | Always `4` | +| length | Length Determinant | One or more bytes, indicating how many bytes follow in the rest of the packet | +| destinationAccount | Length-prefixed String | Length-prefixed address corresponding to the destination account | +| sourceAmount | UInt64 | Amount the sender needs to send, denominated in the asset of the source ledger | +| destinationHoldDuration | UInt32 | How much time the receiver needs to fulfill the payment (in milliseconds) | +| extensions | Length Determinant | Always `0` | + +### QuoteBySourceResponse + +| Field | Type | Short Description | +|:--|:--|:--| +| type | Byte | Always `5` | +| length | Length Determinant | One or more bytes, indicating how many bytes follow in the rest of the packet | +| destinationAmount | UInt64 | Amount that will arrive at the receiver | +| sourceHoldDuration | UInt32 | How long the sender should put money on hold (in milliseconds) | +| extensions | Length Determinant | Always `0` | + +### QuoteByDestinationRequest + +| Field | Type | Short Description | +|:--|:--|:--| +| type | Byte | Always `6` | +| length | Length Determinant | One or more bytes, indicating how many bytes follow in the rest of the packet | +| destinationAccount | Length-prefixed String | Length-prefixed address corresponding to the destination account | +| destinationAmount | UInt64 | Amount that will arrive at the receiver | +| destinationHoldDuration | UInt32 | How much time the receiver needs to fulfill the payment (in milliseconds) | +| extensions | Length Determinant | Always `0` | + +### QuoteByDestinationResponse + +| Field | Type | Short Description | +|:--|:--|:--| +| type | Byte | Always `7` | +| length | Length Determinant | One or more bytes, indicating how many bytes follow in the rest of the packet | +| sourceAmount | UInt64 | Amount the sender needs to send, denominated in the asset of the source ledger | +| sourceHoldDuration | UInt32 | How long the sender should put money on hold (in milliseconds) | +| extensions | Length Determinant | Always `0` | ## Next Steps @@ -138,4 +117,4 @@ After getting a quote using ILQP, the client can display the quote to the user a ## Appendix A: ASN.1 Module -The [InterledgerQuotingProtocol.asn](InterledgerQuotingProtocol.asn) ASN.1 module tentatively describes the binary version of ILQP. This is not final. +The [InterledgerQuotingProtocol.asn](../asn1/InterledgerQuotingProtocol.asn) ASN.1 module describes the binary ILQP messages. diff --git a/0009-simple-payment-setup-protocol/0009-simple-payment-setup-protocol.md b/0009-simple-payment-setup-protocol/0009-simple-payment-setup-protocol.md index 6a77c367..09f766a7 100644 --- a/0009-simple-payment-setup-protocol/0009-simple-payment-setup-protocol.md +++ b/0009-simple-payment-setup-protocol/0009-simple-payment-setup-protocol.md @@ -1,18 +1,22 @@ +--- +title: The Simple Payment Setup Protocol (SPSP) +draft: 4 +--- # Simple Payment Setup Protocol (SPSP) ## Preface -This document describes the Simple Payment Setup Protocol (SPSP), a basic protocol for exchanging payment information between senders and recipients to set up an Interledger payment. +This document describes the Simple Payment Setup Protocol (SPSP), a basic protocol for exchanging payment information between senders and receivers to set up an Interledger payment. SPSP uses the [Pre-Shared Key (PSK)](../0016-pre-shared-key/0016-pre-shared-key.md) transport protocol for condition generation and data encoding. -## Introduction +## Introduction ### Motivation -The Interledger Protocol does not specify how payment details, such as the ILP Packet or Crypto Condition, should be exchanged between the sender and recipient. SPSP is a minimal protocol that uses HTTP for communicating these details. +The Interledger Protocol does not specify how payment details, such as the ILP Packet or Crypto Condition, should be exchanged between the sender and receiver. SPSP is a minimal protocol that uses HTTPS for communicating these details. ### Scope -SPSP provides for exchanging basic payment details needed by a sender to confirm the details of and set up an ILP payment. It is intended for use by end-user applications. +SPSP provides for exchanging basic receiver details needed by a sender to set up an ILP payment. It is intended for use by end-user applications. ### Interfaces @@ -22,89 +26,48 @@ SPSP messages MUST be exchanged over HTTPS. ### Operation -Any SPSP recipient will create a receiving HTTP endpoint called the *receiver*. The sender can query this endpoint to get information about the type of payment that can be made to this receiver. The sender also uses the receiver endpoint to set up the ILP payment. +Any SPSP receiver will run an SPSP server and expose an HTTPS endpoint called the SPSP Endpoint. The sender can query this endpoint to get information about the type of payment that can be made to this receiver. The sender can set up and send multiple ILP payments using the details provided by the receiver. ### Definitions -#### SPSP Client - -The sender application that uses SPSP to interact with the SPSP Server. - -#### SPSP Server - -The server used on the recipient's side to handle SPSP requests. - -#### Receiver - -The specific HTTP endpoint on the SPSP used for setting up a payment. +* **SPSP Client** - The sender application that uses SPSP to interact with the SPSP Server +* **SPSP Server** - The server used on the receiver's side to handle SPSP requests +* **SPSP Endpoint** - The specific HTTPS endpoint on the SPSP server used for setting up a payment +* **PSK Module** - Software included in the SPSP Client and Server that implements the [Pre-Shared Key](../0016-pre-shared-key/0016-pre-shared-key.md) protocol ## Overview ### Relation to Other Protocols -SPSP is used for exchanging payment information before an ILP payment is initiated. It uses the receiver generated [Interledger Payment Requests](../0011-interledger-payment-request) over HTTP. +SPSP is used for exchanging payment information before an ILP payment is initiated. The sender and receiver use the [Pre-Shared Key (PSK)](../0016-pre-shared-key/0016-pre-shared-key.md) transport protocol to generate the condition and fulfillment. The receiver generates the shared secret to be used in PSK and communicates it to the sender over HTTPS. ### Model of Operation -#### Fixed Destination Amount - -SPSP may be used by the sender to send a payment such that the recipient receives a specific amount of their chosen currency or asset type. The model of operation is illustrated with the following example: - -We assume that the sender knows the receiver endpoint (see [Appendix A: (Optional) Webfinger Discovery](#appendix-a-optional-webfinger-discovery)). - -1. The sender's SPSP client queries the receiver endpoint. -2. The receiver endpoint responds with the receiver info, including the receiver's currency code. -3. The sender chooses an amount of the receiver's asset to deliver. -4. The sender's SPSP client submits the payment information, including the destination amount, to the receiver endpoint. -5. The receiver endpoint responds with an [Interledger Payment Request](../0011-interledger-payment-request), which includes the execution condition. -6. The sender's SPSP client uses its ILP module to get a quote in their currency or asset type for the ILP transfer. -7. The sender accepts the quote. -8. The sender's SPSP client uses its ILP module to initiate the ILP payment. -9. The receiver's ILP module registers the incoming transfer held pending the fulfillment of the Crypto Condition. It validates that the transfer matches the packet and regenerates the condition fulfillment using the method recommended in the [Interledger Payment Request](../0011-interledger-payment-request) spec. The ILP module submits the fulfillment to execute the transfer and claim the funds. -10. The sender's SPSP client receives a notification from its ILP module that the transfer has been executed, including the condition fulfillment from the recipient, and notifies the sender that the payment is completed. - -#### Fixed Source Amount +We assume that the sender knows the receiver's SPSP endpoint (see [Appendix A: (Optional) Webfinger Discovery](#appendix-a-optional-webfinger-discovery)). -SPSP may be used by the sender to send a payment of a fixed amount of the sender's chosen currency or asset type. This is illustrated with the following example: - -We assume that the sender knows the receiver endpoint (see [Appendix A: (Optional) Webfinger Discovery](#appendix-a-optional-webfinger-discovery)). - -1. The sender's SPSP client queries the receiver endpoint. -2. The receiver endpoint responds with the receiver info, including the recipient's ILP address. -3. The sender choses an amount of their currency or assets to send. -4. The sender's SPSP client uses its ILP module to quote how much of the recipient's currency or asset type will be delivered to the recipient's ILP address for the given source amount. -5. The sender accepts the quote. -6. The sender's SPSP client submits the payment information, including the destination amount, to the receiver endpoint. -7. The receiver endpoint responds with an [Interledger Payment Requests](../0011-interledger-payment-request), which includes the execution condition. -8. The sender's SPSP client uses its ILP module to create a transfer **using the chosen source amount, NOT by quoting the payment request**, and attaches the ILP packet to the transfer. -9. The receiver's ILP module registers the incoming transfer held pending the fulfillment of the Crypto Condition. It validates that the transfer matches the packet and regenerates the condition fulfillment using the method recommended in the [Interledger Payment Request](../0011-interledger-payment-request) spec. The ILP module submits the fulfillment to execute the transfer and claim the funds. -10. The sender's SPSP client receives a notification from its ILP module that the transfer has been executed, including the condition fulfillment from the recipient, and notifies the sender that the payment is completed. - -#### Invoice - -1. The sender's SPSP client queries the receiver endpoint. -2. The receiver endpoint responds with the receiver info, including the invoice status, amount, and currency code. -3. The sender's SPSP client submits the sender's info, including the sender's ILP address to the receiver endpoint. -4. The receiver endpoint responds with an [Interledger Payment Request](../0011-interledger-payment-request) corresponding to the destination amount. -5. The sender's SPSP client uses its ILP module to get a quote in their currency or asset type for the ILP transfer. -6. The sender accepts the quote. -7. The sender's SPSP client uses its ILP module to initiate the ILP transfer. -8. The receiver's ILP module registers the incoming transfer held pending the fulfillment of the Crypto Condition. It validates that the transfer matches the packet and regenerates the condition fulfillment using the method recommended in the [Interledger Payment Request](../0011-interledger-payment-request) spec. The ILP module submits the fulfillment to execute the transfer and claim the funds. -9. The sender's SPSP client receives a notification from its ILP module that the transfer has been executed, including the condition fulfillment from the recipient, and notifies the sender that the payment is completed. +1. The sender's SPSP Client queries the receiver's SPSP Endpoint. +2. The SPSP Endpoint responds with the receiver info, including the receiver's ILP address and the shared secret to be used in PSK. +3. The sender constructs an ILP payment using the receiver's ILP address. +4. The sender uses PSK to generate the payment condition and format additional data intended for the reciever to be sent with the payment. +5. The sender prepares a local transfer to a connector with the condition and ILP packet attached. +6. The receiver's PSK module registers the incoming transfer, parses and validates the ILP packet and the incoming transfer. +7. The receiver MAY submit the incoming payment to an external system for review to ensure that the funds are wanted. +8. If the payment is expected, the receiver's PSK module submits the condition fulfillment to the ledger to execute the transfer. If not, the PSK module rejects the incoming transfer. +9. If the receiver executed the transfer, the sender's SPSP client receives a notification from its ILP module that the transfer has been executed, including the condition fulfillment from the receiver, and notifies the sender that the payment is completed. If the receiver rejected the transfer, the sender's SPSP client receives a notification with the receiver-specified error message detailing why the payment was rejected. ## Specification -The receiver endpoint is a URI used by the sender to query information about the sender and set up payments. The receiver URI MAY contain query string parameters. The sender SHOULD treat the URI as opaque. +The SPSP endpoint is a URI used by the sender to query information about the receiver and set up payments. The SPSP endpoint URI MAY contain query string parameters. The sender SHOULD treat the URI as opaque. -The receiver endpoint MUST respond to HTTP `GET` and `POST` requests in the following manner: +The SPSP Endpoint MUST respond to HTTPS `GET` requests in the following manner: -### Query (`GET `) +### Query (`GET `) -The sender queries the receiver endpoint to get information about the type of payment that can be made to this receiver: +The sender queries the SPSP endpoint to get information about the type of payment that can be made to this receiver: #### Request ``` http -GET /api/receivers/bob HTTP/1.1 +GET /api/spsp/bob HTTP/1.1 Host: red.ilpdemo.org Accept: application/json ``` @@ -115,166 +78,95 @@ HTTP/1.1 200 OK Content-Type: application/json { - "type": "payee", - "account": "ilpdemo.red.bob", - "currency_code": "USD", - "currency_symbol": "$", - "name": "Bob Dylan", - "image_url": "https://red.ilpdemo.org/api/receivers/bob/profile_pic.jpg" + "destination_account": "example.ilpdemo.red.bob", + "shared_secret": "6jR5iNIVRvqeasJeCty6C-YB5X9FhSOUPCL_5nha5Vs", + "maximum_destination_amount": "100000", + "minimum_destination_amount": "10", + "ledger_info": { + "currency_code": "USD", + "currency_scale": 2 + }, + "receiver_info": { + "name": "Bob Dylan", + "image_url": "https://red.ilpdemo.org/api/spsp/bob/profile_pic.jpg" + } } ``` [Try this request](https://red.ilpdemo.org/api/receivers/bob) -Possible values for `type` are: +##### Response Headers -`payee` -: This is a general receiving account for peer-to-peer payments. +The response MUST contain at least the following headers: -`invoice` -: This is an invoice, meaning it can be paid only once and only with a specific amount. +| Header | Description | +|:----------------|:-----------------------------------------------------------| +| `Content-Type` | MUST be `application/json` to indicates the response is encoded as [JSON](http://www.json.org/). | +| `Cache-Control` | Indicates how long the SPSP Client should cache the response. See supported cache-control directives below. | -##### Payee +To handle as many transactions per second as possible, the SPSP Client caches results from the SPSP Server. The information returned by the SPSP Server is not expected to change rapidly, so repeated requests for the same information are usually redundant. The server communicates how long to cache results for using the HTTP-standard [`Cache-Control` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) in the responses to RESTful API calls. -Payee information consists of basic account details. Amounts are chosen by the sender. +The SPSP Client understands the following Cache-Control directives: -Example Receiver: -``` json -{ - "type": "payee", - "account": "ilpdemo.red.bob", - "currency_code": "USD", - "currency_symbol": "$", - "name": "Bob Dylan", - "image_url": "https://red.ilpdemo.org/api/receivers/bob/profile_pic.jpg" -} -``` +| Directive | Description | +|:--------------|:-------------------------------------------------------------| +| `max-age=` | The client should cache this response for `` seconds. `` MUST be a positive integer | +| `no-cache` | The client must not cache this response | -| Field | Type | Description | -|---|---|---| -| `type` | `"payee"` | Receiver type | -| `account` | ILP Address | ILP Address of the recipient's account | -| `currency_code` | String | Currency code to identify the receiver's currency. Currencies that have [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) codes should use those. Sender UIs SHOULD be able to render non-standard codes | -| `currency_symbol` | String | Symbol for the receiver's currency intended for display in the sender's UI (e.g. `"$"` or `"shares"`). Sender UIs SHOULD be able to render non-standard symbols | -| `name` | String | Full name of the individual, company or organization the receiver represents | -| `image_url` | HTTPS URL | URL that a picture of the recipient can be fetched from. The image MUST be square and SHOULD be 128x128 pixels. | - -If this receiver is not available, an error can be generated at this stage: - -``` http -HTTP/1.1 404 Not Found -Content-Type: application/json - -{ - "id": "InvalidReceiverIdError", - "message": "Invalid receiver ID" -} -``` - -##### Invoice +##### Response Body -Invoice information includes an exact amount as well as the status of the invoice. (Invoices can only be paid once.) - -Example Receiver: -``` json -{ - "type": "invoice", - "account": "ilpdemo.red.amazon.111-7777777-1111111", - "currency_code": "USD", - "currency_symbol": "$", - "amount": "10.40", - "status": "unpaid", - "invoice_info": "https://www.amazon.com/gp/your-account/order-details?ie=UTF8&orderID=111-7777777-1111111" -} -``` +The response body is a JSON object that includes basic account details necessary for setting up payments. | Field | Type | Description | |---|---|---| -| `type` | `"invoice"` | Receiver type | -| `account` | ILP Address | ILP Address of the recipient's account | -| `currency_code` | String | Currency code to identify the receiver's currency. Currencies that have [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) codes should use those. Sender UIs SHOULD be able to render non-standard codes | -| `currency_symbol` | String | Symbol for the receiver's currency intended for display in the sender's UI (e.g. `"$"` or `"shares"`). Sender UIs SHOULD be able to render non-standard symbols | -| `amount` | Decimal String | Value of the invoice in the recipient's currency | -| `status` | Enum: `"paid"`, `"unpaid"`, `"cancelled"` | State of the invoice | -| `invoice_info` | URI | URI where additional information about the invoice can be found. | +| `destination_account` | [ILP Address](../0015-ilp-addresses/0015-ilp-addresses.md) | ILP Address of the receiver's account | +| `shared_secret` | 32 bytes, [base64-url encoded](https://en.wikipedia.org/wiki/Base64#URL_applications) | The shared secret to be used by this specific http client in the [Pre-Shared Key protocol](../0016-pre-shared-key/0016-pre-shared-key.md). Should be shared only by the server and this specific http client, and should therefore be different in each query response. | +| `maximum_destination_amount` | Integer String | Maximum amount, denoted in the minimum divisible units of the ledger, the receiver will accept. This amount may be determined by the ledger's maximum account balance or transfer amount. If the receiver expects a specific amount to be delivered, this may be set equal to the `minimum_destination_amount` | +| `minimum_destination_amount` | Integer String | Minimum amount, denoted in the minimum divisible units of the ledger, the receiver will accept. This amount may be determined by the minimum transfer amount of the ledger. If the receiver expects a specific amounts to be delivered, this may be set equal to the `maximum_destination_amount` | +| `ledger_info` | Object | Details about the destination ledger | +| `ledger_info.currency_code` | String | Currency code to identify the receiver's currency. Currencies that have [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) codes should use those. Sender UIs SHOULD be able to render non-standard codes | +| `ledger_info.currency_scale` | Integer | The scale of the amounts on the destination ledger (e.g. an amount of `"1000"` with a scale of `2` translates to `10.00` units of the destination ledger's currency) | +| `receiver_info` | Object | Arbitrary additional information about the receiver. This field has no schema and the receiver may include any fields they choose. The field names listed below are recommended merely for interoperability purposes. | +| `receiver_info.name` | String | _(OPTIONAL)_ Full name of the individual, company or organization the receiver represents | +| `receiver_info.image_url` | HTTPS URL | _(OPTIONAL)_ URL where the sender can get a picture representation of the receiver | -### Setup (`POST `) +**Note:** Currency amounts are denominated as integer strings instead of native JSON numbers to avoid losing precision during JSON parsing. Applications MUST represent these numbers in a data type that has precision equal or greater than an unsigned 64-bit integer. -When the sender is ready to make a payment, it submits a payment object to the receiver. +##### Errors -#### Request - -##### For a Payee - -``` http -POST /api/receiver/bob HTTP/1.1 -Host: red.ilpdemo.org -Accept: application/json - -{ - "amount": "10.40", - "sender_identifier": "alice@blue.ilpdemo.org", - "memo": "Hey Bob!" -} -``` - -| Field | Type | Description | -|---|---|---| -| `amount` | Decimal String | _Destination amount_ the receiver will receive | -| `sender_identifier` | String | Identifier of the sender | -| `memo` | String | Message for the recipient linked to the payment | - -##### For an Invoice +###### receiver Does Not Exist ``` http -POST /api/receiver/bob HTTP/1.1 -Host: red.ilpdemo.org -Accept: application/json +HTTP/1.1 404 Not Found +Content-Type: application/json { - "sender_identifier": "alice@blue.ilpdemo.org" + "id": "InvalidReceiverError", + "message": "Invalid receiver ID" } ``` -| Field | Type | Description | -|---|---|---| -| `sender_identifier` | String | Identifier of the sender | +### Payment Setup -#### Response -``` http -HTTP/1.1 201 Created -Content-Type: application/json +The sender uses the receiver details to create the ILP packet: -{ - "address": "ilpdemo.red.bob.b9c4ceba-51e4-4a80-b1a7-2972383e98af", - "amount": "10.40", - "expires_at": "2016-08-16T12:00:00Z", - "data": { - "sender_identifier": "alice@blue.ilpdemo.org" - }, - "additional_headers": "asdf98zxcvlknannasdpfi09qwoijasdfk09xcv009as7zxcv", - "condition": "cc:0:3:wey2IMPk-3MsBpbOcObIbtgIMs0f7uBMGwebg1qUeyw:32" -} -``` -The response is an [Interledger Payment Request](../0011-interledger-payment-request). +* The `account` in the ILP packet is the `destination_account` provided by the receiver +* The `amount` is determined by the sender: -The setup is what primes the receiver to expect the incoming payment. The receiver generates the payment request and condition the sender must use to create the ILP Packet. The fulfillment of the condition will serve as the sender's proof of payment. + * The sender uses the `ledger_info.currency_scale` to determine the integer amount to include in the ILP packet (e.g. if they want the receiver to receive 10 units of `ledger_info.currency_code` on their ledger, a `ledger_info.currency_scale` value of 2 would indicate they should set the `amount` to 1000). In cases where the sender knows the integer amount the receiver should get, for example if that amount was requested by the receiver out of band, the sender does not need to use `ledger_info.currency_code` and `ledger_info.currency_scale` + * The sender SHOULD NOT set an amount greater than the `maximum_destination_amount` or lower than the `minimum_destination_amount`, as this will be rejected by the receiver -The receiver has the opportunity to reject an incoming payment before any funds move, for instance because of daily limits: +* The `data` is encoded using the [Pre-Shared Key protocol](../0016-pre-shared-key/0016-pre-shared-key.md): -``` http -HTTP/1.1 422 Unprocessable Entity -Content-Type: application/json + * The key is the `shared_secret` provided by the receiver + * Additional data may be included in the PSK details. SPSP data MUST be JSON. + * Private PSK headers SHOULD include `Content-Type: application/json` and `Content-Length: ` -{ - "id": "LimitExceededError", - "error": "Daily incoming funds limit exceeded" -} -``` +Note that the sender can send as many payments as they want using the same receiver info. The sender SHOULD query the receiver again once the time indicated in the [`Cache-Control` header](#response-headers) has passed. ## Appendix A: (Optional) Webfinger Discovery -Whenever possible, receiver URLs should be exchanged out-of-band and discovery should be skipped. However, in some cases, it may be useful to have a standardized user-friendly identifier. This discovery method describes how to resolve such an identifier to an SPSP receiver endpoint. +Whenever possible, receiver URLs should be exchanged out-of-band and discovery should be skipped. However, in some cases, it may be useful to have a standardized user-friendly identifier. This discovery method describes how to resolve such an identifier to an SPSP endpoint. First, the sender uses Webfinger ([RFC 7033](https://tools.ietf.org/html/rfc7033)) to look up an identifier (e.g. `bob@red.ilpdemo.org`): @@ -291,8 +183,8 @@ Content-Type: application/json "subject": "acct:bob@red.ilpdemo.org", "links": [ { - "rel": "https://interledger.org/rel/receiver", - "href": "https://red.ilpdemo.org/api/receivers/bob" + "rel": "https://interledger.org/rel/spsp/v2", + "href": "https://red.ilpdemo.org/api/spsp/bob" } ] } diff --git a/0010-connector-to-connector-protocol/0010-connector-to-connector-protocol.md b/0010-connector-to-connector-protocol/0010-connector-to-connector-protocol.md index d4c57fb2..0a031961 100644 --- a/0010-connector-to-connector-protocol/0010-connector-to-connector-protocol.md +++ b/0010-connector-to-connector-protocol/0010-connector-to-connector-protocol.md @@ -1 +1,105 @@ -> Placeholder +--- +title: The Connector to Connector protocol +draft: 4 +--- +# Connector to Connector protocol + +## Discovery + +Connectors discover their peers through out-of-band communication, or by looking at https://connector.land and contacting the administrator of another connector. + +Once peered, two connectors have a ledger between them. + +There are two ways for connectors to peer with each other: symmetric, and asymmetric. In symmetric peering, the ledger between the two connectors is administered +collaboratively by the two connectors, optionally relying on a trusted third party (validator). In asymmetric peering, the ledger is administered by one of the two +peers, and the other peer only keeps a non-authoritative shadow ledger. + +Regardless of whether the connectors peer symmetrically or asymmetrically, the initiating connector somehow needs to exchange information with the other connector. +WebFinger can help with that. + +Whether discovery is done with or without use of WebFinger, each peer ends up knowing: + +* the protocol to use to send information like route broadcasts to the other connector +* the protocol to use to add updates to the peering ledger +* the currency code for the peering ledger +* the currency scale for the peering ledger + +### WebFinger-based discovery +In WebFinger-based discovery, both peers still need some out-of-band communication channel (e.g. talking in person or over email/chat), over which one +prospective peer tells the other: +* their intent, "Let's peer using WebFinger!" +* their own hostname +* the currency code they propose for the peer ledger +* the ledger scale they propose for the peer ledger + +And the other peer response with: +* their agreement, "OK, let's peer using WebFinger for discover, and using that currency code and scale for the peer ledger!" +* their own hostname + +Now, both peers look up each other's host resource, for instance: + +* the server wallet1.com looks up https://wallet2.com/.well-known/webfinger?resource=https://wallet2.com +* the server wallet2.com looks up https://wallet1.com/.well-known/webfinger?resource=https://wallet1.com + +This way, each peer has the other peer's public key. They now use ECDH to create a shared secret, from which a ledger prefix and an auth token are derived, +as implemented in [ilp-kit](https://github.com/interledgerjs/ilp-kit). + +### Discovery without WebFinger +When the two connectors do their discovery without WebFinger, the first peer tells the other: +* their intent, "Let's peer without WebFinger!" +* a BTP URI for the other connector to use +* a BTP version to use (currently 'BTP/1.0') +* the currency code they propose for the peer ledger +* the ledger scale they propose for the peer ledger + +And the other peer responds by connecting to the WebSocket indicated by the BTP URI and the protocol version. A BTP URI has one of the following formats: +* `btp+://:@` +* `btp+://@` // `auth_token === ''` is implied +* `btp+://` // `auth_username === ''` and `auth_token === ''` are implied + +The `` is either 'ws' or 'wss'. The `` needs to contain a hostname, and may contain a port identifier and path part. + +Examples: +* 'btp+ws://localhost:8000' +* 'btp+wss://someUsername:someToken@amundsen.michielbdejong.com/api/17q3' + +Both connector-to-connector messages and ledger updates will then be transported over the BTP WebSocket. + +Note that one peer will play the role of WebSocket server, and the other peer will play the role of WebSocket client. Often, but not necessarily, the peer +playing the server role will also administer the peer ledger, and the peer playing the client role will only keep a shadow ledger. + +## Route broadcasts + +Connectors send each other `broadcast_routes` messages, using the message sending functionality of the ledger between them. + +The syntax of such a method is defined by https://github.com/interledgerjs/ilp-connector/blob/v17.0.2/schemas/RoutingUpdate.json and + https://github.com/interledgerjs/five-bells-shared/blob/v22.0.1/schemas/Routes.json. + +When a route is included in a route update from Alice to Bob, Alice is stating she will, at least temporarily, be able to forward a payment to that destination, if the +source amount which Bob would send her is high enough, given the destination amount, and as described by the `points` piece-wise linear function. + +Connectors also exchange quote requests, +in the same way users may request a quote from a connector, see [IL-RFC 8](../0008-interledger-quoting-protocol/0008-interledger-quoting-protocol.md). +This process is called remote quoting. + +Note that the points series ("liquidity curves") in route broadcasts, as well as the +amounts in quote requests and quote responses, are expressed in integer values in the ledger's base unit of the ledger. This means you will need to divide/multiply +by `10 ** currency_scale` for the ledger in question, to convert integers (in ledger units) to floats (in terms of the ledger's announced `currency_code`), and back. + +The curve from the route broadcast is used directly for determining a source amount / destination amount relation, and remote quoting is only used when +a connector knows about a route without knowing its curve information (this can happen if a connector somewhere in the network was explicitly configured with a hard-wired +(default) route in its ilp-connector config; that route then also gets forwarded without curve in route broadcasts across the network, and all connectors that want to use this +route will therefore need to use remote quoting). +All routes that were built up from local pairs will however get broadcast including their curves, based on connector fees, currency exchange rates, and liquidity limits (min and +max limits on the connector's balance on both ledgers) and this requires routes to be re-broadcast when their liquidity curve changes, but ilp-kit will aggregate over time, both +for its own liquidity changes, and route changes which it forwards, into one message per 30 seconds. + +When combining various alternative (parallel) routes, for each section of the liquidity curve, ilp-kit will only consider routes which are as short as the shortest known route, +and from the set of shortest paths, choose the cheapest one. Note that the routing table of each ilp-kit is used for two things: + +* determine whether a payment's source amount is sufficient or not, given its destination amount +* if so, choose the next hop (it's then up to that peer to choose the hop after that, until the destination node is reached) + +## Please expand this document + +This document is a stub, please help expand it! See https://github.com/interledger/rfcs. diff --git a/0011-interledger-payment-request/0011-interledger-payment-request.md b/0011-interledger-payment-request/0011-interledger-payment-request.md index cba9e32d..be62028f 100644 --- a/0011-interledger-payment-request/0011-interledger-payment-request.md +++ b/0011-interledger-payment-request/0011-interledger-payment-request.md @@ -1,140 +1,97 @@ +--- +title: Interledger Payment Requests (IPR) +draft: 2 +--- # Interledger Payment Request (IPR) -## Preface - -This document specifies the Interledger Payment Request (IPR) data format. It is intended for use in protocols in which the recipient of an Interledger payment first communicates a request for payment to the sender. - -This document provides an example flow to illustrate how IPR could be used in a higher level protocol. This document also details a method for generating Interledger transfer conditions that enable recipients to use the condition as an integrity check on incoming transfers and packets, without maintaining state about all outstanding payment requests. +The Interledger Payment Request (IPR) transport protocol is an end-to-end protocol in which the receiver of an Interledger payment first communicates a request for payment to the sender. This document also recommends a method for receivers to generate payment requests such that they can verify incoming payments without storing all outstanding requests. ## Introduction ### Motivation -The Interledger Protocol uses holds based on Crypto Conditions and expiries to ensure a sender's funds are delivered to the destination or returned to the sender's account. +The Interledger Protocol uses holds based on cryptographic conditions and expiries to ensure a sender's funds are delivered to the destination or returned to the sender's account. + +One approach is for conditions to be generated and fulfilled by the receivers of Interledger payments. When the sender of the payment receives the fulfillment, they can be certain that the receiver received the payment. + +Receiver-generated conditions are also useful in building non-repudiable application layer protocols. The receiver would generate the condition and cryptographically sign a statement that the preimage of the specified hash indicates the sender has settled a particular debt. The receiver would only disclose the hash preimage upon receipt of payment. Once the sender gets the preimage, they would be able to demonstrate to third parties that they paid the receiver using the preimage and the original signed statement. -A common approach is for conditions to be generated and fulfilled by the receivers of Interledger transfers. This document specifies one such approach that enables receivers to verify that an incoming transfer matches a payment request they previously created, without the receiver needing to keep a record of all outstanding payment requests. +In most cases, non-repudiability is unnecessary, as it is sufficient for the sender to get proof that the receiver received the payment even if that proof cannot be used to convince third parties. In such cases, the [Pre-Shared Key](../0016-pre-shared-key/0016-pre-shared-key.md) transport protocol is recommended because it does not require end-to-end communication for every payment. ### Scope -This document defines a suggested format for representing payment requests and an approach for receiver-generated conditions. Protocols built on top of Interledger MAY use the format provided here, though they can use any format they choose instead. +This document defines a suggested format for representing payment requests and an approach for receiver-generated conditions. Protocols built on top of IPR MAY use the format provided here, though they can use any format they choose instead. While this document also provides a recommendation for generating transfer conditions, conditions are often opaque to all parties aside from the receiver. Receivers MAY use any condition they choose, however this specification includes best practices that receivers SHOULD take into consideration. -This document does **not** specify how payment requests are communicated from a recipient to a sender. +This document does **not** specify how payment requests are communicated from a receiver to a sender. ### Model of Operation Interledger Payment Requests are intended to be used in higher-level protocols as follows: -1. Recipient creates the payment request and generates the condition using one of algorithms outlined in [Condition Generation](#condition-generation). -2. Recipient communicates the payment request to the sender. -3. Sender generates an ILP Packet from the payment request. -4. Sender prepares a local ledger transfer to a connector, held pending the execution condition given in the payment request and with the ILP Packet attached. -5. Connectors forward the payment until it reaches the recipient. -6. Recipient receives a notification of a held transfer with an ILP Packet attached. -7. Recipient checks the transfer amount against the ILP Packet and checks the payment request has not expired. -8. Recipient regenerates the condition and fulfillment from the ILP Packet attached to the incoming transfer, using the same algorithm as before, and fulfills the transfer's condition. If the condition generated does not match the one in the transfer it means that the ILP Packet has been tampered with and the transfer goes unfulfilled. - -## Payment Request Specification - -### JSON - -```json -{ - "address": "ilpdemo.red.bob.b9c4ceba-51e4-4a80-b1a7-2972383e98af", - "amount": "10.25", - "condition": "cc:0:3:hd5x8kpaDDLQu-KqMyCrlsg5QJ9g9qaFr9ytTwqyCsw:32", - "expires_at": "2016-08-16T12:00:00Z", - "data": { - "re": "dinner the other night" - }, - "additional_headers": "asdf98zxcvlknannasdpfi09qwoijasdfk09xcv009as7zxcv" -} -``` +1. The receiver creates the ILP Packet and generates the condition using one of algorithms outlined in [Condition Generation Recommendations](#condition-generation-recommendations). +2. The receiver communicates the payment request, which includes the ILP Packet and condition, to the sender. +3. The sender quotes the requested ILP packet and initiates an ILP payment with the packet and execution condition from the payment request. The sender uses the `account` and `amount` from the packet but they MUST treat the `data` as opaque. The sender MUST NOT modify the packet at all, or the receiver will reject the payment. +4. When the receiver gets a notification of the incoming transfer, they check the transfer amount against the ILP Packet and check the payment request has not expired. +5. The receiver regenerates the condition and fulfillment from the ILP Packet attached to the incoming transfer, using the same algorithm as before, and fulfills the transfer's condition. +6. If the condition generated by the receiver matches the one in the incoming transfer, they submit the fulfillment to execute the transfer. If the condition does not match, it means that the ILP Packet has been tampered with and the receiver rejects the incoming transfer. -| Field | Type | Short Description | -|:--|:--|:--| -| `address` | ILP Address | Request-specific address | -| `amount` | Decimal String | Amount requested by the recipient | -| `condition` | Crypto Condition | Execution condition for the payment | -| `expires_at` | [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) Timestamp | Expiry of the request | -| `data` | Object | Data to be included in the ILP Packet | -| `additional_headers` | Base64 String | Additional headers for the ILP Packet | +## Condition Generation Recommendations -#### address +It is RECOMMENDED that receivers use an implementation of the [Pre-Shared Key](../0016-pre-shared-key/0016-pre-shared-key.md) transport protocol to generate the packet and condition, because it will already include the best practices for generating conditions and enable the receiver to avoid keeping state of all outstanding payment requests. -The ILP Address the payment is to be routed to. This SHOULD be a request-specific address in the form `.`, such as `ilpdemo.red.bob.b9c4ceba-51e4-4a80-b1a7-2972383e98af`. +Note the receiver's method for generating the condition or encoding data in the ILP packet does not matter to the sender. The sender treats the `data` in the ILP packet as opaque and uses the condition from the payment request without needing to know how the fulfillment is generated. -When using the recommended [Condition Generation](#condition-generation) method, account addresses MUST include a request-specific portion to ensure that conditions are unique per-request. +### Using a PSK Implementation -#### amount +The [Pre-Shared Key](../0016-pre-shared-key/0016-pre-shared-key.md) protocol uses the payment condition as a MAC of the payment details by setting the fulfillment to be an HMAC of the ILP packet with a key derived from a secret shared between the sender and receiver. In IPR there is no shared secret and the condition is only generated by the receiver. Nevertheless, an IPR receiver can use the same methods to generate the condition and regenerate the fulfillment on receipt of the incoming transfer. -The amount requested by the recipient, such as `10.25`, denoted in the assets of the recipient's ledger. +To use a PSK implementation, the receiver follows these steps: -The amount MUST be rounded based on the precision supported by the recipient's ledger. +1. The receiver MAY derive a receiver secret and address as detailed in [PSK: Shared Secret Generation](../0016-pre-shared-key/0016-pre-shared-key.md#shared-secret-generation) but in IPR the receiver MUST NOT share the secret key. +2. The receiver creates the payment packet and condition _using the steps normally executed by the sender_: [PSK: Payment Creation](../0016-pre-shared-key/0016-pre-shared-key.md#1-payment-creation). +3. The receiver communicates the payment request, including the packet and condition, to the sender. +4. If the receiver used the derived secret from step 1, they regenerate that secret from the packet as specified in [PSK: Shared Secret Regeneration](../0016-pre-shared-key/0016-pre-shared-key.md#shared-secret-regeneration). +5. The receiver generates the fulfillment from the secret and packet as specified in [PSK: Payment Fulfillment](../0016-pre-shared-key/0016-pre-shared-key.md#2-payment-fulfillment). -Recipients SHOULD ignore incoming transfers whose amounts are less than the amount specified in the payment request. Recipients MAY ignore incoming transfers whose amounts are greater than requested. - -#### condition +### Alternative Methods and General Best Practices -The Crypto Condition to be used as the execution condition for the payment. See [Condition Generation](#condition-generation) for recommendations the recipient MAY use for creating the condition. +Receivers MAY use any method they choose for generating the condition, such as using a random fulfillment for each request. The receiver MUST be able to identify and verify the ILP Packet for every outstanding payment and produce the corresponding fulfillment. A PSK implementation handles these requirements without storing every fulfillment and packet in a database, by using the condition as a Message Authentication Code (MAC) of the payment request. -#### expires_at +Receivers SHOULD follow these guidelines if implementing a custom system: +* Receivers SHOULD include a payment request-specific nonce in the ILP packet to ensure that conditions are unique even when multiple payments would otherwise have identical packets. +* Receivers SHOULD include an expiry date so that they do not accept incoming payments that are paid too long after their creation. -The timestamp when the request expires. Recipients SHOULD NOT fulfill the conditions of incoming transfers that arrive after the expiry has passed. Senders MAY use the request expiry to determine the expiry of the transfer they put on hold for the first connector. +## Interledger Payment Request Format -#### data +Here is a summary of the fields in the Interledger Payment Request format: -An arbitrary JSON object that should be included in the ILP Packet's user data header. - -#### additional_headers - -Additional binary headers, encoded as a base64 string, that should be included in the ILP Packet. The sender SHOULD treat these as opaque. - -### Binary - -**TODO** +| Field | Type | Short Description | +|:--|:--|:--| +| `version` | UInt8 | IPR Version, `2` for now | +| `packet` | OCTET STRING | ILP Payment packet including the destination address and amount | +| `condition` | UInt256 | Execution condition for the payment | -## Condition Generation +#### version -It is RECOMMENDED that recipients use a Message Authentication Code (MAC) of the details of their payment request in the condition so that the condition can be used as an integrity check on incoming transfers. This allows a recipient to ensure that a transfer matches a request they previously generated, without the recipient needing to maintain a log of all outstanding payment requests. + UInt8 ::= INTEGER (0..127) -Recipients MUST include a payment request-specific component in the account ILP Address to ensure that conditions are unique per-request. +IPR version. This document specifies version 2. -### Preimage Condition from JSON +#### packet -This is how a [PREIMAGE-SHA-256 Crypto Condition](../0002-crypto-conditions) MAY be generated using an HMAC of a JSON-encoded Interledger Payment Request: + OCTET STRING (SIZE(0..65535)) -1. The recipient takes the `address`, `amount`, `expires_at`, and `data` fields from the payment request. Any fields that are used for routing are probably not important to perform an integrity check upon, because they may be modified in transit and there is no further use for them once the ILP packet is received attached to an incoming transfer. -2. The recipient uses a canonical encoding, such as [Canonical JSON](https://www.npmjs.com/package/canonical-json) to create a stringified version of `1.` -3. The recipient uses a secret key to create an HMAC of `2.` -4. The recipient uses the digest of `3.` as the preimage of a [PREIMAGE-SHA-256 Crypto Condition](../0002-crypto-conditions). +The [ILP Payment Packet](https://interledger.org/rfcs/0003-interledger-protocol/draft-3.html#specification). -The following snippet shows how this can be done in JavaScript: +In IPR the sender only uses the `account` and `amount` from the ILP packet and MUST treat the `data` as opaque. -```js -const crypto = require('crypto') // Node.js crypto -const stringify = require('canonical-json') // https://www.npmjs.com/package/canonical-json -const cc = require('five-bells-condition') // https://www.npmjs.com/package/five-bells-condition +The sender MUST NOT modify the ILP packet at all, or the receiver will reject the payment. -const RECIPIENT_HMAC_KEY = Buffer.from('/4t65RDqth7/rxI0j+MqtQyv04Y8mzUCMhAAofhDQIY=', 'base64') -const requestString = stringify({ - "address": "ilpdemo.red.bob.b9c4ceba-51e4-4a80-b1a7-2972383e98af", - "amount": "10.25", - "expires_at": "2016-08-16T12:00:00Z", - "data": { - "re": "dinner the other night" - } -}) +#### condition -const hmac = crypto.createHmac('sha256', RECIPIENT_HMAC_KEY) -hmac.update(requestString) -const cryptoCondition = new cc.PreimageSha256() -cryptoCondition.setPreimage(hmac.digest()) + UInt256 ::= OCTET STRING (SIZE(32)) -const condition = cryptoCondition.getConditionUri() -console.log(condition) // Prints cc:0:3:BC-mUTFsZYXd89QQDiocToUWbQ-XYTh5H5lFQNwhDWE:32 +The ILP payment condition, which is the SHA-256 hash of the fulfillment that will be used to trigger the execution of the payment. -const fulfillment = cryptoCondition.serializeUri() -console.log(fulfillment) // Prints cf:0:aW9EFFuRmltvYcs2ESExvf-0pbzBhUGgpqKT-oUBuuU -``` diff --git a/0012-five-bells-ledger-api/0012-five-bells-ledger-api.md b/0012-five-bells-ledger-api/0012-five-bells-ledger-api.md new file mode 100644 index 00000000..454e7e34 --- /dev/null +++ b/0012-five-bells-ledger-api/0012-five-bells-ledger-api.md @@ -0,0 +1,1359 @@ +--- +title: Five Bells Ledger API +draft: 1 +--- +# Five Bells Ledger API + +The Five Bells Ledger API is a RESTful API served by a ledger (or an adapter), which provides functionality necessary for ILP compatibility. The Five Bells Ledger API provides a single standard API that a ledger can serve in order to ease integration with other Interledger Protocol components and applications, such as the reference ILP Client and ILP Connector. This is not the only way a ledger can become ILP-enabled, but it provides a template that minimizes the integration work necessary for compatibility with ILP software. + +## Overview + +The Five Bells Ledger API defines the following data types and structures: + +- [Transfer resource][] +- [Account resource][] +- [Message resource][] +- [Crypto-Condition][] and [Crypto-Condition Fulfillment][] +- [Error Codes](#api-error-codes) + +The core operations of the API are: + +- [Get Ledger Metadata][] +- [Prepare Transfer][] + - If the transfer is unconditional, it executes immediately. + - If the transfer is conditional, it waits for a matching fulfillment. +- [Submit Fulfillment][] + - Executes a conditional transfer, if the fulfillment matches the condition. +- [Reject Transfer][] +- [Get Transfer][] and check its status +- [Get Transfer Fulfillment][] +- [Get Account][] info including balance +- [Create or Update Account][] +- [Send Message][] to another account +- [Get Auth Token][] for [Authorization and Authentication](#authorization-and-authentication) +- [Websocket][] sub-API for subscribing to transfers and accounts + +The ledger MAY support additional methods, including but not limited to: + +- Health check status +- Look up transfers by account or by [Crypto-Condition][] + + +## Conventions + +The API is designed to be [RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer), extensible, and modular. By design, every resource type contains links to other methods, so you can "naturally" discover other API paths by starting from any object. Clients SHOULD NOT hardcode any [URL][] other than the path to the [Get Ledger Metadata][] method. A ledger MAY host different objects and API methods at different hostnames or base URLs, as long as all client applications can dereference the URLs in the usual way. + + +### JSON + +All methods communicate both ways using [JavaScript Object Notation (JSON)](http://www.json.org/) unless otherwise specified. A ledger MUST use the `Content-Type: application/json` header in responses to indicate that the message body contains JSON. A ledger MAY honor the `Accept` header of the request and return the data in other formats if the client requests them. A ledger MAY interpret the message bodies of requests (PUT and POST methods) as JSON, even if the `Content-Type` header is missing or indicates a different format. The Five Bells Ledger API uses HTTP status codes to indicate the status of API operations; if the ledger encounters an error processing a request, it MUST NOT return an HTTP status code in the 200-299 range even if communication was successful. + +Field names in the JSON objects defined by this API never contain literal period (`.`) characters. By convention, this documentation uses the `.` character to indicate fields nested inside other Object-type fields. In some cases, field values contain literal period (`.`) characters. Additionally, this field contains some objects that can hold arbitrary data. Arbitrary-data objects can contain any legal JSON, including field names with literal period (`.`) characters. Arbitrary-data objects SHOULD NOT contain duplicate keys. + + +### Amounts +[amount]: #amounts +[Amounts]: #amounts + +The Five Bells Ledger API represents numeric currency amounts as strings rather than native JSON numbers. This is because many standard libraries automatically decode numbers to a [IEEE 754 double precision floating point](https://en.wikipedia.org/wiki/IEEE_floating_point) representation. Using IEEE 754 double floats can introduce a loss of precision and rounding errors that are unacceptable for financial services, depending on the range and use cases needed. (In particular, digital assets that are natively represented as 64-bit unsigned integers do not fit properly into IEEE 754 double precision floats.) Amounts in the Five Bells Ledger API MUST match the following regular expression: + +`^[-+]?[0-9]*[.]?[0-9]+([eE][-+]?[0-9]+)?$` + +Client applications can decode numeric strings to whatever representation provides sufficient precision for their specific needs. The ledger MUST report information about the precision it uses in the [Get Ledger Metadata][] response. + + +### Assets and Currency + +The Five Bells Ledger API provides access to a ledger containing a single currency or asset. The [Get Ledger Metadata][] method reports the type of asset or currency used. + +If a provider supports multiple currencies or assets, the provider can serve Five Bells Ledger APIs separately for each such asset by serving it from a different prefix. For example, the [Prepare Transfer][] method could be available at all the following locations: + +- `POST https://ledger.example.com/TZS/transfers` +- `POST https://ledger.example.com/KES/transfers` +- `POST https://ledger.example.com/stocks/MSFT/transfers` + +Any valid URL is a suitable prefix. However, only the same asset served by the same underlying ledger can be directly transferred. + +These instances could be served by the same underlying software and share infrastructure, but it would not be possible to transfer directly from one to the other, since the currency denominations are different. An **ILP Connector** could facilitate cross-currency payments. + +#### Adding Money to the Ledger + +There are several ways to "add" money to the ledger. A ledger MAY use any or all of the following: + +- When an account is created or updated by an administrator with the [Create or Update Account][] method, the administrator MAY set the balance of the account to any legal value. +- A ledger may be initialized in a state where its database contains accounts with positive balances. +- An administrator MAY create an "issuer" account whose balance can be negative. To give a positive balance to an account, execute a transfer from the issuer account. If you use this technique exclusively, the issuer account's total balance is equal to the negative of all balances in the ledger summed together. + + +### Scope + +The Five Bells Ledger API defines one way to implement sufficient functionality to operate a ledger. It is not intended to be an optimal or ideal ledger, especially since such a thing is subjective or at least hard to quantify. (Indeed, Interledger itself was conceived from the conclusion that no one ledger can serve the needs of every person in existence.) In general, this specification reflects to the existing [Five Bells Ledger reference implementation](https://github.com/interledgerjs/five-bells-ledger) so that others may implement compatible software. + + +### Authorization and Authentication + +The ledger MUST authenticate API clients. The RECOMMENDED method of authentication is **Token Authentication**, with tokens being served by the [Get Auth Token][] method. The Get Auth Token method SHOULD be strictly rate limited and authenticated with a secondary authentication mechanism, such as: + +- [HTTP Basic Auth](https://tools.ietf.org/html/rfc2617#section-2) +- [TLS Client Certificates](https://tools.ietf.org/html/rfc5246#section-7.4.6) + +The ledger MUST authenticate [WebSocket][] connections with token-based authentication. + +A ledger MAY allow clients to authenticate HTTPS methods other than the Get Auth Token method using HTTP Basic Auth or TLS Client Certificates. This is NOT RECOMMENDED, especially if the ledger faces the public internet, because it makes it easier for unauthorized clients to put load on (that is, DDoS) the ledger. + +The ledger MUST support authenticating a client in one of the following roles: + +- Administrator - Full control over all ledger operations. +- Account owner (for a specific account) - Can perform operations relating to one specific account, such as authorizing debits from or credits to the account, and viewing the account balance. +- Unauthorized. Read-only access to some global ledger data. A ledger MUST NOT allow an unauthorized user to make a change that modifies accounts or transfers in any way. + +A ledger MAY define additional authorization levels, especially for functions that extend this API. + + +## Data Types + +### Transfer Resource +[Transfer resource]: #transfer-resource + +A transfer represents money being moved around _within a single ledger_. A transfer debits one or more accounts and credits one or more accounts, such that the sum of all credits equals the sum of all debits. A transfer can be conditional upon a supplied [Crypto-Condition][], in which case it executes automatically when presented with the fulfillment for the condition. (Assuming the transfer has not expired or been rejected first.) If no Crypto-Condition is specified, the transfer is unconditional, and executes as soon as it is prepared. + + +A transfer object contains the fields from the following table. Some fields are _Ledger-provided_, meaning they cannot be set directly by clients. All fields, including the `memo` fields, must be implemented for the Five Bells Ledger API to work properly. Fields of nested objects are indicated with a dot (`.`) character; no field names contain a dot literal. + +| Name | Type | Description | +|:-----------------------|:---------------------|:-----------------------------| +| `id` | [URL][] | Client-provided ID for this resource, including a [UUID][], in the format of the [Get Transfer][] URL. MUST be unique within the ledger. See [Transfer IDs][] for more information. | +| `credits` | Array of Objects | Array of objects defining which accounts receive how much in this transfer. A ledger MAY restrict this to length 1. | +| `debits` | Array of Objects | Array of objects defining which accounts send how much in this transfer. A ledger MAY restrict this to length 1. | +| `cancellation_condition` | [Crypto-Condition][] | The condition for canceling the transfer. This field is OPTIONAL to implement. (It is no longer used by other ILP reference implementations.) MUST NOT be present unless the transfer has an `execution_condition`. | +| `execution_condition` | [Crypto-Condition][] | The condition for executing the transfer. If omitted, the transfer executes unconditionally. | +| `expires_at` | [Date-Time][] | The date when the transfer expires and can no longer be executed. | +| `additional_info` | Object | Arbitrary fields attached to this transfer. (For example, the IDs of related transfers in other systems.) | +| `fulfillment` | [URL][] | _(Ledger-provided)_ Path to the fulfillment for this transfer. MUST be an HTTP(S) URL where the client can [submit the fulfillment][Submit Fulfillment] or [get the fulfillment][Get Transfer Fulfillment]. MUST be provided if and only if this transfer has an `execution_condition`. | +| `ledger` | [URL][] | _(Ledger-provided)_ Resource identifier for the ledger where the transfer occurs. MUST be an HTTP(S) URL where you can [get the ledger metadata][Get Ledger Metadata]. | +| `rejection_reason` | String | _(Ledger-provided)_ The reason the transfer was rejected. MUST appear if and only if `state` is `rejected`. | +| `state` | String | _(Ledger-provided)_ The current state of the transfer. Valid states are `proposed`, `prepared`, `executed`, and `rejected`. See [Transfer States][] for more information. | +| `timeline` | Object | _(Ledger-provided)_ Timeline of the transfer's state transitions. | +| `timeline.executed_at` | [Date-Time][] | _(Ledger-provided)_ Time when the transfer was originally executed. MUST appear if and only if `state` is `executed`. This time MUST be equal to or later than the `prepared_at` time. | +| `timeline.prepared_at` | [Date-Time][] | _(Ledger-provided)_ Time when the transfer was originally prepared. MUST appear, even if the transaction is unconditional. | +| `timeline.rejected_at` | [Date-Time][] | _(Ledger-provided)_ Time when the transfer was originally rejected. MUST appear if and only if `state` is `rejected`. This time MUST be equal to or later than the `prepared_at` time. | + +[UUID]: http://en.wikipedia.org/wiki/Universally_unique_identifier + +The `credits` and `debits` fields contain objects defining how much to debit or credit to a particular account. The format for the objects in both arrays is as follows: + +| Field | Type | Description | +|:-------------|:--------|:----------------------------------------------------| +| `account` | [URL][] | An identifier for the account to credit or debit. This MUST be an HTTP(S) URL where the client can call the [Get Account][] method. | +| `amount` | String | Positive decimal amount of money to debit from or credit to this account. See [Amounts][] for formatting rules. | +| `memo` | Object | Arbitrary object with additional information about this credit or debit. | +| `authorized` | Boolean | _(Debit objects only)_ Whether this account has authorized this transfer. The ledger MUST NOT allow a request to set this value to `true` unless the client is authenticated as an Administrator or as the owner of the `account` from this debit. | + +**Note:** Ledgers MUST implement the `memo` field. ILP clients use this field to store the ILP Packet object. Specifically, the reference ILP Client puts the ILP Packet as a base64url-encoded string in the the `ilp` field of the `memo` of the credit to the Connector. To support any possible ILP Packet, the maximum size of the `memo` field MUST be at least **46 kilobytes**. + + +#### Transfer IDs +[Transfer IDs]: #transfer-ids +[Transfer ID]: #transfer-ids + +The transfer's `id` field is one of the main ways of identifying the transfer; it is unique across the ledger, so implementations MAY use it as a primary unique key in a database. The client applications, not the ledger, generate the `id`, so the ledger SHOULD require that the field match the proper form. If a client attempts to create a transfer with an `id` that already exists, the attempt MUST fail. (This protects clients from accidentally submitting the same transfer twice or more.) + +The form for a transfer ID is the ledger's `transfer` [URL][], with a [UUID][] in canonical form as the `{id}` parameter. The UUID MUST be formatted as 32 lowercase hexadecimal digits separated by hyphens into groups of 8-4-4-4-12. + +Depending on what algorithm clients use to generate UUIDs, it may be possible for other account owners to guess in advance which UUIDs might be used, and could preemptively claim `id` values that other account owners might use. A ledger MAY discourage this "squatting" behavior by imposing a limit on how many new transfers an account can generate within a fixed period of time, or with other similar limits. (A ledger could also address this issue in other ways, such as charging a fee for preparing transfers.) Client applications SHOULD use a sufficiently hard-to-predict system for generating UUIDs, such as the random algorithm described in [RFC4122](https://www.ietf.org/rfc/rfc4122). Client applications SHOULD NOT use the same ID to indicate related transfers in multiple ledgers: such behavior can be easily exploited by third parties who claim an ID in a ledger as soon as it gets used in a different ledger. + +#### Transfer States +[Transfer States]: #transfer-states + +The following state diagram shows the possible transitions between a transfer's `state` values: + +[![Transfer state diagram](5BL-transfer-states.png)](5BL-transfer-states.png) + +The [Prepare Transfer][] method creates a new transfer. Unless creator of the transfer is the only account to be debited and the account sets the `authorized` field of the debit to `true`, the transfer is created in the `proposed` state. + +A transfer in the `proposed` state can be modified by other account owners calling the [Prepare Transfer][] method to set the `authorized` field to `true` for debits to their accounts. + +When all a transfer's debits are authorized, the transfer executes immediately if it has no execution condition. If the transfer has an execution condition, funds from the `debits` are held and the transfer transitions to the `prepared` state. + +From the `prepared` state, a transfer can be executed if an account owner calls [Submit Fulfillment][] with a fulfillment that matches the transfer's `execution_condition`. This moves the held funds to the credited accounts, fully executing the transfer and moving it to the `executed` state. + +There are several ways a transfer in the `prepared` state can be rejected, which releases the funds back to the debited accounts: + +- The transfer has an `expires_at` field and the time specified by that field passes +- The transfer has a `cancellation_condition` and an account owner calls [Submit Fulfillment][] with a fulfillment that matches the cancelllation condition. +- The account owner of one of the accounts to be credited calls the [Reject Transfer][] method. + +The states `executed` and `rejected` are final. + +### Account Resource +[Account resource]: #account-resource + +An account resource contains the fields in the following table. Some fields are _Ledger-provided_, meaning they cannot be set directly by clients. Fields that are "Optional" or "Ledger-provided" in the following table may be omitted by clients submitting objects to the API, but those fields are not optional to implement. Fields of nested objects are indicated with a dot (`.`) character; no fields contain a dot literal. + +An account resource usually has an owner who can [authenticate](#authorization-and-authentication) to the ledger. A ledger MUST serve all the following fields to clients authenticated as the account owner. A ledger MAY serve a subset of fields, containing at least the `name` and `id` of the account, to clients not authenticated as the account owner. + +| Name | Type | Description | +|:--------------------------|:--------|:---------------------------------------| +| `name` | String | Name of the account. MUST be unique per account and MUST match the regular expression `^[a-zA-Z0-9._~-]{1,256}$`. After an account is created, this field is immutable. | +| `fingerprint` | String | _(Optional)_ A fingerprint of the account owner's client certificate. This field MUST be available if and only if the ledger supports client certificate authentication. MUST match the regular expression `^[0-9A-Fa-f]{2}(:[0-9A-Fa-f]{2}){19,127}$`. A ledger MAY allow the account owner to set this value. | +| `ledger` | [URL][] | _(Optional)_ The path to the ledger containing this account. MUST be an HTTP(S) URL where the client can [Get Ledger Metadata][]. (When an account is created, the ledger automatically provides this value.) | +| `minimum_allowed_balance` | String | _(Optional)_ The minimum balance permitted on this account. The special value `"-infinity"` indicates no minimum balance. This is a string so that no precision is lost in JSON encoding/decoding. The default value SHOULD be `"0"`. A ledger MUST NOT allow non-administrator users to modify this value directly. | +| `is_admin` | Boolean | _(Optional)_ If `true`, authenticating as the owner of this account gives administrator-level permissions. Only a client with administrator permissions can set this to `true`. | +| `is_disabled` | Boolean | _(Optional)_ If `true`, this account is disabled. Defaults to `false`. Disabled accounts cannot authenticate to the API. A ledger MUST allow this to be set ONLY by administrators. | +| `balance` | String | _(Optional)_ Balance as decimal [amount][]. Defaults to `"0"` at account creation. This can be negative, if the account's `minimum_allowed_balance` allows it. A ledger MAY allow clients with administrator permissions to set this value directly. A ledger MUST NOT allow non-administrator users to modify this value directly. | + + + +### Message Resource +[Message resource]: #message-resource + +Messages are sent through the ledger's [Send Message][] method and received in a [WebSocket][] subscription. All fields of the message are required: + +| Field | Value | Description | +|:---------|:--------|:--------------------------------------------------------| +| `ledger` | [URL][] | The base [URL][] of this ledger. MUST be an HTTP(S) URL where the client can [Get Ledger Metadata][]. | +| `from` | [URL][] | Resource identifier of the account sending the message. MUST be an HTTP(S) URL where the client can [get account information][Get Account]. The sender of a message MUST be authenticated as the owner of this account. | +| `to` | [URL][] | Resource identifier of the account receiving the message. MUST be an HTTP(S) URL where the client can [get account information][Get Account]. | +| `data` | Object | The message to send, containing arbitrary data. A ledger MAY set a maximum length on messages, but that limit MUST NOT be less than 510 UTF-8 characters or 2,048 bytes. | + + +### Crypto-Conditions +[Crypto-Condition]: #crypto-conditions +[Crypto-Condition Fulfillment]: #crypto-conditions + +The [Crypto-Conditions spec](https://github.com/interledger/rfcs/tree/master/0002-crypto-conditions) defines standard formats for _conditions_ and _fulfillments_. + +Conditions are distributable event descriptions, and fulfillments are cryptographically verifiable messages that prove an event occurred. If you transmit a fulfillment, then everyone who has the corresponding condition can agree that the condition has been met. + +Crypto-conditions control the execution of conditional transfers. The Five Bells Ledger API supports conditions and fulfillments in string format. + +The Crypto-Conditions specification anticipates that it will need to expand to keep up with changes in the field of cryptography, so conditions always define which rules and algorithms are necessary to verify the fulfillment. Implementations can use the condition's feature list to determine if they can properly process the fulfillment, without having seen the fulfillment itself. + +Example condition in string format: + +``` +ni:///sha-256;47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU?fpt=preimage-sha-256&cost=0 +``` + +Example fulfillment in string format (a PREIMAGE-SHA-256 fulfillment is the preimage string serialized): + +``` +oAKAAA +``` + +The [five-bells-condition](https://github.com/interledgerjs/five-bells-condition) library provides a JavaScript implementation of Crypto-Conditions. For custom implementations, consider the latest version of the [IETF crypto-conditions spec](https://tools.ietf.org/html/draft-thomas-crypto-conditions-03) as the source of truth. + +**Note:** The rest of the Interledger reference implementations have dropped support for the full range of Crypto-Conditions in favor of using SHA-256 hashlocks everywhere. For compatibility with all Interledger software, Five Bells Ledger transfers should use PREIMAGE-SHA-256 crypto-conditions only. + + +### URLs +[URL]: #urls +[URLs]: #urls +[Metadata URL Name]: #urls +[RFC6570]: https://tools.ietf.org/html/rfc6570 + +The Five Bells Ledger API uses URLs as the main way of identifying and looking up resources in the API. These URLs should be formatted as valid _absolute URLs_ in accordance with the [WHATWG URL Living Standard](https://url.spec.whatwg.org/). The URLs SHOULD use the `https:` scheme, except for some [WebSocket][] paths that use the `wss:` scheme instead. In development or private subnetworks, `http:` and `ws:` are acceptable instead. + +The `urls` field of the [Get Ledger Metadata][] method returns a list of paths to other API methods. Each member of the `urls` field describes one path, which MUST be an HTTP(S)-formatted URL unless otherwise specified. Some paths contain [RFC6570][]-formatted variable sections in curly braces. The `urls` field MUST include all of the following: + +| Path name | Variables | HTTP Method | API Method(s) | +|:-----------------------|:----------|:------------|:--------------------------| +| `account` | `{name}` | GET | [Get Account][], [Create or Update Account][] | +| `transfer` | `{id}` | GET | [Prepare Transfer][], [Get Transfer][] | +| `transfer_fulfillment` | `{id}` | GET, PUT | [Get Transfer Fulfillment][], [Submit Fulfillment][] | +| `websocket` | None | N/A | [WebSocket][] (MUST be a `ws://` or `wss://` URL.) | + +The `urls` field of the metadata MAY also contain other methods provided by the ledger. + +Some resources also contain fields whose values are URLs pointing to other methods. These URLs MUST NOT contain [RFC6570][]-formatted variable sections. The following table maps URL fields to the API methods that can be accessed at those paths: + +| Resource | Field | HTTP Method | API Method | +|:---------|:---------------------------------|:------------|:-----------------| +| Transfer | `ledger` field | GET | [Get Ledger Metadata][] | +| Transfer | `credits` member `account` field | GET | [Get Account][] | +| Transfer | `debits` member `account` field | GET | [Get Account][] | +| Message | `from` field | GET | [Get Account][] | +| Message | `to` field | GET | [Get Account][] | +| Message | `ledger` field | GET | [Get Ledger Metadata][] | + + +### Date-Time +[Date-Time]: #date-time + +All dates and times should be expressed in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time format with precision to the second or millisecond. The time zone offset should always be `Z`, for no offset. In other words, the date-time should be a **string** matching the one of the following formats: + +| Precision | Format | +|:------------|:---------------------------| +| Second | `YYYY-MM-DDTHH:mm:ssZ` | +| Millisecond | `YYYY-MM-DDTHH:mm:ss.sssZ` | + + +## API Methods + +### Get Ledger Metadata +[Get Ledger Metadata]: #get-ledger-metadata + +Receive information about the ledger. + +**Authorization:** This method MUST be accessible to all clients authorized as account owners or administrators. This method MAY be accessible to unauthorized users. + +#### Request Format + +``` +GET / +``` + +**[Metadata URL Name][]:** None (top-level path for the ledger) + +##### Response Format + +A successful result uses the HTTP response code **200 OK** and contains a JSON object with the following fields. (A field name like `foo[].bar` indicates that the row describes the field `bar` in _each_ object contained in the array `foo`.) + +| Field | Value | Description | +|:------------------|:----------|:---------------------------------------------| +| `currency_code` | String | Three-letter ([ISO 4217](http://www.xe.com/iso4217.php)) code of the currency this ledger tracks. | +| `currency_symbol` | String | Currency symbol to use in user interfaces for the currency represented in this ledger. For example, "$". | +| `ilp_prefix` | String | The ILP Address Prefix of the ledger. This must match the definition from [IL-RFC-15: ILP Addresses](https://github.com/interledger/rfcs/blob/master/0015-ilp-addresses/0015-ilp-addresses.md). | +| `connectors` | Array | Array of _recommended_ ILP Connectors. Each member of this list MUST be an object with two fields, as described below. This list MAY be empty. | +| `precision` | Integer | How many total decimal digits of precision this ledger uses to represent currency amounts. | +| `scale` | Integer | How many digits after the decimal place this ledger supports in currency amounts. | +| `urls` | Object | Paths to other methods exposed by this ledger. Each field name is short name for a method and the value is the path to that method. Some fields MUST be present; see [URLs][] for details. | +| `rounding` | String | _(Optional)_ The type of rounding used internally by ledger for values that exceed the reported `scale` or `precision`. If provided, the value MUST be `floor`, `ceiling`, or `nearest`. | +| ... | (Various) | _(Optional)_ Additional arbitrary values as desired. | + +Each member of the `connectors` array MUST have the following fields: + +| Field | Value | Description | +|:-------|:--------|:----------------------------------------------------------| +| `id` | [URL][] | HTTP(S) URL where a client can [get the connector's account at this ledger][Get Account]. | +| `name` | String | The connector's unique username in this ledger. | + +#### Example + +Request: + +``` +GET / +``` + +Response: + +```json +HTTP/1.1 200 OK + +{ + "currency_code": "EUR", + "currency_symbol": "€", + "connectors": [{ + "id": "https://red.ilpdemo.org/ledger/accounts/connie", + "name": "connie" + }], + "urls": { + "health": "https://red.ilpdemo.org/ledger/health", + "transfer": "https://red.ilpdemo.org/ledger/transfers/{id}", + "transfer_fulfillment": "https://red.ilpdemo.org/ledger/transfers/{id}/fulfillment", + "account": "https://red.ilpdemo.org/ledger/accounts/{name}", + "websocket": "wss://red.ilpdemo.org/ledger/websocket/" + }, + "precision": 10, + "scale": 2 +} +``` + + +#### Errors + +- This method does not return any errors under normal circumstances. + + +### Prepare Transfer +[Prepare Transfer]: #prepare-transfer + +Prepares a new transfer (conditional or unconditional) in the ledger, or updates an existing transfer. If updating an existing transfer, only the `authorized` field of a debit can be changed. Only the owner of a particular account may set the `authorized` field of a debit to that account. + +An unconditional transfer executes automatically as soon as all debits are authorized. A conditional transfer holds the debited funds until it is executed by the [Submit Fulfillment][] method or it is rejected. See [Transfer States][] for more information. + +A ledger SHOULD reject a request to create a `proposed` transfer, unless the client is authenticated as one of the accounts in the `debits` or `credits` of the transfer, or the client is authenticated as an administrator. + +**Authorization:** The owner of an account MUST be able to prepare a conditional transfer that debits the account. Non-administrators MUST NOT be able to prepare a transfer that debits a different account. A ledger MAY disallow non-administrators from preparing unconditional transfers. + +#### Request Format + +``` +PUT /transfers/{id} +``` + +**[Metadata URL Name][]:** `transfer` + +##### URL Parameters + +| Field | Value | Description | +|:------|:--------|:----------------------------------------------------------| +| `id` | [URL][] | A new [Transfer ID][] URL, including a [UUID][]. | + +##### Body Parameters + +The message body should be a JSON [Transfer resource][]. The ledger MUST ignore any values specified for "Ledger-provided" fields in the request. The request MAY omit the following fields of the transfer resource: + +| Field | Behavior if Omitted | +|:------------------------------------------------|:---------------------------| +| `cancellation_condition` | The transfer cannot be canceled by crypto-condition. (The transfer can still be [rejected][Get Transfer] by a receiver.) | +| `execution_condition` | The transfer is unconditional, and executes immediately after all debits are authorized. | +| `expires_at` | The transfer has no time-based expiration. | +| `memo` field of objects in `debits` array | No memo is associated with this debit. | +| `authorized` field of objects in `debits` array | The `authorized` value defaults to `false`. The transfer cannot execute until the owner of the account to be debited sets the field to true. | +| ("Ledger-provided" fields) | The ledger provides these fields in the response and subsequent [Get Transfer][] responses. | + +#### Response Format + +A successful result uses the HTTP response code **200 OK** and contains a JSON body with the [Transfer resource][] as saved. + +#### Example + +Request: + +```json +PUT /transfers/3a2a1d9e-8640-4d2d-b06c-84f2cd613204 +Content-Type: application/json + +{ + "id": "https://red.ilpdemo.org/ledger/transfers/3a2a1d9e-8640-4d2d-b06c-84f2cd613204", + "debits": [{ + "account": "https://red.ilpdemo.org/ledger/accounts/alice", + "amount": "50", + "authorized": true + }], + "credits": [{ + "account": "https://red.ilpdemo.org/ledger/accounts/bob", + "amount": "50" + }], + "execution_condition": "ni:///sha-256;f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk?fpt=preimage-sha-256&cost=12", + "expires_at": "2015-06-16T00:00:01.000Z" +} +``` + +Response: + +```json +HTTP/1.1 200 OK + + +{ + "id": "https://red.ilpdemo.org/ledger/transfers/3a2a1d9e-8640-4d2d-b06c-84f2cd613204", + "ledger": "https://red.ilpdemo.org/ledger/", + "debits": [{ + "account": "https://red.ilpdemo.org/ledger/accounts/alice", + "amount": "50", + "authorized": true + }], + "credits": [{ + "account": "https://red.ilpdemo.org/ledger/accounts/bob", + "amount": "50" + }], + "execution_condition": "ni:///sha-256;f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk?fpt=preimage-sha-256&cost=12", + "expires_at": "2015-06-16T00:00:01.000Z", + "state": "prepared", + "timeline": { + "prepared_at": "2015-06-15T10:33:01.461Z" + } +} +``` + +#### Errors + +- [InsufficientFundsError][] - A debited account would go below its `minimum_allowed_balance` if this transfer were executed +- [UnprocessableEntityError][] - The request is formatted properly but contains an otherwise-unspecified semantic problem. +- [AlreadyExistsError][] - The `id` supplied is already used by another transfer. +- [InvalidUriParameterError][] - One of the [URL][] or [UUID][] parameters was not formatted properly. +- [InvalidBodyError][] - The request body was not properly-formatted JSON, did not match the Content-Type provided, or did not match the schema for the requested resource type. +- [UnsupportedCryptoConditionError][] - The transfer included an `execution_condition` whose feature bitstring requires functionality not implemented by this ledger. + + +### Submit Fulfillment +[Submit Fulfillment]: #submit-fulfillment + +Execute or cancel a transfer by submitting a [Crypto-Condition Fulfillment][]. + +To execute a transfer, the transfer MUST begin in the `prepared` state and the submitted fulfillment must satisfy the [Crypto-Condition][] in the transfer's `execution_condition` field. Doing so transitions the transaction to `executed`. + +To cancel a transfer, the submitted fulfillment must satisfy the [Crypto-Condition][] in the transfer's `cancellation_condition` field. Doing so transitions the transaction to `rejected`. + +**Authorization:** The owner of an account MUST be able to submit the fulfillment for a transfer that credits the account. A ledger MAY allow other account owners or unauthorized clients to submit a fulfillment. + +#### Request Format + + +``` +PUT /transfers/{id}/fulfillment +Content-Type: text/plain +``` + +**Caution:** This method requires the request to specify the header `Content-Type: text/plain` + +**[Metadata URL Name][]:** `transfer_fulfillment`. + +##### Body Parameters + +The message body should be a [Crypto-Condition Fulfillment][] in string format. + +#### Response Format + +A successful result uses the HTTP response code **201 Created** ONLY if the fulfillment was executed or canceled as a result of this request. This method returns **200 OK** if the fulfillment matched a condition, but the transfer had already been executed or canceled before processing this request. The message body of the response is the submitted fulfillment as plain text. + +#### Example + +Request: + +``` +PUT /transfers/3a2a1d9e-8640-4d2d-b06c-84f2cd613204/fulfillment +Content-Type: text/plain + +oA6ADEhlbGxvIFdvcmxkIQ +``` + +Response: + +``` +HTTP/1.1 201 Created + +oA6ADEhlbGxvIFdvcmxkIQ +``` + +#### Errors + +- [NotFoundError][] - The transfer does not exist. +- [UnmetConditionError][] - The fulfillment does not match the condition. +- [TransferNotConditionalError][] - The transfer had no condition to fulfill. +- [TransferStateError][] - The transfer was not in the `prepared` state when the request was received. See [Transfer States][] for more information. +- [UnprocessableEntityError][] - The request is formatted properly but contains an otherwise-unspecified semantic problem. +- [InvalidBodyError][] - The request body was not properly-formatted JSON, or did not match the schema for the request. + + +### Reject Transfer +[Reject Transfer]: #reject-transfer + +Reject a prepared transfer. A transfer can be rejected if and only if that transfer is in the `prepared` state. Doing so transitions the transfer to the `rejected` state. + +**Authorization:** Accounts from the `credits` field of a transfer MUST be able to reject the transfer. Any other non-admin user MUST NOT be able to reject the transfer. + +#### Request Format + +``` +PUT /transfers/{id}/rejection +Content-Type: text/plain + +your rejection reason here +``` + +**[Metadata URL Name][]:** `transfer_rejection`. + +This request MUST use the header `Content-Type: text/plain`. + +##### Body Parameters + +The message body contains the rejection reason as plain text. The rejection reason is an arbitrary string that is intended to be a machine-readable identifier indicating the reason for the transfer's rejection. This string should be no more than 2KB or 512 UTF-8 characters. + +#### Response Format + +A successful result uses the HTTP response code **200 OK** and contains a JSON object containing the updated [Transfer resource][] containing the rejection reason. + +#### Example + +Request: + +```json +PUT /transfers/3a2a1d9e-8640-4d2d-b06c-84f2cd613204/rejection +Content-Type: text/plain + +BlacklistedSender +``` + +Response: + +```json +HTTP/1.1 200 OK + +{ + "ledger": "https://red.ilpdemo.org/ledger", + "id": "63a61fa9-fca2-4779-9d50-49dc691b8fbf", + "debits": [{ + "account": "https://red.ilpdemo.org/ledger/accounts/alice", + "amount": "50", + "authorized": true + }], + "credits": [{ + "account": "https://red.ilpdemo.org/ledger/accounts/bob", + "amount": "50" + }], + "amount": "199.99", + "execution_condition": "ni:///sha-256;f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk?fpt=preimage-sha-256&cost=12", + "expires_at": "2018-01-01T00:00:00.000Z", + "state": "rejected", + "timeline": { + "prepared_at": "2016-10-21T22:45:01.000Z", + "rejected_at": "2016-10-22T21:33:34.867Z" + }, + "rejection_reason": "BlacklistedSender" +} +``` + +#### Errors + +- [NotFoundError][] - The transfer does not exist. +- [UnprocessableEntityError][] - The request is formatted properly but contains an otherwise-unspecified semantic problem. This includes cases where the transfer has already been executed or rejected. +- [InvalidBodyError][] - The request's message body was too long or the request did not use the proper Content-Type header (text/plain). +- [TransferStateError][] - The transfer was not in the `prepared` state when the request was received. See [Transfer States][] for more information. + + +### Get Transfer +[Get Transfer]: #get-transfer + +Check the details or status of a local transfer. + +**Authorization:** The accounts from the `credits` and `debits` field of a transfer MUST be able to get that transfer. A ledger MAY allow other account owners or unauthorized clients to get a transfer. + +#### Request Format + +``` +GET /transfers/{id} +``` + +**[Metadata URL Name][]:** `transfer`. Suggested generic path: `/transfers/{id}` + +#### Response Format + +A successful result uses the HTTP response code **200 OK** and contains a JSON body with the requested [Transfer resource][]. + +#### Example + +Request: + +``` +GET /transfers/63a61fa9-fca2-4779-9d50-49dc691b8fbf +``` + +Response: + +```json +HTTP/1.1 200 OK + +{ + "ledger": "https://red.ilpdemo.org/ledger", + "id": "63a61fa9-fca2-4779-9d50-49dc691b8fbf", + "debits": [{ + "account": "https://red.ilpdemo.org/ledger/accounts/alice", + "amount": "50", + "authorized": true + }], + "credits": [{ + "account": "https://red.ilpdemo.org/ledger/accounts/bob", + "amount": "50" + }], + "amount": "199.99", + "execution_condition": "ni:///sha-256;f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk?fpt=preimage-sha-256&cost=12", + "expires_at": "2018-01-01T00:00:00.000Z", + "state": "rejected", + "timeline": { + "prepared_at": "2016-10-21T22:45:01.000Z", + "rejected_at": "2016-10-22T21:33:34.867Z" + }, + "rejection_reason": "BlacklistedSender" +} +``` + +#### Errors + +- [NotFoundError][] - No transfer with a matching ID was found. +- [InvalidUriParameterError][] - The transfer ID from the request was not a valid [UUID][]. + + +### Get Transfer Fulfillment +[Get Transfer Fulfillment]: #get-transfer-fulfillment + +Retrieve the fulfillment for a transfer that has been executed or rejected. This is separate from the [Transfer resource][] because it can be very large. + +**Authorization:** The ledger MUST allow account owners from the account's `debits` and `credits` to get the transfer's fulfillment. A ledger MAY allow other account owners or unauthorized users to get a transfer's fulfillment. + +#### Request Format + +``` +GET /transfers/{id}/fulfillment +``` + +**[Metadata URL Name][]:** `transfer_fulfillment`. + +##### URL Parameters + +| Field | Value | Description | +|:------|:------|:------------------------------------------------------------| +| `id` | UUID | The [UUID][] of the Transfer whose fulfillment to retrieve. | + +#### Response Format + +A successful result uses the HTTP response code **200 OK** and the header `Content-Type: text/plain`. The body contains the Transfer's [Crypto-Condition Fulfillment][] in text format. + +#### Example + +Request: + +``` +GET /transfers/3a2a1d9e-8640-4d2d-b06c-84f2cd613204/fulfillment +``` + +Response: + +``` +HTTP/1.1 200 OK +Content-Type: text/plain + +oA6ADEhlbGxvIFdvcmxkIQ +``` + +#### Errors + +- [NotFoundError][] - The specified transfer does not exist, +- [InvalidUriParameterError][] + + +### Get Account +[Get Account]: #get-account + +Get an account resource. + +**Authorization:** The owner of an account MUST be able to get the account resource. A ledger MAY allow other account owners or unauthorized users to get other account resources. A ledger MAY return a subset of fields when it returns an account resource to any client other than the account owner (for example, omitting the balance). + +#### Request Format + +``` +GET /accounts/{name} +``` + +**[Metadata URL Name][]:** `account`. + +#### Response Format + +A successful response uses the HTTP response code **200 OK** and contains the [Account resource][] in JSON format. + +#### Example + +Request: + +``` +GET /accounts/bob +``` + +Response: + +```json +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "id": "https://red.ilpdemo.org/ledger/accounts/bob", + "name": "bob", + "balance": "5007734.0", + "ledger": "https://red.ilpdemo.org/ledger", + "fingerprint": "88:90:3a:e7:e5:1c:c4:51:05:4b:0c:2b:3f:41:df:bf:0c:21:f3:78", + "minimum_allowed_balance": "-infinity" +} +``` + +#### Errors + +- [NotFoundError][] +- [InvalidUriParameterError][] + + +### Create or Update Account +[Create or Update Account]: #create-or-update-account + +Update an account in the ledger. + +**Authorization:** The owner of an account MUST be able to update the account, except for specific fields. An administrator MAY be able to update any fields of any account. + +#### Request Format + +``` +PUT /accounts/{name} +``` + +**[Metadata URL Name][]:** `account`. + +##### URL Parameters + +| Field | Type | Description | +|:-------|:-------|:-----------------------------------------------------------| +| `name` | String | The unique name of the account to create or update. MUST match the regular expression `^[a-zA-Z0-9._~-]{1,256}$`. | + +##### Request Body + +The request is an [Account Resource][]. Any omitted fields are left unchanged. + +#### Response Format + +A successful response uses the HTTP status code **200 OK** and contains a JSON object with the [Account Resource][] as saved. + +#### Example + +Request: + +``` +PUT /accounts/bob +Content-Type: application/json + +{ + "name": "bob", + "minimum_allowed_balance": "-10000", + "fingerprint": "88:90:3a:e7:e5:1c:c4:51:05:4b:0c:2b:3f:41:df:bf:0c:21:f3:78" +} +``` + +Response: + +``` +200 OK +Content-Type: application/json + +{ + "name": "bob", + "minimum_allowed_balance": "-10000", + "ledger": "https://red.ilpdemo.org/ledger", + "is_disabled": false, + "fingerprint": "88:90:3a:e7:e5:1c:c4:51:05:4b:0c:2b:3f:41:df:bf:0c:21:f3:78" +} +``` + +#### Errors + +- [InvalidBodyError][] - The request was not valid JSON, or did not match the schema for the request. +- [InvalidUriParameterError][] - One of the URLs, URIs, or UUIDs in the body is not validly formatted. +- [UnprocessableEntityError][] - The object was syntactically valid but had a semantic issue. +- [UnauthorizedError][] - The client was not authenticated with permissions to modify the account as requested. + + + + +### Send Message +[Send Message]: #send-message + +Try to send a notification to another account. The message is only delivered if the other account is subscribed to [account notifications](#subscribe-to-account) on a WebSocket connection. ILP Clients and ILP Connectors use this method to request and send quotes. + +**Authorization:** The `from` field of the message MUST match the account owner. Unauthenticated users MUST NOT be able to send messages. A ledger MAY allow admin connections to send messages whose `from` value matches the ledger's base [URL][]. + +#### Request Format + +``` +POST /messages +``` + +**[Metadata URL Name][]:** `message`. + +The request body contains a [Message object][] as JSON. + +#### Response Format + +A successful response uses the HTTP response code **201 Created** and contains no message body or an empty message body. + +#### Example + +Request: + +```json +POST /messages +Content-Type: application/json + +{ + "from": "https://blue.ilpdemo.ripple.com/ledger/accounts/bob", + "to": "https://blue.ilpdemo.ripple.com/ledger/accounts/alice", + "data": { + "method": "quote_request", + "id": "721e4126-98a1-4974-b35a-8a8f4655f934", + "data": { + "source_amount": "100.25", + "source_address": "example.eur-ledger.alice", + "destination_address": "example.usd-ledger.bob", + "source_expiry_duration": "6000", + "destination_expiry_duration": "5" + } + } +} +``` + +Response: + +``` +HTTP/1.1 204 No Content +``` + +#### Errors + +- [InvalidBodyError][] - The request was not valid JSON, or did not match the schema for the request. +- [InvalidUriParameterError][] - One of the URLs, URIs, or UUIDs in the body is not validly formatted. +- [UnprocessableEntityError][] - The object was syntactically valid but had a semantic issue. + + +### Get Auth Token +[Get Auth Token]: #get-auth-token + +Get a token that can be used to authenticate future requests. + +**Note:** This method is REQUIRED for ledgers to authenticate [WebSocket][] connections. If the ledger does not authenticate WebSocket connections, this method is OPTIONAL. The ledger MAY allow clients to authenticate for any other methods of the API using the tokens returned by this method. + +#### Request Format + +``` +GET /auth_token +``` + +Depending on the [authentication mechanism](#authorization-and-authentication) used by this ledger, the ledger MAY require the HTTP `Auth` header with a valid username and password, or the ledger may require a different method of authentication. + +**[Metadata URL Name][]:** `auth_token`. + +#### Response Format + +A successful response uses the HTTP response code **200 OK** and contains a JSON object with a token that can be used for subsequent requests. The ledger can generate the token with [JWT](https://jwt.io/) or any similar system. + +#### Example + +Request: + +``` +GET /auth_token +Authorization: Basic myUsername:securePassphrase +``` + +Response: + +```json +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "token": "9AtVZPN3t49Kx07stO813UHXv6pcES" +} +``` + +#### Errors + +- [Unauthorized][] +- [Forbidden][] + + + +## WebSocket +[WebSocket]: #websocket + +**[Metadata URL Name][]:** `websocket`. Suggested generic path: `/websocket` (with `wss://` protocol) + +Clients can subscribe to live, read-only notifications of ledger activity by opening a [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) connection to this path and sending a subscription request. + +### WebSocket Authentication + +Clients MUST authenticate themselves with a token from the [Get Auth Token][] method. This token can be supplied in one of two ways: + +- In the `token` query parameter. For example: + + wss://red.ilpdemo.org/ledger/websocket/?token=9AtVZPN3t49Kx07stO813UHXv6pcES + +- As a "Bearer" token in the `Authorization` HTTP header of the request (before the protocol switch to WebSocket). For example: + + GET /ledger/websocket + Authorization: Bearer 9AtVZPN3t49Kx07stO813UHXv6pcES + Upgrade: websocket + +Clients should prefer to use the `Authorization` header on WebSocket requests, if the client supports it. + +A ledger MAY restrict the data that can be accessed, as long as it complies with the following rules: + +- An account owner MUST be able to subscribe to changes to its own account. +- An account owner MUST be able to subscribe to changes to transactions where the account is the in the `credits` or `debits` fields. +- An account owner MUST be able to subscribe to messages to its account. Account owners and unauthorized users MUST NOT be able to subscribe to messages to other accounts. + +The authentication you use when opening the connection applies to all subscriptions made in the connection. + +A ledger MUST support multiple independent WebSocket connections with the same authentication. (This provides connection redundancy for notifications, which is helpful for ILP Connectors.) + +### WebSocket Messages + +After the connection is established, the client and ledger communicate by passing [JSON-RPC 2.0](http://www.jsonrpc.org/specification) messages back and forth. Both the client application and ledger can take the roles of "client" and "server" in JSON-RPC terms. The client application can submit requests to subscribe or unsubscribe from specific categories of message. The ledger responds directly to the client's requests with confirmation messages, and also sends "notification" requests to the client application. (Notifications are identified by the `id` value `null`.) + +A ledger MUST support the following subscription command: + +- [Subscribe to Account](#subscribe-to-account) + +A ledger MAY support the following subscription commands, or define additional subscription commands: + +- [Subscribe to Transfer](#subscribe-to-transfer) +- Subscribe to All Transfers + - This is useful when you have an application monitoring activity on all accounts and fulfilling transfers for others. + +#### Subscribe to Account + +This request replaces any existing account subscriptions on this WebSocket connection. The client sends a JSON object to the ledger with the following fields: + +| Field | Value | Description | +|:-------------------|:-----------------|:-------------------------------------| +| `id` | String or Number | An arbitrary identifier for this request. MUST NOT be `null`. The immediate response to this request identifies itself using the same `id`. | +| `jsonrpc` | String | MUST have the value `"2.0"` to indicate this is a JSON-RPC 2.0 request. | +| `method` | String | MUST be the value `"subscribe_account"`. | +| `params` | Object | Information on what subscriptions to open. | +| `params.accounts` | Array | Each member of this array must be the [URI][] of an account to subscribe to, as a string. This replaces existing subscriptions; if the array length is zero, the client is unsubscribed from all account notifications. | + +##### Response +The ledger acknowledges the request immediately by sending a JSON object with the following fields: + +| Field | Value | Description | +|:----------|:-----------------|:----------------------------------------------| +| `id` | String or Number | The `id` value from the request. This helps distinguish responses from other messages. | +| `jsonrpc` | String | MUST have the value `"2.0"` to indicate this is a JSON-RPC 2.0 message. | +| `result` | Number | Updated number of active account subscriptions on this WebSocket connection. In practice, this is usually the length of the `params.accounts` array from the request. | + +Later, the ledger responds with [notifications](#websocket-notifications) whenever any of the following occurs: + +- A transfer affecting the account is prepared (`event` type: `transfer.create`) +- A transfer affecting the account changes state. For example, from prepared to executed or to expired. (`event` type: `transfer.update`) +- Someone [sends a message](#send-message) to the account. (`event` type: `message.send`) +- A ledger MAY define additional types of notifications that get sent when you are subscribed to an account. Each new type of notification MUST have a unique `event` value. + +Example: + +``` +--> { + "jsonrpc": "2.0", + "method": "subscribe_account", + "params": { + "accounts": ["https://ledger.example/accounts/alice"] + }, + "id": 1 + } +<-- { + "jsonrpc": "2.0", + "result": 1, + "id": 1 + } +``` + + +#### Subscribe to Transfer + +The client sends a JSON object to the ledger with the following fields: + +| Field | Value | Description | +|:-------------------|:-----------------|:-------------------------------------| +| `id` | String or Number | An arbitrary identifier for this request. MUST NOT be `null`. | +| `jsonrpc` | String | MUST have the value `"2.0"` to indicate this is a JSON-RPC 2.0 request. | +| `method` | String | MUST be the value `"subscribe_transfer"`. | +| `params` | Object | Information on what subscriptions to open. | +| `params.transfers` | Array | Each member of this array must be the [URL][] of a transfer to subscribe to, as a string. This array replaces any existing transfer subscriptions on this WebSocket connection. If the array length is zero, the client is unsubscribed from all transfer notifications. | + +##### Response +The ledger acknowledges the request immediately by sending a JSON object with the following fields: + +| Field | Value | Description | +|:----------|:-----------------|:----------------------------------------------| +| `id` | String or Number | The `id` value from the request. This helps distinguish responses from other messages. | +| `jsonrpc` | String | MUST have the value `"2.0"` to indicate this is a JSON-RPC 2.0 request. | +| `result` | Number | Updated number of active transfer subscriptions on this WebSocket connection. In practice, this is usually the length of the `params.transfers` array from the request. | + +Later, the ledger responds with [notifications](#websocket-notifications) whenever any of the following occurs: + +- The transfer changes state. For example, from prepared to executed or to expired. (`event` type: `transfer.update`) + + +#### WebSocket Notifications + +The ledger sends notifications to connected clients when certain events occur, according to the current subscriptions of the clients. Every notification is sent at most once per WebSocket connection to the ledger, even if a client is subscribed to multiple categories of message that should prompt the same notification. If a client has multiple connections open, the ledger MUST attempt to send a message on every open connection. + +All event notifications from the ledger are in the format of JSON objects with the following fields: + +| Field | Value | Description | +|:---------------------------|:--------|:--------------------------------------| +| `jsonrpc` | String | MUST have the value `"2.0"` to indicate this is a JSON-RPC 2.0 notification. | +| `id` | Null | MUST be `null` to indicate a notification. | +| `method` | String | MUST be the value `"notify"` | +| `params` | Object | Nested object with information about the notification. | +| `params.event` | String | The type of event that prompted this notification. Valid types include `transfer.create`, `transfer.update`, and others. See [Event Types](#event-types) for more information. | +| `params.id` | [URL][] | _(Optional)_ A [UUID][] to uniquely identify this notification. | +| `params.resource` | Object | An object related to `event` that occurred. In most cases, this is a [Transfer resource][] as it was created or updated. | +| `params.related_resources` | Object | _(Not present in all responses)_ Contains additional resources related to this notification in named sub-fields, depending on the `event` type. In particular, this MUST contain the fulfillment when a transfer is updated to the `executed` state. | + +##### Event Types +[Event Types]: #event-types + +A ledger MAY define custom `event` types. A ledger MUST support at least the following `event` values: + +| Value | Resource | Description | +|:------------------|:----------------------|:---------------------------------| +| `transfer.create` | [Transfer resource][] | Occurs when a new transfer is prepared. Sent to clients subscribed to the accounts from the `debits` and `credits` fields of the transfer. If the transfer is unconditional, this notification indicates the state of the transaction after execution. The `related_resources` field is omitted. | +| `transfer.update` | [Transfer resource][] | Occurs when a transfer changes state from `prepared` to `executed` or `rejected`. If the transfer was executed, the `execution_condition_fulfillment` field of `related_resources` MUST contain the fulfillment. If the transfer was rejected, the `related_resources` field is empty. | +| `message.send` | [Message resource][] | Occurs when someone else sends a message. | + +If a ledger creates custom event types, their values should follow the convention `{resource}.{event}`. + + +## API Error Codes + +The Five Bells Ledger API may return errors using HTTP codes in the range 400-599, depending on the type of error. The message body of the error response is a JSON object with additional information about the nature of the error. + +Every error response contains at least the following fields: + +| Field | Type | Description | +|:----------|:-------|:--------------------------------------------------------| +| `id` | String | A unique error code for this specific type of error, such as `UnmetConditionError`. | +| `message` | String | A longer, human-readable description for the cause of the error. | + +Errors may also have additional arbitrary fields describing the cause or context of the error. + +**Note:** Any fields other than `id` and `message` should be considered optional and informational. Clients MUST NOT depend on the presence of those fields. The examples in this spec contain suggestions for additional fields, but those should not be taken as requirements. + +### Unauthorized +[Unauthorized]: #unauthorized + +The [authentication information](#authorization-and-authentication) supplied to this request was insufficient for one of the following reasons: + +- This method requires authentication but none was provided +- The credentials were provided using a system not supported by this method. (For example, using Basic Auth on a method that requires token authentication.) + +A ledger MAY return any message body using any content type with this error. (This makes it easier to use proxy servers and stock server configuration to handle authorization.) + +**HTTP Status Code:** 401 Unauthorized + +```json +HTTP/1.1 401 Unauthorized + +{ + "error_id": "Unauthorized", + "message": "Auth token not found" +} +``` + + +### Forbidden +[Forbidden]: #forbidden + +The request provided + +- The credentials were malformed or don't match the known account information +- The authenticated role does not have permission to perform the requested operation + +A ledger MAY return any message body using any content type with this error. (This makes it easier to use proxy servers and stock server configuration to handle authorization.) + +**HTTP Status Code:** 403 Forbidden + +```json +HTTP/1.1 403 Forbidden + +{ + "error_id": "Forbidden", + "message": "Client certificate doesn't match known fingerprint" +} +``` + +### InvalidUriParameterError +[InvalidUriParameterError]: #invaliduriparametererror + +At least one provided [URL][] or [UUID][] parameter was invalid. + +**HTTP Status Code:** 400 Bad Request + +```json +HTTP/1.1 400 Bad Request + +{ + "error_id": "InvalidUriParameterError", + "message": "id is not a valid Uuid", + "validationErrors": [{ + "message": "String does not match pattern: ^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", + "params": { + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" + }, + "code": 202, + "dataPath": "", + "schemaPath": "/pattern", + "subErrors": null, + "stack": "..." + }] +} +``` + + +### InvalidBodyError +[InvalidBodyError]: #invalidbodyerror + +The submitted JSON entity does not match the required schema. + +**HTTP Status Code:** 400 Bad Request + +```json +HTTP/1.1 400 Bad Request + +{ + "error_id": "InvalidBodyError", + "message": "Body did not match schema Transfer", + "validationErrors": [{ + "message": "Missing required property: id", + "params": { + "key": "id" + }, + "code": 302, + "dataPath": "", + "schemaPath": "/required/0", + "subErrors": null, + "stack": "..." + }] +} +``` + +### NotFoundError +[NotFoundError]: #notfounderror + +The requested resource could not be found. + +**HTTP Status Code:** 404 Not Found + +```json +HTTP/1.1 404 Not Found + +{ + "error_id": "NotFoundError", + "message": "Unknown transfer." +} +``` + +### UnprocessableEntityError +[UnprocessableEntityError]: #unprocessableentityerror + +The provided entity is syntactically correct, but there is a generic semantic problem with it. + +**HTTP Status Code:** 422 Unprocessable Entity + +```json +HTTP/1.1 422 Unprocessable Entity + +{ + "error_id": "UnprocessableEntityError", + "message": "Debits and credits are not equal" +} +``` + +### InsufficientFundsError +[InsufficientFundsError]: #insufficientfundserror + +The source account does not have sufficient funds to satisfy the request. + +**HTTP Status Code:** 422 Unprocessable Entity + +```json +HTTP/1.1 422 Unprocessable Entity + +{ + "error_id": "InsufficientFundsError", + "message": "Sender has insufficient funds.", + "account": "santiago" +} +``` + +### AlreadyExistsError +[AlreadyExistsError]: #alreadyexistserror + +The specified entity already exists and may not be modified. + +**HTTP Status Code:** 422 Unprocessable Entity + +```json +HTTP/1.1 422 Unprocessable Entity + +{ + "error_id": "AlreadyExistsError", + "message": "Can't modify transfer after execution." +} +``` + +### UnauthorizedError +[UnauthorizedError]: #unauthorizederror + +You do not have permissions to access or modify this resource in the requested way. + +**HTTP Status Code:** 403 Forbidden + +```json +HTTP/1.1 403 Forbidden + +{ + "error_id": "UnauthorizedError", + "message": "Only the beneficiary can post the fulfillment." +} +``` + +### UnmetConditionError +[UnmetConditionError]: #unmetconditionerror + +The submitted fulfillment does not meet the specified [Crypto-Condition][]. + +**HTTP Status Code:** 422 Unprocessable Entity + +```json +HTTP/1.1 422 Unprocessable Entity + +{ + "error_id": "UnmetConditionError", + "message": "Fulfillment does not match condition.", + "condition": "ni:///sha-256;f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk?fpt=preimage-sha-256&cost=12", + "fulfillment": "oA6ADGhlbGxvIHdvcmxkLg" +} +``` + +### TransferNotConditionalError +[TransferNotConditionalError]: #transfernotconditionalerror + +Occurs when a client tries to submit a fulfillment to a transfer that does not have a [Crypto-Condition][] to fulfill. + +**HTTP Status Code:** 422 Unprocessable Entity + +```json +HTTP/1.1 422 Unprocessable Entity + +{ + "error_id": "TransferNotConditionalError", + "message": "Transfer does not have a condition.", + "transfer_id": "da405506-0fbe-4454-9b6f-c413977a7f3c" +} +``` + +### UnsupportedCryptoConditionError +[UnsupportedCryptoConditionError]: #unsupportedcryptoconditionerror + +The transfer uses a [Crypto-Condition][] with features not supported by this ledger's implementation of Crypto-Conditions. + +**HTTP Status Code:** 422 Unprocessable Entity + +```json +HTTP/1.1 422 Unprocessable Entity + +{ + "error_id": "UnsupportedCryptoConditionError", + "message": "Ledger does not support this CryptoCondition type.", + "condition": "ni:///sha-256;f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk?fpt=preimage-sha-256&cost=12", +} +``` + + + +### TransferStateError +[TransferStateError]: #transferstateerror + +The requested operation could not be executed because the transfer was not in the correct state when the request was received. This occurs if you try to execute or reject a transfer that has already been executed, rejected, or expired. + +**HTTP Status Code:** 422 Unprocessable Entity + +```json +HTTP/1.1 422 Unprocessable Entity + +{ + "error_id": "TransferStateError", + "message": "Transfer 3f1080b4-b296-4de5-8a16-55362ee0624f expired 594726 seconds ago", + "expires_at": "2016-10-25T08:41:59.795Z", + "current_time": "2016-10-25T08:51:54.521Z", + "transfer_id": "3f1080b4-b296-4de5-8a16-55362ee0624f" +} +``` diff --git a/0012-five-bells-ledger-api/5BL-transfer-states.png b/0012-five-bells-ledger-api/5BL-transfer-states.png new file mode 100644 index 00000000..b927e995 Binary files /dev/null and b/0012-five-bells-ledger-api/5BL-transfer-states.png differ diff --git a/0012-five-bells-ledger-api/5BL-transfer-states.uxf b/0012-five-bells-ledger-api/5BL-transfer-states.uxf new file mode 100644 index 00000000..65746cbc --- /dev/null +++ b/0012-five-bells-ledger-api/5BL-transfer-states.uxf @@ -0,0 +1,366 @@ + + + 10 + + Relation + + 220 + 60 + 140 + 150 + + lt=<- +Prepare Transfer + + + + + + 10.0;130.0;10.0;10.0 + + + UMLSpecialState + + 220 + 60 + 20 + 20 + + type=initial + + + + UMLSpecialState + + 220 + 180 + 40 + 40 + + type=decision + + + + Relation + + 250 + 180 + 240 + 60 + + lt=<- +[transfer contains debits +that aren't set to +"authorized": true] + 220.0;20.0;10.0;20.0 + + + UMLState + + 470 + 180 + 100 + 40 + + proposed + + + + Relation + + 240 + 110 + 380 + 110 + + lt=<- +Prepare Transfer +(by a different account) + 10.0;80.0;10.0;20.0;360.0;20.0;360.0;90.0;330.0;90.0 + + + UMLState + + 380 + 360 + 100 + 40 + + prepared + + + + Relation + + 230 + 210 + 210 + 110 + + lt=<- +[all debits are authorized.] +Funds become held. + 10.0;90.0;10.0;10.0 + + + UMLState + + 100 + 610 + 100 + 40 + + executed + + + + UMLState + + 420 + 700 + 100 + 40 + + rejected + + + + Relation + + 450 + 380 + 220 + 260 + + lt=<- +Any credited account +calls Reject Transfer. + 10.0;240.0;10.0;190.0;200.0;190.0;200.0;10.0;30.0;10.0 + + + UMLSpecialState + + 220 + 300 + 40 + 40 + + type=decision + + + + Relation + + 120 + 310 + 200 + 250 + + lt=<- +[no execution_condition] + + + + + 10.0;230.0;10.0;10.0;100.0;10.0 + + + Relation + + 250 + 310 + 360 + 70 + + lt=<- +[has execution_condition] + + + 160.0;50.0;160.0;10.0;10.0;10.0 + + + Relation + + 140 + 390 + 280 + 170 + + lt=<- +Submit Fulfillment +with fulfillment +matching the +execution_condition. + 10.0;150.0;10.0;100.0;260.0;100.0;260.0;10.0 + + + UMLSpecialState + + 130 + 670 + 20 + 20 + + type=final + + + + Relation + + 130 + 640 + 30 + 50 + + lt=<- + 10.0;30.0;10.0;10.0 + + + UMLSpecialState + + 450 + 780 + 20 + 20 + + type=final + + + + Relation + + 450 + 730 + 30 + 70 + + lt=<- + 10.0;50.0;10.0;10.0 + + + UMLTimer + + 780 + 340 + 120 + 90 + + expires_at time +passes + + + + Relation + + 440 + 390 + 190 + 260 + + lt=<- +Submit Fulfillment +with fulfillment +matching the +cancellation_condition. + + + + + + 10.0;240.0;10.0;10.0 + + + UMLSyncBarHorizontal + + 700 + 430 + 80 + 20 + + lw=5 + + + + + Relation + + 470 + 350 + 270 + 110 + + lt=<- +[has expires_at] + 250.0;90.0;250.0;20.0;10.0;20.0;10.0;30.0 + + + Relation + + 740 + 350 + 100 + 110 + + lt=<- + 10.0;90.0;10.0;10.0;80.0;10.0 + + + Relation + + 460 + 430 + 300 + 220 + + lt=<- + 10.0;200.0;10.0;180.0;280.0;180.0;280.0;10.0 + + + UMLSpecialState + + 440 + 620 + 40 + 40 + + type=decision + + + + Relation + + 450 + 650 + 190 + 70 + + lt=<- +Held funds are released + + 10.0;50.0;10.0;10.0 + + + UMLSpecialState + + 120 + 530 + 40 + 40 + + type=decision + + + + Relation + + 130 + 560 + 150 + 70 + + lt=<- +Transfer executes + + 10.0;50.0;10.0;10.0 + + diff --git a/0014-http-ilp/0014-http-ilp.md b/0014-http-ilp/0014-http-ilp.md new file mode 100644 index 00000000..af0f89a9 --- /dev/null +++ b/0014-http-ilp/0014-http-ilp.md @@ -0,0 +1,140 @@ +--- +title: HTTP-ILP +draft: 3 +--- +# HTTP-ILP + +> A standard for paid HTTP requests. + +# Design Goals + +Design a protocol to pay for HTTP requests. + +Criteria: + +* Minimum number of roundtrips +* Interaction with HTTP server is via HTTP (actually HTTPS) calls only - we want to tie into the existing load balancing and security infrastructure (HTTPS), no need to run any JavaScript/Websocket client on the HTTP server or other shenanigans + +# Flows + +## 1. Setup + +###### Scenario + +**Ankita** is a *server admin* who owns a file hosting service at `myservice.example` and would like to provide an API where her users can upload files without having to register first. In order to enable this in a standards-compliant way, she decides to use HTTP-ILP. + +### 1.1. Server admin installs an ILP payment module + +After deciding to use HTTP-ILP, Ankita searches the web on instructions on how to set this up. She finds an open-source HTTP-ILP server module which is compatible with the REST framework ([Koa](https://koajs.com)) that she is already using, for instance [koa-ilp](https://github.com/justmoon/koa-ilp). + +She installs the HTTP-ILP server module. The module provides middleware which she adds to the different API endpoints in order to set prices for each one. + +According to the documentation of the HTTP-ILP server module, she learns that she can pass an [Interledgerjs plugin](../0004-ledger-plugin-interface/0004-ledger-plugin-interface.md) to the module, to receive payments. + +### 1.2. Server admin sets up a new receiver + + +Ankita creates a dedicated subaccount under her account at her Interledger service provider, and takes a note of that subaccount's credentials. + +### 1.3. Server admin enters credentials into the HTTP-ILP server module config + +Next, Ankita logs back into her server and edits a config file of the HTTP-ILP server module to enter the plugin type and credentials she obtained: + +```sh +export PLUGIN_NAME=ilp-plugin-btp-client +export PLUGIN_CONFIG={"server":"btp+wss://ankita+filepay:fxPERNaS4FGlC8H7eg6UfYVlglmFynFc8nh5la9PBGM@nexus.justmoon.com"} +``` + +Next, she restarts her server to load the new configuration. + +### 1.4. Paid HTTP server fetches receiver information + +When the HTTP-ILP server module loads up, it calls [plugin#connect](../0004-ledger-plugin-interface/0004-ledger-plugin-interface.md#connect) so that the plugin +gets a subscription to its ledger. Through this subscription, the plugin can listen for incoming payments. +The module then uses [plugin#getAccount](../0004-ledger-plugin-interface/0004-ledger-plugin-interface.md#getaccount) to look up the Interledger address from the plugin +and caches it in memory for a certain period. + +This completes the setup process. The server is now ready to receive paid API requests. + +## 2. Client interaction + +###### Scenario + +**Marat** is a *graphic designer* who would like to upload a mockup image to share with a client. His friend recommended a tool which doesn't require any signup or configuration and uses his existing ILP account to pay for the storage and bandwidth fees. + +![Sequence Diagram](sequence.png) + +### 2.1. Client generates a server-specific token from the hostname and its local secret: + +The uploader tool contains an HTTP-ILP client module +like [superagent-ilp](https://github.com/justmoon/superagent-ilp) or [ilp-curl](https://github.com/sharafian/ilp-curl). +This client module may for instance generate and locally save a `client_secret` using 256 bits of cryptographically secure randomness. + +Before making the paid HTTP request, the HTTP-ILP client module generates a **token**, for instance using such a `client_secret` and the `hostname` of the server it is about to make a request to: + +* Token: `HMAC(SHA256, client_secret, hostname)` + +### 2.1. Client attempts to call API using token + +The paid HTTP request is fired off: + +``` http +OPTIONS /upload HTTP/1.1 +Host: myservice.example +Pay-Token: 7y0SfeN7lCuq0GFF5UsMYZofIjJ7LrvPvsePVWSv450 +Unhash-Content-Length: 123 +``` + +Note that the client hasn't paid at this point and is only making the request to solicit a response from the server, that's why the OPTIONS verb is used. In this example, +`Unhash-Content-Length` is an application-specific header which describes the request which the client intends to make. + +### 2.2. Server responds with payment details + +The server returns an HTTP code of `204 No Content` and includes response headers showing the amount, an ILP address and a shared secret. +The amount expresses how much the request would have cost, as a decimal string and counted in the base unit of the ledger to which the ILP address belongs. + +``` http +HTTP/1.1 204 No Content +Pay: interledger-psk2 us.nexus.ankita.~recv.filepay SkTcFTZCBKgP6A6QOUVcwWCCgYIP4rJPHlIzreavHdU 10 +Pay-Balance: 0 +``` + +The client can now use the shared secret to create a condition to pay this host. The shared secret may for instance be generated by the server as follows: + +* Shared Secret: `HMAC(SHA256, receiver_secret, token)` + +The shared_secret is now a shared secret between the client and server, but will be unknown to any third-party connectors between them. + +### 2.3. Client initiates a PSK2 payment to refill its balance + +In order to refill its balance, the client now creates a PSK2 payment with the following properties: + +* destinationAccount: `'us.nexus.ankita.~recv.filepay'` +* destinationAmount: `100` +* sharedSecret: `'SkTcFTZCBKgP6A6QOUVcwWCCgYIP4rJPHlIzreavHdU'` +* Memo: `pay_token` + +As the PSK2 payment progresses, the token's balance is increased. + +### 2.6. Sender/client receives the fulfillment. + +Once the payment is complete, the client will retry its original request: + +``` http +POST /upload HTTP/1.1 +Host: myservice.example +Pay-Token: 7y0SfeN7lCuq0GFF5UsMYZofIjJ7LrvPvsePVWSv450 +``` +``` +[...] +``` + +The request succeeds: + +``` http +HTTP/1.1 200 OK +Pay: interledger-psk2 us.nexus.ankita.~recv.filepay SkTcFTZCBKgP6A6QOUVcwWCCgYIP4rJPHlIzreavHdU 10 +Pay-Balance: 90 +``` + +Notice how the 100 units credit from the payment was added to the balance and the 10 unit cost for the current request was subtracted. diff --git a/0014-http-ilp/sequence.mmd b/0014-http-ilp/sequence.mmd new file mode 100644 index 00000000..dd28e56f --- /dev/null +++ b/0014-http-ilp/sequence.mmd @@ -0,0 +1,29 @@ +sequenceDiagram +participant Payer +participant Interledger +participant PayeeWallet +participant Payee +activate Payer +Payer->>+Payee: OPTIONS preflight +Payee-->>-Payer: 204 preflight response +Payer->>Interledger: Quote Request +activate Interledger +Interledger->>Payer: Quote Response +deactivate Interledger +Payer->>Interledger: Prepare Payment +activate Interledger +Interledger->>PayeeWallet: Prepare Payment +deactivate Interledger +activate PayeeWallet +PayeeWallet->>+Payee: Webhook Request +deactivate PayeeWallet +Payee-->>-PayeeWallet: Webhook Response +activate PayeeWallet +PayeeWallet-->>Interledger: Fulfill Payment +deactivate PayeeWallet +activate Interledger +Interledger-->>Payer: Payment Fulfilled +deactivate Interledger +Payer->>+Payee: POST Paid Request +Payee-->>-Payer: 200 OK +deactivate Payer diff --git a/0014-http-ilp/sequence.png b/0014-http-ilp/sequence.png new file mode 100644 index 00000000..f3b2b7d8 Binary files /dev/null and b/0014-http-ilp/sequence.png differ diff --git a/0014-http-ilp/sequence.svg b/0014-http-ilp/sequence.svg new file mode 100644 index 00000000..c54f15f0 --- /dev/null +++ b/0014-http-ilp/sequence.svg @@ -0,0 +1,20 @@ +PayerInterledgerPayeeWalletPayeeAttempt Request402 Payment RequiredQuote RequestQuote ResponsePrepare PaymentPrepare PaymentWebhook RequestWebhook ResponseFulfill PaymentPayment FulfilledRetry Original Request200 OKPayerInterledgerPayeeWalletPayee \ No newline at end of file diff --git a/0015-ilp-addresses/0015-ilp-addresses.md b/0015-ilp-addresses/0015-ilp-addresses.md new file mode 100644 index 00000000..2d5c94dc --- /dev/null +++ b/0015-ilp-addresses/0015-ilp-addresses.md @@ -0,0 +1,189 @@ +--- +title: ILP Addresses +draft: 2 +--- +# ILP Addresses - v1.0.0 + +_ILP addresses_ provide a way to [route](#routing) payments to their intended destination through a recursive series of hops, including any number of ILP Connectors. (This happens after the payment is set up on by a higher-level payment setup protocol such as [SPSP](../0009-simple-payment-setup-protocol/0009-simple-payment-setup-protocol.md).) Addresses are not meant to be user-facing, but allow several ASCII characters for easy debugging. + +Addresses can be subdivided into two categories: + +- **Destination Addresses** are complete addresses that can receive payments. A destination address always maps to one account in a ledger. (It can also provide more specific information, such as an invoice ID or a sub-account.) Destination addresses MUST NOT end in a period (`.`) character. +- **Address Prefixes** are incomplete addresses representing a group of destination addresses. Many depths of grouping are possible: groups of accounts or sub-accounts, an individual ledger or subledger, or entire neighborhoods of ledgers. Address prefixes MUST end in a period (`.`) character. Payment setup protocols MUST reject payments to address prefixes. + +Both types of address usually contain one or more period characters as separators. + +## Routing + +Connectors maintain a _routing table_ of ILP addresses. Routing is a recursive lookup through the routing tables of any number of connectors. When a connector receives a query, it finds the [longest prefix match](https://en.wikipedia.org/wiki/Longest_prefix_match) for the queried address. Then, it follows one of the following cases: + +- If the matching address is marked for local delivery, the connector prepares a transfer to that address in one of the ledgers connected to it. The connector maps the ILP address to an account within the ledger. (This is the base case.) +- If the matching address is marked as forwarded delivery, it has the address of another connector associated with it in the routing table. The connector makes a routing lookup on the connector associated with the address. (This is the recursive case.) + +## Address Requirements + +ILP Addresses must meet the following requirements: + +1. The address MUST begin with a prefix indicating the allocation scheme. See [Allocation Schemes](#allocation-schemes) for more information. +2. Each "segment" of the address MUST contain one or more of the following characters: + - Alphanumeric characters, upper or lower case. (Addresses are **case-sensitive** so that they can contain data encoded in formats such as base64url.) + - Underscore (`_`) + - Tilde (`~`) + - Hyphen (`-`) +3. Each segment MUST be separated from other segments by a period character (`.`). +4. Address prefixes MUST end in a period (`.`) character and MAY contain any number of segments after the allocation scheme prefix. +5. Destination addresses MUST NOT end in a period (`.`) character, and MUST contain at least two segments after the allocation scheme prefix. +6. The total length of an ILP Address must be no more than **1023 characters** including the allocation scheme prefix, separators, and all segments. + +The following ABNF specification defines the format for the contents of all ILP addresses and address prefixes. (You must also enforce the overal length requirement of 1023 characters or less.) + +```abnf +address = scheme separator *prefix [segment] + ; the last segment is REQUIRED for destination addresses + +scheme = "g" / "private" / "example" / "peer" / "self" / + "test" / "test1" / "test2" / "test3" + +separator = "." + +prefix = 1*(segment separator) + +segment = 1*( ALPHA / DIGIT / "_" / "~" / "-" ) +``` + +You can also use the following regular expressions to verify the same requirements: + +All Addresses: + + (?=^.{1,1023}$)^(g|private|example|peer|self|test[1-3]?)[.]([a-zA-Z0-9_~-]+[.])*([a-zA-Z0-9_~-]+)?$ + +Address prefix: + + (?=^.{1,1023}$)^(g|private|example|peer|self|test[1-3]?)[.]([a-zA-Z0-9_~-]+[.])*$ + +Destination address + + (?=^.{1,1023}$)^(g|private|example|peer|self|test[1-3]?)[.]([a-zA-Z0-9_~-]+[.])+[a-zA-Z0-9_~-]+$ + +(If your regular expression engine does not support lookahead, you must drop the first parenthesis and separately enforce the overall length requirement of 1023 characters or less.) + +## Allocation Schemes + +The allocation scheme is the first part of an address, which indicates how the address is assigned. Here is a summary of the prefixes that are currently defined: + +| Prefix | Allocation Scheme | Definition and Use Case | +|:---------------------------------------|:--------------------------------|:--------------| +| `g.` | [Global Allocation Scheme][] | ILP addresses that are intended to send and receive money from any other address in the global scheme. | +| `private.` | Private allocation | For ILP addresses that only have meaning in a private subnet or intranet. Analogous to the [192.168.0.0/16 range in IPv4](https://en.wikipedia.org/wiki/Private_network). | +| `example.` | Examples | For "non-real" addresses that are used as examples or in documentation. Analogous to ["555 phone numbers"](https://en.wikipedia.org/wiki/555_%28telephone_number%29) in the USA. | +| `test.`, `test1.`, `test2.`, `test3.` | Interledger testnet and testing | For addresses used on the public Interledger testnet and in local tests, such as unit or integration tests of compatible software. | +| `local.` | Ledger-local | For addresses that are only valid in the context of a local ledger. Analogous to [link-local addresses](https://en.wikipedia.org/wiki/Link-local_address) in IP. | +| `peer.` | Peering | Similar to ledger-local addresses, but specifically for use in a peering relationship. The [ilp-plugin-virtual](https://github.com/interledgerjs/ilp-plugin-virtual) is an example of an existing implementation that uses this. | +| `self.` | Local loopback | For addresses that are only valid on the local machine. | + +## Global Allocation Scheme +[Global Allocation Scheme]: #global-allocation-scheme + +The global allocation scheme for ILP Addresses is the scheme that most addresses are expected to use. It uses the prefix `g.`. Addresses under this scheme are expected to be connected to all other such addresses, to the extent that the current trust and liquidity permit. + +This scheme has no central issuing authority or mechanism, so more than one entity can use the same address. In this case, some connectors may prepare a route to a different account than intended. In this failure case, no money moves because the receiver does not send the fulfillment. Participants in the interledger can reduce the chances of encountering this failure case by choosing addresses carefully and by properly managing connectors' routing tables. + +The global allocation scheme does not allow you to make any assumptions about the meaning of the segments. Segments in the same place could have different meanings to different ledgers or connectors. However, to make routing work well, we recommend placing segments in the following order: + +- [Neighborhoods](#neighborhoods) +- [Ledger identifiers](#ledger-identifiers) +- [Account identifiers](#account-identifiers) +- [Interactions](#interactions) + +Not all addresses contain all this information, and some addresses may use multiple segments to represent some of this information. Other concepts that may help to understand the global allocation scheme include: + +- [Ledger Prefixes](#ledger-prefixes) +- [Nested Ledgers](#nested-ledgers) + + +### Neighborhoods + +_Neighborhoods_ are leading segments with no specific meaning, whose purpose is to help route to the right area. At this time, there is no official list of neighborhoods, but the following list of examples should illustrate what might constitute a neighborhood: + +- `crypto.` for ledgers related to decentralized crypto-currencies such as Bitcoin, Etherium, or XRP. +- `sepa.` for ledgers in the [Single Euro Payments Area](https://en.wikipedia.org/wiki/Single_Euro_Payments_Area). +- `dev.` for Interledger Protocol development and early adopters + +The goal of neighborhoods is to group connectors and ledgers that know about each other, so that routing is more efficient. If a neighborhood is too large or not well-connected, it can be further subdivided into nested sub-neighborhoods. For example, if the `dev.` neighborhood contains too many routes to store efficiently, or if the ledgers in that neighborhood tend only to be connected to other ledgers from the same country, development ledgers hosted in Luxembourg might choose `dev.luxembourg.` as a more closely-knit neighborhood. + + +### Ledger Identifiers + +An address must have at least one segment to identify the ledger itself. These are called the _ledger identifier_ segments. The ledger identifier can be multiple segments, which can be useful if a ledger is divided into logical or physical subledgers. The ledger identifier segments distinguish a ledger from other ledgers in its same neighborhood. + +If fees are necessary for connecting to a subledger, payments to that subledger must be routed through a connector, although it is not necessary for the address itself to include a connector. + +[ilp-connector]: https://github.com/interledgerjs/ilp-connector + + +### Account Identifiers + +The _account identifiers_ are one or more segments that serve as a unique identifier of the account within the ledger. The connector (or its ledger plugin) maps these to accounts within a ledger. For some ledgers, a simple conversion rule may suffice; other ledgers may require a lookup table. The [five-bells-ledger-plugin][] reference implementation uses one full segment exactly as the account identifier. + +[five-bells-ledger-plugin]: https://github.com/interledgerjs/ilp-plugin-bells + +### Interactions + +_Interactions_ are additional segments after the account identifier portion of an address. Ledgers and ledger plugins may use the interactions segment of an address when generating notifications, so programs listening for payments can respond differently based on this portion of the address. Interactions may be unique per payment or purpose. + +To prevent multiple listeners from reacting to incoming payments intended for each other, the "interactions" segment of an address should start with a segment identifying how the payment was planned. (This is a similar function to the role of ports in Internet Protocol.) + + +### Ledger Prefixes + +A _ledger prefix_ is the entire set of segments leading up to the [account identifiers](#account-identifiers) of accounts in that ledger. In other words, a ledger prefix usually contains all of the following: + +- [Allocation Scheme Prefix](#allocation-schemes) +- [Neighborhoods](#neighborhoods) +- [Ledger identifiers](#ledger-identifiers) + +For two ledgers to be connected, those ledgers MUST have different prefixes. When doing a local delivery, the [ilp-connector][] reference implementation uses the prefix to choose a ledger plugin. + +If at all possible, a ledger should advertise a unique prefix for itself. This could be reported in a "metadata" API method or in official communications from the ledger operator. If a ledger cannot or does not specify its prefix, whoever is running connectors to the ledger should agree upon a prefix to use. See also: [Nested Ledgers](#nested-ledgers). + + +### Nested Ledgers + +A ledger can be addressed relative to another _"locator"_ ledger. Smaller and lesser-known ledgers may find it easier to advertise routes to themselves in this manner. That way, rather than needing to have the ledger known to every connector in the same large neighborhood, only the connectors attached to the locator ledger need to know how to route to the smaller connector. + +A connector can advertise routes to addresses that are prefixed by the connector's address in a ledger, if the connector proves it owns those addresses (for example, by sending messages using a ledger's messaging service from the account that address describes). + +If `example.dev.acme.blue` is an account at ACME ledger belonging to Blue Connector, Blue Connector can advertise prefixes such as `example.dev.acme.blue.waygate.` to its peer connectors that are also on ACME ledger. This way, payments that could be routed to `example.dev.acme.blue` can also be routed to accounts in the nested WayGate ledger through Blue Connector, with ACME ledger as the locator ledger. Other connectors _could_ manually add routes that shortcut to the `example.dev.acme.blue.waygate.` ledger without going through Blue Connector or ACME ledger. + +Using addresses that are nested this way depends on having a connector advertise the routes; if it doesn't, then only the manually-added "shortcut" routes can deliver money to addresses under the nested ledger prefix. Thus, it makes most sense if the connector at the locator ledger is operated by, or part of the same entity as, the nested ledger. + + +### Example Global Allocation Scheme Addresses + +`g.acme.bob` - a destination address to the account "bob" in the ledger "acme". + +`g.us-fed.ach.0.acmebank.swx0a0.acmecorp.sales.199.~ipr.cdfa5e16-e759-4ba3-88f6-8b9dc83c1868.2` - destination address for a particular invoice, which can break down as follows: + +- Neighborhoods: `us-fed.`, `0.`, `ach.` +- Ledger identifiers: `acmebank.`, `swx0a0.`, `acmecorp.` +- Account identifiers: `sales`, `199` +- Interactions: `~ipr`, `cdfa5e16-e759-4ba3-88f6-8b9dc83c1868`, `2` + +`g.` - the shortest possible address prefix. Includes all entries that are in the global allocation scheme. + +`g.crypto.bitcoin.` - address prefix for the public Bitcoin blockchain + +`g.crypto.rcl.xrp.ra5nK24KXen9AHvsdFTKHSANinZseWnPcX` - Address for sending XRP to a particular user of the Ripple Consensus Ledger, which breaks down as follows: + +- Neighborhood: `crypto.` +- Ledger identifiers: `rcl.`, `xrp.` +- Account identifier: `ra5nK24KXen9AHvsdFTKHSANinZseWnPcX` + +`g.dev.ilp-blue.blue.ilp-cyan.aquahuman.~psk.6373df86-a8d1-4aaa-930d-7d5a622913bc` - Address of a particular invoice using a nested ledger, which can break down as follows: + +- Neighborhood: `dev.` +- Ledger identifier (locator ledger): `ilp-blue.` +- Connector account identifier (locator ledger): `blue.` +- Ledger identifier (nested ledger): `ilp-cyan.` +- Account identifier (nested ledger): `aquahuman.` +- Interactions: `~psk`, `6373df86-a8d1-4aaa-930d-7d5a622913bc` diff --git a/0016-pre-shared-key/0016-pre-shared-key.md b/0016-pre-shared-key/0016-pre-shared-key.md new file mode 100644 index 00000000..8d8e5f8a --- /dev/null +++ b/0016-pre-shared-key/0016-pre-shared-key.md @@ -0,0 +1,300 @@ +--- +title: The Pre-Shared Key Transport Protocol (PSK) +draft: 4 +--- +# Pre-Shared Key Transport Protocol (PSK) + +The Pre-Shared Key (PSK) protocol is an end-to-end transport protocol, used by the sender and receiver of an ILP payment to decide on a condition and fulfillment for a payment. By default, the protocol also encrypts any additional data sent along with the payment, using [AES-256-GCM](https://en.wikipedia.org/wiki/Galois/Counter_Mode). The full ILP payment is authenticated through an [HMAC-SHA-256](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code) which is used to generate the fulfillment of a PSK payment. The entirety of the PSK data, including public headers, encrypted private headers, and encrypted private data, is encoded into an octet-stream that forms the data portion of the [ILP Packet](https://interledger.org/rfcs/0003-interledger-protocol/draft-4.html#specification). The PSK data is authenticated via AES-256-GCM in addition to the HMAC-SHA-256 which authenticates the full ILP payment. + +Pseudocode for this protocol can be read [at the bottom of this spec](#pseudocode). + +As the name suggests, PSK relies on a pre-shared secret key. In application-layer protocols like [SPSP](https://github.com/interledger/rfcs/blob/master/0009-simple-payment-setup-protocol/0009-simple-payment-setup-protocol.md), the receiver generates the shared key and shares it with the sender over an end-to-end secure communication channel such as HTTPS. Alternatively, a [Diffie-Hellman key exchange](https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange) could be used directly to generate the shared secret key. + +An advantage of PSK is that the pre-shared secret key only needs to be shared once. As long as both parties possess this key and are listening for transfers, they can send payments between one another. + +A disadvantage of PSK is that it is repudiable. Although the sender does get cryptographic proof that the recipient received the payment, that proof cannot be used to convince 3rd parties that the sender did indeed send the funds, because the sender could have generated the fulfillment themselves. However, simple proof for the sender that the recipient got the funds is sufficient in many applications. The [Interledger Payment Request](https://github.com/interledger/rfcs/blob/master/0011-interledger-payment-request/0011-interledger-payment-request.md) transport protocol should be used instead in cases where non-repudiable proof is required. + +## Flow + +1. The pre-shared secret key (also referred to as `shared_secret`) is shared out of band. +2. The sender creates a 16-byte (128-bit) nonce with cryptographically-secure randomness. +3. The sender constructs the PSK data: + 1. The sender starts with the PSK status line: `PSK/1.0\n` + 2. The sender appends the [public headers](#public-headers) (including the nonce), followed by `\n\n`. + 3. If the public `Encryption` header starts with `aes-256-gcm`, then the remainder of the PSK data after the public headers (padded with [PKCS](https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7)) will be encrypted using AES-256-GCM with the PSK encryption key, using the nonce as the initialization vector. The 16-byte AES-256-GCM authentication tag is attached to the `Encryption` header, encoded in base64url. The PSK encryption key is the HMAC of the string `'ilp_key_encryption'` with the pre-shared secret key. Note: The ciphertext is raw binary data, and is not base64 encoded. If the public `Encryption` header is set to `none`, then the remainder of the PSK data will be appended in unaltered cleartext. + 4. The sender appends the [private headers](#private-headers), followed by `\n\n`. + 5. The sender appends the application data (in its raw binary format). +4. The sender calculates the PSK condition key by the HMAC of the string `'ilp_psk_condition'` with the shared secret. +5. The sender calculates the fulfillment of their payment by taking the HMAC of the full ILP packet with the PSK condition key. +6. The sender creates the condition of their payment by computing the SHA-256 hash of the fulfillment. +7. The sender quotes and sends their payment, setting the data field of the ILP packet to the PSK data. +8. The receiver gets notified by their ledger of an incoming prepared transfer with an ILP packet. +9. If the public `Encryption` header is set, then the receiver derives the payment encryption key from the pre-shared secret key, sets the AES-256-GCM initialization vector to the `Nonce` header in the PSK public headers, and uses them to decrypt the private headers and their data. The AES-256-GCM authentication tag is attached to the `Encryption` header. If the public `Encryption` header is set to `none`, the receiver parses the private headers and their data in clear-text. +10. The receiver verifies that the amount in the incoming transfer matches the amount in the ILP packet. The receiver may also call an external system to make sure the incoming funds are expected. +11. The receiver fulfills the incoming transfer with the HMAC of the ILP packet, using the same PSK condition key as the sender, derived from the shared secret. + +## Data Format + +The PSK details are divided into two sets of headers. Each set of headers is formatted like those found in [HTTP Requests](https://tools.ietf.org/html/rfc7230#section-3.2) and follows the format `Header-Name: Header-Value`. Note that implementations have to validate inputs before using it as part of `Header-Name` or `Header-Value`. + +**PSK Headers MUST NOT contain any line feed characters such as `\n`**. Implementations failing to enforce this requirement may be vulnerable to [Header Injection Attacks](https://en.wikipedia.org/wiki/HTTP_header_injection). + +Application data is appended to the private headers, after a blank line. This object is then encrypted and appended to the public headers as binary data, after a blank line. Both private and public headers are parsed in the exact same way as HTTP headers. All strings are UTF-8 encoded. + +### Nonce + +Every PSK payment must generate a random 16-byte (128-bit) nonce. A cryptographically secure source of randomness **MUST** be used. + +The nonce is included in the PSK data, which is then put into the ILP packet and hashed to generate the payment's fulfillment. Because each PSK payment's fulfillment is an HMAC of its ILP packet and the shared secret, repeating the same ILP packet without a different nonce would yield a payment with the same fulfillment. A malicious connector could save up the fulfillments of PSK payments and then submit them whenever a packet is repeated exactly. + +The nonce is also used to set the initialization vector of AES, ensuring that the same data will encrypt to different ciphertext across different payments. This is important to prevent connectors from figuring out the cleartext of the application data. If the nonce is repeated, then any payments containing that repeated nonce can be decrypted. + +### Public Headers + +The public headers and their data are formatted as below: + +```http +PSK/1.0 +Nonce: fpwpAhlN588 +Key: hmac-sha-256 +Encryption: aes-256-gcm 58EowcXBk3qBIvJ0kmvdCh +Header: data_everyone_can_see + +... +``` + +The public headers come after a single status line, which must be `PSK/1.0`. Public headers are visible to all parties transmitting the packet, so they SHOULD contain only the bare minimum required for the intended parties to decrypt and validate the private headers. + +PSK payments use an HMAC of the PSK details as a fulfillment, to prove that the contents are unmodified. The pre-shared secret key is the key used in the HMAC. + +The data attached to these headers is an encrypted binary blob. This encrypted blob contains the private PSK headers. + +Despite the fact that connectors can read the public headers' data, connectors should not be required to do so, nor should they rely on PSK details being present on all transfers. This information is only intended for the initial sender and final receiver of a payment. + +The decryption key is derived from the pre-shared secret key, and the AES-256-GCM initialization vector is set to the value of the `Nonce` header (`fpwpAhlN588` in the above example). The nonce MUST be generated with cryptographically-secure randomness. **If the nonce is reused with the same shared secret, it could leak unencrypted data or allow money to be stolen by malicious parties.** The 16-byte AES-256-GCM authentication tag used for decryption is attached to the `Encryption` header, encoded in base64url (`58EowcXBk3qBIvJ0kmvdCh` in the above example). The encryption MUST use [PKCS](https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7) for padding. + +| Header | Value | +|--------|-------| +| `Nonce` | _(Required)_ A [base64url-encoded](https://en.wikipedia.org/wiki/Base64#URL_applications) nonce, used to generate ensure uniqueness of the PSK data and as an initialization vector (IV) for AES-256-GCM encryption. The nonce **MUST** be 16 bytes, and **MUST** be generated with cryptographically-secure randomness. | +| `Encryption` | _(Required)_ Supported values are `aes-256-gcm ` and `none`. If it is set to `aes-256-gcm`, then private headers and application data will be AES-256-GCM encrypted, and `` will be the 16-byte authentication tag returned by the cipher, encoded in base64url. If it is set to `none`, then private headers and application data will be in cleartext. This cleartext will still be appended to the public headers after an empty line. If the value is neither `aes-256-gcm` nor `none`, the receiver MUST reject the incoming payment with an `S06: UnexpectedPayment` error. | +| `Key` | _(Optional)_ The algorithm by which the receiver generates the shared secret. In the normal use of PSK 1.0 described in this spec, the secret is generated by the receiver only and the sender does not know the algorithm used. In other cases, for example using [Diffie-Hellman](https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange), the sender would include the algorithm and a sender-specified key for the recipient to use to derive the shared secret. If the receiver does not understand the `Key` value, the receiver MUST reject the incoming payment with the error message `S06: UnexpectedPayment`. | +| ... | _(Optional)_ Additional headers. These can be read by any connectors. | + +### Private Headers + +The private headers and their data are formatted as below: + +```http +Expires-At: 2017-03-13T16:58:06.901Z +Header-A: some_stuff +Header-B: some_other_stuff +Header-C: some_more_stuff +Date: 2017-03-13T16:57:56.901Z +Content-Type: application/json + +{"foo":"bar"} +``` + +Only the payment's sender and receiver can decrypt and read the private headers and their data. The private headers contains an arbitrary message body preceded by the following headers: + +| Header | Value | +|--------|-------| +| `Expires-At` | _(Optional)_ An [ISO8601 UTC timestamp](https://en.wikipedia.org/wiki/ISO_8601), after which this PSK payment has expired. | +| `Content-Type` | _(Optional)_ The [media type](http://www.iana.org/assignments/media-types/media-types.xhtml) of the data in the message body | +| ... | _(Optional)_ Additional arbitrary headers. Where appropriate, it is RECOMMENDED to match the headers defined for HTTP messages ([RFC7230](https://tools.ietf.org/html/rfc7230#section-3.2)) | + +## Pseudocode + +The following pseudocode outlines the generation procedure for the necessary values involved in PSK. It follows only the case where encryption is enabled, for simplicity. + +### 1. Payment Creation + +This is executed by the sender for each payment once they have the `shared_secret` and `destination_account` from the receiver: + +```js +/** + * @param shared_secret (sent by the receiver) + * @param destination_account (sent by the receiver) + * + * @param headers (a list of HTTP style headers, which will be encrypted) + * @param public_headers (a list of HTTP style headers, which will be left + * unencrypted) + * @param application_data (arbitrary binary data used for higher level + * protocols, which will be encrypted) + * @param destination_amount (amount that the receiver will receive on their + * ledger, used to construct the ILP packet) + */ + +// Private headers and application data are included first, separated by an empty line. + +private_headers = create_empty_buffer() +private_headers += headers.join('\n') +private_headers += '\n\n' +private_headers += application_data + +// A key is created in order to encrypt the private headers and the application +// data. A nonce is used to generate the key, so that the same private headers +// will not produce the same ciphertext. Also note that the nonce's inclusion +// in the ILP packet ensures that multiple payments using the same shared +// secret result in different hashes. + +nonce = random_bytes(16) + +// If you want to send data unencrypted, you can skip this encryption step and +// set the public 'Encryption' header to 'none' instead of 'aes-256-gcm'. The +// receiver side must always check the public 'Encryption' header to check +// whether or not encryption is enabled. This pseudocode only describes the +// case where encryption is turned on, for simplicity. The nonce is used as +// the IV (initialization vector) of AES-256-GCM. The 16-byte auth tag of GCM +// will be attached to the 'encryption' header in base64url. The encrypted data +// should be automatically padded with PKCS. + +payment_encryption_key = hmac_sha_256(shared_secret, 'ilp_psk_encryption') +encrypted_data, auth_tag = aes_256_gcm({ + key: payment_encryption_key, + iv: nonce, + data: private_headers +}) + +// The PSK data object is constructed from the public headers, encrypted private +// headers, and the encrypted application data. The nonce and the AES-256-GCM +// authentication tag are included in the public headers. + +psk_data = create_empty_buffer() +psk_data += 'PSK/1.0\n' +psk_data += 'Nonce: ' + nonce +psk_data += 'Encryption: aes-256-gcm ' + auth_tag +psk_data += public_headers.join('\n') + +// Encrypted private headers and application data are appended after an empty line. + +psk_data += '\n\n' +psk_data += encrypted_data + +// The ILP packet includes the destination account, amount and PSK data. + +ilp_packet = create_ilp_packet({ + amount: destination_amount, + account: destination_account, + data: psk_data +}) + +// The sender generates the payment condition from the packet and shared +// secret, and uses it for their outgoing transfer. The receiver will perform +// this same derivation when fulfilling the payment. For the sender, the +// fulfillment is just an intermediate value in calculating the condition. + +psk_condition_key = hmac_sha_256(shared_secret, 'ilp_psk_condition') +fulfillment = hmac_sha_256(psk_condition_key, ilp_packet) +condition = sha_256(fulfillment) + +// The sender will now quote and prepare this payment with the condition +// and the ilp_packet attached. +``` + +### 2. Payment Fulfillment + +This is executed by the receiver for each payment once they have gotten the notification of the incoming transfer: + +```js +/** + * @param shared_secret (same as the one used by the sender) + * @param account (the receiver's account, as provided by their ledger plugin) + * @param ilp_packet (the ILP packet attached to the incoming payment) + */ + +// The nonce is taken from the public headers and used as the IV +// (initialization vector) of AES-256-GCM. The auth tag for GCM decryption is +// the second space-separated field of the 'encryption' header. Note that the +// header names are case-insensitive but the values are case-sensitive, just as +// in HTTP. If the public 'Encryption' header were set to 'none', this +// decryption function would be omitted, as packet.data would contain the +// plaintext of the private headers and application data. + +psk_data = packet.data +nonce = psk_data.headers['nonce'] +auth_tag = psk_data.headers['encryption'].split(' ')[1] +payment_decryption_key = hmac_sha_256(shared_secret, 'ilp_psk_encryption') +private_headers = aes_256_gcm_decipher({ + key: payment_decryption_key, + iv: nonce, + tag: auth_tag, + data: psk_data.data +}) + +// After extracting and decrypting the PSK data, the receiver passes it to +// another function to perform any application-level logic on whether or not to +// accept this payment. + +review_payment(private_headers, psk_data, ilp_packet, ... ) + +// Having reviewed the payment without exception, the receiver regenerates the +// fulfillment from the shared secret and the ILP packet, and then uses it to +// fulfill the incoming payment. + +psk_condition_key = hmac_sha_256(shared_secret, 'ilp_psk_condition') +fulfillment = hmac_sha_256(psk_condition_key, ilp_packet) + +// The receiver submits the fulfillment to execute the incoming transfer. +``` + +## Appendix A: Recommended Algorithm for Generating Shared Secrets + +The receiver MAY use any method they choose for determining the `shared_secret`. This algorithm is RECOMMENDED because it allows the receiver to listen for incoming PSK payments with many different shared secrets without needing to store all of the various secret values. + +The receiver maintains one `receiver_secret`, which is HMACed with a random public `token` to generate the shared secret initially and regenerate it from incoming payment packets. + +### Shared Secret Generation + +This is executed by the receiver, using a single `receiver_secret`, each time a new `shared_secret` is desired (NOT once per payment): + +```js +/** + * @param account (this is the address the receiver will listen on, as + * returned by their ledger plugin) + * @param receiver_secret (a random 32-byte secret generated and stored by the receiver) + */ + +// The token is attached to the receiver's ILP address, allowing them to +// regenerate the shared secret every time they receive a payment, instead of +// keeping a store of all shared secrets they've given out. + +token = random_bytes(16) + +// The receiver id differentiates this receiver from other receivers on the +// same account with different receiver_secrets. When they receive a payment, +// the receiver can make sure the receiver id in the ILP address is their own. + +receiver_id = hmac_sha_256(receiver_secret, "ilp_psk_receiver_id").slice(0, 8) +destination_account = account + "." + base64url(receiver_id) + base64url(token) + +// The shared secret is generated using a hard-coded string and the token. + +shared_secret_generator = hmac_sha_256(receiver_secret, "ilp_psk_generation") +shared_secret = hmac_sha_256(shared_secret_generator, token) + +// The shared_secret and the destination_account are transmitted to the sender. +``` + +### Shared Secret Regeneration + +This is executed each time a payment is received, before [2. Payment Fulfillment](#2-payment-fulfillment): + +```js +/** + * @param ilp_packet (the ILP packet attached to the incoming payment) + * @param receiver_secret (same as generated above) + * @param account (the receiver's account, as provided by their ledger plugin) + */ + +// The receiver id and the shared secret are regenerated from the account in +// the ILP packet. Remember from the previous section that this address contains +// the token appended after the receiver id. + +receiver_id = hmac_sha_256(receiver_secret, "ilp_psk_receiver_id").slice(0, 8) +token = ilp_packet.account.slice(account.length + receiver_id.length) +shared_secret_generator = hmac_sha_256(receiver_secret, "ilp_psk_generation") +shared_secret = hmac_sha_256(shared_secret_generator, token) + +// Now the receiver can continue with Payment Fulfillment. +``` diff --git a/0017-ledger-requirements/0017-ledger-requirements.md b/0017-ledger-requirements/0017-ledger-requirements.md new file mode 100644 index 00000000..038d37c1 --- /dev/null +++ b/0017-ledger-requirements/0017-ledger-requirements.md @@ -0,0 +1,95 @@ +--- +title: Ledger Layer Requirements +draft: 1 +--- +# Ledger Layer Requirements + +## Introduction + +Ledger protocols are responsible for executing the individual transfers that constitute an Interledger transaction. They are also used by connectors to communicate with each other. Ledger layer protocols can differ widely depending on the type of ledger. For example, a central ledger will likely use a very different protocol than a blockchain, but for interledger purposes they are both ledgers and may be accessed using the same primitive operations as defined in this architecture. + +## Requirements + +### Minimal Support + +For minimal Interledger support, a ledger MUST have the ability to transfer funds from one account to another. All additional functionality can be implemented in a separate "adapter" service. To use ILP in that case, users of the ledger must also trust the adapter. For further details, see [Appendix A](#appendix-a-holds-without-native-ledger-support). + +### Basic Support + +For basic Interledger support, a ledger MUST fulfill the requirements for minimal support and also the following: + +The ledger MUST provide authorization holds, conditional upon a cryptographic hash and timeout as described below. During an authorization hold, money is put aside for a specific transfer until that transfer's outcome has been decided. + +Transfers using authorization holds can be in four distinct states: + +![Transfers start in the proposed state, transition to the prepared state and finally to the executed state, which is final. Proposed and prepared transfers can also transition to the rejected state, which is final as well.](../shared/graphs/transfer-states.svg) + +* Proposed -- Nothing has happened yet. +* Prepared -- Funds are held. +* Executed -- The transfer has completed. +* Rejected -- The transfer has been canceled (and funds returned to the sender.) + +**Hint:** Authorization holds are the financial equivalent of a [two-phase commit](http://foldoc.org/two-phase%20commit). + +The ledger MUST be able to release the held funds to the receiver upon receiving a 32-byte preimage whose SHA-256 hash matches a value provided by the sender. The ledger MUST accept ONLY 32 byte preimages or support specifying the fulfillment length when the transfer is prepared. + +The ledger MUST support releasing held funds back to the sender after a timeout. + +The ledger MUST support attaching a short message or *memo* to each transfer. + +The ledger MUST support notifications to account holders when transfers are prepared, executed, or rejected that affect their accounts. + +### Full Support + +The ledger MUST fulfill the requirements for basic support and also the following: + +The ledger MUST support memos up to 65535 bytes. + +The ledger MUST support sending an authenticated message of up to 65535 bytes to the holder of another account on the ledger. + +The ledger MUST support a way to look up a fulfillment by condition hash. It SHOULD automatically reject new transfers (that have not been prepared yet) that have an execution condition for which the ledger already knows the fulfillment. This aids in [error recovery](#error-recovery). + +The ledger MUST support preparing, executing, and rejecting transfers in 1 second or less. + +The ledger MUST define an [ILP Address](../0015-ilp-addresses/0015-ilp-addresses.md) prefix and scheme such that accounts on the ledger can be addressed using canonical ILP addresses. + +It MUST support receivers (and ONLY receivers) rejecting incoming transfers. Receivers MUST be able to submit a rejection message of up to 65535 bytes. The sender of the transfer MUST be notified that the transfer has been rejected and the notification MUST include the rejection message. + +## Example Protocols + +### Five Bells Ledger Protocol (5BLP) + +Five Bells Ledger Protocol (5BLP) is a RESTful, JSON-based protocol that was developed specifically to provide the minimum functionality required for full Interledger support. + +A reference implementation of a ledger using 5BLP can be found [here](https://github.com/interledger/five-bells-ledger). + +### Blockchain Protocols (e.g. Bitcoin) + +Blockchains are distributed, peer-to-peer systems that provide consensus over a single shared state. Any blockchain that supports escrowed funds transfers is in principle capable of acting as a ledger connected to the Interledger. + +For example, Bitcoin supports SHA-256 hashlocked escrow transfers which means it can participate in ILP Interledger transactions. Bitcoin's [BIP-65](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki) proposal provided the timeouts required for Basic level support. + +### Legacy Protocols (e.g. ACH, ISO 20022) + +Legacy protocols often do not provide escrow functionality. In this case, the protocol can either be upgraded, or a highly trusted participant (e.g. a bank) can act as an escrow provider. + +### Proprietary Wallet Protocols (e.g. PayPal API) + +There are large numbers of proprietary ledgers out there. This includes web-based and mobile wallets. These types of systems can usually be extended with cryptographic escrow functionality by their operator in order to connect them to the Interledger. + +### Other Proprietary Protocols (e.g. Skype API) + +Some proprietary protocols intentionally do not provide general ledger functionality. A common example are stored-value systems, such as gift cards, loyalty points or pre-paid accounts. Such systems can still be connected to the Interledger in a limited capacity. For example a pre-paid account ledger could be set up to act as a receiving ledger, but not as a sending or intermediate ledger. + +By creating two classes of users -- resellers and end users -- and only allowing transfers if the sender is a reseller and the recipient is a user, merchants can create Interledger-capable ledgers which behave like stored-value systems. Such systems allow deposits, but do not allow withdrawals. + +## Appendix A: Holds Without Native Ledger Support + +Not all ledgers support held transfers. In the case of a ledger that doesn't, the sender and recipient of the local ledger transfer MAY choose a commonly trusted party to carry out the hold functions. There are three options: + +1. The sender MAY trust the receiver. The sender will perform a regular transfer in the first step and the receiver will perform a transfer back if the condition has not been met in time. + +2. The receiver MAY trust the sender. The sender will notify the receiver about the intent to transfer. If the receiver provides a fulfillment for the condition before the expiry date, the sender will perform a regular transfer to the receiver. + +3. The sender and receiver MAY appoint a mutually trusted third-party which has an account on the local ledger. The sender performs a regular transfer into a neutral third-party account. In the first step, funds are transfered into the account belonging to the neutral third-party. + diff --git a/0018-connector-risk-mitigations/0018-connector-risk-mitigations.md b/0018-connector-risk-mitigations/0018-connector-risk-mitigations.md new file mode 100644 index 00000000..307f8df1 --- /dev/null +++ b/0018-connector-risk-mitigations/0018-connector-risk-mitigations.md @@ -0,0 +1,44 @@ +--- +title: Connector Risk Mitigations +draft: 1 +--- +# Connector Risk Mitigations + +Interledger connectors take some risk in exchange for the revenue they generate from facilitating payments. This document outlines the major categories of risks connectors face and suggests some possible mitigations. + +This is a work in progress and is not an exhaustive list. + +**Monitoring is a must! Even connectors that implement all of these strategies should monitor their transaction patterns and use warnings or kill switches to avoid losing money in the case of an unexpected attack.** + +## Fulfillment Failure + +The main risk connectors face in Interledger is being unable to fulfill the incoming transfer after their outgoing transfer has been executed. + +Possible mitigations include: + +* **Connecting Reliable Ledgers** - Connectors should choose the ledgers they connect in part based on the reliability and speed of fulfillment notifications and the time the ledgers require to process fulfillments once they are submitted +* **Adjusting Transfer Expiry Window** - Connectors determine the window of time they require between when the incoming and outgoing transfers expire. Connectors should set this window such that they are confident that they will be able to deliver the fulfillment in time even if the outgoing transfer is executed at the last possible moment. +* **Denial of Service Protections** - Connectors should take standard [Denial of Service](https://en.bitcoin.it/wiki/Weaknesses#Denial_of_Service_.28DoS.29_attacks) attack prevention steps to prevent attackers from overloading the connectors' servers with network traffic +* **Prioritizing Fulfillments** - Connectors should prioritize fulfilling transfers over preparing new ones or responding to quote requests and may even have separate processes or machines responsible for those different behaviors +* **Redundant Instances** - Connectors should run redundant processes and machines to increase the difficulty of interfering with their processing +* **Avoiding Public Endpoints** - Connectors may reduce attack vectors by using communication channels and protocols that do not entail having a publicly-accessible server (for example using [WebRTC](https://webrtc.org/) or [Virtual Private Networks](https://en.wikipedia.org/wiki/Virtual_private_network) to communicate with peers + +## Liquidity Exhaustion + +Attackers could tie up connectors' liquidity by preparing payments through them that the attacker knows will fail. For example, an attacker could prepare numerous payments to itself and then not fulfill any of them. + +Possible mitigations include: + +* **Managing "Payment Bandwidth"** - Connectors should monitor the percentage of their total liquidity that is on hold at any given time for a given peer, customer, or destination prefix and may reject incoming payment requests if that party exceeds their allocated "payment bandwidth". Connectors may allocate less bandwidth to unknown or untrustworthy senders or receivers. Note that ILP Packets contain the destination account but not the source account, so connectors should apply this logic to the immediate peer or customer that they receive the payments from and possibly the destination account or prefix. +* **Preferring Smaller Payments** - Smaller payments place a smaller percentage of the connector's liquidity on hold and thus each one carries less risk that the payment being prepared and then failing would create significant opportunity cost +* **Blacklisting Senders and Receivers** - Connectors may refuse to facilitate payments from certain sources or to certain destinations if their rate of failed payments is unusually high + +## Exchange Rate Volatility + +Once connectors prepare their outgoing transfers, they are committed to the payment even if the exchange rate between the assets fluctuates. + +Possible approaches include: + +* **Accounting for Slippage** - Connectors may add a buffer to their expected exchange rate to account for some movement in the price, and they may add a premium for especially volatile currencies +* **Preferring Shorter Timeouts** - Connectors may provide better exchange rates for payments with shorter timeouts because they carry less risk that the price will move drastically while the payment is in flight + diff --git a/0019-glossary/0019-glossary.md b/0019-glossary/0019-glossary.md new file mode 100644 index 00000000..fd87b996 --- /dev/null +++ b/0019-glossary/0019-glossary.md @@ -0,0 +1,257 @@ +--- +title: Glossary +draft: 1 +--- +# Glossary + +## Account +An administrative item that acts as a bucket of assets on a ledger. + +## Application Layer +The set of protocols built upon the [Transport Layer](#transport-layer) that communicate payment details between senders and receivers. Application Layer protocols specify all of the data and methods needed to set up a payment for a specific category of use cases or applications. For example, the Simple Payment Setup Protocol uses WebFinger and HTTPS for account detail lookup. + +## Arbitrage +The practice of doing [cyclic transactions](#cyclic-transaction) which start and end on the same [ledger](ledger), +or groups of simultaneous transactions which add up to one cyclic transaction. +Taking advantage of quirks and fluctuations in exchange rates, +the goal is to achieve a [destination amount](#destination-amount) which is higher than the [source amount](#source-amount). + +## Asset +Something you can own. Can be a physical and unique item, a quantity of a physical substance, a specific physical collection of coins and bank notes, +a non-physical quantity of currency, a non-physical claim to a resource, a reputation for keeping one's word, someone else's intention to obey the +terms of a contract, etc. Some types of assets can be more easily traded on a ledger than others. Also, for some asset pairs it is easier to determine +a fair exchange rate than for others. + +## Balance +The amount of a ledger's assets held by a given account. This concept is not as clearly defined as it may seem at first glance. +When part of an account's balance is not readily usable, it is up to the ledger implementation to decide whether an account's +"balance" includes only the readily usable balance, or all of it, or some number inbetween. + +## Centralized Ledger +A ledger which is operated by a single entity. + +## Conditional Transfer +Each local transfer is first *prepared* and then either *executed* or *rejected*. When a transfer is prepared, the ledger puts the funds of the source account on hold with a *cryptographic condition* and *timeout*. If the condition is fulfilled before the timeout, the transfer is executed and the funds are transferred. If the timeout is reached, the transfer expires and the ledger returns the funds to the source account automatically. +Inspired by the [Lightning Network](http://lightning.network), Interledger uses the digest of the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) hash function as the condition for transfers. The fulfillment is a valid 32-byte preimage for the hash specified when the transfer was prepared. Ledgers are responsible for validating fulfillments. [Transport Layer](#transport-layer) protocols are used by the sender and receiver to generate the condition for a particular payment. +To be fully Interledger-compatible, ledgers MUST support conditional transfers, though it is possible to send Interledger payments over a ledger that does not natively support the recommended features. See [IL-RFC 17](../0017-ledger-requirements/0017-ledger-requirements.md) for the full description and tiers of ledger requirements. + +## Connector +A party who chains one transfer to the next in an interledger payment. +A connector receives a local transfer on one ledger in exchange for making another local transfer on a different ledger. A single interledger payment may include multiple connectors and may traverse any number of ledgers. +Connectors take some risk, but this risk can be managed and is primarily based upon the connector's chosen ledgers and direct peers. + +## Cyclic Transaction +A transaction where the destination account is the same account, on the same ledger, as the source account. This can be useful +when rebalancing liquidity (to enable future payments), or when rebalancing stored value (to spread risk, or to take advantage +of changing exchange rates). + +## Destination Account +The account of the receiver, whose address is included in the +[interledger packet](#interledger-packet). +Note that anyone can claim to have a certain [Interledger address](#interledger-address); address ownership is not enforced. + +## Destination Amount +The amount to be received by the receiver. + +## Destination Ledger +The ledger on which the final receiver of an Interledger payment holds their account + +## Distributed Ledger +A ledger that is operated by a group of entities and/or run on multiple servers. This term is used somewhat interchangeably with "Blockchain" or "Decentralized Ledger". + +## Exchange Rate +The price of one ledger's asset in terms of another ledger's asset. +Connectors may generate revenue from the difference in value between incoming and outgoing transfers. +Senders may request quotes from multiple connectors to determine the best price before sending a payment. +The exchange rate between source and destination is determined by the product of exchange rates at each hop. + +## Executed +A final state of a transfer indicating that the funds were transferred to the account of the immediate recipient. See also [rejected](#rejected). + +## Execution Phase +The second phase of an ILP payment, in which the fulfillment is passed back from the receiver, via the connector(s), to the sender. + +## Final State +The state an interledger payment ends up in. Has to be either +[rejected](#rejected) or [executed](#executed). + +## Fulfillment +A 32-byte value used to trigger the execution of a transfer. In most Interledger payments, the fulfillment is known only to the receiver +(or in the case of the Pre-Shared Key protocol it is known to the sender and the receiver). For more details see +[conditional transfer](#conditional-transfer). + +## Hold +Part of the sender's, connector's, and/or receiver's account balance is "on hold" when that balance +is reserved for a specific ILP payment and is temporarily unavailable for use in other payments. +The money is on hold while the ILP transfer is in the `prepared` state. +If the transfer is executed, the money is no longer on hold, and added to the balance of the receiver of the transfer. +If the transfer is rejected, the money is no longer on hold either, but is instead returned to the balance of the sender of the transfer. + +## Hop +One of the transfers that make up a payment. Specifically, a 'multi-hop' payment is one involving more than one ledger, and thus at least one connector. +Likewise, 'best hop', as used in the ilp-connector source code when making routing decisions, is best interpreted as 'best next transfer'. + +## IL-RFC +Interledger request for comments. A document describing a part of the protocol stack, or a topic related to it, such as this glossary. The official list of IL-RFCs is maintained in +[gh:interledger/rfcs](https://github.com/interledgers/rfcs). + +## Incoming (Transfer, Ledger, Amount) +The transfer/ledger/amount, relative to the party who plays the 'receiver' role. See also [Outgoing](#outgoing-transfer-ledger-amount). + +## Interledger (in "Let's use Interledger for that!") +The [Interledger protocol stack](#interledger-protocol-stack). + +## interledger (in "An interledger network") +Of a network of two or more ledgers, indicating that it uses the Interledger protocol stack. See also [Interledger (The)](#interledger-the) +The adjective 'interledger' is also sometimes used to simply refer to transactions that cross multiple ledgers, even if ILP is not used (similar to +'international' meaning 'across multiple nations'). + +## Interledger (in "The Interledger") +The main public network of ledgers connected via the Interledger protocol stack. See [ConnectorLand](https://connector.land) for statistics about it. + +## Interledger Addresses +Interledger addresses provide a ledger-agnostic way to address ledgers and accounts. Interledger addresses are dot-separated strings that contain prefixes to group ledgers. An example address might look like `g.us.acmebank.acmecorp.sales.199` or `g.crypto.bitcoin.1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2`. +When initiating an Interledger payment, the sender attaches an ILP packet to the local transfer to the connector. The packet is relayed by connectors and attached to each transfer that comprises the payment. In some cases, ledger protocols may define alternative ways to communicate the packet. +Note that anyone can claim to have a certain Interledger address, there is no registry of them. +Whether or not a payment ends up at the intended receiver is ultimately safe-guarded by the hashlock condition, +not by enforcement of address ownership. +See [IL-RFC 15](../0015-ilp-addresses/0015-ilp-addresses.md) + +## Interledger Architecture +The [Interledger protocol stack](interledger-protocol-stack), and the design principles behind it. + +## Interledger Layer +The lowest layer of the Interledger protocol stack (not counting the 'ledger layer' below it), consisting of +the [ILP](#interledger-protocol-ilp) Interledger protocol, and the [ILQP](#ilqp) quoting protocol, + +## Interledger Module +The part of a software application that processes ILP payments. +Analoguous to the network card of an internet-connected computer. + +## Interledger Packet +The binary data packet that is forwarded along all hops from sender to receiver, and which provides the receiver +with metadata which they may need before they are willing and able to fulfill the payment. +It also contains the destination account's address and destination amount, on which connectors will base their routing decisions - +both in choosing the onward path, and in deciding whether their incoming amount was big enough, in comparison to the destination +amount mentioned in the ILP packet, to even merit an attempt to route the payment at all. See also [routing table](#routing-table). + +## Interledger Payment Request (IPR) +A Transport Layer protocol in which the receiver generates the payment details and condition. The receiver does not share the secret used to generate the condition and fulfillment with the sender or anyone else, but the sender must ask the recipient to generate and share a condition before sending each payment. IPR is primarily useful for building non-repudiable application layer protocols, in which the sender's posession of the fulfillment proves to third parties that the sender has paid the receiver for a specific obligation. +See [IL-RFC 11](../0011-interledger-payment-request/0011-interledger-payment-request.md). + +## Interledger Protocol (ILP) +The core of the Interledger protocol suite, described in [IL-RFC 3](../0003-interledger-protocol/0003-interledger-protocol.md). +Colloquially the whole Interledger stack is sometimes referred to as "ILP". Technically, however, the Interledger Protocol is only one layer in the +[Interledger protocol stack](#interledger-protocol-stack). + +## Interledger protocol stack +The stack consisting of the [Interledger layer](#interledger-layer), +a choice of [transport layer](#transport-layer) protocols, and the [application layer](#application-layer). + +## Interledger Quoting Protocol (ILQP) +The protocol that defines how senders request quotes from connectors to determine the source or destination amount for an Interledger payment. Quoting is optional and senders MAY cache quotes and send repeated payments through the same connector. +See [IL-RFC 8](../0008-interledger-quoting-protocol/0008-interledger-quoting-protocol.md). + +## Intermediate (transfer, ledger, amount) +Any transfer/ledger/amount that is neither directly adjacent to the sender, nor to the receiver. + +## Ledger +Stateful systems that track the ownership of [assets](#asset). Ledgers contain buckets of assets known as [accounts](#account) and record [transfers](#transfer) between them. +Each account has a [balance](#balance), which is the amount of the ledger's assets the account holds. Account balances may be positive or negative, representing assets or liabilities. + +## Liquidity +Maximum amount which can be moved back and forth over a route. + +## Local Payment +A payment that contains just one [transfer](#transfer) and zero [hops](#hop), thus staying within the same ledger. + +## Outgoing (Transfer, Ledger, Amount) +The transfer/ledger/amount, relative to the party who plays the sender role. See also [Incoming](#incoming-transfer-ledger-amount). + +## Payment +The movement of assets from one party to another. May imply that this movement is across multiple ledgers, as opposed to a local transfer. +A payment consists of one or more [ledger transfers](#transfer). Roughly synonymous with +[transaction](#transaction), and preferable in source code where 'transaction' may be confused with database transactions. +The word payment has an emphasis on the context of the negotiation between the sender and receiver. For instance, +it may be paying a debt, or a purchase, it may be a pull payment or a push payment. All these aspects which put a transaction in a wider +context and link it to real-world interactions, contracts, and motivations between parties make a transaction into a payment. A +[cyclic transaction](#cyclic-transaction) is generally not considered a payment, because the sending party of such a transaction is also the receiving party. + +## Payment channel +A method used by two parties on the same ledger to make multiple transfers such that the ledger enforces the final net positions without the ledger being informed of each individual transfer. One or both parties will generally escrow some of their assets on the ledger and transfers are made by exchanging "claims" that update the net balance. When one of the parties wishes to close the channel, they submit the final balance to the ledger. Different implementations have different rules about which party is allowed to close the channel, the consequences of doing so, and the mechanisms for settling disputes. For more details see [Bitcoin Payment Channels](https://en.bitcoin.it/wiki/Payment_channels). Note that payment channels are similar to [trustlines](#trustlines) except the ledger is used to escrow one or both parties' assets and provide clear resolution mechanisms for disputes over the final net positions. + +## Peer balance +See [trustline](#trustline). + +## Peering +Connectors "peer" with one another to exchange information used to determine the best route for a payment, and to route it (connect its chain of +transfers during the [preparation phase](#preparation-phase) and [execution phase](#execution-phase). +A peering relationship is usually implemented using either a [trustline](#trustline) or a [payment channel](#payment-channel).. + +This is similar to [how internet service providers peer with each other](https://en.wikipedia.org/wiki/Peering). + +## Preparation Phase +The first phase of an ILP payment, in which the ILP packet is passed from the sender, via the connector(s), to the receiver. +Also, money is put [on hold](#hold) during this phase. + +## Prepared +The state of a transfer in which the source account's funds are put on hold by the ledger until either the expiry is reached or the condition is fulfilled. See [Conditional Transfer](#conditional-transfer). + +## Pre-Shared Key (PSK) +The recommended transport layer for most use cases. +In Pre-Shared Key (PSK) protocol, the sender and receiver use a shared secret to generate the payment condition, authenticate the ILP packet, and encrypt application data. Using PSK, the sender is guaranteed that fulfillment of their transfer indicates the receiver got the payment, provided that no one aside from the sender and receiver have the secret and the sender did not submit the fulfillment. See [IL-RFC 16](../0016-pre-shared-key/0016-pre-shared-key.md). + +## Proposed +[DEPRECATED] The 'proposed' state of a payment is no longer used in practice; nowadays, payments skip this state, and are immediately created in their 'prepared' state. +The official [Interledger whitepaper](https://interledger.org/interledger.pdf) still describes it, though. + +## Receiver +The party who is the beneficiary (payee) of a transfer or a payment (transaction). +When Alice sends a payment through a connector to Bob, +it's ambiguous whether by "the receiver" you mean the receiver of the transfer +(i.e., the connector), or the receiver of the payment (i.e., Bob), so use this +term carefully. + +## Rejected +One of the final states a transfer can end up in, that means the funds have been returned to the sender. See also [executed](#executed). + +## Route +A set of [transfers](transfer), chained together by the [connectors](#connector) between them. +This can refer to both the "route" an actual payment has taken or the theoretical +way that a future payment could go. The former is sometimes also called the path. + +## Routing table +A lookup table, used by a connector to decide who their outgoing receiver should be (the next +hop, which may be another connector, or the destination receiver), and what the lowest outgoing +amount is which they can safely offer for the outgoing transfer (equal to the destination amount in case +the outgoing is the destination transfer). + +## Sender +The party who is the initiator (payer) of a transfer or a payment (transaction). + +## Simple Payment Setup Protocol (SPSP) +SPSP uses Webfinger ([RFC 7033](https://tools.ietf.org/html/rfc7033)) and an HTTPS-based protocol for communicating account, amount, and Pre-Shared Key details from the receiver to the sender. +See [IL-RFC 9](../0009-simple-payment-setup-protocol/0009-simple-payment-setup-protocol.md). + +## Source (transfer, ledger, amount) +The transfer/ledger/amount directly adjacent to the sender. + +## Transaction +See [Payment](#payment). Use of the term 'transaction' is discouraged, due to possible confusion +with for instance database transactions in source code. + +## Transfer +The movement of assets on one single ledger. Multiple transfers can be chained together into one [multi-hop](#hop) [payment](#payment). +Also sometimes called 'ledger transfer'. + +## Transport Layer +The middle layer of the Interledger protocol stack, which determines the condition and encoding of the data in the +ILP Packet, and what details the sender and receiver need to discuss beforehand. It can be either +either [Pre-Shared Key](#psk) or [Interledger Payment Request](#ipr). It is called the transport layer because +it provides the end-to-end hashlock condition which ultimately connects the individual +ledger transfers together, into one Interledger payment. In that sense, the transport layer "transports" the payment +over one or more transfers. + +## Trustline +A trustline between two participating parties (participants) is a trust relationship between two parties, +in which each keep track of their own balance. diff --git a/0020-explain-like-im-five/0020-explain-like-im-five.md b/0020-explain-like-im-five/0020-explain-like-im-five.md new file mode 100644 index 00000000..cd4283f7 --- /dev/null +++ b/0020-explain-like-im-five/0020-explain-like-im-five.md @@ -0,0 +1,145 @@ +--- +title: Interledger Protocol - Explain Like I'm Five (ELI5) +draft: 3 +--- +## Interledger Protocol: Explain Like I'm Five (ELI5) + +### Introduction + +A _protocol_ is set of rules about how to do something. + +The Internet can be seen as a set of rules about how to send messages. For example, when a person follows the rules of the Internet to send an email, it can be said that the person is _using the Internet Protocol to transmit information._ + +An _asset_ is a word meaning anything valuable. For example, money is an asset. + +Interledger (aka _Interledger Protocol_, or _ILP_) is a set of rules about how to send assets. For example, when a person follows the rules of the Interledger to send money, it can be said that the person is _using the Interledger Protocol to transmit an asset._ + +Interledger, like the Internet, isn't only used by people. It can be used by a computer, a group of computers, or a group of people and/or computers acting together as a company or corporation. The word _party_ is sometimes used to describe a person, thing, or any group of people or things that can _participate in an action._ + +The Interledger Protocol has four basic building blocks that participate together in the action of transmitting an asset from one party to another. + +These building blocks - described in the next section - were created to fit together in enough ways to transmit an asset of any kind, through any system or systems, anywhere in the world, and handle any necessary exchanges of asset type along the way. + +What the Internet did for information, Interledger aims to do for value. + +_Key point: There are many kinds of assets, even within an asset group such as "money". For example, US Dollars, Japanese Yen and Mexican Peso's are all different kinds of money. Gold and Silver are different kinds of "precious metals" - another asset group. There are also many systems to keep track of who owns what asset, and to handle exchanges of one kind of asset for another. The problem is, the systems don't always connect to each other very well, if they connect at all. It can be difficult, costly and time-consuming to send value around the world at the present time. ILP makes it easy, cost-effective and fast._ + +### ILP Basics: Building Blocks + +- Sender +- Receiver +- Connector +- Ledger + +**Senders** want to send an asset to a particular receiver. Senders know the amount being sent and to whom. Sending something valuable can cost money, and in ILP this cost may be subtracted from the asset amount as it is transmitted. So, senders are aware that what arrives could be less than what is sent, and are prepared for how much that could be. + +**Receivers** are expecting or allowing an asset to arrive from a particular sender. Receivers may know how much to expect, or to expect something to arrive and from whom, but not how much. + +**Connectors** want to link senders with receivers to make ILP payments possible. Connectors also link up with other connectors. This is done to connect as many senders with receivers as possible, through chains of connectors that are built for each payment. + +**Ledgers** are like books that get written in to record how much of a particular asset different parties have. For example, a ledger for a group of people and their money could record how many dollars each person has. In the real world, an example of a ledger might be a banks list of customers bank accounts and the amount each customer has. A ledger also has something like a "ledger manager" that senders, receivers and connectors trust to write changes in the ledger book correctly. If a sender and receiver are on the same ledger, the sender can make an ILP payment to the receiver without a connector's help. + +_Note: In a real bank, it's possible for an account to hold multiple types of assets. For example, one bank account could hold amounts in US dollars, Euro's, and Japanese Yen. To keep things simple in the Interledger Protocol, each "ledger" is limited to recording how much different parties have **of exactly the same kind of asset**. If another asset is used by a sender or receiver, another ledger is kept for that asset. From ILP's point of view, a particular party may have several ledgers associated with them._ + +### ILP Security: Trusted & Trustless + +When you have an asset stored somewhere, like money in a bank, what do you really have? The thing you really have is _trust_ that when you go to that place and ask for your asset back, that it will be given to you. In the case of a bank, you show them your bankcard and ask for your money. Usually that process is automated: you put your card into a bank ATM, tell it how much you want, and out it comes. + +If you didn't trust a particular bank to give you your money back when you ask for it, you probably wouldn't give it to them in the first place. + +This is important because this idea of trust is at the core of how the pieces of ILP fit together, and - interestingly - how this arrangement enables _trustless payments_. Let's see how it works: + +**Payment Quotes: Where To & How Much?** + +As much as possible about an ILP payment is worked out in advance to be sure each party is happy with what's going to happen, and that the funds will remain safe while in transition. While it's possible for a sender to make a payment to a receiver on the same ledger, examining that scenario won't involve much of what makes ILP interesting. So let's take the example that the sender and receiver are on different ledgers. + +Before the payment is made, the sender needs to be happy with how much the payment will cost. The process, called _quoting_, goes like this: + +1) First, a sender asks a connector: "Do you know this receiver?" + +2) If the connector knows the receiver, great. If not, the connector talks to its "peers" (its "connector friends") to see if any know the receiver, or know another connector that might know the receiver... and so on. Through this process, a chain of connectors linking the sender to the receiver is discovered. This chain may involve just one connector, or it may involve many. + +3) Connectors can each charge a little bit for their part in handling the payment. After the chain is built, the connector closest the sender explains how much the receiver will get if the payment goes down that chain. That is, the connector gives the sender a _quote_. If it's much less than what the sender wants to send, the sender will probably choose a different connector. So connectors try make the cost to the sender as little as possible, while still earning something. + +4) The sender decides whether or not to use that connector, based on how much it will cost. If it's a "no", the sender can start over asking a different connector, or asking the same one again (who may figure out a way to make it cheaper this time). When the sender is happy with the cost, the payment can go ahead. + +For each payment, a chain with links of trust is formed between the sender, connector - or connectors - and the receiver. Each link in the chain trusts the one before it not to waste its time, but no link needs to trust any other links in the chain or even know about them. What's great about this, is it means the sender and the receiver don't need to trust each other, or even know who each other is. + +_Key point: If a sender makes a payment with ILP, it is assured that either the receiver will receive the amount the sender agreed to, or the sender will get their original amount returned. The sender doesn't need to trust anyone in-between, and the receiver can be sure the sender agreed to the transfer (and so won't later try get the asset back). This is what makes ILP payments "trustless"._ + +**Payment Security** + +Even with all this worked out, there are still a couple of things that could go wrong. For example, what happens if a connector lies about how much the payment will cost? It's unlikely, but if it did happen, ILP guarantees the sender will get their original asset back in their account. Then of course, the sender could try sending again a different way. In a small number of cases, this situation may result in a connector losing money. That's why connectors charge a little bit for making payments, and it's also why they want to choose carefully to link up with ledgers they can trust. Also, if a connector did lie, all connectors will know about it and "unfriend" them to make it difficult for that connector to be involved in an ILP payment again. + +Much of what happens throughout an ILP payment is actually only there to protect the asset from exactly this kind of thing during its transition from one ledger to another. One of the main techniques used is called a _cryptographic condition_. This is a complex topic, so we're going to use an analogy to represent it. Instead of a "cryptographic condition", let's call it a _special box._ + +*The Special Box* + +Before a payment can happen, the sender and the receiver have a private talk, and decide that its ok if the sender sends an asset to the receiver using ILP at some future time. When the payment happens, it may go through many connectors that the sender doesn't know or trust, and the sender wants to be sure the asset arrives safely with the receiver and nobody else. To help with this, the sender and receiver come up with a secret password, write it down a few times on some pieces of paper, and put each one in a special box. These special boxes are impossible to open, or to see inside. But, there is a slit in the top anyone can insert a piece of paper with a password written on it. If the password is the same as the original, the box opens. If not, the box stays closed. The sender passes a few of these special boxes to their ledger manager, who takes one of the boxes and hands the rest along. Every party involved in the payment will take one and use it to make sure everything is going according to plan. + +_Key point: even though all parties will have special boxes after the payment is prepared, none of the parties but the sender and the receiver will have the password, nor anything valuable that could be taken advantage of. The is important because it means at this point, the payment can be backed-out-of, without any risk to any party of their assets. When a payment is prepared, but before it actually goes ahead, the special boxes don't give the various parties any power over the assets involved. The special boxes only give the various parties the **ability to check a password is correct.**_ + +### How ILP Works: An Example Payment + +Let's imagine someone wants to send $10 US Dollars (USD) from their US bank account, to their friend's bank account in Japan. In this case, the person in the US is the sender, and their friend in Japan is the receiver. The problem is the friends Japanese bank won't accept USD, it only accepts Japanese Yen (JPY). What to do? + +This is where the connector comes in. The connector has an account for USD at the same bank as the sender. So from ILP's point of view, _the connector and the sender have accounts on the same ledger._ + +That same connector also happens to have an account for JPY at the same bank as the receiver. So from ILP's point of view _the connector and the receiver have accounts on the same ledger._ + +If a connector has accounts on the same ledger as the sender and the receiver, an ILP payment involving the connector is now possible. + +Let's say the connector offered the sender a price of ¥105 Japanese Yen for each $1 USD, and the sender was happy with that price. That would mean the sender would send $10 USD, and the receiver would receive ¥1050 JPY (¥105 x $10 = ¥1050). + +Here's what happens next: + +1) The sender says to the ledger manager of the USD ledger: + +- "I've decided to send $10 USD. Can you write in the ledger book that its not in my account anymore?" +- "Then, can you write that this $10 is in the ledgers special 'holding' account?" +- "Also, here's four special boxes. Keep one, and give the other three to this connector." +- "Finally, tell the connector if they give you a password that opens this box, you'll erase the entry in the holding account, and record instead that the $10 USD is in the connectors account!" + +2) The USD ledger manager erases $10 USD from the senders account, puts $10 USD in the holding account, tells the connector about everything, keeps a box, and gives the connector the other three boxes. Now, its the connectors turn to do a similar thing on the JPY ledger. The connector keeps a box, and then says to the JPY ledger manager: + +- "I'm going to give someone on your ledger ¥1050 JPY. Can you take ¥1050 from my account, and put it in the special 'holding' account? +- "Then, can you take these two boxes, keep one, and give the other to the receiver?" +- "Finally, wait for a password from the receiver - if the password opens your box, put the ¥1050 in the receivers account, tell the receiver you've done that, and let me know too!" + +3) The JPY ledger manager does all this, and gives the receiver the last box. The receiver hears all about the payment and puts the password they previously arranged with the sender into the box. The box opens! So, the receiver knows this box came from the sender, and that this payment is a real payment the sender wants to make. The receiver also knows that right now, some Japanese Yen are on hold for the receiver in the JPY ledger, waiting for this password. The receiver wants the money, and trusts the JPY ledger manager. At this point, what the receiver does determines whether the payment goes ahead or not. This is called the payment "executing". The receiver now causes the payment to execute, by giving the password to the JPY ledger manager. + +4) The JPY ledger manager puts this password from the receiver into the box, and it opens! The JPY ledger manager changes the entries in the ledger, so that the holding account doesn't have the ¥1050 JPY, and the receiver's account has it instead. The ledger manager then tells the connector what happened, and hands this password on to them. + +5) The connector tries the password in the box, and of course - it opens. The connector now knows this password will open the box the USD ledger manager has, and the ledger manager will put $10 USD into the connector's account on the USD ledger. The connector trusts the USD ledger manager, and so hands the password on to them. + +6) The USD ledger manager tries the password and when the box opens, puts the USD from the holding account into the connectors account. + +So how are things now? The sender has no USD, that's good - those USD were meant to be sent away. The receiver has ¥1050 JPY. That's good, that's just what the sender wanted. The payment is complete! + +_Note: The connector now has $10 USD, and no JPY. But that connector could wait to be contacted by another sender, this time one from Japan, who wants to send JPY to the US. The connector could then offer $1 USD for, say, ¥109 JPY each. When that payment is done, the connector will have ¥1090 JPY on the JPY ledger, a little bit more than the ¥1050 the connector had before the first payment. That's one way connectors can earn money by linking senders and receviers on the Interledger._ + +### The Magic of ILP: Payments Are An Illusion + +Something interesting about all of this, is that on the Interledger, it may well be correct to say that no assets are actually ever "sent" anywhere. What actually happens is the various ledger managers involved all agree to write in their ledger books at about the same time. What's written first is that the asset is _not_ in the senders account, and then that it _is_ in the sending ledgers holding account. Then its written that the asset is _not_ in the connectors account, but _is_ in the receiving ledgers holding account. When the password is received, the ledger managers do the same thing, but from the receiving ledgers holding account to the receivers account, and the sending ledgers holding account, to the connectors account. Assets are appearing and disappearing _locally_ on different ledgers, and because this is happening at about the same time, and because everyone involved is happy with what's happening, it creates the effect of an asset _seeming_ to be sent from the sender's account to the receivers account, and maybe changing form along the way. But really, that's an illusion. + +_Note: The reason for the "holding" part is that it protects the assets. If anything goes wrong with the payment while the assets are on hold - for example, if the receiver rejects the payment - the assets can simply be taken from the holding accounts and put back into the accounts they came from. In fact, in the case of the example given, if the receiver rejected the payment the assets would **have** to be put back. This is because if the receiver rejected the payment, no password would have been given that could open the boxes, so none of the ledger managers would have done anything._ + +**Asset Exchange & Connectors** + +What's particularly useful about about ILP payments, is that the _kind_ of asset being sent doesn't need to be the same kind as the asset that is received. The asset kind may be exchanged a few times during a payment. + +In the example above, if there wasn't a convenient connector available for USD and JPY, the payment could have involved two or more connectors. For example, the first connector could be happy to gain US Dollars on the senders ledger, and provide Mexican Peso's on another ledger. A second connector may be happy to gain some Mexican Peso's on the first connectors ledger, and lose some Japanese Yen on the receivers ledger. In this case the payment would have involved seven parties acting together. One sender, two connectors, one receiver and three ledgers. + +When looked at this way, each connector could also be seen as just another sender, and in truth, given the way ILP works under the hood - it's a little like that. + +### Further Reading & What You Didn't Know You Know + +We've taken an elementary look at ILP and how it works. In the course of this document, we've skipped over some complex terms commonly used to describe ILP, but we haven't skipped over basic functionality or the principles at work. + +Hopefully, with what you know now, you'll be able to more readily comprehend other ILP documentation, based on a firm understanding of the basic principles. What you may not realise is just how much you know already: + +**What's Really In The Special Box?** + +The "special box" was used as an analogy for a "cryptographic condition". If you read about these elsewhere in the ILP documentation, you might find them associated with the terms "pre-image", "digest", "hash", "fulfillment" and if those weren't frightening enough, "SHA-256". + +But you actually already know what most of these mean. The "cryptographic condition" is the whole arrangement of the box, its hidden password and the checking a password matches the one inside. The "pre-image" is just another word for the password. The "digest", or "hash" is a kind of mushed-up version of that password. Checking for a "valid pre-image of the hash", is the same as checking "the password matches the one in the box", and if it does match, "fulfilling the condition". Finally, "SHA-256" is just a fancy name for a particular type of special box that may be used in ILP. There are many types of these special boxes that all do almost the same thing, and they all have scary names. Don't let it discourage you! diff --git a/0021-plugin-rpc-api/0021-plugin-rpc-api.md b/0021-plugin-rpc-api/0021-plugin-rpc-api.md new file mode 100644 index 00000000..244bfab2 --- /dev/null +++ b/0021-plugin-rpc-api/0021-plugin-rpc-api.md @@ -0,0 +1,225 @@ +--- +title: Plugin RPC API +draft: 3 +--- +# Plugin RPC API + +**This has been superseded by the [IL-RFC 23: Bilateral Transfer Protocol (BTP)](../0023-bilateral-transfer-protocol/0023-bilateral-transfer-protocol.md).** + +## Description + +> TODO + +## Specification + +### Send Transfer + +#### Request + +```http +POST /rpc/?method=send_transfer&prefix=peer.me HTTP/1.1 +Host: rpchost +Accept: application/json +Content-Type: application/json +Authorization: Bearer ABCXYZ + +[ + { + "id": "0e798bd6-213b-4b2f-bc1c-040788e7bae5", + "ledger": "peer.me.", + "to": "peer.me.Y_luxphkAy6ddYzuXb9lXxS60zg5tHjrPh8zz_BfwEA", + "amount": "348807", + "ilp": "ARwAAAAAB1TUwA5nLnVzLm5leHVzLmJvYgMEEEEA", + "executionCondition": "7td8LdXdYkv-6WXWdMlPZ1DhROwRFdazA0m3kTz4LUI", + "expiresAt": "2017-06-14T11:58:18.509Z", + "direction": "outgoing" + } +] +``` + +#### Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json + +true +``` + +### Send Request (ILQP) + +#### Request + +```http +POST /rpc/?method=send_request&prefix=peer.me HTTP/1.1 +Host: rpchost +Accept: application/json +Content-Type: application/json +Authorization: Bearer ABCXYZ + +[ + { + "ledger": "peer.me.", + "to": "peer.me.Y_luxphkAy6ddYzuXb9lXxS60zg5tHjrPh8zz_BfwEA", + "ilp": "ARwAAAAAB1TUwA5nLnVzLm5leHVzLmJvYgMEEEEA" + } +] +``` + +#### Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "ledger": "peer.me.", + "to": "peer.me.P74ZwUNQr3QFK7UjCU4Is9ZuWUHtMqIuA", + "ilp": "AEEEEMgYvJmLzVHel5mLzVnLn5AwUT1BAAAAAwRA" +} +``` + +### Send Request (Routing) + +#### Request + +```http +POST /rpc/?method=send_request&prefix=peer.me HTTP/1.1 +Host: rpchost +Accept: application/json +Content-Type: application/json +Authorization: Bearer ABCXYZ + +[ + { + "ledger": "peer.me.", + "from": "peer.me.P74ZwUNQr3QFK7UjCU4Is9ZuWUHtMqIuA", + "to": "peer.me.Y_luxphkAy6ddYzuXb9lXxS60zg5tHjrPh8zz_BfwEA", + "custom": { + "method": "broadcast_routes", + "data": { + "new_routes": [ { + "source_ledger": "peer.me.", + "destination_ledger": "g.ledger.", + "points": "AAAAAAAAAAAAAAAAAAAAAP////////////////////8=", + "min_message_window": 1, + "paths": [ [] ], + "source_account": "peer.me.P74ZwUNQr3QFK7UjCU4Is9ZuWUHtMqIuA" + } ], + "hold_down_time": 600000, + "unreachable_through_me": [] + } + } + } +] +``` + +#### Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "ledger": "peer.me.", + "to": "peer.me.P74ZwUNQr3QFK7UjCU4Is9ZuWUHtMqIuA", + "custom": {} +} +``` + +### Fulfill Condition + +#### Request + +```http +POST /rpc/?method=fulfill_condition&prefix=peer.me HTTP/1.1 +Host: rpchost +Accept: application/json +Content-Type: application/json +Authorization: Bearer ABCXYZ + +[ + "0e798bd6-213b-4b2f-bc1c-040788e7bae5", + "yMy5Sy5dTjQASrNjS0SywjbwH9nQaiFMWJv1QD3Q_VE" +] +``` + +#### Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json + +true +``` + +### Reject Incoming Transfer + +#### Request + +```http +POST /rpc/?method=reject_incoming_transfer&prefix=peer.me HTTP/1.1 +Host: rpchost +Accept: application/json +Content-Type: application/json +Authorization: Bearer ABCXYZ + +[ + "0e798bd6-213b-4b2f-bc1c-040788e7bae5" +] +``` + +#### Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json + +true +``` + +### Get Limit + +#### Request + +```http +POST /rpc/?method=get_limit&prefix=peer.me HTTP/1.1 +Host: rpchost +Accept: application/json +Content-Type: application/json +Authorization: Bearer ABCXYZ + +[] +``` + +#### Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json + +"-5000000000" +``` + +### Get Balance + +#### Request + +```http +POST /rpc/?method=get_balance&prefix=peer.me HTTP/1.1 +Host: rpchost +Accept: application/json +Content-Type: application/json +Authorization: Bearer ABCXYZ + +[] +``` + +#### Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json + +"10230045000" +``` diff --git a/0022-hashed-timelock-agreements/0022-hashed-timelock-agreements.md b/0022-hashed-timelock-agreements/0022-hashed-timelock-agreements.md new file mode 100644 index 00000000..4e895a03 --- /dev/null +++ b/0022-hashed-timelock-agreements/0022-hashed-timelock-agreements.md @@ -0,0 +1,186 @@ +--- +title: Hashed-Timelock Agreements (HTLAs) +draft: 2 +--- + +# Hashed-Timelock Agreements (HTLAs) +> Generalization of [Hashed-Timelock Contracts (HTLCs)](#background-on-hashed-timelock-contracts-htlcs) used to secure Interledger payments. + +_This document assumes some familiarity with HTLCs and Interledger. It briefly summarizes both but it may be best read after [IL-RFC 1: Interledger Architecture](../0001-interledger-architecture/0001-interledger-architecture.md)._ + +Interledger provides secure multi-hop payments using conditional transfers. Some ledgers natively provide conditional transfers using Hashed-Timelock Contracts (HTLCs). However, not all ledgers support HTLCs. + +Hashed-Timelock Agreements (HTLAs) are a generalization of HTLCs that can be implemented over any type of ledger, whether or not the ledger supports HTLCs. HTLAs work with public and private blockchains, centralized ledgers, payment channels, and even with cash or cases where there is no ledger. There is a spectrum of HTLA types that require varying levels of ledger functionality and bilateral trust. This document describes how HTLAs work and outlines the features and tradeoffs of the different options. + +**Note: Interledger payments can securely cross multiple types of HTLAs at the same time. The selection of HTLA type is strictly a bilateral decision. The type of HTLA used does not affect the security of others in the path.** For details on this see [Interledger Across Diverse HTLAs](#interledger-across-diverse-htlas). + +## Table of Contents + +1. [Background on Hashed-Timelock Contracts (HTLCs)](#background-on-hashed-timelock-contracts-htlcs) +2. [Hashed-Timelock Agreements (HTLAs)](#hashed-timelock-agreements) +3. [HTLAs Without Ledger Support](#htlas-without-ledger-supoprt) +4. [Interledger Across Diverse HTLAs](#interledger-across-diverse-htlas) +5. [Spectrum of HTLA Types](#spectrum-of-htla-types) + 1. [Conditional Payment Channels (with HTLCs)](#conditional-payment-channels-with-htlcs) + 2. [On-Ledger Holds/Escrow (using HTLCs)](#on-ledger-holds-escrow-using-htlcs) + 3. [Simple Payment Channels](#simple-payment-channels) + 4. [Trustlines](#trustlines) +6. [Appendix: Additional HTLA Types](#appendix-additional-htla-types) + +_____ + +## Background on Hashed-Timelock Contracts (HTLCs) + +A [Hashed-Timelock Contract (HTLC)](https://en.bitcoin.it/wiki/Hashed_Timelock_Contracts) is a conditional transfer where the condition is enforced by the ledger. It is a concept from the Bitcoin community that is used in the [Lightning Network](https://lightning.network). + +When the transfer is "prepared", the sender's funds are put on hold by the ledger, pending the fulfillment of a predefined condition. The condition is a hashlock, or the digest of a cryptographic hash function, such as SHA-256 in Lightning and Interledger. The "contract" stipulates that the recipient may claim the funds by presenting a valid preimage of the hash digest before a given timeout. After the timeout, the funds are automatically returned to the sender. This is the Hashed-Timelock Contract. + +HTLCs are enforced by the ledger, so the parties transacting only need to trust the ledger to correctly execute the contract. However, this mechanism requires support from the ledger and only works with ledgers that implement hashlocks and timeouts. + +Interledger is designed to work with all ledgers, so it must support both ledgers with and without support for hashlocks and timeouts. + +## Hashed-Timelock Agreements (HTLAs) + +A contract is a certain type of agreement that is enforced by a third party. Hashed-Timelock Agreements (HTLAs) generalize the idea of HTLCs to include agreements that are enforced by a ledger. The principle has been in use in Interledger since the project was started but the HTLA term was proposed more recently in [this thread on the Interledger mailing list](https://lists.w3.org/Archives/Public/public-interledger/2017Jun/0009.html). + +HTLAs enable secure Interledger payments through all types of ledgers, including those that do not support conditional transfers. + +## HTLAs Without Ledger Support + +Two parties using an HTLA over a ledger that does not support hashlocks and timeouts could proceed in the following manner. The sender would send a message to the recipient telling them that they want to "prepare" a transfer with a given hashlock and timeout. The parties agree that if the recipient presents the preimage of the hash before the timeout, the transfer is executed and the sender owes the recipient the money. Debts are settled using simple transfers on the ledger. + +In the case of a post-funded agreement, the sender can settle their debt with the recipient either for every payment, if their ledger is fast and has low fees, or once the total amount owed reaches the parties' trust/credit limit. In this case, the recipient must trust the sender to pay what they owe. The risk can be limited by capping how much the sender can send before they settle up. + +For pre-funded agreements, the sender transfers either the amount of an individual payment or a bulk amount to the recipient. The recipient then deducts the amount of each "conditional transfer" executed from the pre-funded amount. In this case, the sender must trust the recipient not to steal the pre-funded amount, but this risk can obviously be limited by capping the pre-funded amount. + +HTLAs thus work with ledgers that support hashlocks and timeouts (as HTLCs) and with ledgers that do not. + +For more details on HTLAs without ledger support see the sections below on [Simple Payment Channels](#simple-payment-channels) and [Trustlines](#trustlines). + +## Interledger Across Diverse HTLAs + +A single Interledger payment can cross multiple different types of HTLAs. The Interledger Protocol ensures that the security of each HTLA is independent of the other HTLA types used in the path. + +This is a critical point for understanding HTLAs and Interledger, so we will use an example flow to illustrate it. + +### Example Payment Flow + +In this rendition of the classic Alice to Bob payment, Alice has an account on a blockchain that implements HTLCs and Bob has an account with a bank that does not implement HTLCs: + +``` + On-Ledger HTLC Payment Channel Trustline +Alice --(Blockchain A)--> Connector 1 --(Blockchain B)--> Connector 2 --(Bank C)--> Bob +``` + +Each `--(...)-->` represents a different type of HTLA, but all of them use the same hashlock. + +The Interledger protocol flow (detailed below) ensures that each participant only needs to care about the HTLAs in which they are directly involved. Failure in a different part of the path -- or the selection of a seemingly unwise HTLA type -- does not affect any other agreements. + +#### Interledger Protocol payment flow: + +1. `Alice` and `Bob` agree on the hashlock `H`. The preimage `P` is only known to `Bob` (and to `Alice` in the case of [PSK](../0016-pre-shared-key/0016-pre-shared-key.md)). +2. `Alice` prepares the transfer to `Connector 1` by creating and funding an HTLC on `Blockchain A` with hashlock `H` (see [On-Ledger Holds/Escrow](#on-ledger-holds-escrow-using-htlcs)). +3. `Connector 1` prepares a transfer to `Connector 2` via their shared payment channel, also using hashlock `H` (see [Simple Payment Channels](#simple-payment-channels)). +4. `Connector 2` prepares a transfer to `Bob` on their shared trustline using Hashlock `H` (see [Trustlines](#trustlines)). +5. If `Bob` produces the preimage `P` before the transfer timeout, `Connector 2` will "pay" `Bob` by increasing his balance on their trustline. +6. If `Connector 2` sends `P` to `Connector 1` before their transfer times out, `Connector 1` will send a signed claim to pay `Connector 2`. (Note that whether or not `Connector 1` actually does this is irrelevant to the other parties in the path. `Connector 2` has already paid out to `Bob` and that cannot be undone. `Connector 1` can submit the preimage to `Blockchain A` and still refuse to give the claim to `Connector 2`. This is a known and manageable risk for `Connector 2` and no other parties are affected by it.) +7. If `Connector 1` submits `P` to `Blockchain A` before the timeout, the transfer will be executed and `Alice` will receive the proof `P` that `Bob` was paid. (Note again that whether the HTLC on `Blockchain A` is properly executed only matters to `Alice` and `Connector 1`. The properties of a ledger only matter to the parties holding accounts on that ledger.) + +One way to think of this is that HTLAs abstract away what it means to "get paid". That is just an agreement between two parties and no one else cares how that is structured. All that matters is that each individual is happy with the agreements they're directly a part of. Since all of the hashlocks will be the same, either all of the transfers will happen or none of them will (barring the connector fulfillment failure). + +For more details on the Interledger protocol suite and security, see [IL-RFC 1: Interledger Architecture](../0001-interledger-architecture/0001-interledger-architecture.md). + +_Internet "hat tip": the importance of being able to integrate **any** type of network was demonstrated by [RFC 1149: A Standard for the Transmission of IP Datagrams on Avian Carriers](https://tools.ietf.org/html/rfc1149)._ + +## Spectrum of HTLA Types + +The various types of HTLAs present a tradeoffs between complexity and risk. The more functionality a ledger provides, the less the users of that ledger need to trust one another. + +| | Conditional Payment Channels (with HTLCs) | On-Ledger Holds/Escrow (using HTLCs) | Simple Payment Channels | Trustlines | +|---|---|---|---|---| +| **Ledger Support Required** | High | High | Medium | Low | +| **Implementation Complexity** | High | Medium | Low | Low | +| **Bilateral Risk** | Low | Low | Medium | High | + +### Conditional Payment Channels (with HTLCs) + +* **Ledger Features Required:** Payment Channels with HTLC Updates +* **Non-Functional Ledger Requirements:** Fast* +* **Money at Risk:** None +* **Examples:** [Lightning-Style Channels](https://lightning.network)* + +With conditional payment channels, participants set up the channel by depositing funds in a shared, temporary account on the ledger. When a conditional transfer is prepared, the sender sends the recipient a signed update to the channel that includes a hashlock and timeout. The recipient may redeem the transfer amount if and only if they can present the hash preimage before the timeout. If the sender and recipient agree that the preimage was delivered before the timeout, they can exchange signed statements with the newly agreed upon channel balance. If there is a dispute, the recipient can present the last claim and the hashlock preimage to the ledger and the ledger will determine whether the preimage is valid and was submitted before the timeout. + +Using conditional payment channels, the sender and recipient can transact without any funds being at risk, because all disputes will be mediated by the ledger. This can be used as a mechanism to enable a greater volume of payments "through" a ledger than the ledger can natively support. + +* **Note on ledger speed:** Conditional payment channels may be most appropriate for fast ledgers, because the timeout of each transfer must account for the ledger's processing time. The recipient relies on the fact that they can redeem their funds even if there is a dispute with the sender by presenting their claims and the hashlock preimages to the ledger. However, there are numerous reasons for senders and connectors to want or require that Interledger payments execute or fail quickly (for example for retries and to reduce exchange rate risk). As a result, conditional payment channels may only work with Interledger if the ledger can process claims in a few seconds or less. + +### On-Ledger Holds/Escrow (using HTLCs) + +* **Ledger Features Required:** Transfers with HTLCs +* **Non-Functional Ledger Requirements:** Fast, Low Fees, High Throughput +* **Money at Risk:** None +* **Examples:** [Ethereum Escrow Contract](https://github.com/interledgerjs/ilp-plugin-ethereum), [XRP Escrow](https://ripple.com/build/transactions/#escrowcreate) + +If a ledger provides support for HTLCs and is fast and inexpensive enough, participants can send all Interledger payments directly through the ledger. The sender prepares the conditional transfer by putting funds into a ledger-provided hold account pending a given hashlock and timeout. If the recipient presents the hash preimage before the timeout, the ledger executes the transfer and deposits the funds into the recipient's account automatically. If the timeout is reached, the ledger returns the funds to the sender. + +Using ledger-provided conditional transfers enables parties to transact with no risk, but the ledger must be capable of processing a high volume of payments quickly and with low fees. + +### Simple Payment Channels + +* **Ledger Features Required:** Unconditional, Unidirectional Payment Channels +* **Non-Functional Ledger Requirements:** N/A +* **Money at Risk:** Total Prepared/Fulfilled Without Claims +* **Examples:** [Bitcoin CLTV Channels](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki), [XRP PayChan](https://ripple.com/build/payment-channels-tutorial/) + +Simple payment channels allow parties to send a greater volume of payments than the ledger can process itself. To set up a simple, unidirectional payment channel the sender puts funds into a temporary account on the ledger shared with the recipient. Funds can only be withdrawn from the ledger by presenting a claim signed by both parties that specifies the portion of the funds that will be transferred to the recipient and amount that will be returned to the sender. The sender effectively pays the recipient by sending signed claims to the recipient (not the ledger) that entitle the recipient to withdraw a greater portion of the channel's funds. + +To use a simple payment channel in an HTLA, the sender "prepares" the conditional transfer simply by sending a message to the recipient (including the hashlock and timeout). If the recipient presents the hash preimage before the timeout, the sender sends a new signed claim to the recipient to cover the total amount of the transfers sent thus far. + +The agreement between the sender and recipient dictates how disputes are handled, including cases in which the recipient thinks they submitted the preimage in time and the sender thinks otherwise. Both parties should enforce their own view of the timeout, rather than deferring to the other party. + +In this post-funded model, the recipient must trust the sender for the total amount of the prepared or executed transfers that is not yet covered by payment channel claims. The recipient limits their risk by capping the amount of the transfers they are willing to accept from the sender without the corresponding claims. The risk could be flipped from the recipient to the sender by making the sender pre-fund each transfer by the claim along with the message to prepare the transfer. Pre-funding with simple payment channels is slightly more complex because the recipient needs to send another payment channel update back to the sender if the transfer is rolled back. + +The functionality necessary for simple payment channels is present in nearly all major blockchains today, including Bitcoin (even without [SegWit](https://en.bitcoin.it/wiki/Segregated_Witness)), Ethereum, XRP, Zcash, and Chain. + +### Trustlines + +* **Ledger Features Required:** Transfers Between Accounts +* **Non-Functional Ledger Requirements:** N/A +* **Money at Risk:** Total Trustline Balance +* **Examples:** [`ilp-plugin-virtual`](https://github.com/interledgerjs/ilp-plugin-virtual) + +When a ledger does not provide any support for Interledger, parties can still connect to the Interledger using trustlines. The sender prepares the transfer by sending the recipient a message including the hashlock and timeout. If the recipient produces the hash preimage before the timeout, the sender's debt is increased by the transfer amount. Like with simple payment channels, the participants must agree on how to solve disputes, including disagreements about whether a hash preimage was submitted in time. The sender can continue sending payments until they reach the maximum balance allowed by the recipient. At that point, the sender settles their balance by making a transfer on the ledger. + +In this model, one of the parties must trust the other for the full outstanding balance of the trustline. This may be suitable in cases where the sender and recipient are friends or have a long-standing business relationship. + +Notably, trustlines work over any type of "ledger" -- including cash! As long as there is a way to transfer funds between the sender and recipient, they can send Interledger payments and settle only when the trust limit is reached. If the payment flows in both directions are balanced, there may never even be a need to settle. + +## Appendix: Additional HTLA Types + +This appendix includes additional HTLA types that are possible but were not mentioned above either because they are less desirable or have yet to be implemented. + +### Third Party Escrow + +A third party escrow provider can be used if the ledger supports fast, inexpensive transfers but does not support holds and the sender and recipient do not trust one another. In this case, the sender prepares a transfer by sending the funds to the escrow provider, along with the hashlock and timeout. If the recipient submits the hashlock preimage before the timeout, the escrow provider transfers the funds to the recipient. If not, the escrow provider transfers the funds back. + +It is preferable for ledgers to implement holds natively, but this HTLA type may be used in cases where that is not possible or not happening. Both the sender and recipient must trust the escrow provider for the full value of each transfer, either because of reputation or legal arrangement. If the sender and recipient do trust one another up to some limit, they can avoid the need for a third party by using Trustlines. + +### Notarized Payment Channels + +Disputes may arise in payment channel-based HTLAs because of disagreements over timeouts. Since there is no single source of truth as to whether the hashlock preimage was submitted in time, clock skew or network delays could cause the recipient to think the transfer should be executed while the sender thinks it expired. + +To mitigate the risk of timeout disputes, the sender and recipient could agree to use a third party notary to act as the timekeeper. The sender prepares the transfer by submitting the details to the notary and the recipient (unless the notary also acts as a message broker). The recipient submits the preimage to the notary, who enforces the timeout. The notary signs a statement attesting to whether the preimage was recieved in time and gives the signed message to both the sender and recipient. + +Third-party notaries could be used with simple or more complex payment channels. In the simple, unconditional type, the notary's decision is not used in the actual payment channel updates but the notary merely as the decision-maker in the agreement between the sender and recipient. In the more complex variety, the payment channels could be constructed such that the channel updates are conditional upon the signed statement from the notary. When using third-party notaries the sender and recipient must trust the notary not to be colluding with one of the parties and to not sign two conflicting statements. + +### Third Party Payment Channels + +Falling in-between the guarantees of Third Party Escrow and Notarized Payment Channels, a "Third Party Payment Channel" involves escrowing money in a payment channel and giving a third party authority to create claims. To set up the payment channel, the sender funds a temporary account on the ledger as in a Simple Payment Channel. However, the third party's key is authorized to create claims on the channel balance instead of using the sender's public key. + +When the sender prepares a transfer, they send a message to the recipient including the hashlock and timeout. To execute the transfer, the recipient submits the preimage to the third party. If the third party gets the preimage before the timeout, they pay the recipient from the sender's funds by signing a new claim. + +In this model, the recipient does not need to trust the sender at all. The recipient instead trusts the third party to honestly enforce the timeouts and sign claims when preimages are submitted in time. This means that the recipient only needs to trust the third party for the transfers that have been executed but are not yet covered by claims. + +The sender has to trust the third party not to create a claim for more than they owe, which means they trust the third party for the payment channel's whole balance. However, unless the third party is colluding with the receiver, they have no incentive to pay out more than the sender owes. + diff --git a/0023-bilateral-transfer-protocol/0023-bilateral-transfer-protocol.md b/0023-bilateral-transfer-protocol/0023-bilateral-transfer-protocol.md new file mode 100644 index 00000000..75c09697 --- /dev/null +++ b/0023-bilateral-transfer-protocol/0023-bilateral-transfer-protocol.md @@ -0,0 +1,326 @@ +--- +title: Bilateral Transfer Protocol 2.0 (BTP/2.0) +draft: 6 +--- +# Bilateral Transfer Protocol 2.0 (BTP/2.0) + +## Preface + +This document describes version 2.0 of the Bilateral Transfer Protocol (BTP), a request/response +protocol for bilateral WebSocket links between Interledger connectors. + +## Introduction + +### Motivation + +When two Interledger connectors send ILPv4 packets over HTTP POST, +they each need to act as an HTTP server at times. If one of the connectors runs behind a firewall, +this may be impossible. Therefore, BTP uses WebSockets instead of HTTP. With +WebSockets, only one of the connectors needs to be publicly addressable. + +However, WebSockets don't provide a mechanism for relating responses to requests. BTP adds this missing +request/response layer, between WebSockets and the request/response pairs exchanged by the connectors. + +### Scope + +This document describes the flow and data format that BTP uses, but not +sub-protocols. Sub-protocols include optional functionality like ledger +metadata, balance, automated settlement, and dispute resolution. Some protocols +are documented on [the wiki +page](https://github.com/interledger/interledger/wiki/Interledger-over-CLP). +They are carried in the protocol data of BTP packets. + +The BTP packet format is described exactly in the [BTP ASN.1 +spec](https://github.com/interledger/rfcs/blob/master/asn1/BilateralTransferProtocol.asn). + +## Terminology + +- **BTP** is the bilateral transfer protocol, as described by this document and the + ASN.1 spec. + +- A **Sub-Protocol** is a protocol which isn't defined by BTP and is carried + in the protocol data (see below). The first one is the primary sub-protocol, + subsequent entries are secondary sub-protocols. + +- A **BTP Connection** is a websocket connection over which BTP packets are + sent. Websockets (as opposed to raw TLS sockets) are used because they provide + message framing and can be used from the browser. + +- **BTP Packets** are the protocol data units described in this document. They are + formally defined in the [BTP ASN.1 spec](https://github.com/interledger/rfcs/blob/master/asn1/BilateralTransferProtocol.asn). + +- **Peers** are the parties on a BTP connection. Your peer is the party on the + other side of the BTP connection. + +- The **Bilateral Ledger** is the ledger which the peers on a BTP connection + are keeping track of. When a peer keeping Authoritative State receives a BTP +packet, they process it and adjust their copy of the bilateral ledger. The +bilateral ledger is not to be confused with the Underlying Ledger. + +- **Authoritative State** is the authoritative view of the Bilateral Ledger's + state, maintained by one or both of the peers. Because both peers on a BTP +connection can keep authoritative state, they can get into dispute if they +disagree on the state of a transfer. This usually happens when network latency +causes the peers to disagree about expiries. If one party keeps authoritative +state, the other party must trust them not to tamper with it. + +- A request is **In-Flight** if the request has been sent out, but no response + has been received yet. + +## Overview + +BTP is broken up into one different RPC requests, which can get two different +responses. Every BTP packet follows a common structure: + +``` + +---------------+ +1 | Type (1) | + +---------------+ +1 | Request ID | +2 | (2) | +3 | | +4 | | + +---------------+ +1 | Length Prefix | +2 | (3) | + +---------------+ + | Packet- | + | Specific | + | Data | + . (4) | + . | + . | + | | + +---------------+ +1 | Sub-Protocol | +2 | Count (5) | + +---------------+ + | Sub-Protocol | + | Data | + . (6) | + . | + . | + | | + +---------------+ +``` + +1. **Type**: A 1-byte value describing what type of BTP packet this is. +The values are described below, in [BTP Type IDs](#btp-type-ids). + +2. **Request ID**: A random 4-byte value used to correlate requests +and responses. This value MAY be sequential instead of random, but care must +be taken so that duplicate IDs are never in-flight at the same time. + +3. **Length Prefix**: A 1-byte (if under 128) or 2-byte value, containing the +combined length of the packet-specific data and protocol data sections. + +4. **Packet-Specific Data:** Fields specific to the type of BTP packet. Variable +length. + +5. **Sub-Protocol Count:** Variable-length integer containing the number of +sub-protocols carried by this packet. + +5. **Sub-Protocol Data:** A list of protocols, containing a string (the protocol's name), a 1-byte flag (containing the MIME type), and a length-prefixed octet string (containing the protocol's data). Exact description is below in [Sub-Protocol Data Format](#sub-protocol-data-format). + +### BTP Type IDs + +| ID | Type | Request/Response | +|:--|:--|:--| +| 1 | `Response` | Response | +| 2 | `Error` | Response | +| 3 | (not used) | | +| 4 | (not used) | | +| 5 | (not used) | | +| 6 | `Message` | Request | +| 7 | `Transfer` | Request | + +### Sub-Protocol Data Format + +```asn1 +ContentType ::= INTEGER { + applicationOctetString (0), + textPlainUtf8 (1), + applicationJson (2) +} (0..255) + +ProtocolData ::= SEQUENCE OF SEQUENCE { + protocolName IA5String, + contentType ContentType, + data OCTET STRING +} +``` + +## Authentication + +Before anything else, when a client connects to a server, it sends a special +`Message` request. Its primary `protocolData` entry MUST have name `'auth'`, +content type `MIME_APPLICATION_OCTET_STREAM`, +and empty data, and among the secondary entries, there MUST be a UTF-8 +<<<<<<< HEAD +`'auth_token'` entry, and there MAY be a UTF-8 `'auth_username'` entry. The further secondary +protocol data entries of this `Message` request MAY also be used to send +additional information to the server. In situations where no authentication +is needed, the `'auth_token'` data can be set to the +empty string, but it cannot be omitted. In situations where the `'auth_token'` +acts as a bearer token, and the value of `'auth_username'` can be derived from +the value of `'auth_token'` deterministically, the `'auth_username'` entry can either +be omitted, or set to the empty string. +======= +`'auth_token'` entry. The further secondary +protocol data entries of this `Message` request MAY also be used to send +additional information to the server. In situations where no authentication +is needed, the `'auth_token'` data can be set to the +empty string, but it cannot be omitted. +>>>>>>> 35e6dd7... docs: BTP/2.0 + +If the client sends any BTP call that is not a Message, or sends a Message call +whose primary sub-protocol is not `auth`, the server should respond with an `Error` +and close the connection. + +The server responds with a `Response` or `Error` as appropriate. Again, the +`protocolData` field there MAY be used to send additional information to +the client. To be clear, the server responds with an `Error` if: + +* any other packet is sent before the auth data +* the provided authentication data is invalid or incorrect +* any of the other protocol rules are violated (e.g. having two subprotos with the same name) +* it takes too long before the authentication data is sent. + +If the server sent an `Error`, it subsequently closes the connection. +If the server sent a `Response`, the BTP connection is open, until either +one of the parties closes it. At the BTP level, the client and server play +identical roles. + +If the client does not send an Auth packet within a reasonable time, the +server optionally sends a Message informing the client that the authentication timed out, +and then closes the connection. If the client did send an Auth packet, but +got neither a `Response` nor an `Error` back from the server, the client +closes the connection. + +If the connection is ever dropped and reconnected then it must be re-authenticated. + +## Sub-protocols + +In order to understand the different BTP calls, it is necessary to distinguish between the first ("primary") and subsequent ("secondary") sub-protocol entries. The primary sub-protocol +entry defines what type of action or information is requested of the recipient of the message. The secondary sub-protocols should not request additional actions or information. If multiple actions or +pieces of information are required, multiple separate Messages should be sent. The secondary sub-protocols should only modify the request made in the primary sub-protocol, or provide additional contextual data which can be consumed in a readonly way (without affecting the result). + +For example, the primary sub-protocol entry of a Message might represent a quote request, while one additional secondary sub-protocol entry may be present, indicating this request was forwarded by a proxy. + +Likewise, only the primary sub-protocol data in a Response indicates whether result of the request from the Message being responded to actually succeeded or not. + +In Error calls, the distinction between primary and secondary sub-protocol entries is less strict. + +## Flow + +BTP uses a simple RPC flow. A request-type BTP packet is sent, and a +response-type BTP packet is sent in response with the same request ID. The +request types are `Message` and `Transfer`, and the +response types are `Response` and [`Error`](#error). + +Because it would be too slow to atomically save all requestIds that are +processed, they are not idempotent. It is the responsibility of the requestor +to make sure they don't duplicate requestIds. The implementation should ensure +that no two in-flight requests are sent out with the same requestId. The +responder should always send back a response to a request with the same +requestId. + +There are also a couple of tricky cases to handle: + +- If an unexpected BTP packet is received, no response should be sent. An unexpected BTP packet is a response for which a request was not sent, or a response for a request which has already been responded to. +- If an unreadable BTP packet is received, no response should be sent. An unreadable BTP packet is one which is structurally invalid, i.e. terminates before length prefixes dictate or contains illegal characters. + +These behaviors are important for preventing accidental feedback loops. If an +unexpected packet triggered an error, that error may be unexpected to the +sender. The sender would reply with another unexpected error, causing an +infinite loop. Unreadable packets must be ignored too. If an application got +onto a BTP connection and spoke the wrong protocol, it would trigger an error +from BTP. This might trigger an error from the application, and it would +devolve into another infinite loop. + +### Message + +```asn1 +Message ::= SEQUENCE { + protocolData ProtocolData +} +``` + +`Message` is used for sending information to the peer. It contains no +packet-specific data, only protocol data. [ILP](https://github.com/interledger/rfcs/blob/master/0003-interledger-protocol/0003-interledger-protocol.md#ilp-payment-packet-format) packets are attached under the +protocol name `ilp` with content-type `application/octet-stream`. + +- `Response` is returned if the peer acknowledges the `Message`. If the peer + has data to send in reply (e.g. a quote response), it is carried in the +protocol data. + +- `Error` is returned if the peer is not able to process the `Message`, or there + was an unexpected error and further `Message`s should not be sent. This does +not include ILP errors such as "No quote found", only the cases in which the +`Message` cannot be sent/processed at all. + +### Error + +``` +Error ::= SEQUENCE { + -- Standardized error code + code IA5String (SIZE (3)), + -- Corresponding error code + name IA5String, + -- Time of emission + triggeredAt GeneralizedTime, + -- Additional data + data OCTET STRING (SIZE (0..8192)), + -- + protocolData ProtocolData +} +``` + +`Error` is a response-type message, returned when an error occurs on the BTP +level. It has packet-specific data which resembles the [ILP Error +format](https://github.com/interledger/rfcs/blob/master/0003-interledger-protocol/0003-interledger-protocol.md#ilp-error-format), +but irrelevant fields have been taken off and new error codes have been +written: + +#### Error Codes + +Errors marked with a `T` are temporary, and can be retried after a short +(1-60s) wait. If a retry fails again with a temporary error, a BTP client +SHOULD wait longer before trying again. Errors marked with `F` are final, and +the same request MUST NOT be retried. + +| Code | Name | Description | +|:--|:--|:--| +| **T00** | UnreachableError | Temporary error, indicating that the connector cannot process this request at the moment. Try again later. | +| **F00** | NotAcceptedError | Data were symantically invalid. | +| **F01** | InvalidFieldsError | At least one field contained structurally invalid data, e.g. timestamp full of garbage characters. | +| **F03** | TransferNotFoundError | The transferId included in the packet does not reference an existing transfer. | +| **F04** | InvalidFulfillmentError | The fulfillment included in the packet does not match the transfer's condition. | +| **F05** | DuplicateIdError | The transferId and method match a previous request, but other data do not. | +| **F06** | AlreadyRolledBackError | The transfer cannot be fulfilled because it has already been rejected or expired. | +| **F07** | AlreadyFulfilledError | The transfer cannot be rejected because it has already been fulfilled. | +| **F08** | InsufficientBalanceError | The transfer cannot be prepared because there is not enough available liquidity. | + +### Transfer + +```asn1 +Transfer ::= SEQUENCE { + amount UInt64, + -- + protocolData ProtocolData +} +``` + +`Transfer` is used to send proof of payment, payment channel claims, or other +settlement information to the other connector. +The amount should indicate the additional value of this settlement state (compared to the +previous settlement state), in a unit that was agreed out-of-band. + +- `Response` is returned if the peer acknowledges the `Transfer`. This means + the transfer is now completed and has been applied to the balance. If a +`Response` has been returned, balances MUST have been updated. + +- `Error` is returned if the peer does not accept the transfer. This could be + because some protocol data are malformed, or because the peer believes that +the sender's balance is insufficient. If an `Error` has been returned, the peer +MUST NOT have updated their balance. diff --git a/0024-ledger-plugin-interface-2/0024-ledger-plugin-interface-2.md b/0024-ledger-plugin-interface-2/0024-ledger-plugin-interface-2.md new file mode 100644 index 00000000..4197ea5b --- /dev/null +++ b/0024-ledger-plugin-interface-2/0024-ledger-plugin-interface-2.md @@ -0,0 +1,299 @@ +--- +title: The Javascript Ledger Plugin Interface Version 2 +draft: 3 +--- +# Javascript Ledger Plugin Interface Version 2 + +The Interledger Protocol is a protocol suite for making payments across multiple different settlement systems. + +This spec defines a JavaScript ledger abstraction interface for Interledger clients and connectors to communicate and route payments across different ledger protocols. While the exact methods and events defined here are specific to the JavaScript implementation, this may be used as a guide for ledger abstractions in other languages. + +To send ILP payments through a new ledger, one must implement a ledger plugin that exposes the interface defined below. This can be used with the ILP Client and Connector and should work out of the box. + +~This spec depends on the [ILP spec](../0003-interledger-protocol/).~ + +## Class: LedgerPlugin +`class LedgerPlugin` + +###### Methods +| | Name | +|:--|:--| +| `new` | [**LedgerPlugin**](#new-ledgerplugin) ( opts, api ) | +| | [**connect**](#ledgerpluginconnect) ( options ) `⇒ Promise.` | +| | [**disconnect**](#ledgerplugindisconnect) ( ) `⇒ Promise.` | +| | [**isConnected**](#ledgerpluginisconnected) ( ) `⇒ Boolean` | +| | [**sendData**](#ledgerpluginsenddata) ( data ) ⇒ Promise.<Buffer> | +| | [**sendMoney**](#ledgerpluginsendmoney) ( amount ) ⇒ Promise.<undefined> | +| | [**registerDataHandler**](#ledgerpluginregisterdatahandler) ( dataHandler ) ⇒ undefined | +| | [**deregisterDataHandler**](#ledgerpluginderegisterdatahandler) ( ) ⇒ undefined | +| | [**registerMoneyHandler**](#ledgerpluginregistermoneyhandler) ( moneyHandler ) ⇒ undefined | +| | [**deregisterMoneyHandler**](#ledgerpluginderegistermoneyhandler) ( ) ⇒ undefined | + +###### Constants + +| | Name | +|:--|:--| +| `static` | [**version**](#ledgerpluginversion) = 2 | + +###### Events +| Name | Handler | +|:--|:--| +| [**connect**](#event-connect) | `( ) ⇒` | +| [**disconnect**](#event-disconnect) | `( ) ⇒` | +| [**error**](#event-error) | `( ) ⇒` | + +### Instance Management + +#### new LedgerPlugin +new LedgerPlugin( **opts** : object, **api**? : [PluginServices](#class-pluginservices) ) + +Create a new instance of the plugin. Each instance typically corresponds to a different ledger. However, some plugins MAY deviate from a strict one-to-one relationship and MAY internally act as a virtual connector to more than one counterparty. + +The first parameter `opts` is a configuration object the shape of which is specific to each plugin. Plugins will often be configured through environment variables, so it is recommended that the `opts` SHOULD be JSON serializable. However, plugins MAY use non-serializable values to offer advanced features. + +The second parameter `api` is optional and is used to pass additional environment services to the plugin, such as a logger or a key-value store. Most plugins SHOULD work even if this parameter is `undefined`, but MAY offer less functionality in that case (e.g. no persistence.) + +Throws `InvalidFieldsError` if the constructor is given incorrect arguments in `opts`. Throws `TypeError` if `opts` is not an object or `api` is defined and not an object. Throws `InvalidServicesError` if a service is required, but not provided via `api`. + +###### Parameters +| Name | Type | Description | +|:--|:--|:--| +| opts | [PluginOptions](#class-pluginoptions) | Object containing ledger-related settings. May contain plugin-specific fields. | + +###### Example +```js +const ledgerPlugin = new LedgerPlugin({ + + // auth parameters are defined by the plugin + + _store: { + // persistence may be required for internal use by some ledger plugins + // (e.g. when the ledger has reduced memo capability and we can only put an ID in the memo) + // Store a value under a key + put: (key, value) => { + // Returns Promise. + }, + // Fetch a value by key + get: (key) => { + // Returns Promise. + }, + // Delete a value by key + del: (key) => { + // Returns Promise. + } + } +}) +``` + +For a detailed description of these properties, please see [`PluginOptions`](#class-pluginoptions). + +#### LedgerPlugin.version +LedgerPlugin.**version**:Number + +Always `2` for this version of the Ledger Plugin Interface. + +### Connection Management + +#### LedgerPlugin#connect +ledgerPlugin.connect( options:[ConnectOptions](#class-connectoptions ) ⇒ Promise.<undefined> + +`options` is optional. + +Initiate ledger event subscriptions. Once `connect` is called the ledger plugin MUST attempt to subscribe to and report ledger events. Once the connection is established, the ledger plugin should emit the [`connect`](#event-connect-) event. If the connection is lost, the ledger plugin SHOULD emit the [`disconnect`](#event-disconnect-) event. + +Rejects with `InvalidFieldsError` if credentials are missing, and `NotAcceptedError` if credentials are rejected. +Rejects with `TypeError` if `options.timeout` is passed but is not a `Number`. + +#### LedgerPlugin#disconnect +ledgerPlugin.disconnect() ⇒ Promise.<undefined> + +Unsubscribe from ledger events. + +#### LedgerPlugin#isConnected +ledgerPlugin.isConnected() ⇒ Boolean + +Query whether the plugin is currently connected. + +#### Event: `connect` +ledgerPlugin.on('connect', () ⇒ ) + +Emitted whenever a connection is successfully established. + +#### Event: `disconnect` +ledgerPlugin.on('disconnect', () ⇒ ) + +Emitted when the connection has been terminated or lost. + +#### Event: `error` +ledgerPlugin.on('error', ( **err**:Error ) ⇒ ) + +General event for fatal exceptions. Emitted when the plugin experienced an unexpected unrecoverable condition. Once triggered, this instance of the plugin MUST NOT be used anymore. + +### Sending + +#### LedgerPlugin#sendData +ledgerPlugin.sendData( **data**:Buffer ) ⇒ Promise.<Buffer> + +Sends data to the counterparty of the account and returns a response asynchronously. Request data is passed in as a `Buffer` and response data is returned as a `Buffer`. + +###### Parameters +| Name | Type | Description | +|:--|:--|:--| +| data | Buffer | Binary request data | + +###### Returns +**`Promise.`** A promise which resolves when the response has been received. + +This method MAY reject with any arbitrary JavaScript error. + +###### Example +```js +const responseBuffer = await p.sendData(requestBuffer) +``` + +#### LedgerPlugin#sendMoney +ledgerPlugin.sendMoney( **amount**:string ) ⇒ Promise.<undefined> + +Transfer `amount` units of money from the caller to the counterparty of the account. + +All plugins MUST support amounts in a range from one to some maximum. + +### Receiving + +#### LedgerPlugin#registerDataHandler +ledgerPlugin.registerDataHandler( **dataHandler**: ( data: Buffer ) ⇒ Promise<Buffer> ) ⇒ undefined + +Set the callback which is used to handle incoming prepared data packets. The callback should expect one parameter (the data as a Buffer)) and return a promise for the resulting response data packet (as a Buffer.) If an error occurs, the callback MAY throw an exception. In general, the callback should behave as [`sendData`](#ledgerpluginsenddata) does. + +If a data handler is already set, this method throws a `DataHandlerAlreadyRegisteredError`. In order to change the data handler, the old handler must first be removed via [`deregisterDataHandler`](#ledgerpluginderegisterdatahandler). This is to ensure that handlers are not overwritten by accident. + +If an incoming packet is received by the plugin, but no handler is registered, the plugin SHOULD respond with an error. + +#### LedgerPlugin#deregisterDataHandler +ledgerPlugin.deregisterDataHandler( ) ⇒ undefined + +Removes the currently used data handler. This has the same effect as if [`registerDataHandler`](#ledgerpluginregisterdatahandler) had never been called. + +If no data handler is currently set, this method does nothing. + +#### LedgerPlugin#registerMoneyHandler +ledgerPlugin.registerMoneyHandler( **moneyHandler**: ( amount: string ) ⇒ Promise<undefined> ) ⇒ undefined + +Set the callback which is used to handle incoming money. The callback should expect one parameter (the amount) and return a promise. If an error occurs, the callback MAY throw an exception. In general, the callback should behave as [`sendMoney`](#ledgerpluginsendmoney) does. + +If a money handler is already set, this method throws a `MoneyHandlerAlreadyRegisteredError`. In order to change the money handler, the old handler must first be removed via [`deregisterMoneyHandler`](#ledgerpluginderegistermoneyhandler). This is to ensure that handlers are not overwritten by accident. + +If incoming money is received by the plugin, but no handler is registered, the plugin SHOULD return an error (and MAY return the money.) + +#### LedgerPlugin#deregisterMoneyHandler +ledgerPlugin.deregisterMoneyHandler( ) ⇒ undefined + +Removes the currently used money handler. This has the same effect as if [`registerMoneyHandler`](#ledgerpluginregistermoneyhandler) had never been called. + +If no money handler is currently set, this method does nothing. + +## Class: PluginServices +class PluginServices + +Plugin services are optionally passed in to the [`LedgerPlugin`](#class-ledgerplugin) +constructor when a plugin is being instantiated. Which services are provided +MAY vary based on the host environment or none MAY be available at all. + +###### Special Fields +| Type | Name | Description | +|:--|:--|:--| +| `Object` | [store](#pluginservices-store) | Simple key-value store object | +| `Object` | [log](#pluginservices-log) | Simple logger object | + +### Fields + +#### PluginServices#store +**store**:Object + +Provides callback hooks to the host's persistence layer. + +Most plugins SHOULD work (possibly with higher trust or degraded experience) without a `store`. However, if a plugin is not able to function without a store and none is provided, the constructor MUST throw an `InvalidServicesError`. + +Method names are based on the popular LevelUP/LevelDOWN packages. + +###### Example +```js +{ + // Store a value under a key + put: (key, value) => { + // Returns Promise. + }, + // Fetch a value by key + get: (key) => { + // Returns Promise. + }, + // Delete a value by key + del: (key) => { + // Returns Promise. + } +} +``` + +#### PluginServices#log +**log**:Object + +Provides logging hooks to the host. Hosts MAY use this feature to prefix log lines with the identifier of the plugin instance. + +If this parameter is not provided, the plugin SHOULD use a suitable default logging mechanism. + +The logging methods support [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting. The following formatters are available: + +| Formatter | Representation | +|-----------|----------------| +| `%O` | Pretty-print an Object on multiple lines. | +| `%o` | Pretty-print an Object all on a single line. | +| `%s` | String. | +| `%d` | Number (both integer and float). | +| `%j` | JSON. Replaced with the string '[Circular]' if the argument contains circular references. | +| `%%` | Single percent sign ('%'). This does not consume an argument. | + +Log messages MUST NOT contain private keys or other credentials. + +###### Example +```js +{ + // Extremely verbose debug information + debug: (message, ...params) => { } + // Notable events that may happen during normal operation + info: (message, ...params) => { } + // Warnings indicate unusual events that call for the user's attention + warn: (message, ...params) => { } + // Errors indicate something went wrong + error: (message, ...params) => { } +} +``` + +## Class: ConnectOptions +class ConnectOptions + +###### Fields +| Type | Name | Description | +|:--|:--|:--| +| `Number` | [timeout](#connectoptions-timeout) | Amount of time before the client SHOULD give up trying to connect (in milliseconds) | + +### Fields + +#### ConnectOptions#timeout +**timeout**:Number + +The number of milliseconds that the plugin should spend trying to connect before giving up. + +If falsy, use the plugin's default timeout. +If `Infinity`, there is no timeout. + +## Errors + +Various methods defined in the LPI throw errors; others can reject the Promise they return with errors. In both cases, these errors need to be derived from JavaScript's `Error.prototype`, and need to have the `.name` field set to their own name, as a String. Example: + +```js +function InvalidFieldsError(message) { + this.name = 'InvalidFieldsError' + this.message = (message || '') +} +InvalidFieldsError.prototype = Error.prototype +``` diff --git a/0025-pre-shared-key-2/0025-pre-shared-key-2.md b/0025-pre-shared-key-2/0025-pre-shared-key-2.md new file mode 100644 index 00000000..37cba15c --- /dev/null +++ b/0025-pre-shared-key-2/0025-pre-shared-key-2.md @@ -0,0 +1,236 @@ +--- +title: Pre-Shared Key V2 (PSKv2) Transport Protocol +draft: 2 +--- +# Pre-Shared Key V2 (PSKv2) Transport Protocol + +Version 2 of the Pre-Shared Key Transport Protocol is a request/response protocol built on ILP that handles condition generation and data encryption and authentication. It can be used for individual payments or messages, [end-to-end quoting](#end-to-end-quoting), and as a building block for streaming and chunked payments use cases. + +**Table of Contents** + +* [Overview](#overview) + + [Relation to Other Protocols](#relation-to-other-protocols) + + [Setup](#setup) + + [Encryption](#encryption) + + [Conditions and Fulfillments](#conditions-and-fulfillments) + + [End-To-End Quoting](#end-to-end-quoting) +* [Model of Operation](#model-of-operation) + + [Informational Quote](#informational-quote) + + [Single Payment](#single-payment) +* [Specification](#specification) + + [Encryption](#encryption-1) + + [Data Packet](#data-packet) + + [Condition and Fulfillment Generation](#condition-and-fulfillment-generation) + +## Overview + +Like [PSKv1](./0016-pre-shared-key/0016-pre-shared-key.md), PSKv2 uses a secret shared between the sender and receiver to generate the conditions and fulfillments for payments without the need for repeated end-to-end communication. PSKv2 can be used to send individual packets or multiple, as well as unfulfillable test payments that may be used for quotes. + +### Relation to Other Protocols + +PSKv2 is designed to work with ILPv4 Prepare, Fulfill, and Reject packets. + +PSKv2 may be used by applications or higher-level protocols for sending individual requests and responses or for informational quotes. + +PSKv2 does not include features for segmenting and reassembling packets for Chunked Payments, but it may be used in higher-level protocols that implement such functionality. Those higher-level protocols may include additional fields in the data field of the PSK packets to group or order individual requests and responses. + +### Setup + +Before using PSKv2, the sender and receiver use an authenticated, encrypted communication channel (generally provided as part of an [Application Layer Protocol](./0001-interledger-architecture/0001-interledger-architecture.md#application-layer)) to exchange: + +- PSK version (such as `2.0` for this version) +- A 32-byte random or pseudorandom shared secret +- The receiver's ILP address + +The receiver MAY generate the shared secret from a longer-term secret and a nonce, as described in [PSKv1's Appendix A: Recommended Algorithm for Generating Shared Secrets](./0016-pre-shared-key/0016-pre-shared-key.md#appendix-a-recommended-algorithm-for-generating-shared-secrets). In this case, the receiver would append the nonce (**not** the secret) to their ILP address to create the destination address they communicate to the sender. + +### Encryption + +In PSKv2.0, request and response data is encrypted using [AES-256-GCM](https://en.wikipedia.org/wiki/Galois/Counter_Mode) with a 12-byte Initialization Vector (IV) and a 16-Byte Authentication Tag. + +If subsequent versions support additional encryption algorithms, those details should be exchanged between the sender and receiver when they establish the pre-shared key. If a receiver attempts to decrypt an incoming packet but is unable to (perhaps because the sender is using an unsupported cipher), they SHOULD reject the incoming transfer with an `F06: Unexpected Payment` error. + +### Conditions and Fulfillments + +The sender and receiver generate the condition and fulfillment using the pre-shared key that gives this protocol its name. The fulfillment is computed from the pre-shared key and the encrypted PSK data. The condition is the hash of the fulfillment. + +As in PSKv1, this method for generating the condition and fulfillment enables many payments to be sent without end-to-end communication for each one. However, because the sender also knows the fulfillment up-front, neither version of PSK allows the sender to use the fulfillment as a non-repudiable proof of payment. For use cases where this is desired, a Transport Protocol like the [Interledger Payment Request (IPR)](./0011-interledger-payment-request/0011-interledger-payment-request.md) should be used instead. + +### End-To-End Quoting + +In contrast to PSKv1, which depends on the [Interledger Quoting Protocol (ILQP)](./0008-interledger-quoting-protocol/0008-interledger-quoting-protocol.md), PSKv2 has two built-in mechanisms for discovering the exchange rate of a given path: informational quotes and dynamic price discovery. + +For purely informational quotes, PSKv2 uses unfulfillable test payments to the receiver to determine the exchange rate for a given path. The condition is set to a random 32-byte condition (with no known fulfillment). When the receiver gets the incoming payment, they reject the incoming transfer and include in their response how much arrived in that transfer. The sender can use the source amount they set and the receiver's response to determine the rate. + +When a sender is sending many payments to the receiver, they need a dynamic view of the exchange rate because the rate may change during the course of the interaction. To handle these cases, every PSKv2 payment response includes the amount that arrived in the final transfer to the receiver. Similar to the informational quotes, the sender can use the packet's source amount and the receiver's response to determine and monitor the exchange rate. + +**Judging Prices by Source Units:** With any type of Interledger quoting, whether end-to-end or using ILQP, senders SHOULD judge prices in their source units. Receivers can lie about how much arrives in a test payment for a quote or they could run their own ledger and connector that happen to have a bad exchange rate. Therefore, senders must assume that whatever amount the receiver says has arrived represents the "real" rate of the path. + +**Linear Exchange Rates and Fixed Destination Amount Quotes:** End-to-End Quoting works best with linear exchange rates. If the rate for a path is linear, it can be determined by sending a single test payment for an arbitrary amount. The source amount required to deliver a fixed destination amount can be easily determined by dividing the source amount by the rate. If exchange rates are non-linear, getting an informational quote for a fixed destination amount would require a binary search, which is not ideal. However, in an Interledger network primarily or exclusively suited to small payments, there should be little reason for rates to vary by payment size. + +## Model of Operation + +### Informational Quote + +1. Sender creates a packet with a type of 4, a random Request ID, and the Request Amount set to 2^64 - 1 (the maximum unsigned 64-bit integer, or 18446744073709551615). Sender [encrypts](#encryption-envelope) the packet using the encryption key derived using the method described in [Encryption Pseudocode](#encryption-pseudocode). +2. Sender generates a random 32-byte condition. +3. Sender sends an ILP Prepare packet with the condition, encrypted data and either the chosen source amount (in the case of a fixed source amount payment) or an arbitrary amount (in the case of a fixed destination amount). +4. Receiver is notified of the incoming Prepare packet, rederives the encryption key as described in [Encryption Pseudocode](#encryption-pseudocode), and decrypts the PSK Request. +5. Receiver compares the Request Amount from the Request with the amount in the Prepare packet they received. They note that the incoming transfer amount is too low. +6. Receiver creates a PSK Error packet, using the same Request ID from the sender's Request and setting the Request Amount to the amount that arrived in the Prepare packet. Receiver [encrypts](#encryption-envelope) the packet using the same encryption key as before. +7. Receiver replies to the ILP Prepare packet with an ILP Reject that includes the encrypted PSK Error packet in the Reject's data field. +8. Sender gets the ILP Reject packet, decrypts the PSK Error and divides the receiver's Request Amount by the amount the sender sent in the original ILP Prepare packet to determine the approximate path exchange rate. + +### Single Payment + +1. Sender determines the source amount and minimum destination amounts for the packet, potentially using the exchange rate as determined by an [Informational Quote](#informational-quote) or from sending previous requests. +2. Sender creates a PSK Request with a random Request ID and the Request Amount set to the destination amount determined in step 1. Sender MAY include additional data in the PSK Request. Sender derives the encryption key as described in [Encryption Pseudocode](#encryption-pseudocode) and [encrypts](#encryption-envelope) the Request. +3. Sender generates a condition as described in [Fulfillable Condition](#fulfillable-condition). +4. Sender sends an ILP Prepare using the source amount determined in step 1, the condition from step 3, and the encrypted Request from step 2 as the data. +5. Receiver is notified of the incoming ILP Prepare packet, rederives the encryption key as described in [Encryption Pseudocode](#encryption-pseudocode), and decrypts the data. (The receiver SHOULD return an ILP Reject with the code `F06: Unexpected Payment` if they are unable to decrypt the data) +6. Receiver checks to ensure that the amount in the ILP Prepare packet is greater than or equal to the Request Amount specified in the PSK Request. The receiver SHOULD return an ILP Reject packet with the code `F99: Application Error` and an encrypted PSK Error packet, which includes the amount that arrived in the prepare, as the data. +7. Receiver creates a PSK Response with the same Request ID and the Request Amount set to the amount that arrived in the ILP Prepare packet. The receiver then [encrypts](#encryption-envelope) the data using the same encryption key. +8. Receiver generates fulfillment as described [below](#fulfillment-generation) and fulfills the incoming transfer with the fulfillment and the encrypted response data. The receiver SHOULD return an ILP Reject with an `F05: Wrong Condition` code if they are unable to regenerate the correct condition. +9. Sender gets the ILP Fulfill packet and decrypts the PSK Response from the data. They SHOULD ignore the Response if the Request ID does not match their original request or if the PSK packet is not a Response type. They may use the Request Amount from the Response to estimate the path exchange rate. They may also use the data in the PSK Response from the receiver. + +## Specification + +### Encryption + +Request and response data are encrypted using [AES-256-GCM](https://en.wikipedia.org/wiki/Galois/Counter_Mode) with a random 12-byte Initialization Vector (IV) and a 16-byte Authentication Tag. + +#### Encryption Envelope + +Also see the [ASN.1 definition](../asn1/PreSharedKeyV2.asn). + +``` +(Numbers represent bytes) + + 4 8 12 16 20 24 28 32 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Random IV | Authentication Tag | Ciph..| ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Ciphertext | +\ \ +\ \ +| Ciphertext | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +| Field | Type | Description | +|---|---|---| +| Random IV | 12-Byte UInt | Nonce used as input to the AES-GCM algorithm. Also ensures conditions are random. Senders MUST NOT encrypt two packets with the same nonce | +| Authentication Tag | 16-Byte UInt | Authentication tag produced by AES-GCM encryption that ensures data integrity | +| Ciphertext | 0-32739 Bytes | Encrypted data (see below for contents) | + +#### Encryption Pseudocode + +Note that the encryption key is derived from the shared secret using an HMAC. + +``` +iv = random_bytes(12) +encryption_key = hmac_sha256(shared_secret, "ilp_psk_encryption") +{ ciphertext, auth_tag } = aes_256_gcm(encryption_key, iv, data) +``` + +### Data Packet + +All PSKv2.0 requests and responses encode their data in the same byte format, though the meanings of the fields differ depending on whether it is a request or a response. + +The following is encrypted, as defined [above](#encryption), and set as the data in an ILP Prepare, Fulfill, or Reject packet. + +Also see the [ASN.1 definition](../asn1/PreSharedKeyV2.asn). + +``` +(Numbers represent bytes) + + 4 8 12 16 20 24 28 32 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +|T| ReqID | Request Amount| Application Data | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Application Data | +\ \ +\ \ +| Application Data | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Junk Data | +\ \ +\ \ +| Junk Data | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +#### Request + +PSK Request packets should ONLY be sent in ILP Prepare packets. + +| Field | Type | Description | +|---|---|---| +| Type (T) | Always `4` (UInt8) | Indicates that this is a PSK2 request. Request packets SHOULD be rejected or ignored if they are not in an ILP Prepare packet. | +| Request ID (ReqID) | UInt32 | ID used to correlate requests and responses. Senders CANNOT rely on receivers enforcing uniqueness, but they SHOULD ensure that responses carry the same Request ID as the outgoing request | +| Request Amount | UInt64 | Minimum amount that should arrive in the ILP Prepare packet for the receiver to fulfill it. Receivers SHOULD NOT accept packets where the Request Amount is greater than the ILP Prepare amount | +| Data | [OER Variable-Length Octet String](http://www.oss.com/asn1/resources/books-whitepapers-pubs/Overview%20of%20OER.pdf) | User data carried along with the payment. Note this data is encrypted and authenticated | +| Junk Data | 0-? Bytes | _(Optional)_ Extra data included to obscure what the ILP payment is for. Receivers SHOULD ignore this data. Later versions of this protocol MAY define additional fields that will be added after the `data` field and older implementations will simply disregard those. | + +#### Response + +PSK Response packets should ONLY be sent in ILP Fulfill packets. + +| Field | Type | Description | +|---|---|---| +| Type (T) | Always `5` (UInt8) | Indicates that this is a PSK2 response. Response packets SHOULD be rejected or ignored if they are not in an ILP Fulfill packet. | +| Request ID (ReqID) | UInt32 | ID used to correlate requests and responses. Receivers MUST use the same Request ID from the Request in their Response. Senders SHOULD ignore responses whose Request ID does not match their original request. | +| Request Amount | UInt64 | Amount that arrived in the ILP Prepare packet the receiver got. This is used to help senders determine the path exchange rate and how much the receiver has gotten. Note the sender must trust the receiver to report this honestly. | +| Data | [OER Variable-Length Octet String](http://www.oss.com/asn1/resources/books-whitepapers-pubs/Overview%20of%20OER.pdf) | User data carried along with the payment. Note this data is encrypted and authenticated | +| Junk Data | 0-? Bytes | _(Optional)_ Extra data included to obscure what the ILP payment is for. Receivers SHOULD ignore this data. Later versions of this protocol MAY define additional fields that will be added after the `data` field and older implementations will simply disregard those. | + +#### Error + +PSK Response packets should ONLY be sent in ILP Reject packets. + +| Field | Type | Description | +|---|---|---| +| Type (T) | Always `6` (UInt8) | Indicates that this is a PSK2 error. Response packets SHOULD be rejected or ignored if they are not in an ILP Reject packet. | +| Request ID (ReqID) | UInt32 | ID used to correlate requests and responses. Receivers MUST use the same Request ID from the Request in the Error. Senders SHOULD ignore responses whose Request ID does not match their original request. | +| Request Amount | UInt64 | Amount that arrived in the ILP Prepare packet the receiver got. This is used to help senders determine the path exchange rate and how much the receiver has gotten. Note the sender must trust the receiver to report this honestly. | +| Data | [OER Variable-Length Octet String](http://www.oss.com/asn1/resources/books-whitepapers-pubs/Overview%20of%20OER.pdf) | User data carried along with the payment. Note this data is encrypted and authenticated | +| Junk Data | 0-? Bytes | _(Optional)_ Extra data included to obscure what the ILP payment is for. Receivers SHOULD ignore this data. Later versions of this protocol MAY define additional fields that will be added after the `data` field and older implementations will simply disregard those. | + + +### Condition and Fulfillment Generation + +There are two methods the sender can use to generate the condition, depending on whether they want the payment to be fulfillable or not. + +#### Unfulfillable Condition + +If the sender does not want the receiver to be able to fulfill the payment (as for an informational quote), they can generate an unfulfillable random condition. + +``` +condition = random_bytes(32) +``` + +#### Fulfillable Condition + +If the sender does want the receiver to be able to fulfill the condition, the condition MUST be generated in the following manner. + +The `shared_secret` is the pre-shared key that gives this protocol its name. The `data` is the **encrypted** PSK Request. + +``` +hmac_key = hmac_sha256(shared_secret, "ilp_psk2_fulfillment") +fulfillment = hmac_sha256(hmac_key, data) +condition = sha256(fulfillment) +``` + +#### Fulfillment Generation + +The following pseudocode details how the receiver regenerates the fulfillment from the data. + +**Note:** Senders MAY use unfulfillable conditions to get an informational quote. Receivers MUST return an [Error Response](#error) packet that includes the amount that arrived on the incoming transfer, even if they are unable to regenerate the fulfillment from the request. + +The `shared_secret` is the pre-shared key that gives this protocol its name. The `data` is the **encrypted** PSK Request. + +``` +hmac_key = hmac_sha256(shared_secret, "ilp_psk2_fulfillment") +fulfillment = hmac_sha256(hmac_key, data) +``` + diff --git a/0026-payment-pointers/0026-payment-pointers.md b/0026-payment-pointers/0026-payment-pointers.md new file mode 100644 index 00000000..51998e0d --- /dev/null +++ b/0026-payment-pointers/0026-payment-pointers.md @@ -0,0 +1,149 @@ +--- +title: Payment Pointers +draft: 1 +--- +# Payment Pointers and Payment Setup Protocols + +## Abstract + +Payment pointers are a standardized identifier for accounts that are able to receive payments. In the same way that an email address provides an identifier for a mailbox in the email ecosystem a payment pointer is used by an account holder to share the details of their account with anyone that wishes to make a payment to them. + +A payment pointer can be resolved to an "https" URL that provides the location of a payment setup service endpoint at which a sender can initiate a payment to the receiver. + +## Design Goals + +The following design goals are met by this specification: + +### Unique and Easily Recognizable + +Various standardized and defacto-standardized identifiers are widely used on the Internet today such as email addresses and social media handles. The payment pointer must be obviously unique so that it is not confused with another type of identifier and also so that other identifiers are not assumed to be payment pointers. + +### Simple Transcription + +It should be easy for someone to exchange their payment pointer with a potential payee either by saying it, writing it down or sending it in a digital message. + +### Flexible + +The payment pointer should not be tightly couple to a specific payment setup protocol or protocol version. It should be possible for any new payment setup protocol to leverage payment pointers. + +### Widely Usable + +It should be simple for individuals or small companies to host their own payment setup protocol receiver endpoints at a URL that can be resolved via a simple and easily recognizable payment pointer. Likewise, it should be possible for a payment services provider to host payment setup protocol receiver endpoints for multiple entities without the risk of hosting them at endpoint URLs that conflict with their other services. To that end the provider should have the option of hosting different receiver endpoints under the same domain and a different path or at a different sub-domain for each receiver. + +## Payment Setup Protocols + +A payment setup protocol is defined very generically as a protocol for exchanging payment information between a sender and receiver before executing the payment. + +An example of a payment setup protocol is the [Simple Payment Setup Protocol](../0009-simple-payment-setup-protocol/) used to setup an Interledger payment. + +Payment pointers MUST resolve to an endpoint hosted by a payment receiver at which a payment setup protocol can be initiated. This endpoint is known as the payment setup protocol receiver endpoint. + +A payment setup protocol receiver endpoint is identified by an https URL as defined in [RFC7230](https://tools.ietf.org/html/rfc7230#section-2.7.2). It MUST accept HTTP GET requests to initiate the protocol. + +Payment setup protocols SHOULD define a custom MIME type for their message definitions and use HTTP content negotiation between the client and endpoint server to determine the payment setup protocol and protocol versions supported by the requesting client and the endpoint server. + +Example: + +The MIME type for SPSP is: `application/spsp+json` + +## Payment Pointer Syntax + +This specification uses the Augmented Backus-Naur Form (ABNF) notation of [RFC2234](https://tools.ietf.org/html/rfc2234) and imports the following rules from [RFC3986](https://tools.ietf.org/html/rfc3986#section-3.3)): _host_ and _path-abempty_. + +The syntax of a payment pointer is: + +``` + "$" host path-abempty +``` + +Example: `$example.com/bob` + +Note that the character set of a payment pointer, as with a valid URL, is limited to valid ASCII characters. Further work may be done to define mappings from other character sets that support international characters (similar to the rules for Internationalized Domain Names) however, such mappings are not defined in this specification. Implementations that attempt to interpret a Payment Pointer that contains non-ASCII characters should be aware of the security risks. + +## Payment Setup Protocol Receiver Endpoint Resolution + +Given a payment pointer of the form `"$" host path-abempty` the payment setup protocol receiver endpoint URL is: + +``` + "https://" host path-abempty +``` + +If _path-abempty_ is empty or equal to `/` then it is assigned the value `/.well-known/pay`. + +Note that this is a restricted profile of the data allowed in a full https URL. Where the URL syntax supports an _authority_ the payment pointer syntax only supports a _host_ which excludes the _userinfo_ and _port_. The payment pointer syntax also excludes the _query_ and _fragment_ parts that are allowed in the URL syntax. + +Payment pointers that do not meet the limited syntax of this profile MUST be considered invalid and should not be used to resolve a receiver endpoint. + +The resolved endpoint may redirect the client to another URL but the client MUST ensure it affords the sender an opportunity to verify both the originally resolved and ultimate endpoint hosts (See the security risks described below for more details). + +Examples: + +The following payment pointers resolve to the specified endpoint URLS: + +``` +$example.com -> https://example.com/.well-known/pay +$example.com/invoices/12345 -> https://example.com/invoices/12345 +$bob.example.com -> https://bob.example.com/.well-known/pay +$example.com/bob -> https://example.com/bob +``` + +# Payment Initiation + +Given a payment pointer for a receiver, a sending client should resolve the https URL of the payment setup protocol receiver endpoint according to the rules defined in this specification. + +The client should then initiate an https connection with the endpoint server and perform an http GET request to the endpoint URL. + +The client should express the protocols it supports by including the media types of the protocol messages in the `Accept` header of the request. + +Example: + +``` +GET /.well-known/pay HTTP/1.1 +Host: bob.example.com +Accept: application/spsp+json, application/otherprotocolformat +``` + +# IANA Considerations + +## Well-Known URI + +This specification registers a new well-known URI. + +URI suffix: "pay" + +Change controller: Interledger Payments Community Group (W3C) + +Specification document(s): This document + +# Security Considerations + +Payment pointers have many of the same security risks as URLs so it follows that the security considerations are the same even though payment pointers do not in and of themselves pose a security threat. + +However, as payment pointers are used to provide a compact set of instructions for initiation of a payment, care must be taken to properly interpret the data within a payment pointer, to prevent payments being made to the wrong recipient or leaking of sensitive data. + +## Reliability and Consistency + +There is no guarantee that once a payment pointer has been used resolve a payment setup protocol receiver endpoint, the same service will be hosted at that URI in the future. + +## Semantic Attacks and Phishing + +Because payment pointers only support a limited profile of the data in a URL, not all of the attacks described in RFC3986 apply to payment pointers. + +However it is possible for a malicious actor to construct a payment pointer that appears to point to a trusted receiver but in fact points to a malicious actor. As a result the pointer is constructed that is intended to mislead a human user by appearing to identify one (trusted) naming authority while actually identifying a different authority hidden behind the noise. + +As detailed in [RFC7230], the "https" scheme (Section 2.7.2) is intended to prevent (or at least reveal) many of these potential attacks on establishing the authority behind a pointer, provided that the negotiated TLS connection is secured and the client properly verifies that the communicating server's identity matches the target URIs authority component (see [RFC2818]).Correctly implementing such verification can be difficult (see [The Most Dangerous Code in the World](#The Most Dangerous Code in the World: Validating SSL Certificates in Non-browser Software)). + +Payment Setup Protocols should take heed of this risk in their design and provide a mechanism for the sender to reliably verify the receiver's identity. This may be as simple as relying on the certificate of the receiving endpoint server to identify the receiver, or may be handled explicitly within the protocol. + +Sending clients MUST provide a way for a sender, after providing a pointer to the client, to verify that the entity they are sending to (or at least the entity hosting the payment setup protocol receiver endpoint) is who they intended otherwise senders can easily be fooled into sending money to the wrong receiver. An example of such a mechanism is the use of extended validation certificates at the endpoint. + +# References + +## The Most Dangerous Code in the World: Validating SSL Certificates in Non-browser Software + +Georgiev, M., Iyengar, S., Jana, S., Anubhai, R., Boneh, D., and V. Shmatikov, +"The Most Dangerous Code in the World: Validating SSL Certificates in Non-browser Software", +In Proceedings of the 2012 ACM Conference on Computer and Communications Security (CCS'12), pp. 38-49, October 2012, +. + + diff --git a/0027-interledger-protocol-4/0027-interledger-protocol-4.md b/0027-interledger-protocol-4/0027-interledger-protocol-4.md new file mode 100644 index 00000000..3521fdd8 --- /dev/null +++ b/0027-interledger-protocol-4/0027-interledger-protocol-4.md @@ -0,0 +1,194 @@ +--- +title: Interledger Protocol V4 (ILPv4) +draft: 1 +--- + +# Interledger Protocol V4 + +Interledger is a protocol for sending packets of money across different payment networks or ledgers. ILPv4 is a simplification of previous versions of the protocol that is optimized for routing large volumes of low-value packets, also known as "penny switching". ILPv4 can be integrated with any type of ledger, including those not built for interoperability, and it is designed be used with a variety of higher-level protocols that implement features ranging from quoting to sending larger amounts of value with chunked payments. + +## Overview + +### Design Goals + +- **Neutrality** - Interledger is not tied to any company, currency, or network. +- **Interoperability** - ILP should be usable across any type of ledger, even those that were not built for interoperability. +- **Security** - Senders, connectors, and receivers should be protected from one another and especially isolated from risks posed by parties that they are indirectly connected to. Connectors should not be able to steal money from senders, and senders should not be able to tie up too much of connectors' funds or otherwise interfere with their operation. +- **Simplicity** - The core ILP should be as simple as possible. There is room for significant variability at lower and higher layers of the [Interledger stack](../0001-interledger-architecture/0001-interledger-architecture.md#interledger-protocol-suite), but the Interledger layer is the one part where widespread agreement is needed for interoperability. Simplifying the core minimizes what people need to agree upon and enables broader adoption. +- **End-to-End Principle** - Inspired by the Internet, any features that do not need to be implemented by the core Interledger Protocol and network of connectors should be built into the edges of the network (the sender and receiver). + +### Terminology + +- **Account** - Two peers establish an account with one another to track the current obligations they hold with one another. + * **Balance** - The account balance represents the difference between the value of ILP packets sent and received and the amount of value that has been transferred through an underlying ledger or payment channel (for example, if peer A has signed payment channel updates for 100 units to peer B, peer A sent ILP packets worth 150 units through B, A received ILP packets worth 30 units from B, then peer A owes 20 units to peer B). Each peer maintains their record of the account balance on their own system, or in some cases one peer may trust the other to maintain the account. Connectors may refuse to process further ILP packets if an account balance goes too low. + * **Asset, Scale, and Precision** - Each account is denominated in a single asset and peers agree on the precision and scale they will use for the amounts of that asset when they express amounts in messages between one another (for example, a scale of 9 means that an amount of 1000000000 in an ILP packet sent between those peers represents 1 unit of that asset). When a connector receives an ILP Prepare packet, they use the asset type and scale of the connection through which the packet was received to determine the meaning of the amount in the packet. + * **Funding and Rebalancing** - Accounts may be pre-funded (in which case the customer or peer trusts the connector for the value of the account balance) or post-funded (in which case the connector trusts the customer or peer for the amount the account is allowed to go negative). + * **Difference from "Ledger Accounts"** - Note that the mentions of "accounts" in this document refer to those that Interledger participants _hold with one another_, rather than the "accounts" they may hold on underlying. Ledger accounts or payment channels are only used for funding and rebalancing Interledger accounts and ledgers are not directly part of the ILPv4 packet flow. +- **Bandwidth** - Connectors may limit the total value of ILP packets an account can send during a given period. The limit may either be based on how much the connector trusts the account-holder, represented by the minimum balance limit on the account, or it may be used to prevent one account from tying up all of the connector's bandwidth with its peers. +- **Condition and Fulfillment** - ILP packets are secured with conditions so that they cannot be lost or stolen by intermediary connectors. ILP Prepare packets contain a condition in the form of a SHA-256 hash. When the receiver receives the packet, they present the fulfillment, in the form of a valid preimage of the condition hash, to execute the transfer. Unlike in earlier versions of ILP, the conditions and fulfillments are NOT assumed to be enforced by the underlying ledger but they help peers reconcile their account balances. Two peers should only consider an ILP packet executed (and therefore affect their account balances) if the fulfillment is a valid preimage of the condition and the fulfillment is received before the Prepare's expiry. See the [Pre-Shared Key V2 (PSKv2)](../0025-pre-shared-key-2/0025-pre-shared-key-2.md) protocol for an example of how a higher-level protocol may generate conditions and fulfillments. +- **ILP Packets** - ILPv4 has three packet types: Prepare, Fulfill, and Reject, which roughly correspond to request, response, and error messages. Connectors forward Prepare packets from senders to receivers and the connectors relay the Fulfill or Reject packets back from the receivers to the senders. +- **Ledger** - A generic term for any system that tracks transfer of value between, and balances on, accounts. In earlier versions of Interledger, ILP packets were transmitted through transfers on ledgers. In ILPv4, ledgers are used primarily to adjust balances between participants, to rebalance their accounts, following the exchange of one or more ILP Packets between the participants. +- **Participant** - A participant in an Interledger payment has accounts with one or more other participants and takes one or more of the following roles: + * **Sender** - The party that originates payment + * **Receiver** - The final recipient of a payment + * **Connector** - The intermediaries between a sender and a receiver that forward ILP Packets. They may generate revenue from spreads on currency conversion, through subscription fees, or other means. +- **Payment** - In the context of this specification, a payment is understood to mean the transfer of value from the sender (payer) to the receiver (payee). Higher-level protocols may execute a "payment" by sending a series of ILP Packets whose sum is equal to the desired payment value. +- **Payment Channel** - A method of using signed claims against funds held on a ledger, rather than on-ledger transfers that may be too slow or expensive, to rebalance accounts. See [Payment Channels in ILPv4](#payment-channels-in-ilpv4) for more details. +- **Peer** - A participant with which another participant holds an account. +- **Transport and Application Layer Protocols** - Senders and receivers generally use higher-level protocols to agree on the condition and other details for each ILP packet. These protocols may also handle encryption and authentication of the data sent with ILP packets. For more details see the [Interledger Architecture overview](../0001-interledger-architecture/0001-interledger-architecture.md#interledger-protocol-suite) or the [Pre-Shared Key V2 (PSKv2)](../0025-pre-shared-key-2/0025-pre-shared-key-2.md) protocol. + +### Relation to Other Protocols + +ILPv4 packets may be sent over any communication channel between peers. Account balances are adjusted when accepting and forwarding ILP packets and may be rebalanced using any available means, ranging from ledger transfers triggered by an API, to signed updates to payment channels, or the physical delivery of cash or other goods. + +In most cases, ILPv4 will be used with a Transport Layer protocol such as the [Pre-Shared Key V2 (PSKv2)](../0025-pre-shared-key-2/0025-pre-shared-key-2.md) protocol, which handles the generation of ILP conditions and fulfillments. ILPv4 may also be used with higher-level protocols that implement chunking and reassembly of higher-value payments. + +### Differences from Previous Versions of ILP + +- **Designed for Smaller, More Homogenous Packet Amounts** - ILPv4 applies a number of simplifications by only supporting low-value packets. Larger amounts can be sent through higher-level protocols that implement chunked payments, but the core network is optimized for sending large volumes of small packets. This renders unnecessary some of the more complex features of previous versions like Liquidity Curves for expressing how exchange rates may vary depending on the packet amount. Lower-value packets also help to minimize [connector risks](../0018-connector-risk-mitigations/0018-connector-risk-mitigations.md). +- **Payment Channels, Not On-Ledger Escrow** - Since ILPv4 is optimized for smaller packets, speed and cost are of greater importance than in previous versions. ILPv4 uses ledgers or payment channels for settling bilateral payment obligations but ILPv4 packets are sent just between connectors, rather than through the underlying ledgers themselves. This enables packet timeouts to be short, because they do not need to include the processing time of slower ledgers, which further reduces [connector risks](../0018-connector-risk-mitigations/0018-connector-risk-mitigations.md). See [Why Unconditional Payment Channels](#why-unconditional-payment-channels) for more details. +- **Forwarding, Not Delivery** - ILPv4 connectors forward packets based on their local exchange rates, in contrast with the first version of ILP in which connectors would attempt to deliver a fixed destination amount. Now, instead of fixed destination amount delivery being built into the core protocol, senders may use higher-level protocols to indicate the minimum amount the receiver should accept for a given packet and receivers can reject packets with less than that. This significantly simplifies the connector behavior, because the do not need to maintain up-to-date price information on the entire rest of the network and can simply apply their local rate instead. If receivers want to receive no more than a certain amount, they can reject packets that go too far over the amount and senders can retry the packet with lower amounts. +- **Quoting is an Application Concern** - Connectors are only responsible for forwarding ILP packets and do not need to implement a [separate protocol for quoting](../0008-interledger-quoting-protocol/0008-interledger-quoting-protocol.md). Applications can use test packets to determine the exchange rate of a particular path. + +### Why Unconditional Payment Channels + +The only requirement for ledgers to be integrated with ILPv4 is that they must be able to make simple transfers so that Interledger participants can rebalance their accounts. If ledger transfers are fast and inexpensive, participants can settle their account balances more frequently and set lower trust limits (minimum or maximum account balances) with one another. + +Payment channels are a way to use signed claims against funds held on a ledger instead of on-ledger transfers to rebalance accounts. Two peers can sign an unlimited number of updates to their shared payment channel without needing to pay or wait for a potentially expensive or slow on-ledger transfer. For example, they may exchange a signed claim after every ILP packet is fulfilled to keep the trust limit as low as a single packet's value. When the peers are finished interacting, or at some predetermined time, they submit the latest claim to the ledger to split the held funds appropriately between the two participants. + +ILPv4 can use [unidirectional](../0022-hashed-timelock-agreements/0022-hashed-timelock-agreements.md#simple-payment-channels) or bidirectional payment channels, but it will most likely NOT use [conditional channels](../0022-hashed-timelock-agreements/0022-hashed-timelock-agreements.md#conditional-payment-channels-with-htlcs) that enforce Hashed Timelock Contracts (HTLCs). Conditional payment channels require participants to account for the ledger's longer processing time in the timeouts for ILP packets. Short timeouts are important for senders to be guaranteed a quick and definitive resolution of their packets. Short timeouts also help connectors limit the risk that malicious senders could tie up their bandwidth or take advantage of the "free option problem" (ensure that packets are held until just before their expiry and then fulfilled only if the exchange rate moved in the sender's favor). In contrast, unconditional payment channels do not require longer timeouts while enabling peers to rebalance frequently and minimize their bilateral trust. + +## Flow + +### Prerequisites + +1. Sender has an account with at least one connector +2. Sender and connector have funds on some shared ledger or settlement system to rebalance their accounts with one another. If the ledger supports payment channels, participants may have unidirectional or bidirectional payment channels open with one another to rebalance their accounts. +3. Sender and connector have an authenticated communication channel through which they will send ILP packets, such as a Secure WebSocket (WSS). Note that connectors use their authenticated communication channels with their peers and customers to determine the source of each packet (packets do not contain the source address) +4. Sender and receiver use a higher-level protocol and/or out-of-band authenticated communication channel to agree on the destination account and packet condition. They may use a protocol like the [Pre-Shared Key V2](../0025-pre-shared-key-2/0025-pre-shared-key-2.md) protocol, which establishes a shared secret and can be used to generate conditions for many packets. + +### ILP Packet Lifecycle + +1. Sender constructs an ILP Prepare packet with the receiver's destination account, the agreed-upon condition, and an amount and expiry of their choice. The sender may include additional data, the formatting of which is determined by the higher-level protocol. +2. Sender sends the ILP Prepare packet to a connector over their authenticated communication channel. +3. Connector gets the packet and determines which account to debit based on which communication channel it was received through (each account holder will either have an open connection, such as a WebSocket, or each message will be sent with authentication information, such as in an HTTPS request). +4. Connector checks whether the sender has sufficient balance or credit to send the amount specified in the packet. If so, the connector reduces the sender's available balance by the value of the packet. If not, the connector returns an ILP Reject packet. +5. Connector uses its local routing tables and the destination ILP address from the packet to determine which next hop to forward the packet to. The connector may use an exchange orderbook or any other method it chooses to set its exchange rate between the incoming and outgoing assets. +6. Connector modifies the packet amount and expiry to apply their exchange rate and move the expiry timestamp earlier (for example, each connector may subtract one second from the expiry to give themselves a minimum of one second to pass the Fulfill on in step 10). +7. Connector forwards the packet to the next hop, which may be another connector. All subsequent connectors go through steps 3-7 (treating the previous connector as the sender) until the packet reaches the receiver. +8. Receiver checks the ILP Prepare packet, according to whatever is stipulated by the higher-level protocol (such as checking whether the amount received is above some minimum specified by the sender). Receiver also checks that the Prepare would not put the connector's unsettled balance above the maximum the receiver tolerates (this is the same as the minimum balance limit of the connector _with the receiver_). +9. To accept the packet, the receiver returns an ILP Fulfill packet, containing the preimage of the condition in the ILP Prepare packet. The receiver sends the Fulfill back through the same communication channel the Prepare packet was received from. The receiver may include additional data in the Fulfill packet, the formatting of which is determined by the higher-level protocol. If the receiver does not want the ILP Prepare packet or it does not pass one of the checks, the receiver returns an ILP Reject packet instead. +10. If the receiver returned an ILP Fulfill, the connector checks that the fulfillment hashes to the original condition and that the receiver returned it before the expiry in the ILP Prepare packet. If the fulfillment is valid, the connector returns the same ILP Fulfill packet to the previous connector (using the same communication channel they originally received the ILP Prepare from) and the connector credits the receiver's account with the amount in the original ILP Prepare packet. If the receiver returned an ILP Reject packet or the Prepare expired before the Fulfill was returned, the connector returns an ILP Reject packet to the previous connector (note that connectors SHOULD return ILP Reject packets when the Prepare expires, even if they have not yet received a response from the next participant). Each connector repeats this step (crediting the account of the participant that returned the Fulfill) until the Fulfill (or Reject) packet reaches the sender. +11. Sender checks that the preimage in the ILP Fulfill packet matches the condition in their Prepare packet and may read any data returned by the receiver. The sender may keep a local record of the total value of packets fulfilled to determine how much they owe their connector. +12. Sender repeats the process, starting from step 1, as many times as is necessary to transfer the desired total amount of value. + +### Rebalancing Accounts + +Participants may have pre-funded or post-funded accounts with one another. + +In the case of pre-funded accounts, senders send some amount to their connector to cover packets they will send. Senders can use transfers on an underlying ledger, payment channel updates, or any other means to send value to the connector. Senders can pre-fund their accounts as much or as little as they choose based on how much they trust the connector to forward ILP packets for them and the volume of packets the sender expects to send. + +In the case of post-funded accounts, senders transfer value to their connector after they have recevied the fulfillments for one or more outgoing ILP packets. If participants use payment channels, they may choose to sign an update to the payment channel to cover the value of each packet sent and thus minimize the amount the connector must trust the sender for. If participants use some other means of rebalancing, such as transfers on a slower ledger or delivery of physical assets, they may wish to rebalance their accounts less frequently and settle for larger amounts. + +## Specification + +### Packet Format + +ILPv4 packets SHOULD be encoded using the ASN.1 Octet Encoding Rules. + +#### Type-Length Wrapper + +All ILP packets are wrapped in the following envelope: + +| Field | Type | Description | +|---|---|---| +| `type` | UInt8 | ID of the ILP Packet type | +| `data` | Variable-Length Octet String | Packet contents, prefixed with their length | + +See the ASN.1 definition [here](../asn1/InterledgerPacket.asn). + +#### ILP Prepare + +ILP Prepare packets are `type` 12. + +The `amount` and `expiresAt` are fixed-length fields so that they may be modified in-place (without copying the rest of the packet) by each connector. These are the only fields that are modified by connectors as the packet is forwarded. + +| Field | Type | Description | +|---|---|---| +| `amount` | UInt64 | Local amount, denominated in the minimum divisible unit of the asset of the bilateral relationship. This field is modified by each connector, who applies their exchange rate and adjusts the amount to the appropriate scale and precision of the outgoing account | +| `expiresAt` | Fixed-Length [Interledger Timestamp](../asn1/InterledgerTypes.asn) | Date and time when the packet expires. Each connector changes the value of this field to set the expiry to an earlier time, before forwarding the packet. | +| `executionCondition` | UInt256 | SHA-256 hash digest of the `fulfillment` that will execute the transfer of value represented by this packet | +| `destination` | [ILP Address](../0015-ilp-addresses/0015-ilp-addresses.md) | ILP Address of the receiver | +| `data` | Variable-Length Octet String | End-to-end data. Connectors MUST NOT modify this data. Most higher-level protocols will encrypt and authenticate this data, so receivers will reject packets in which the data is modified | + +**Note:** There is no `from` or `source` address in the ILP Prepare packet. Each hop MUST determine which of their immediate peers or customers the packet came from (to debit the correct account) based on the _authenticated_ communication channel the packet was received through. Also, higher-level protocols MAY communicate the sender's address to the receiver if the use case calls for receivers to send ILP Prepare packets to the sender (they can communicate responses to the sender using the `data` field in ILP Fulfill and Reject packets). + +See the ASN.1 definition [here](../asn1/InterledgerProtocol.asn). + +#### ILP Fulfill + +ILP Fulfill packets are `type` 13. + +| Field | Type | Description | +|---|---|---| +| `fulfillment` | UInt256 | 32-byte preimage of the `executionCondition` from the corresponding ILP Prepare packet | +| `data` | Variable-Length Octet String | End-to-end data. Connectors MUST NOT modify this data | + +See the ASN.1 definition [here](../asn1/InterledgerProtocol.asn). + +#### ILP Reject + +ILP Reject packets are `type` 14. + +| Field | Type | Description | +|---|---|---| +| `code` | 3-Character IA5String | [ILP Error Code](#error-codes) | +| `triggeredBy` | [ILP Address](../0015-ilp-addresses/0015-ilp-addresses.md) | ILP Address of the party that created this error | +| `message` | UTF8 String | User-readable error message, primarily intended for debugging purposes | +| `data` | Variable-Length Octet String | End-to-end data. Connectors MUST NOT modify this data | + +See the ASN.1 definition [here](../asn1/InterledgerProtocol.asn). + +### Error Codes + +#### F__ - Final Error + +Final errors indicate that the payment is invalid and should not be retried unless the details are changed. + +| Code | Name | Description | Data Fields | +|---|---|---|---| +| **F00** | **Bad Request** | Generic sender error. | (empty) | +| **F01** | **Invalid Packet** | The ILP packet was syntactically invalid. | (empty) | +| **F02** | **Unreachable** | There was no way to forward the payment, because the destination ILP address was wrong or the connector does not have a route to the destination. | (empty) | +| **F03** | **Invalid Amount** | The amount is invalid, for example it contains more digits of precision than are available on the destination ledger or the amount is greater than the total amount of the given asset in existence. | (empty) | +| **F04** | **Insufficient Destination Amount** | The receiver deemed the amount insufficient, for example you tried to pay a $100 invoice with $10. | (empty) | +| **F05** | **Wrong Condition** | The receiver generated a different condition and cannot fulfill the payment. | (empty) | +| **F06** | **Unexpected Payment** | The receiver was not expecting a payment like this (the data and destination address don't make sense in that combination, for example if the receiver does not understand the transport protocol used) | (empty) | +| **F07** | **Cannot Receive** | The receiver (beneficiary) is unable to accept this payment due to a constraint. For example, the payment would put the receiver above its maximum account balance. | (empty) | +| **F08** | **Amount Too Large** | The packet amount is higher than the maximum a connector is willing to forward. Senders MAY send another pakcet with a lower amount. Connectors that produce this error SHOULD encode the amount they received and their maximum in the `data` to help senders determine how much lower the packet amount should be. | See [ASN.1](../asn1/InterledgerErrorData.asn) | +| **F99** | **Application Error** | Reserved for application layer protocols. Applications MAY use names other than `Application Error`. | Determined by Application | + +#### T__ - Temporary Error + +Temporary errors indicate a failure on the part of the receiver or an intermediary system that is unexpected or likely to be resolved soon. Senders SHOULD retry the same payment again, possibly after a short delay. + +| Code | Name | Description | Data Fields | +|---|---|---|---| +| **T00** | **Internal Error** | A generic unexpected exception. This usually indicates a bug or unhandled error case. | (empty) | +| **T01** | **Peer Unreachable** | The connector has a route or partial route to the destination but was unable to reach the next connector. Try again later. | (empty) | +| **T02** | **Peer Busy** | The next connector is rejecting requests due to overloading. If a connector gets this error, they SHOULD retry the payment through a different route or respond to the sender with a `T03: Connector Busy` error. | (empty) | +| **T03** | **Connector Busy** | The connector is rejecting requests due to overloading. Try again later. | (empty) | +| **T04** | **Insufficient Liquidity** | The connector would like to fulfill your request, but either the sender or a connector does not currently have sufficient balance or bandwidth. Try again later. | (empty) | +| **T05** | **Rate Limited** | The sender is sending too many payments and is being rate-limited by a ledger or connector. If a connector gets this error because they are being rate-limited, they SHOULD retry the payment through a different route or respond to the sender with a `T03: Connector Busy` error. | (empty) | +| **T99** | **Application Error** | Reserved for application layer protocols. Applications MAY use names other than `Application Error`. | Determined by Application | + +#### R__ - Relative Error + +Relative errors indicate that the payment did not have enough of a margin in terms of money or time. However, it is impossible to tell whether the sender did not provide enough error margin or the path suddenly became too slow or illiquid. The sender MAY retry the payment with a larger safety margin. + +| Code | Name | Description | Data Fields | +|---|---|---|---| +| **R00** | **Transfer Timed Out** | The transfer timed out, meaning the next party in the chain did not respond. This could be because you set your timeout too low or because something look longer than it should. The sender MAY try again with a higher expiry, but they SHOULD NOT do this indefinitely or a malicious connector could cause them to tie up their money for an unreasonably long time. | (empty) | +| **R01** | **Insufficient Source Amount** | The amount received by a connector in the path was too little to forward (zero or less). Either the sender did not send enough money or the exchange rate changed. The sender MAY try again with a higher amount, but they SHOULD NOT do this indefinitely or a malicious connector could steal money from them. | (empty) | +| **R02** | **Insufficient Timeout** | The connector could not forward the payment, because the timeout was too low to subtract its safety margin. The sender MAY try again with a higher expiry, but they SHOULD NOT do this indefinitely or a malicious connector could cause them to tie up their money for an unreasonably long time. | (empty) | +| **R99** | **Application Error** | Reserved for application layer protocols. Applications MAY use names other than `Application Error`. | Determined by Application | diff --git a/0028-web-monetization/0028-web-monetization.md b/0028-web-monetization/0028-web-monetization.md new file mode 100644 index 00000000..ac9a153c --- /dev/null +++ b/0028-web-monetization/0028-web-monetization.md @@ -0,0 +1,78 @@ +--- +title: Web Monetization +draft: 1 +--- + +# Web Monetization + +Web Monetization is a proposed browser API that uses ILP micropayments to monetize a site. It can be polyfilled by extensions, or can be implemented directly into an ILP-enabled browser. + +## Overview + +### Terminology + +- The **webmaster** is the party who is running a site. +- The **user** is the party who is accessing the site. + +### Design Goals + +- Should be extremely simple for webmasters to use in their site. +- Backend infrastructure should be optional; should be usable on a static site. +- Should not require any interaction with the user. +- Should give user's agent a choice about how much to spend, and which sites to support. +- Should give advanced webmasters a way to associate payments with their users, in order to unlock premium experiences. +- Should pay continuously as the user consumes content. + +### Relation to Other Protocols + +The reason this is not using the W3C Web Payments API is that Web Monetization is intended for continuous payments rather than discrete payments. It is also not designed to have any user interaction. The idea is to provide a direct alternative to advertisements, rather than an alternative to existing checkout methods. + +With advertisements, the browser decides whether to display the ads and the user decides whether to engage with the ads. With Web Monetization, the browser decides whether to pay the site and, if so, how much to pay. + +Web Monetization makes use of [Payment Pointers](../0026-payment-pointers/0026-payment-pointers.md) in order to associate a site with an ILP destination, and [SPSP](../0009-simple-payment-setup-protocol/0009-simple-payment-setup-protocol.md) in order to set up ILP payments. + +The browser or browser extension which provides Web Monetization will likely use the [Ledger Plugin Interface](../0004-ledger-plugin-interface/0004-ledger-plugin-interface) in order to trigger payment when a site requests it. + +## Flow + +- The user visits a webpage. The page is loaded and the page's javascript is run. +- The site checks whether `window.monetize` is defined. This function is injected into the page by the browser or browser extension which provides Web Monetization. +- If `window.monetize` is not defined, the webpage may show the user a message asking them to install a Web Monetization extension in order to support this site. +- If `window.monetize` is defined, the webpage calls `monetize({ receiver })`, where `receiver` is their payment pointer. This function returns a promise. +- The user's browser or Web Monetization extension decides whether to pay the page. +- If the user's browser decides not to pay, an error is thrown from the `monetize` promise. +- If the user's browser pays, the `monetize` promise resolves. +- The webpage may run some code in order to thank the user or offer them additional content. This does not guarantee that the site way paid, because it runs client-side. If confirmation of receipt is required, the backend which provides the SPSP server can be queried. + +## Specification + +#### monetize + +`window.monetize({ receiver: string }) -> Promise` + +Request the user's browser or Web Monetization extension to send money to the specified `receiver`. +If the browser does not support Web Monetization and there is no Web Monetization extension, this +function will not be defined. + +If this call is successful, the user's browser will begin to send payment to the `receiver` by resolving +it as a payment pointer and then using SPSP. If `receiver` does not begin with a `$` then it will be queried +as an SPSP endpoint as a URL directly. + +The amount of money that the user decides to send is up to them. The user SHOULD pay continuously with time, +and SHOULD only pay when the user has the monetized page active. + +This function does not guarantee that the user has paid the server. The backend which runs the SPSP receiver +will have to be queried in order to confirm how much has been paid to a specific receiver. + +##### Parameters + +| Name | Type | Description | +|:---|:---|:---| +| opts | `Object` | The options for monetization. | +| opts.receiver | `String` | The [payment pointer](../0009-simple-payment-setup-protocol/draft-4.html) or [SPSP endpoint](../0026-payment-pointers/draft-1.html) to which ILP payments should be sent. | + +##### Returns + +`Promise` - A promise which resolves if the user decides to send money to the page. + +Rejects with `Error` if the user's browser decides not to send money to this page. diff --git a/0029-loopback-transport/0029-loopback-transport.md b/0029-loopback-transport/0029-loopback-transport.md new file mode 100644 index 00000000..afd0eb0c --- /dev/null +++ b/0029-loopback-transport/0029-loopback-transport.md @@ -0,0 +1,40 @@ +--- +title: The Loopback Transport Protocol (LT) +draft: 1 +--- +# Loopback Transport Protocol (LT) + +The Loopback Transport protocol is a technique for transporting value over the Interledger. It relies on the establishment of a temporary, direct, ILPv4 link between sender and receiver (the loopback link). The flow is as follows. + +## One-shot Flow + +1. The loopback connection is established out of band. For instance, the application layer protocol may allow the receiver to specify a [BTP/2.0](../0023-bilateral-transfer-protocol/0023-bilateral-transfer-protocol.md) URL for the sender to connect to. +1. The sender discovers the loopback address using [IL-DCP](https://github.com/interledgerjs/ilp-protocol-ildcp). +1. The sender's application layer protocol tells the LT module to send a payment for a given source amount, minimum destination amount, and expiry time. +1. The sender chooses a fulfillment at random. +1. The sender creates the condition of their payment by computing the SHA-256 hash of the fulfillment. +1. The sender sets the destination address to the loopback address for this lookback link. +1. The sender sets a unique identifier as the data for the payment +1. Using this condition, address, amount, and data, the sender prepares an ILPv4 payment to a connector of her choice. +1. The payment will find its way from this connector, via any number of additional connectors, to the receiver. +1. The receiver checks that the destination address matches the loopback address he announced to the sender earlier, and forwards the ILPv4 `prepare` message to the sender, over the loopback link. +1. The sender's LT module checks the destination amount and the time left before expiry are sufficient, and if so, uses the fulfillment to fulfill the payment. +1. If for whatever reason the payment does not arrive in time, or the minimum destination amount is not met, the LT module responds to the application layer protocol with an error message instead + +## Chunked Flow + +Wnen sending multiple ILPv4 payments, as chunks in a chunked payment, the parameters passed from application layer to the transport layer are different. The application layer specifies a desired cummulative destination amount, and a deadline. The transport layer module then creates chunks of varying size and expiry duration, and accepts only the chunks whose exchange rate is best. If the cummulative value of chunks accepted so far is behind schedule for reaching the desired total before the deadline, chunks that arrive with slightly worse exchange rates will be accepted too. If the cummulative value is increasing ahead of schedule, the threshold will be adjusted to accept only the chunks with the very best exchange rates. + +## How to use + +When designing an application layer protocol, it's advisable to support both LT and PSK. Even though LT is far simpler than PSK, the extra loopback hop means that LT payments inherently have a higher latency than PSK payments, where the receiver can immediately fulfill incoming prepared payments. In situations where the sender can easily formulate instructions for the receiver about when to accept a payment and when not, and the sender trusts the receiver to execute these instructions in good faith, the speed up achieved with PSK means money needs to be tied up for less time, and this may translate to lower transaction fees, depending on connector pricing policies. So the application layer protocol needs to provide a choice between LT and PSK. + +If LT is chosen, the loopback link needs to be established; this will usually require some data exchange (e.g., one of the two parties sends connection details to the other party). + +The flow described above assumes one party only acts as the sender, and the other party only acts as the receiver, but once the loopback link is established, it can easily be used in both directions (in that case, each participant would send an IL-DCP request to the other participant, to ask for a loopback address). + +Once the loopback link is up, and each party who wants to act as a sender knows what their loopback address is, LT exposes two methods and one event to the application layer: one method for the one-shot flow, one for the chunked flow, one event that gets triggered whenever an incoming payment gets fulfilled, and one for when an outgoing payment gets fulfilled. + +It is up to the application layer protocol to decide how much to send, and whether to use a one-shot or a chunked flow. + +In future, hooks may be provided through which the application layer can also decide to reject incoming payments instead of looping them back to the appropriate sender. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..cb4f3955 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,46 @@ +# CONTRIBUTING + +Contributions to the Interledger RFC repo follow the following document publishing process. + +## Background + +The work of the Interledger community is being done under the framework of a W3C Community Group and is therefor guided by the [processes](https://www.w3.org/community/about/agreements/) laid out by the W3C for these groups. + +Also influencing this process is the well-established and popular IETF RFC process. + +## Process + +The ILP RFC process attempts to be easy to use but also rigorous enough that there are permalinks to revisions of documents for reference purposes. + +The process differentiates between specifications that are incubating (similar to IETF Internet Drafts) and those that are mature (similar to IETF RFCs). + +The two document types are *Working Drafts* and *Candidate Specifications*. + +1. When a community member wishes to submit a new document for comment they will submit a Pull Request to the [RFCs repo](https://github.com/interledger/rfcs/) which adds a new Markdown file. The file MUST follow the naming convention `0000-title`, where title is a lower case title with spaces replaced by hyphens (`-`), and have front-matter (similar to GitHub pages rendered from MarkDown) specifying at least a `title` and `draft` (an integer, starting at 1 and incrmenting with each revision of the RFC). + +Example: +``` +--- +title: The Interledger Architecture +draft: 1 +--- +# Interledger Architecture + +Lorum Ipsum etc +``` + +2. One of the project maintainers will review the submission and assign an RFC number to the document, making a follow up commit to the PR which renames the file and folder as appropriate. + +3. Subsequent updates to the document will trigger a publishing workflow that creates an HTML rendered version of the document that contains a permalink to the draft. Each revision must increment the `draft` number in the front-matter or the publishing will fail. + +4. These Interledger RFCs are living documents that start as Working Drafts and are iterated on until they are considered stable. + +5. *Working Drafts* have an RFC number AND a draft number starting at 1 and increasing by 1 each time the content is changed. + +6. When a Working Draft is considered stable there is a call for review from the community to publish the document as a *Candidate Specification*. + +7. Assuming there is consensus to publish, the document becomes a *Candidate Specification* and no further substantive changes are allowed under the same RFC number. (The draft number changes to `FINAL`). + +8. *Candidate Specifications* will also be published as W3C Community Group reports and may be published as IETF Internet Drafts if appropriate. + +9. A different template is used for *Working Drafts* and *Candidate Specifications* to help readers differentiate between them. diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 2973330b..00000000 --- a/Gruntfile.js +++ /dev/null @@ -1,30 +0,0 @@ -module.exports = function(grunt) { - - // Project configuration. - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - watch: { - scripts: { - files: ['0002-crypto-conditions/0002-crypto-conditions.md'], - tasks: ['kramdown_rfc2629'], - options: { - spawn: false, - }, - }, - }, - kramdown_rfc2629: { - options: { - outputs: ['text', 'html'], - outputDir: '0002-crypto-conditions/output', - removeXML: false - }, - your_target: { - src: ['0002-crypto-conditions/0002-crypto-conditions.md'] - }, - }, - }); - - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-kramdown-rfc2629'); - -}; \ No newline at end of file diff --git a/README.md b/README.md index 040e2233..ef2051f0 100644 --- a/README.md +++ b/README.md @@ -5,22 +5,58 @@ This repository contains a collection of various specifications and documentation related to the Interledger Protocol (ILP). These documents or requests-for-comment (RFCs) are published by various authors and publication here does not imply official status unless otherwise specified in the document. -## Index +The process for submitting an RFC is documented in the [RFC Process](CONTRIBUTING.md). -Here is a list of published Interledger RFCs. +For the main reference implementation of the ILP stack, see [Interledger.js](https://github.com/interledgerjs). -* **[IL-RFC 1: Interledger Architecture](0001-interledger-architecture/0001-interledger-architecture.md)** +## Interledger Overview and Explanatory Docs - Basic overview of the Interledger architecture. +* **[1: Interledger Architecture](0001-interledger-architecture/0001-interledger-architecture.md)** -* **[IL-RFC 2: Crypto-conditions](0002-crypto-conditions/0002-crypto-conditions.xml)** + Overview of the Interledger architecture. - Cryptographic conditions provide a flexible mechanism for delegation and authentication. Conditions include hashlocks, signatures and operators to compose them. +* **[19: Glossary](./0019-glossary/0019-glossary.md)** -* **[IL-RFC 3: Interledger Protocol](0003-interledger-protocol/0003-interledger-protocol.md)** + Definitions of Interledger terminology. - Describes the Interledger protocol, including the format of the Interledger header, used for sending payment instructions across different ledgers and connectors. +* **[20: Explain Like I'm Five (ELI5)](0020-explain-like-im-five/0020-explain-like-im-five.md)** -* **[IL-RFC 4: Ledger Plugin Interface](0004-ledger-plugin-interface/0004-ledger-plugin-interface.md)** + Basic explanation of Interledger. + +## Core Interledger Protocol Specs + + +* **[27: Interledger Protocol V4 (ILPv4)](0027-interledger-protocol-4/0027-interledger-protocol-4.md)** + + Specifies the Interledger Protocol and Interledger Packet, which are used for sending payment instructions across different ledgers and connectors. This is the core protocol in the Interledger stack. + +* **[15: ILP Addresses](0015-ilp-addresses/0015-ilp-addresses.md)** + + Specifies the Interledger Address format for ledgers and accounts. + +## Protocols Built Upon ILP + +* **[9: Simple Payment Setup Protocol (SPSP)](0009-simple-payment-setup-protocol/0009-simple-payment-setup-protocol.md)** + + A basic Application Layer protocol that uses HTTPS to exchange details needed to set up an Interledger payment. + +* **[25: Pre-Shared Key V2 (PSKv2)](0025-pre-shared-key-2/0025-pre-shared-key-2.md)** + + The recommended Transport Layer protocol for most use cases, which handles quoting, individual payments, chunked payments, and streaming payments using a shared secret between the sender and receiver. + +## Ledger Layer + +* **[24: Ledger Plugin Interface V2](0024-ledger-plugin-interface-2/0024-ledger-plugin-interface-2.md)** + + Ledger abstraction used in the JavaScript implementation. This can be used as a model for defining such plugins in other languages. + +* **[23: Bilateral Transfer Protocol (BTP)](0023-bilateral-transfer-protocol/0023-bilateral-transfer-protocol.md)** + + Recommended API for trustlines and payment channels. + +## Routing + +* **[10: Connector-To-Connector Protocol (CCP)](0010-connector-to-connector-protocol/0010-connector-to-connector-protocol.md)** + + A protocol used by connectors to communicate routing information. - JavaScript interface for ledger plugins, which implement all functionality necessary to make Interledger payments through a ledger. This can be used as a model for defining such plugins in other languages. diff --git a/asn1/BilateralTransferProtocol.asn b/asn1/BilateralTransferProtocol.asn new file mode 100644 index 00000000..0dd43ebd --- /dev/null +++ b/asn1/BilateralTransferProtocol.asn @@ -0,0 +1,124 @@ +BilateralTransferProtocol +DEFINITIONS +AUTOMATIC TAGS ::= +BEGIN + +IMPORTS + UInt8, + UInt32, + UInt64, + UInt128, + UInt256 + FROM GenericTypes +; + +-- Zero or more protocolData entries, each with a unique +-- protocolName, describe protocol-specific information about +-- unpaid and conditionally paid requests and results +-- sent over the BilateralTransferProtocol. +-- The protocolName of each entry describes the protocol for +-- interpreting the data. Peers need to establish beforehand +-- which protocols each understand, and which names they +-- use, which can be agreed ad-hoc, except that protocolName 'ilp' +-- is reserved for the Interledger protocol. +-- When multiple protocolData items are sent in a request, +-- only the first one will be decisive in triggering the +-- response, the rest of the protocolData items are considered +-- side protocols that piggyback on the first one. +-- The contentType byte can be used to allow debug tools to +-- display the contents of the protocolData without understanding +-- it. + +ContentType ::= INTEGER { + applicationOctetStream (0), + textPlainUtf8 (1), + applicationJson (2) +} (0..255) + +ProtocolData ::= SEQUENCE OF SEQUENCE { + protocolName IA5String, + contentType ContentType, + data OCTET STRING +} + +-- Response and Error are the response types. +-- When using these in a BTP packet, the requestId should match +-- the requestId of the request they respond to. + +Response ::= SEQUENCE { + protocolData ProtocolData +} + +Error ::= SEQUENCE { + -- Standardized error code + code IA5String (SIZE (3)), + -- Corresponding error code + name IA5String, + -- Time of emission + triggeredAt GeneralizedTime, + -- Additional data + data OCTET STRING (SIZE (0..8192)), + -- + protocolData ProtocolData +} + +-- Prepare, Fulfill, Reject, and Message are the request types. +-- Each request should have a unique requestId. + +Prepare ::= SEQUENCE { + transferId UInt128, + amount UInt64, + executionCondition UInt256, + expiresAt GeneralizedTime, + -- + protocolData ProtocolData +} + +Fulfill ::= SEQUENCE { + transferId UInt128, + fulfillment UInt256, + -- + protocolData ProtocolData +} + +Reject ::= SEQUENCE { + transferId UInt128, + -- + protocolData ProtocolData +} + +Message ::= SEQUENCE { + protocolData ProtocolData +} + +Transfer ::= SEQUENCE { + amount UInt64, + -- + protocolData ProtocolData +} + +CALL ::= CLASS { + &typeId UInt8 UNIQUE, + &Type +} WITH SYNTAX {&typeId &Type} + +CallSet CALL ::= { + {1 Response} | + {2 Error} | + {3 Prepare} | + {4 Fulfill} | + {5 Reject} | + {6 Message} | + {7 Transfer} +} + +BilateralTransferProtocolPacket ::= SEQUENCE { + -- One byte type ID + type CALL.&typeId ({CallSet}), + -- Used to associate requests and corresponding responses + requestId UInt32, + -- Length-prefixed main data + data CALL.&Type ({CallSet}{@type}) +} + +END diff --git a/asn1/CryptoConditions.asn b/asn1/CryptoConditions.asn deleted file mode 100644 index b41a1ffd..00000000 --- a/asn1/CryptoConditions.asn +++ /dev/null @@ -1,134 +0,0 @@ ----- -CryptoConditions DEFINITIONS AUTOMATIC TAGS ::= -BEGIN - -/** - * CONTAINERS - */ - -Condition ::= SEQUENCE { - version INTEGER (0..255), - type FulfillmentType, - requiredSuites OCTET STRING, - fingerprint OCTET STRING, - maxFulfillmentLength INTEGER (0..MAX) -} - -Fulfillment ::= SEQUENCE { - version INTEGER (0..255), - type FulfillmentType, - payload OCTET STRING -} - -FulfillmentType ::= INTEGER { - preimageSha256(0), - rsaSha256(1), - prefixSha256(2), - thresholdSha256(3), - ed25519(4) -} (0..65535) - -/** - * FULFILLMENT PAYLOADS - */ - --- For preimage conditions, the payload *is* the preimage - -PrefixSha256FulfillmentPayload ::= SEQUENCE { - prefix OCTET STRING, - subfulfillment Fulfillment -} - -ThresholdSha256FulfillmentPayload ::= SEQUENCE { - threshold INTEGER (0..4294967295), - subfulfillments SEQUENCE OF ThresholdSubfulfillment -} - -ThresholdSubfulfillment ::= SEQUENCE { - weight INTEGER (0..4294967295) DEFAULT 1, - condition Condition OPTIONAL, - fulfillment Fulfillment OPTIONAL -} - -RsaSha256FulfillmentPayload ::= SEQUENCE { - modulus OCTET STRING (SIZE(128..512)), - signature OCTET STRING (SIZE(128..512)) -} - -Ed25519FulfillmentPayload ::= SEQUENCE { - publicKey OCTET STRING (SIZE(32)), - signature OCTET STRING (SIZE(64)) -} - -/** - * FINGERPRINTS - */ - --- SHA-256 hash of the fingerprint contents -Sha256Fingerprint ::= OCTET STRING (SIZE(32)) -- digest - --- 32-byte Ed25519 public key -Ed25519Fingerprint ::= OCTET STRING (SIZE(32)) -- publicKey - -/** - * FINGERPRINT CONTENTS - * - * The content that will be hashed to arrive at the fingerprint. - */ - --- The preimage type hashes the raw contents of the preimage - -PrefixSha256FingerprintContents ::= SEQUENCE { - version INTEGER (0..255), - type FulfillmentType, - prefix OCTET STRING, - condition Condition -} - -ThresholdSha256FingerprintContents ::= SEQUENCE { - version INTEGER (0..255), - type FulfillmentType, - threshold INTEGER (0..4294967295), - subconditions SEQUENCE OF ThresholdSubcondition -} - -ThresholdSubcondition ::= SEQUENCE { - weight INTEGER (0..4294967295), - condition Condition -} - -RsaSha256FingerprintContents ::= INTEGER (0..MAX) -- modulus - -/** - * EXAMPLES - */ - -exampleCondition Condition ::= -{ - version 1, - type preimageSha256, - requiredSuites '03'H, - fingerprint 'E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'H, - maxFulfillmentLength 2 -} - -exampleFulfillment Fulfillment ::= -{ - version 1, - type preimageSha256, - payload '00'H -} - -exampleRsaSha256FulfillmentPayload RsaSha256FulfillmentPayload ::= -{ - modulus 'B30E7A938783BABF836850FF49E14F87E3F92D5C46E33FECA3E4F0B22358580B11765995F4B8EEA7FB4712C2E1E316F7F775A953D232216A169D9A64DDC007120A400B37F2AFC077B62FE304DE74DE6A119EC4076B529C4F6096B0BAAD4F533DF0173B9B822FD85D65FA4BEFA92D8F524F69CBCA0136BD80D095C169AEC0E095'H, - signature '48E8945EFE007556D5BF4D5F249E4808F7307E29511D3262DAEF61D88098F9AA4A8BC0623A8C975738F65D6BF459D543F289D73CBC7AF4EA3A33FBF3EC4440447911D72294091E561833628E49A772ED608DE6C44595A91E3E17D6CF5EC3B2528D63D2ADD6463989B12EEC577DF6470960DF6832A9D84C360D1C217AD64C8625BDB594FB0ADA086CDECBBDE580D424BF9746D2F0C312826DBBB00AD68B52C4CB7D47156BA35E3A981C973863792CC80D04A180210A52415865B64B3A61774B1D3975D78A98B0821EE55CA0F86305D42529E10EB015CEFD402FB59B2ABB8DEEE52A6F2447D2284603D219CD4E8CF9CFFDD5498889C3780B59DD6A57EF7D732620'H -} - -exampleEd25519FulfillmentPayload Ed25519FulfillmentPayload ::= -{ - publicKey 'EC172B93AD5E563BF4932C70E1245034C35467EF2EFD4D64EBF819683467E2BF'H, - signature 'B62291FAD9432F8F298B9C4A4895DBE293F6FFDA1A68DADF0CCDEF5F47A0C7212A5FEA3CDA97A3F4C03EA9F2E8AC1CEC86A51D452127ABDBA09D1B6F331C070A'H -} - -END diff --git a/asn1/GenericExtensionHeaderFormat.asn b/asn1/GenericExtensionHeaderFormat.asn deleted file mode 100644 index 6f195e49..00000000 --- a/asn1/GenericExtensionHeaderFormat.asn +++ /dev/null @@ -1,9 +0,0 @@ -GenericExtensionHeaderFormat DEFINITIONS AUTOMATIC TAGS ::= -BEGIN - -GenericHeaderPreamble ::= SEQUENCE { - nextHeader INTEGER (0..65535), - size INTEGER (0..65535) -} - -END diff --git a/asn1/GenericTypes.asn b/asn1/GenericTypes.asn new file mode 100644 index 00000000..420d0a64 --- /dev/null +++ b/asn1/GenericTypes.asn @@ -0,0 +1,67 @@ +GenericTypes +DEFINITIONS +AUTOMATIC TAGS ::= +BEGIN + +-- Small Integers +Int8 ::= INTEGER (-128..127) +UInt8 ::= INTEGER (0..255) + +Int16 ::= INTEGER (-32768..32767) +UInt16 ::= INTEGER (0..65535) + +Int32 ::= INTEGER (-2147483648..2147483647) +UInt32 ::= INTEGER (0..4294967295) + +Int64 ::= INTEGER (-9223372036854775808..9223372036854775807) +UInt64 ::= INTEGER (0..18446744073709551615) + +-- Large (Cryptographic) Integers +UInt96 ::= OCTET STRING (SIZE(12)) +UInt128 ::= OCTET STRING (SIZE(16)) +UInt160 ::= OCTET STRING (SIZE(20)) +UInt192 ::= OCTET STRING (SIZE(24)) +UInt224 ::= OCTET STRING (SIZE(28)) +UInt256 ::= OCTET STRING (SIZE(32)) +UInt384 ::= OCTET STRING (SIZE(48)) +UInt512 ::= OCTET STRING (SIZE(64)) + +-- Variable-Length Integers +VarInt ::= INTEGER +VarUInt ::= INTEGER (0..MAX) + +-- Binary Floating Point (IEEE 754) + +Float32 ::= REAL ( -- binary32 + 0 | + WITH COMPONENTS { + mantissa (-16777215..16777215), -- 23 bits + 1 sign bit + base (2), + -- Note that the exponent range in ASN.1 is given as the exponent relative to + -- the integer mantissa, i.e. mantissa * 2 ^ exponent, whereas in IEEE 754, the + -- exponent is relative to a binary fraction using the 23 fractional bits and + -- an implicit integer part of one. This is why the exponent range is offset + -- by 23, i.e. -149..104 instead of -126..127. + exponent (-149..104) + } +) + +Float64 ::= REAL ( -- binary64 + 0 | + WITH COMPONENTS { + mantissa (-9007199254740991..9007199254740991), -- 52 bits + 1 sign bit + base (2), + -- Note the exponent range in ASN.1 is given as the exponent relative to the + -- integer mantissa, i.e. mantissa * 2 ^ exponent, whereas in IEEE 754, the + -- exponent is relative to a binary fraction using the 52 fractional bits and + -- an implicit integer part of one. This is why the exponent range is offset + -- by 53, i.e. -1075..970 instead of -1022..1023. + -- TODO: Why isn't this offset by 52 instead of 53? + exponent (-1075..970) + } +) + +-- Variable-Length Data +VarBytes ::= OCTET STRING + +END diff --git a/asn1/InterledgerErrorData.asn b/asn1/InterledgerErrorData.asn new file mode 100644 index 00000000..60c931e6 --- /dev/null +++ b/asn1/InterledgerErrorData.asn @@ -0,0 +1,22 @@ +InterledgerErrorData +DEFINITIONS +AUTOMATIC TAGS ::= +BEGIN + +IMPORTS + UInt64 + FROM GenericTypes +; + +-- Each of the following should be included as the `data` field in an ILP Reject packet + +-- F08: Amount Too Large +AmountTooLargeErrorData ::= SEQUENCE { + -- Local amount received by the connector + receivedAmount UInt64, + + -- Maximum amount (inclusive, denominated in same units as the receivedAmount) the connector will forward + maximumAmount UInt64 +} + +END diff --git a/asn1/InterledgerPacket.asn b/asn1/InterledgerPacket.asn new file mode 100644 index 00000000..20cd4fef --- /dev/null +++ b/asn1/InterledgerPacket.asn @@ -0,0 +1,61 @@ +InterledgerPacket +DEFINITIONS +AUTOMATIC TAGS ::= +BEGIN + +IMPORTS + UInt8, + VarBytes + FROM GenericTypes + + InterledgerPrepare, + InterledgerFulfill, + InterledgerReject + FROM InterledgerProtocol + + InterledgerProtocolPaymentData, + ForwardedPaymentData, + InterledgerProtocolErrorData, + InterledgerProtocolFulfillmentData, + InterledgerProtocolRejectionData + FROM LegacyInterledgerProtocol + + QuoteLiquidityRequestData, + QuoteLiquidityResponseData, + QuoteBySourceAmountRequestData, + QuoteBySourceAmountResponseData, + QuoteByDestinationAmountRequestData, + QuoteByDestinationAmountResponseData + FROM InterledgerQuotingProtocol +; + +PACKET ::= CLASS { + &typeId UInt8 UNIQUE, + &Type +} WITH SYNTAX {&typeId &Type} + +PacketSet PACKET ::= { + {1 InterledgerProtocolPaymentData} | + {2 QuoteLiquidityRequestData} | + {3 QuoteLiquidityResponseData} | + {4 QuoteBySourceAmountRequestData} | + {5 QuoteBySourceAmountResponseData} | + {6 QuoteByDestinationAmountRequestData} | + {7 QuoteByDestinationAmountResponseData} | + {8 InterledgerProtocolErrorData} | + {9 InterledgerProtocolFulfillmentData} | + {10 ForwardedPaymentData} | + {11 InterledgerProtocolRejectionData} | + {12 InterledgerPrepare} | + {13 InterledgerFulfill} | + {14 InterledgerReject} +} + +InterledgerPacket ::= SEQUENCE { + -- One byte type ID + type PACKET.&typeId ({PacketSet}), + -- Length-prefixed header + data PACKET.&Type ({PacketSet}{@type}) +} + +END diff --git a/asn1/InterledgerPaymentRequest.asn b/asn1/InterledgerPaymentRequest.asn new file mode 100644 index 00000000..8a7cf926 --- /dev/null +++ b/asn1/InterledgerPaymentRequest.asn @@ -0,0 +1,24 @@ +InterledgerPaymentRequest +DEFINITIONS +AUTOMATIC TAGS ::= +BEGIN + +IMPORTS + UInt8, + UInt256 + FROM GenericTypes + + InterledgerPacket + FROM InterledgerPacket +; + +InterledgerPaymentRequest ::= SEQUENCE { + -- IPR Version + version UInt8, + -- Cryptographic condition + condition UInt256, + -- InterledgerPacket containing InterledgerProtocolPayment + packet InterledgerPacket +} + +END diff --git a/asn1/InterledgerProtocol.asn b/asn1/InterledgerProtocol.asn new file mode 100644 index 00000000..d787ae00 --- /dev/null +++ b/asn1/InterledgerProtocol.asn @@ -0,0 +1,55 @@ +InterledgerProtocol +DEFINITIONS +AUTOMATIC TAGS ::= +BEGIN + +IMPORTS + UInt64, + UInt256 + FROM GenericTypes + + Address, + Timestamp + FROM InterledgerTypes +; + +InterledgerPrepare ::= SEQUENCE { + -- Local amount (changes at each hop) + amount UInt64, + + -- Expiry date + expiresAt Timestamp, + + -- Execution condition + executionCondition UInt256, + + -- Destination ILP Address + destination Address, + + -- Information for recipient (transport layer information) + data OCTET STRING (SIZE (0..32767)) +} + +InterledgerFulfill ::= SEQUENCE { + -- Execution condition fulfillment + fulfillment UInt256, + + -- Information for sender (transport layer information) + data OCTET STRING (SIZE (0..32767)) +} + +InterledgerReject ::= SEQUENCE { + -- Standardized error code + code IA5String (SIZE (3)), + + -- Participant that originally emitted the error + triggeredBy Address, + + -- User-readable error message + message UTF8String (SIZE (0..8191)), + + -- Machine-readable error data, dependent on code + data OCTET STRING (SIZE (0..32767)) +} + +END diff --git a/asn1/InterledgerProtocolHeader.asn b/asn1/InterledgerProtocolHeader.asn deleted file mode 100644 index 829e393e..00000000 --- a/asn1/InterledgerProtocolHeader.asn +++ /dev/null @@ -1,19 +0,0 @@ -InterledgerHeader -DEFINITIONS -AUTOMATIC TAGS ::= -BEGIN - -IMPORTS - Address, - Amount - FROM Types -; - -InterledgerProtocolHeader ::= SEQUENCE { - version INTEGER(0..255), - destinationAddress Address, - destinationAmount Amount, - nextHeader INTEGER(0..65535) -} - -END diff --git a/asn1/InterledgerQuotingProtocol.asn b/asn1/InterledgerQuotingProtocol.asn index a10e5ea2..cec01b26 100644 --- a/asn1/InterledgerQuotingProtocol.asn +++ b/asn1/InterledgerQuotingProtocol.asn @@ -1,41 +1,105 @@ -InterledgerQuotingProtocol DEFINITIONS AUTOMATIC TAGS ::= +InterledgerQuotingProtocol +DEFINITIONS +AUTOMATIC TAGS ::= BEGIN IMPORTS - Address, - Amount - FROM Types - - GenericHeaderPreamble - FROM GenericExtensionHeaderFormat + UInt32, + UInt64 + FROM GenericTypes + + Address, + LiquidityCurve + FROM InterledgerTypes ; -QuoteRequest ::= SEQUENCE { - preamble GenericHeaderPreamble, - addresses AddressSet, - sourceAmount Amount OPTIONAL, - destinationAmount Amount OPTIONAL, - -- How long the receiver needs to fulfill the payment (when using TTP/ATP) - -- Defaults to 1.5 seconds - destinationHoldDuration IlpHoldDuration DEFAULT 1500000 +-- Request to receive liquidity information between the current ledger and the +-- destination account. This information is sufficient to locally quote any +-- amount until the curve expires. +QuoteLiquidityRequestData ::= SEQUENCE { + destinationAccount Address, + -- How much time the receiver needs to fulfill the payment (in milliseconds) + destinationHoldDuration UInt32, + + -- Enable ASN.1 Extensibility + extensions SEQUENCE { + ... + } +} + +QuoteLiquidityResponseData ::= SEQUENCE { + -- Curve describing the liquidity (relationship between input and output + -- amounts) for the quoted route + liquidity LiquidityCurve, + -- Common prefix of all addresses for which this liquidity curve applies. + -- If the curve only applies to the destinationAccount requested this MUST + -- be set equal to that address. If the curve applies to other accounts + -- with a certain prefix, this value should be set to that prefix. For more + -- on ILP Address Prefixes, see IL-RFC-0015: + -- https://interledger.org/rfcs/0015-ilp-addresses/ + appliesToPrefix Address, + -- How long the sender should put money on hold (in milliseconds) + sourceHoldDuration UInt32, + -- Maximum time where the connector (and any connectors after it) expects to + -- be able to honor this liquidity curve. Note that a quote in ILP is + -- non-committal, meaning that the liquidity is only likely to be available, + -- but not reserved and therefore not guaranteed. + expiresAt GeneralizedTime, + + -- Enable ASN.1 Extensibility + extensions SEQUENCE { + ... + } +} + +-- Quoting with a specified source amount to determine destination amount +QuoteBySourceAmountRequestData ::= SEQUENCE { + destinationAccount Address, + sourceAmount UInt64, + -- How much time the receiver needs to fulfill the payment (in milliseconds) + destinationHoldDuration UInt32, + + -- Enable ASN.1 Extensibility + extensions SEQUENCE { + ... + } } -QuoteResponse ::= SEQUENCE { - preamble GenericHeaderPreamble, - connectorAccount IlpAccountName, - -- Address that this quote response relates to - address Address, - -- Source or destination amount, depending on whichever one was NOT - -- provided in the request. - requestedAmount Amount, - sourceHoldDuration IlpHoldDuration +QuoteBySourceAmountResponseData ::= SEQUENCE { + -- Amount that will arrive at the receiver + destinationAmount UInt64, + -- How long the sender should put money on hold (in milliseconds) + sourceHoldDuration UInt32, + + -- Enable ASN.1 Extensibility + extensions SEQUENCE { + ... + } } -AddressSet ::= SEQUENCE OF Address +-- Quoting with a specified destination amount to determine source amount +QuoteByDestinationAmountRequestData ::= SEQUENCE { + destinationAccount Address, + destinationAmount UInt64, + -- How much time the receiver needs to fulfill the payment (in milliseconds) + destinationHoldDuration UInt32, -IlpAccountName ::= OCTET STRING + -- Enable ASN.1 Extensibility + extensions SEQUENCE { + ... + } +} + +QuoteByDestinationAmountResponseData ::= SEQUENCE { + -- Amount the sender needs to send based on the requested destination amount + sourceAmount UInt64, + -- How long the sender should put money on hold (in milliseconds) + sourceHoldDuration UInt32, --- Length of hold in microseconds -IlpHoldDuration ::= INTEGER (0..18446744073709551615) + -- Enable ASN.1 Extensibility + extensions SEQUENCE { + ... + } +} END diff --git a/asn1/InterledgerTypes.asn b/asn1/InterledgerTypes.asn new file mode 100644 index 00000000..f03f895f --- /dev/null +++ b/asn1/InterledgerTypes.asn @@ -0,0 +1,79 @@ +InterledgerTypes +DEFINITIONS +AUTOMATIC TAGS ::= +BEGIN + +IMPORTS + UInt64 + FROM GenericTypes +; + +-- Readable names for special characters that may appear in ILP addresses +hyphen IA5String ::= "-" +period IA5String ::= "." +underscore IA5String ::= "_" +tilde IA5String ::= "~" + +-- A standard interledger address +Address ::= IA5String + (FROM + ( hyphen + | period + | "0".."9" + | "A".."Z" + | underscore + | "a".."z" + | tilde ) + ) + (SIZE (1..1023)) + +-- -------------------------------------------------------------------------- + +-- We are using ISO 8601 and not POSIX time, because ISO 8601 increases +-- monotonically and never "travels back in time" which could cause issues +-- with transfer expiries. It is also one of the most widely supported and most +-- well-defined date formats as of 2017. +-- +-- Our actual wire format leaves out any fixed/redundant characters, such as +-- hyphens, colons, the "T" separator, the decimal period and the "Z" timezone +-- indicator. +-- +-- The wire format is four digits for the year, two digits for the month, +-- two digits for the day, two digits for the hour, two digits for the minutes, +-- two digits for the seconds and three digits for the milliseconds. +-- +-- I.e. the wire format is: 'YYYYMMDDHHmmSSfff' +-- +-- All times MUST be expressed in UTC time. + +Timestamp ::= PrintableString (SIZE(17)) + +-- -------------------------------------------------------------------------- + +-- Liquidity curves describe the relationship between input and output amount +-- for a given path between a pair of ledgers. +-- +-- The curve is expressed as a series of points given as coordinates of the form +-- . If a sender sends `inputAmount` units to the +-- connector, the recipient will receive `outputAmount`. The curve may represent +-- the liquidity through a single connector, or multiple liquidity curves can be +-- combined into one to represent the liquidity through a given path of +-- connectors. +-- +-- Points are ordered by inputAmount. The inputAmount is strictly increasing +-- from point to point. The outputAmount is monotonically increasing, meaning +-- each successively point must have an equal or greater outputAmount. +-- +-- The first point represents the minimum amount that can be transacted, while +-- the final point represents the maximum amount that can be transacted. +-- +-- If a query does not match a point exactly, implementations MUST use linear +-- interpolation. When querying by outputAmount, if multiple points match +-- exactly, the lowest inputAmount of any of these points MUST be returned. + +LiquidityCurve ::= SEQUENCE OF SEQUENCE { + x UInt64, + y UInt64 +} + +END diff --git a/asn1/LegacyInterledgerProtocol.asn b/asn1/LegacyInterledgerProtocol.asn new file mode 100644 index 00000000..c2dd65cd --- /dev/null +++ b/asn1/LegacyInterledgerProtocol.asn @@ -0,0 +1,78 @@ +LegacyInterledgerProtocol +DEFINITIONS +AUTOMATIC TAGS ::= +BEGIN + +IMPORTS + UInt64 + FROM GenericTypes + + Address + FROM InterledgerTypes +; + +-- the following packet type is experimental and may still change +ForwardedPaymentData ::= SEQUENCE { + -- Destination ILP Address + account Address, + -- Information for recipient (transport layer information) + data OCTET STRING (SIZE (0..32767)), + -- Enable ASN.1 Extensibility + extensions SEQUENCE { + ... + } +} + +InterledgerProtocolPaymentData ::= SEQUENCE { + -- Amount which must be received at the destination + amount UInt64, + -- Destination ILP Address + account Address, + -- Information for recipient (transport layer information) + data OCTET STRING (SIZE (0..32767)), + -- Enable ASN.1 Extensibility + extensions SEQUENCE { + ... + } +} + +InterledgerProtocolErrorData ::= SEQUENCE { + -- Standardized error code + code IA5String (SIZE (3)), + -- Corresponding error name + name IA5String, + -- Participant that originally emitted the error + triggeredBy Address, + -- Participants that forwarded the error + forwardedBy SEQUENCE OF Address, + -- Time of emission + triggeredAt GeneralizedTime, + -- Additional data + data OCTET STRING (SIZE (0..8192)), + -- Enable ASN.1 Extensibility + extensions SEQUENCE { + ... + } +} + +InterledgerProtocolFulfillmentData ::= SEQUENCE { + -- Information for sender (transport layer information) + data OCTET STRING (SIZE (0..32767)) +} + +InterledgerProtocolRejectionData ::= SEQUENCE { + -- Standardized error code + code IA5String (SIZE (3)), + -- Participant that originally emitted the error + triggeredBy Address, + -- User-readable error message + message UTF8String (SIZE (0..8191)), + -- Machine-readable error data, dependent on code + data OCTET STRING (SIZE (0..32767)), + -- Enable ASN.1 Extensibility + extensions SEQUENCE { + ... + } +} + +END diff --git a/asn1/PreSharedKeyV2.asn b/asn1/PreSharedKeyV2.asn new file mode 100644 index 00000000..fda290a1 --- /dev/null +++ b/asn1/PreSharedKeyV2.asn @@ -0,0 +1,46 @@ +PreSharedKeyV2 +DEFINITIONS +AUTOMATIC TAGS ::= +BEGIN + +IMPORTS + UInt8, + UInt32, + UInt64, + UInt96, + UInt128, + VarBytes + FROM GenericTypes +; + +Psk2EncryptionEnvelope ::= SEQUENCE { + -- Random nonce + initializationVector UInt96, + + -- Authentication tag (output of AES-GCM encryption) + authenticationTag UInt128, + + -- Encrypted PSK Packet + -- Note: This is NOT encoded as a variable length (length-prefixed) + -- octet string. The size is constrained by the length of the data + -- field in the InterledgerPrepare, InterledgerFulfill, or InterledgerReject + -- packet containing this Psk2EncryptionEnvelope + cipherText OCTET STRING (SIZE (0..32739)) +} + +Psk2Packet ::= SEQUENCE { + -- Type ID + type UInt8, + + -- ID used to correlate requests and responses + requestId UInt32, + + -- In PSK2 Requests, this indicates the minimum the receiver should accept + -- In PSK2 Responses and Errors, it is the amount that the receiver got + requestAmount UInt64, + + -- Application data carried in the encrypted PSK Request/Response/Error + data VarBytes +} + +END diff --git a/asn1/Types.asn b/asn1/Types.asn deleted file mode 100644 index de802338..00000000 --- a/asn1/Types.asn +++ /dev/null @@ -1,22 +0,0 @@ -Types DEFINITIONS AUTOMATIC TAGS ::= -BEGIN - --- GeneralizedTime is a standard ASN.1 time format based on ISO 8601. It was --- tempting to use the Unix epoch, however unfortunately, standard POSIX --- time is discontinuous. If we used POSIX time for expiries for example, a --- transfer might expire and then un-expire when a leap second occurs. Since --- Interledger protocols depend on causality, we cannot use POSIX time and --- since we do not wish to create our own time standard, we end up using ISO --- 8601. -Timestamp ::= GeneralizedTime - --- A standard interledger address is a hierarchical list of octet strings -Address ::= SEQUENCE OF OCTET STRING - --- A standard interledger amount is a base-10 floating point amount -Amount ::= SEQUENCE { - exponent INTEGER (-128..127), - mantissa INTEGER -} - -END diff --git a/asn1/UniversalModeHeader.asn b/asn1/UniversalModeHeader.asn deleted file mode 100644 index e0e7d898..00000000 --- a/asn1/UniversalModeHeader.asn +++ /dev/null @@ -1,21 +0,0 @@ -UniversalModeHeader DEFINITIONS AUTOMATIC TAGS ::= -BEGIN - -IMPORTS - Timestamp - FROM Types - - Condition - FROM CryptoConditions - - GenericHeaderPreamble - FROM GenericExtensionHeaderFormat -; - -UniversalModeHeader ::= SEQUENCE { - preamble GenericHeaderPreamble, - condition Condition, - expiresAt Timestamp -} - -END diff --git a/circle.yml b/circle.yml index 5774041d..e80f7641 100644 --- a/circle.yml +++ b/circle.yml @@ -7,8 +7,10 @@ machine: - git config --global push.default simple test: override: - - echo "no tests" - post: + # Check the ASN.1 syntax + - ./scripts/check-asn.sh + # Generate graphs for specs + - node scripts/generate_graphs.js # Generate specs for website - node scripts/generate_web.js deployment: @@ -17,3 +19,6 @@ deployment: commands: # Publish spec - node scripts/publish_web.js +general: + artifacts: + - "web" diff --git a/package.json b/package.json index 1f30d771..6c5a6e90 100644 --- a/package.json +++ b/package.json @@ -7,11 +7,10 @@ "devDependencies": { "cheerio": "^0.20.0", "ejs": "^2.4.1", + "front-matter": "^2.1.2", "glob": "^7.0.3", - "grunt": "^1.0.1", - "grunt-contrib-watch": "^1.0.0", - "grunt-kramdown-rfc2629": "0.0.2", - "marked": "^0.3.5" + "marked": "^0.3.5", + "viz.js": "^1.7.1" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" diff --git a/scripts/check-asn.sh b/scripts/check-asn.sh new file mode 100755 index 00000000..dc1400fd --- /dev/null +++ b/scripts/check-asn.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +if [[ $(git diff master -- asn1 | wc -l) == 0 ]]; then + echo 'No changes were made in the asn1 folder, skipping this check' + exit +fi + +cat ./asn1/*.asn | + # Convert POST body to CRLF + sed $'s/$/\r/' | + # "@-" means input is from stdin. + # "%24" is a URL-encoded "$" (curl expects the field name already escaped). + curl --silent -X POST \ + --data-urlencode '__EVENTTARGET=' \ + --data-urlencode '__EVENTARGUMENT=' \ + --data-urlencode '__LASTFOCUS=' \ + --data-urlencode '__VIEWSTATE=/wEPDwUKLTc1MTgyNjExOA9kFgJmD2QWAgIDD2QWAgIBD2QWCgIBDxBkDxYJAgICAwIEAgUCBgIHAggCCQIKFgkQBQ5JVFMgQ0FNIHYxLjMuMgUOSVRTIENBTSB2MS4zLjJnEAUPSVRTIERFTk0gdjEuMi4yBQ9JVFMgREVOTSB2MS4yLjJnEAUJSVRTIEoyNzM1BQlJVFMgSjI3MzVnEAULTkJBUCB2Ni4yLjAFC05CQVAgdjYuMi4wZxAFDFJBTkFQIFY2LjIuMAUMUkFOQVAgVjYuMi4wZxAFClJSQyB2OC42LjAFClJSQyB2OC42LjBnEAUMUzFBUCB2OC4xMC4wBQxTMUFQIHY4LjEwLjBnEAUIVEFQLTAzMTAFCFRBUC0wMzEwZxAFC1gyQVAgdjguOS4wBQtYMkFQIHY4LjkuMGcWAWZkAg0PEGRkFgFmZAIhDxBkZBYBZmQCIw9kFgICAQ8QZGQWAWZkAikPDxYCHgRUZXh0BVp7DQogICJuYW1lIjoiQWRhbSIsDQogICJmaXJzdC13b3JkcyI6IkhlbGxvIFdvcmxkIiwNCiAgImFnZSI6ew0KICAgICJiaWJsaWNhbCI6OTMwDQogIH0NCn1kZBgBBR5fX0NvbnRyb2xzUmVxdWlyZVBvc3RCYWNrS2V5X18WAQUdY3RsMDAkTWFpbkNvbnRlbnQkY2JOb1JlbGF4ZWS9r/9ep6vlfHBa1v0m9rFsUTVAg3p/w6Px4rcxgVnXcw==' \ + --data-urlencode '__VIEWSTATEGENERATOR=CA0B0334' \ + --data-urlencode '__EVENTVALIDATION=/wEdACTvBuL05c27tEMecJdb7IRZ1Cn+EpYSvY49XXrT29VY4DlKzLXE7UdGV+Sp4JynSVOUQai7x7H8epe7LjGkdO1cgsEkCnvatlsBmoGzDwbENG99/MJQf84nGOuvH0Nqme50SlF0/MXZPvJGTJ0aiVXFjEbYeNpryczPOfAPjZw2TmCy9br2E6vqtwSo6KnqMCHIg3IlVtr0nSnHI6hlNOXtzxTmDO+Ma32VfVY1O54Wy0hi53xAgYeVolslSp4aO/uZQKCK8xCNszfebGf8YBtrE5yR22Zoj/1cYhs6e19KIZKKpUt+2UttFP/gcQ0eOF+UdPim13mnFKHrBb176tgjv459Su9vLXbk7rGI/PHmqd2HCK7GUrcrWVpACKFv2zCj/gTCNXHIux3sexLf2Mopi4gRuE0nuD6kWraOU4UYoGvORuieq817u1hpS2zmlXhRoegCA2sek8Eg96dPkbe7PDNaWsaFKb9VSHutwb/KaCXkW74IirQHSJdIWFDq3cm2kYKX8ahKano1pLMtFiSPavO4bWHbVWZygdSrHRt9UsSuHQk/BqLrHPQunBKreLXh1857L5mvx/OFU09s5cbOyjCnzAPFMOG1G4/BC4S5xhCiOdJHJUbujzdFcUQssJa95wMqz3SaX35ivgp6JALGcpMWZ7v8HJ2Gj4qwh9D1X9Y1v2qihQEbJxac2paax0I9iDsfElO+PLcN/u2h7Km2mYq67nirnTn51aDV5CXNC1r9bT38/5lWR/G/on5UtiU+I9+9txLsDx7sc0JrUj3zVyNZYO29gB72z26Q+eQbXw==' \ + --data-urlencode 'ctl00%24MainContent%24tbASN1Code@-' \ + --data-urlencode 'ctl00%24MainContent%24CompileButton=Compile' \ + 'http://asn1-playground.oss.com/' | + grep 'MainContent_ConsoleOutput' | + sed 's//\n/g' | + # Print the parser output. + tee /dev/stderr | + grep --quiet '0 error messages, 0 warning messages' diff --git a/scripts/generate_graphs.js b/scripts/generate_graphs.js new file mode 100644 index 00000000..a056b3c5 --- /dev/null +++ b/scripts/generate_graphs.js @@ -0,0 +1,20 @@ +'use strict' + +const fs = require('fs') +const path = require('path') +const viz = require('viz.js') + +const buildGraph = (graph) => { + const dotFile = path.resolve(__dirname, graph + '.dot') + const svgFile = path.resolve(__dirname, graph + '.svg') + + const dotData = fs.readFileSync(dotFile, 'utf8') + const svgData = viz(dotData) + fs.writeFileSync(svgFile, svgData, 'utf8') +} + +for (let file of fs.readdirSync(path.resolve(__dirname, '../shared/graphs'))) { + if (file.endsWith('.dot')) { + buildGraph(path.resolve(__dirname, '../shared/graphs') + '/' + file.substring(0, file.length - 4)) + } +} diff --git a/scripts/generate_web.js b/scripts/generate_web.js index 70c9df5d..151bb9ba 100644 --- a/scripts/generate_web.js +++ b/scripts/generate_web.js @@ -7,6 +7,7 @@ const path = require('path') const exec = require('child_process').execSync const marked = require('marked') const cheerio = require('cheerio') +const fm = require('front-matter') // We need a custom renderer to make sure we generate the same header IDs as // Github. @@ -18,30 +19,78 @@ renderer.heading = function (text, level, raw) { .replace(/ /g, '-') return '' - + text - + '\n'; + + level + + ' id="' + + this.options.headerPrefix + + candidateId + + '">' + + text + + '\n'; } +// Override relative links from .md files to the base folder +// (Allows github to be linked properly, and website as well) +renderer.link = function (href, title, text) { + // Converts something like `../xxxx-/xxxx-.md` to `../xxxx-` + href = href.replace(/^(\.{2}\/\d{4}-.*)\/(\d{4}-.*\.md)$/g, '$1') + return marked.Renderer.prototype.link.call(this, href, title, text); +}; + let cwd = path.resolve(__dirname, '..') exec('rm -rf web', { cwd }) exec('git clone git@github.com:interledger/rfcs.git --branch gh-pages --single-branch web', { cwd }) exec('cp -r ????-* web', { cwd }) +exec('cp -r shared web', { cwd }) const template = ejs.compile(fs.readFileSync('tmpl/rfc.ejs.html', 'utf8')) const files = glob.sync('????-*/????-*.md') +let buildPass = true files.forEach((file) => { - const markdownContent = fs.readFileSync(file, 'utf8') - const renderedMarkdown = marked(markdownContent, { renderer }) + + const rfcNumber = file.match(/^(\d\d\d\d)/)[0] + const fileContent = fs.readFileSync(file, 'utf8') + const fmContent = fm(fileContent) + + if(!fmContent.attributes.title && !fmContent.attributes.deprecated) { + buildPass = false; + console.error("No title specified for " + file) + return + } + const title = fmContent.attributes.title + + if(!fmContent.attributes.draft) { + if(fmContent.attributes.deprecated) { + fmContent.attributes.draft = 'FINAL' + } else { + buildPass = false; + console.error("Draft number required for " + file) + return + } + } + const draftNumber = fmContent.attributes.draft + + if((draftNumber^0) !== draftNumber && draftNumber !== 'FINAL') { + buildPass = false; + console.error("Invalid draft number found for " + file) + return + } + + const deprecated = fmContent.attributes.deprecated + + if(deprecated && draftNumber !== 'FINAL') { + console.error("Deprecated RFC must be FINAL in " + file) + return + } + + const renderedMarkdown = marked(fmContent.body, { renderer }) const $ = cheerio.load(renderedMarkdown) - const title = $('h1').text() + + const permalink = './draft-' + draftNumber + '.html' + const draftFile = 'web/' + path.dirname(file) + '/draft-' + draftNumber + '.html' + const indexFile = 'web/' + path.dirname(file) + '/index.html' // Ensure heading IDs are unique const idMap = new Map() @@ -70,7 +119,7 @@ files.forEach((file) => { return { type: tag.get(0).tagName, title: tag.text(), - anchor: tag.attr('id') + anchor: '#' + tag.attr('id') } }).get() @@ -81,12 +130,63 @@ files.forEach((file) => { $('img').addClass('img-responsive') const content = $.html() + const renderedHtml = template({ title, content, toc, rfcNumber, draftNumber, deprecated }) + + //Versioning + if (fs.existsSync(draftFile)) { + const existingHtml = fs.readFileSync(draftFile, 'utf8') + if (existingHtml != renderedHtml) { + console.error('Draft number must be incremented if content is changed for ' + file) + buildPass = false + return + } + console.log('No changes in ' + file) + return + } - const renderedHtml = template({ title, content, toc }) + console.log('Writing ' + draftFile) + fs.writeFileSync(draftFile, renderedHtml) + console.log('Writing ' + indexFile) + fs.writeFileSync(indexFile, renderedHtml) - fs.writeFileSync('web/' + path.dirname(file) + '/index.html', renderedHtml) }) +const asnFiles = glob.sync('asn1/*') +const asnToc = [] +asnFiles.forEach((file) => { + if (file.endsWith('.md')) { + asnToc.unshift({ type: 'h2', title: 'ASN Modules', anchor: 'index.html' }) + } else { + const asnName = path.basename(file) + asnToc.push({ type: 'h3', title: asnName, anchor: asnName + '.html' }) + } +}) +asnFiles.forEach((file) => { + const fileContent = fs.readFileSync(file, 'utf8') + if (file.endsWith('.md')) { + const htmlFile = 'web/asn1/index.html' + const content = marked(fileContent) + const renderedHtml = template({ title: 'Interledger ASN.1', content, toc: asnToc, deprecated : false }) + console.log('Writing ' + htmlFile) + fs.writeFileSync(htmlFile, renderedHtml) + } else { + const basename = path.basename(file) + const content = '
' + escape(fileContent) + '
' + const renderedHtml = template({ title: basename, content, toc: asnToc, deprecated: false }) + const htmlFile = 'web/asn1/' + basename + '.html' + console.log('Writing ' + htmlFile) + fs.writeFileSync(htmlFile, renderedHtml) + } +}) + +function escape(html) { + return html + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} + cwd = path.resolve(__dirname, '../web') const status = exec('git status --porcelain', { cwd }).toString('utf8') if (!status.length) { @@ -94,3 +194,5 @@ if (!status.length) { } else { console.log(status) } + +process.exit(buildPass ? 0 : 1) diff --git a/shared/graphs/interledger-model.dot b/shared/graphs/interledger-model.dot new file mode 100644 index 00000000..8691bb89 --- /dev/null +++ b/shared/graphs/interledger-model.dot @@ -0,0 +1,16 @@ +graph G { + node [shape = box,height=.1,fontname = "helvetica"]; + newrank=true; + + s [shape=box style=filled color=lightgrey label="Sender"]; + r [shape=box style=filled color=lightgrey label="Receiver"]; + + c1 [shape=box style=filled color=lightgrey label="Connector"]; + c2 [shape=box style=filled color=lightgrey label="Connector"]; + + s:e -- c1:w; + c1:e -- c2:w; + c2:e -- r:w; + + { rank=same; s c1 c2 r } +} diff --git a/shared/graphs/interledger-model.svg b/shared/graphs/interledger-model.svg new file mode 100644 index 00000000..05343b53 --- /dev/null +++ b/shared/graphs/interledger-model.svg @@ -0,0 +1,52 @@ + + + + + + +G + + + +s + +Sender + + + +c1 + +Connector + + + +s:e--c1:w + + + + +r + +Receiver + + + +c2 + +Connector + + + +c1:e--c2:w + + + + +c2:e--r:w + + + + diff --git a/shared/graphs/protocol-suite.dot b/shared/graphs/protocol-suite.dot new file mode 100644 index 00000000..f8347dc4 --- /dev/null +++ b/shared/graphs/protocol-suite.dot @@ -0,0 +1,71 @@ +graph G { + node [shape = box,height=.1,fontname = "helvetica"]; + newrank=true; + + app [label="Application\l" shape="plaintext" width=1.5] + transport [label="Transport\l" shape="plaintext" width=1.5] + interledger [label="Interledger\l" shape="plaintext" width=1.5] + ledger [label="Ledger\l" shape="plaintext" width=1.5] + + subgraph cluster_0 { + style=filled; + color=lightgrey; + node [style=filled,color=white]; + app1 [label=SPSP]; + tr1 [label=PSK]; + im1 [label="ILP"]; + lli1 [label="L1 Plugin"]; + app1 -- tr1 -- im1 -- lli1; + label = "Sender"; + fontname = "helvetica"; + } + + subgraph cluster_1 { + style=filled; + color=lightgrey; + node [style=filled,color=white]; + im2 [label="ILP"]; + lli2[label="L1 Plugin"]; + lli3 [label="L2 Plugin"]; + im2:sw -- lli2:n; + im2:se -- lli3:n; + label = "Connector"; + fontname = "helvetica"; + } + + subgraph cluster_2 { + style=dotted; + color=black; + penwidth=1.5; + node [style=dotted,color=black]; + im3 [label="ILP"]; + lli4[label="L2 Plugin"]; + lli5 [label="LN Plugin"]; + im3:sw -- lli4:n [style=dotted]; + im3:se -- lli5:n [style=dotted]; + label = "Connector (N-1 Instances)"; + fontname = "helvetica"; + } + + subgraph cluster_3 { + style=filled; + color=lightgrey; + node [style=filled,color=white]; + app2 [label=SPSP]; + tr2 [label=PSK]; + im4 [label="ILP"]; + lli6 [label="LN Plugin"]; + app2 -- tr2 -- im4 -- lli6; + label = "Receiver"; + fontname = "helvetica"; + } + + lli1 -- lli2; + lli3 -- lli4 [style=dotted]; + lli5 -- lli6 [style=dotted]; + + { rank=same; app app1 app2 } + { rank=same; transport tr1 tr2 } + { rank=same; interledger im1 im2 im3 im4 } + { rank=same; ledger lli1 lli2 lli3 lli4 lli5 lli6} +} diff --git a/shared/graphs/protocol-suite.svg b/shared/graphs/protocol-suite.svg new file mode 100644 index 00000000..a9ebc25b --- /dev/null +++ b/shared/graphs/protocol-suite.svg @@ -0,0 +1,202 @@ + + + + + + +G + + +cluster_0 + +Sender + + +cluster_1 + +Connector + + +cluster_2 + +Connector (N-1 Instances) + + +cluster_3 + +Receiver + + + +app +Application + + + +transport +Transport + + + +interledger +Interledger + + + +ledger +Ledger + + + +app1 + +SPSP + + + +tr1 + +PSK + + + +app1--tr1 + + + + +im1 + +ILP + + + +tr1--im1 + + + + +lli1 + +L1 Plugin + + + +im1--lli1 + + + + +lli2 + +L1 Plugin + + + +lli1--lli2 + + + + +im2 + +ILP + + + +im2:sw--lli2:n + + + + +lli3 + +L2 Plugin + + + +im2:se--lli3:n + + + + +lli4 + +L2 Plugin + + + +lli3--lli4 + + + + +im3 + +ILP + + + +im3:sw--lli4:n + + + + +lli5 + +LN Plugin + + + +im3:se--lli5:n + + + + +lli6 + +LN Plugin + + + +lli5--lli6 + + + + +app2 + +SPSP + + + +tr2 + +PSK + + + +app2--tr2 + + + + +im4 + +ILP + + + +tr2--im4 + + + + +im4--lli6 + + + + diff --git a/shared/graphs/transfer-states.dot b/shared/graphs/transfer-states.dot new file mode 100644 index 00000000..88039f33 --- /dev/null +++ b/shared/graphs/transfer-states.dot @@ -0,0 +1,14 @@ +digraph G { + node [shape = box,height=.1,fontname = "helvetica"]; + + Proposed -> Prepared -> Executed + Proposed:s -> Rejected:w + Prepared:s -> Rejected:w + + Executed -> Rejected [style=invis] + + Executed [group=g] + Rejected [group=g] + + { rank=same Proposed Prepared Executed } +} diff --git a/shared/graphs/transfer-states.svg b/shared/graphs/transfer-states.svg new file mode 100644 index 00000000..c26d5f47 --- /dev/null +++ b/shared/graphs/transfer-states.svg @@ -0,0 +1,62 @@ + + + + + + +G + + + +Proposed + +Proposed + + + +Prepared + +Prepared + + + +Proposed->Prepared + + + + + +Rejected + +Rejected + + + +Proposed:s->Rejected:w + + + + + +Executed + +Executed + + + +Prepared->Executed + + + + + +Prepared:s->Rejected:w + + + + + + diff --git a/tmpl/rfc.ejs.html b/tmpl/rfc.ejs.html index ba2cfb91..54966b7f 100644 --- a/tmpl/rfc.ejs.html +++ b/tmpl/rfc.ejs.html @@ -65,15 +65,34 @@
+ <% if (deprecated) { -%> +

+ This specification has been deprecated. + <% if (typeof deprecated === 'string') { -%> +
It has been replaced by this new specification. + <% } -%> +

+ <% } -%> <%- content %>