|
|
Protocol /
Ripple Distributed Protocol v0.5A protocol for establishing mutual credit accounts between users on different servers, and transmitting value through the resulting credit network in real-time. Contents0. Definitions 1. Introduction 0. DefinitionsTerms
Cryptographic Algorithms
1. IntroductionRipple is a protocol for transmitting value through a credit network formed by mutual credit accounts between "nodes". The protocol consists of several parts:
Ripple servers can pass messages directly to each other over TCP connections, secured by TLS. Messages can also be encrypted to a node on an unknown server and relayed there via one or more intermediaries using the Relay message. Broadcast messages are passed across the data network between servers: the Ripple makes heavy use of public key cryptography, and public keys are used to identify nodes, transactions, and other entities in the network. To distribute and manage these keys, Ripple has a basic public key infrastructure consisting of Ripple servers can declare the existence of nodes to other servers, and then establish lines of credit with nodes on other servers. They can then pass and request IOUs over established lines of credit. Servers can then broadcast details of their nodes' lines of credit across the network so they can be used in multi-hop transactions in which a series of connected of nodes, called a path or chain of intermediaries, is asked to pass forward IOUs on specific lines of credit simultaneously in order to transfer value between two nodes that may not have a line of credit between them. The Ripple credit network consists of nodes and lines of credit between them, forming a directed graph of potential value flow. A node is represents an exchange hub that can transmit value from any of its incoming lines of credit to any of its outgoing lines of credit. In general, a Ripple user will have multiple nodes. Arbitrary exchanges between a user's connections can be accomplished by assigning a separate node for each connection and a line of credit for each exchange that the user wishes to perform. Multi-hop transactions use a two-phase commit protocol. The originating node, called the payer, determines a suitable path through the credit network connecting it and the destination node, called the recipient. The payer informs the recipient of the payment. If the recipient approves, the payer begins a two-phase commit by sending a commit-ready message called a At every step, the protocol ensures that messages committing entities to particular actions are signed by their identifying public keys, so they may be later held to those commitments. 2. Message TransportRipple messages are passed over TCP connections, secured by TLS. Connected Ripple servers are considered peers in that they can each pass the same set of messages to each other, as opposed to client and server, where one sends requests and the other responds. For efficiency, connections should preferably be long-lasting, rather than set up for each message and then closed immediately. Messages and their responses are passed asynchronously, meaning that multiple requests may be sent by one server on the same connection before any potential responses to those requests are received back, and the responses may come in any order, so that the responding server may take advantage of its parallel processing capabilities. Servers may open multiple TCP connections with each other if head-of-line blocking becomes a bottleneck for messages on a single connection (or for any other reason). TODO: Define default port for Ripple servers. In the future, SCTP has desirable features as a Ripple transport layer, once it becomes more commonplace and easy to use in various networking frameworks and hosting arrangements. FramingSince TCP is a byte-stream protocol, Ripple messages must be framed to preserve message boundaries. Ripple's framing semantics are inspired by the BEEP and SPDY protocols. Each frame is defined by: +-----+------+---+-------------------------+ | Ver | Type | M | Length (24 bits) | +-----+------+---+-------------------------+ | Message Number (32 bits) | +------------------------------------------+ | Data | | ... | +------------------------------------------+
Each message can be broken up into multiple frames, so high-priority messages do not need to wait in line while long low-priority messages are being transferred, but rather can be sent in between frames of long messages. Therefore it is recommended to break messages into relatively short frames that can be transferred nearly instantaneously. Each
3. Message FormatRipple messages are defined using Google's Protocol Buffers mechanism. Fields of type Each Ripple message consists of a
message RippleMessage {
required Header header = 1;
optional bytes body = 2;
repeated Signature signatures = 3;
optional bytes proof_of_work = 4;
}
TODO: Add hop-limit or valid-until for routing broadcasts? Signatures are to validate the header and body sections; any other signatures and
Header
enum MessageType {
TIME = 0;
RELAY = 1;
INVENTORY = 2;
INVENTORY_REQUEST = 3;
NODE = 10;
CONNECT = 11;
IOU = 12;
KEY_CERTIFICATE = 20;
KEY_REVOCATION = 21;
EXCHANGE_RATE = 22;
ATOMICITY_FEE_SET = 23;
CREDIT = 24;
CREDIT_CHECK = 30;
PAYMENT_INIT = 31;
PAYMENT_ACCEPT = 32;
PROMISE = 33;
PROMISE_RELEASE = 34;
COMMIT = 35;
PROMISE_RENEW = 36;
STATUS_QUERY = 40;
STATUS = 41;
ERROR = 100;
}
message Header {
required MessageType type = 1;
required string version = 2;
required double time = 3;
optional bytes message_id = 4;
optional bytes to_key_id = 5;
optional string to_alias = 6;
optional bytes from_key_id = 7;
optional string from_alias = 8;
}
Time FormatTimes are given as 64-bit floating point values (ie, Protocol Buffer Public Key Format
message PublicKey {
required bytes modulus = 1;
}
Ripple uses RSA keys of any length The canonical hash identifier of a key is the result of SHA-256 applied to the modulus. IdentifiersThere are two types of identifiers: 1. Entities associated with a public key are identified by the canonical hash of the key. Fields containing such identifiers usually end in 2. Entities not associated with a public key are identified by a 16-byte UUID generated by any method (Protocol Buffers Signatures
message Signature {
optional bytes signer_key_id = 1;
required bytes signature = 2;
}
Encrypted Messages
message EncryptedMessage {
required bytes recipient_key_id = 1;
required bytes encrypted_key = 2;
required bytes ciphertext = 3;
}
To encrypt a Decimal NumbersDecimal numbers (ie, monetary amounts), are encoded as Protocol Buffer ASCII/UTF-8 strings to avoid rounding error introduced by binary encoding. A period '.' is used as the decimal separator, and negative numbers are prefixed with '-'. When rounding is necessary, the round half to even method must be used. 4. Core MessagesTimeAllows connected servers to discover what time the other thinks it is (before transmission delay), so they can compensate appropriately when performing time-sensitive actions involving the other server. A server can send a Time message at any time during the connection. The other server should respond immediately with its own Time message. Time has no message body. The actual time is contained in the header. Relay
message Relay {
required EncryptedMessage message = 1;
required hop_limit = 2;
}
Message encrypted to destination node to pass on to Inventory
message InventoryItem {
required bytes source = 1;
required bytes message_id = 2;
required MessageType type = 3;
}
message Inventory {
repeated InventoryItem items = 1;
}
Broadcast to let neighbouring servers know about new broadcast messages. Another server would be considered a neighbour if one of its nodes is given a credit limit by one of a server's local nodes. When two servers first become neighbours, the server sending the initial Connect message should also send the first Inventory message. Then the other server need only send an Inventory containing Inventory Request
message InventoryRequest {
repeated InventoryItem items = 1;
}
Request to receive the listed broadcast messages. The requested messages should then be sent individually as separate messages. 5. Account MessagesNodeTODO: Maybe allow extra keys for signing some types of messages (like Credit?) to enable servers only partial control over a node (can't issue any extra credit, for example)? Maybe a different key for broadcast messages than for neighbour messages?
message Node {
optional string host = 1;
}
Let another server know about a node that wishes to establish a line of credit with one of its nodes. The node's identifying key is given by
Connect
message Connect {
required bytes line_of_credit_id = 1;
optional bytes linked_line_of_credit_id = 2;
required uint32 precision = 3;
required uint32 scale = 4;
optional string units = 5;
optional string credit_offered = 6;
optional string iou_offered = 7;
optional string note = 8;
optional bytes proof_of_id = 9;
optional bytes old_node_id = 10;
}
Indicates a desire for the sending node to connect and register a line of credit with the receiving node. Must be signed by sender. The receiving node would eventually send back its own Lines of credit only transfer value in one direction, with the node initiating the connection accepting value from the node receiving the connection invitation. Two lines of credit can be linked together to form a single bidirectional mutual credit account with a single balance, by setting Nodes must agree on how numeric amount values for the line of credit will be stored, so each can maintain identical records of the account. In particular, messages refering to this line of credit may contain values that go beyond the precision and scale specified, and these values must be rounded when being considered.
To move a line of credit to a different node, send another IOU
message IOU {
required bytes iou_id = 1;
required bytes line_of_credit_id = 2;
required string amount = 3;
optional bytes transaction_key_id = 4;
optional string memo = 5;
}
Signed by sender, indicates change in a line of credit's mutual credit account balance by If an IOU is a consequence of a routed transaction, the If 6. Broadcast MessagesAll broadcast messages must be signed by their source's identifying key. Key Certificate
message KeyCertificate {
required bytes key_id = 1;
required PublicKey key = 2;
optional double valid_from = 3;
optional double valid_until = 4;
optional bytes revocation_hash = 5;
optional bytes supersedes_key_id = 6;
}
Announces a public key and its validity period. If a previously-announced key ID is given in Must be signed by the given key, and the key that it supersedes, if any. Key Revocation
message KeyRevocation {
required bytes revoked_key_id = 1;
required KeyCertificate revocation_key = 2;
required bytes salt = 3;
optional string reason = 4;
optional double compromised_since = 5;
optional KeyCertificate replacement_key = 6;
}
Used in an emergency to revoke a compromised key using a secret revocation key. For example, if a node's private identity key is compromised and used to move the node to another host with a new identity key, a revocation of the old key indicating that it was compromised prior to the move would notify its partners that the new identity is not legitimate and to halt all dealings until the situation is sorted out. To be eligible for revocation, a The purpose of the Must be signed by the revocation key. Node (Broadcast)Node messages may also be broadcast. The broadcast form is identical to the local form used when creating lines of credit. A broadcast Node message is distinguishable from a local Node message, and its meaning the sending server wishes to use the node to connect to a node on the receiving server, by the fact that it is announced by an Inventory message, and received in response to an InventoryRequest. Exchange Rate
message ExchangeRate {
required bytes rate_key_id = 1;
required string rate = 2;
optional string in_units = 3;
optional string out_units = 4;
optional string description = 5;
optional double valid_until = 6;
}
Signed by the key whose ID is Atomicity Fee Set
enum AtomicityMode = {
BARE = 0;
}
message AtomicityFeeSet {
required bytes atomicity_fee_set_key_id = 1;
message AtomicityFee {
required AtomicityMode atomicity_mode = 1;
optional string atomicity_rate = 2 [default = "1"];
optional string atomicity_flat_fee = 3 [default = "0"];
}
repeated AtomicityFee atomicity_fees = 2;
}
Allows nodes to define a set of conversion factors and flat fees to be applied to incoming transactions on a line of credit, depending on the type of atomicity offered by the transaction. This document only defines a single atomicity mode CreditTODO: - Advertise precision/scale of LoC?
message Credit {
required bytes partner_node_key_id = 1;
required bytes line_of_credit_id = 2;
enum Direction = {
IN = 0;
OUT = 1;
}
required Direction direction = 3;
message CreditChunk {
required uint32 chunk_id = 1;
optional string amount = 2;
optional string exchange_rate = 3 [default = '1'];
optional bytes exchange_rate_key_id = 4;
optional bytes atomicity_fee_set_key_id = 5;
}
repeated CreditChunk chunks = 4;
}
Broadcast credit advertisement. Signed by A node may advertise multiple "chunks" on the same line of credit, in order to, for example, offer a better exchange rate for a portion of credit available in order motivate its use. Exchange rates are set at either end unilaterally, so both partners need not agree on chunks.
Credit amounts set by each partner do not have to agree. When they do not, the lower of the two amounts prevails. Each node has a conceptual base unit, which line-of-credit (LoC) values can be converted to and from:
Here, IN rate is calculated by multiplying A node is a conceptual entity that performs exchanges between all of its incoming lines of credit and all of its outgoing lines of credit. To indicate that some some exchanges are not performed, use multiple nodes. For efficiency, nodes may not wish to advertise the exact credit limits, but rather some other lower number that will accomodate enough transactions to be useful, but will not need frequent updating. This behaviour should generate fewer broadcast messages. Payers can still potentially get approval for larger transactions by making Credit Checks prior to initiating payment. 7. Transaction MessagesCredit Check
message Transfer {
required bytes line_of_credit_id = 1;
required string amount = 2;
optional uint32 chunk_id = 3;
optional EncryptedMessage onion_forward = 4;
}
message Exchange {
repeated Transfer in_transfers = 1;
repeated Transfer out_transfers = 2;
optional bool wait_for_merge = 3;
optional bytes forward_to_node_key_id = 4;
optional string forward_to_host = 5;
}
message CreditCheck {
required bytes check_id = 1;
required PublicKey checker_key = 2;
optional EncryptedMessage exchange_onion = 3;
message CheckResponse {
required bytes node_key_id = 1;
required bool transaction_possible = 2;
}
repeated EncryptedMessage responses = 4;
}
Used to inquire from one or more nodes whether a particular transaction is possible at the moment. The response to a credit check is a boolean When a propagating credit check branches and then merges again, only one branch needs to carry responses from the branch point and before, and only one branch needs to contain
Payment Init
message PaymentInit {
optional bytes request_id = 1;
optional bytes transaction_key_id = 2;
required string amount = 3;
optional string units = 4;
optional string memo = 5;
optional bytes proof_of_id = 6;
optional bytes data = 7;
}
Sent by either payer or recipient to the other payment endpoint to initiate or request payment, respectively. To request payment, the recipient specifies a
Payment Accept
message PaymentAccept {
required bytes transaction_key_id = 1;
required bytes commit_key_id = 2;
required PublicKey commit_key = 3;
required PaymentInit payment_init = 4;
optional string commit_url = 5;
optional bytes proof_of_id = 6;
}
Returned by the recipient in response to a payment init, if it chooses to accept the payment. Must be signed by the recipient. The PaymentAccept combined with the Commit will form proof of payment for the payer.
Promise
message Promise {
required bytes transaction_key_id = 1;
required PublicKey transaction_key = 2;
required bytes commit_key_id = 3;
required PublicKey commit_key = 4;
required bytes line_of_credit_id = 5;
required string amount = 6;
required double expiry = 7;
optional bytes exchange_onion = 8;
optional commit_url = 9;
}
TODO: Ensure promises are valid long enough to have a chance of committing the transaction?
A signed promise to pass forward an IOU of value
Promise Release
message PromiseRelease {
required bytes transaction_key_id = 1;
}
A promise holder may release the promisor of its promise by sending a Commit
message Commit {
required bytes commit_key_id = 1;
required Signature commit_signature = 2;
}
Commit is generated by the recipient and used to trigger promises back through the intermediaries to the payer. A promise holder redeems the promise its holds with its neighbour by sending a Commit message with the same In To help propagate the Commit in case of an intermediary server outage, the recipient should publish the Commit at the
This behaviour is difficult or impossible to verify or enforce, therefore Status Query
message StatusQuery {
required bytes transaction_id = 1;
}
The payer uses this to request information about the current state of the transaction from another participant. Must be signed by the Status
message Status {
required bytes transaction_id = 1;
message StatusChunk {
required bytes line_of_credit_id = 1;
optional bytes chunk_id = 2;
enum StatusCode {
AWAITING_PROMISE = 0;
PROMISE_RECEIVED = 1;
PROMISE_RECEIVED_REJECTED = 2;
PROMISE_SENT = 3;
PROMISE_SENT_REJECTED = 4;
PROMISE_RELEASED = 5;
PROMISE_EXPIRED = 6;
COMMIT_RECEIVED = 10;
COMMIT_SENT = 11;
}
repeated StatusCode codes = 3;
}
repeated StatusChunk chunks = 2;
}
8. Error MessagesError
message Error {
enum ErrorCode {
// TODO: List all error codes here.
}
optional ErrorCode code = 1;
optional string message = 2;
}
|