| 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
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 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 (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.
- - - -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 [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.
-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.
-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.
-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 [itu.X680.2015]. Implementations of this spec MUST support encoding and decoding using Octet Encoding Rules (OER) as defined in [itu.X696.2015].
-Crypto-conditions use the following types within string encoding:
- - -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.
-Below are the string and binary encoding formats for a condition.
-Conditions are ASCII encoded as:
--"cc:" BASE16(type) ":" BASE16(featureBitmask) ":" - BASE64URL(fingerprint) ":" BASE10(maxFulfillmentLength) --
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)
-
-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. | -
Below are the string and binary encoding formats for a fulfillment.
-Fulfillments are ASCII encoded as:
--"cf:" BASE16(type) ":" BASE64URL(payload) --
Fulfillments are binary encoded as:
-
-Fulfillment ::= SEQUENCE {
- type ConditionType,
- payload OCTET STRING
-}
-
-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. | -
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. | -
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 [crypto-conditions-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:
--cc:0:3:dB-8fb14MdO75Brp_Pvh4d7ganckilrRl13RS_UmrXA:66 --
Example fulfillment:
--cf:0:VGhlIG9ubHkgYmFzaXMgZm9yIGdvb2QgU29jaWV0eSBpcyB1bmxpbWl0ZWQgY3JlZGl0LuKAlE9zY2FyIFdpbGRl --
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:
-
-PrefixSha256FingerprintContents ::= SEQUENCE {
- prefix OCTET STRING,
- condition Condition
-}
-
-
-
-
-PrefixSha256FulfillmentPayload ::= SEQUENCE {
- prefix OCTET STRING,
- subfulfillment Fulfillment
-}
-
-
-
-Example condition:
--cc:1:25:7myveZs3EaZMMuez-3kq6u69BDNYMYRMi_VF9yIuFLc:102 --
Example fulfillment:
--cf:1:DUhlbGxvIFdvcmxkISAABGDsFyuTrV5WO_STLHDhJFA0w1Rn7y79TWTr-BloNGfiv7YikfrZQy-PKYucSkiV2-KT9v_aGmja3wzN719HoMchKl_qPNqXo_TAPqny6Kwc7IalHUUhJ6vboJ0bbzMcBwo --
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:
-
-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.
- - -
-ThresholdSha256FulfillmentPayload ::= SEQUENCE {
- threshold INTEGER (0..4294967295),
- subfulfillments SEQUENCE OF ThresholdSubfulfillment
-}
-
-ThresholdSubfulfillment ::= SEQUENCE {
- weight INTEGER (0..4294967295) DEFAULT 1,
- condition Condition OPTIONAL,
- fulfillment Fulfillment OPTIONAL
-}
-
-
-
-Example condition:
--cc:2:2b:mJUaGKCuF5n-3tfXM2U81VYtHbX-N8MP6kz8R-ASwNQ:146 --
Example fulfillment:
--cf:2:AQEBAgEBAwAAAAABAQAnAAQBICDsFyuTrV5WO_STLHDhJFA0w1Rn7y79TWTr-BloNGfivwFg --
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
-}
-
-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))
-}
-
-
-
-
-RsaSha256FulfillmentPayload ::= SEQUENCE {
- modulus OCTET STRING (SIZE(128..512)),
- signature OCTET STRING (SIZE(128..512))
-}
-
-
-
-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 [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.
-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 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.
-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.
-
-Ed25519FulfillmentPayload ::= SEQUENCE {
- publicKey OCTET STRING (SIZE(32)),
- signature OCTET STRING (SIZE(64))
-}
-
-
-
-Example condition:
--cc:4:20:7Bcrk61eVjv0kyxw4SRQNMNUZ-8u_U1k6_gZaDRn4r8:96 --
Example fulfillment:
--cf:4:7Bcrk61eVjv0kyxw4SRQNMNUZ-8u_U1k6_gZaDRn4r-2IpH62UMvjymLnEpIldvik_b_2hpo2t8Mze9fR6DHISpf6jzal6P0wD6p8uisHOyGpR1FISer26CdG28zHAcK --
| - [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. | -
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
-
---<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
-
-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 | -
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, -94104
- 94104
- 94104
- 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: - + 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 - +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 @@ - -⇒ Promise.<[LedgerInfo](#class-ledgerinfo)> |
-| | [**getPrefix**](#getprefix) ( ) `⇒ Promise⇒ [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 `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.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.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:
+
+[](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.
+
+
+
+### 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 @@
+
\ 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:
+
+
+
+* 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.