Ripple Protocol Specification 0.1

Ryan Fugger
rafspam (at) yahoo.ca
September 2004

Warning: Under Construction! (Are there any XML wizards out there?)

Contents

Overview

Network Structure
Security
Root URL
Units

Accounts
Creating an Account
Modifying Accounts
Correcting Inconsistencies Between Nodes
Acquiring Root URLs
UUID Privacy
Digital Signatures

Transactions

Unit Conversion

Errors
HTTP Error Codes

Overview

Ripple is a currency and payment system within a decentralized peer-to-peer trust network. A trust relationship or connection in Ripple is an agreement to accept IOUs up to a certain limit from another party.

Ripple keeps track of the IOU balances between trusted parties and uses these private credit arrangements to enable payments between untrusted strangers without the recipient having to rely on the payer's or any other untrusted party's creditworthiness. It does this by finding a chain of intermediary nodes connecting the payer to the recipient, and propagating IOUs along that chain. Once a payment is completed, the payer actually owes the first link in the chain, and the recipient is owed by the last link.

The Ripple protocol specifies how nodes on different servers communicate with each other to establish trust relationships founded on mutual credit and participate in payments through paths of trusted intermediaries.

Network Structure

Users interact with the Ripple network through software agents called nodes. Ripple nodes live at unique URLs on secure HTTP servers and communicate by HTTP POST and GET of XML documents to and from those URLs. A server may host any number of nodes. The Ripple protocol defines how nodes hosted on different servers communicate with each other.

Root URL

A node's home URL is called its root URL, from which an XML root document containing information about that node can be retrieved using HTTP GET. The root URL point to a location on the node's server, and is where messages for the node will be POSTed by other nodes.

<?xml version="1.0"?>

<node id="e679e1a0-f719-11d8-9669-0800200c9a66"
      root="http://myhost.org/ripple/rfugger/">
  <owner>
    <name>Ryan Fugger</name>
  </owner>
  <server>
    <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      ...
    </KeyInfo>
  </server>
  <units-accepted>
    <units uri="urn:ripple:units:CAD"/>
    <units uri="urn:ripple:units:USD"/>
    <units uri="urn:ripple:units:hour"/>
    <units uri="urn:ripple:units:gAu"/>
    <units uri="urn:ripple:units:ozAu"/>
  </units-accepted>
  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    ...
  <Signature/>
</node> 

The node has a permanent UUID identifier given at the id attribute of the node element, which is preferred for identifying that node, since URLs can change. The root attribute gives the root URL of the node.

The owner element is optional.

The server element gives the server's SSL/TLS certificate, or at least the subject key identifier (fingerprint), using the XML Signature specification's KeyInfo element. This is optional but recommended in conjunction with the Signature element to enable self-signed server certificates.

The Signature element, also from the XML Signature specification, gives the owner's digital signature of the entire document, using the enveloped signature transform to embed itself. Optional.

Units

The <units-accepted> element lets other nodes know which units they can use to denominate payments to this node. Units are identified by URIs. Standard units are prefixed with urn:ripple:units: followed by a three-letter ISO-4217 currency code, or one of the following:

Clearly, if a node accepts grams of gold, it should accept ounces of gold and perform the conversion transparently. The same goes for energy units: joules, kJ, and kWh.

Communities are free to invent their own units.

In future, more elements may be added to the root document to allow a node to specify other policies, such as requiring a certain type of payer identity authentication before accepting payment.

Security

Ripple uses HTTPS. Nodes authenticate one another indirectly by authenticating each other's server with SSL/TLS. SSL/TLS also provides encryption for communications between nodes.

The first time two Ripple servers communicate, they exchange public key certificates as part of client/server authentication in the SSL/TLS protocol, and store each other's certificates for future authentication. It is up to the individual server to decide whether to accept a particular certificate as valid or not.

The first time a Ripple server receives a request from a particular foreign node, it records that node's server, and verifies that it matches that node's root URL. If another server later claims to be hosting that same node, its messages can be rejected with an appropriate error message.

UUID Privacy

A UUID may contain the MAC address of the machine that created it, or information that may be used as an identifying characteristic. For extra privacy, implementations may use random generation (version 4) of UUIDs.

Accounts

To participate in Ripple payments, a node must either accept IOUs from another node, have another node accept its IOUs, or both. In other words, it must either grant credit to another node, have credit granted to it, or both. When two nodes connect in such a way, they create a mutual credit account that keeps track of the IOU balance between the two nodes. Account data, including currency units, maximum amount of credit granted and accepted by either node, and the current IOU balance, is stored locally at both nodes.

Creating an Account

To begin creating an account, one node must formally offer credit to another by POSTing the offer as an XML document to the other node's root URL.

POST http://appleby.org/ripple/alice/ HTTP/1.1
Host: appleby.org
Content-Type: text/xml
Content-Length: 924 <?xml version="1.0" encoding="ISO-8859-1"?> <account-message id="897a3e43-152b-4620-b526-0e69b5abd901" xmlns="urn:ripple:0_1"> <header> <to> <node id="58f202ac-22cf-11d1-b12d-002035b29092"/> </to> <from> <node id="e679e1a0-f719-11d8-9669-0800200c9a66"/> </from> <action>create</action> <note>Hi Alice. I'm offering you $100 credit like we talked about.</note> </header> <account id="a52b27e0-f3c7-11d8-9669-0800200c9a66" url="http://myhost.org/ripple/rfugger/a52b27e0/"> <units uri="urn:ripple:units:CAD"/> <node id="e679e1a0-f719-11d8-9669-0800200c9a66" root="http://myhost.org/ripple/rfugger/"> <owner> <name>Ryan Fugger</name> </owner> <IOU-accept-limit>100.00</IOU-accept-limit> <IOU-issue-limit>100.00</IOU-issue-limit> <routing> <hint>Canada</hint> <hint>British Columbia</hint> <hint>Vancouver</hint> <hint>Commercial Drive</hint> <hint>Kelowna</hint> </routing> </node> </account> </account-message>

In this example, Ryan is offering Alice $100 credit by POSTing to her node's root URL, which happens to be http://appleby.org/ripple/alice/.

The account element represents the account to be created. The id attribute identifies the account at both nodes and the url attribute is where the originating node's version of the account's data may be found.

The units element is the denomination the balance and credit limits will be stored as.

The node element gives information about the Ripple node making the offer of credit.

The note element can be used to attach text to any Ripple XML message.

Deciding Whether to Accept a Credit Offer

Next, the owner of the node that received the credit offer must decide to either accept or reject the offer. Ripple does not specify a particular method of authenticating the identity of the owner of the node making the credit offer. One way is for the offerer to sign the account-creation message with his personal key (see Digital Signatures, below). Another is for the receiving node's owner to talk to the person supposedly making an offer of credit to verify the offer.

To reject the offer, she POSTs a rejection message to the root URL of the offering node:

POST /ripple/rfugger/ HTTP/1.1
Host: myhost.org
Content-Type: text/xml
Content-Length: 262 <?xml version="1.0" encoding="ISO-8859-1"?> <account-message action="reject" id="d2ecf4f5-20c8-4152-9cbb-c1ae2c0d5972"> <account id="a52b27e0-f3c7-11d8-9669-0800200c9a66"/> <source> <node id="58f202ac-22cf-11d1-b12d-002035b29092"/> </source> <reason>No Thanks.</reason> </account-reject>

To accept, she POSTs:

POST /ripple/rfugger/ HTTP/1.1
Host: myhost.org
Content-Type: text/xml
Content-Length: 564 <?xml version="1.0" encoding="ISO-8859-1"?> <account-accept id="1b84691b-10c0-4938-b58e-55c9bdb322e8"> <account id="a52b27e0-f3c7-11d8-9669-0800200c9a66" > <node id="58f202ac-22cf-11d1-b12d-002035b29092" root="http://appleby.org/ripple/alice/"> <owner>Alice Appleby</owner> <IOU-accept-limit>150.00</IOU-accept-limit> <routing> <hint>USA</hint> <hint>Washington</hint> <hint>Seattle</hint> <hint>58f202ac-22cf-11d1-b12d-002035b29092</hint> </routing> </node> </account> <source> <node id="58f202ac-22cf-11d1-b12d-002035b29092"/> </source> </account-accept>

Here Alice has provided a symmetrical set of account information for her node.

Note that the reciprocal offer of credit is optional. Alice could very well reply with an IOU-accept-limit of 0.00 if she didn't wish to accept any of Ryan's IOUs. An account can function normally with credit only going one way (this is what a normal bank account is: IOUs I accept from the bank).

Also note that one of Alice's node's keywords is her node ID, which is unique to her node and not likely to be used by any of her neighbours.

At this point, the basic account information is known to both nodes and the account is usable.

Modifying Accounts

If the Ryan did not wish to accept Alice's full offer of $150 credit, but wished to limit his debt with Alice to $50, he could respond with:

POST /ripple/69240/ HTTP/1.1
Host: appleby.org
Content-Type: text/xml
Content-Length: 362 <?xml version="1.0" encoding="ISO-8859-1"?> <account-modify id="03089206-27ef-4e8c-ade6-ecfe45b0414c"> <account id="a52b27e0-f3c7-11d8-9669-0800200c9a66"> <node id="e679e1a0-f719-11d8-9669-0800200c9a66"> <IOU-issue-limit>50.00</IOU-issue-limit> </node> </account> <source> <node id="e679e1a0-f719-11d8-9669-0800200c9a66"/> </source> </account-modify>

State of the Account

The state of the account could be described as the following XML:

<account id="a52b27e0-f3c7-11d8-9669-0800200c9a66">
  <units uri="urn:ripple:units:CAD"/>
  <node id="e679e1a0-f719-11d8-9669-0800200c9a66"
        root="http://myhost.org/ripple/rfugger/">
    <owner>Ryan Fugger</owner>
    <IOU-accept-limit>100.00</IOU-accept-limit>
    <IOU-issue-limit>50.00</IOU-issue-limit>
    <balance>0.00</balance>
  </node>
  <node id="58f202ac-22cf-11d1-b12d-002035b29092"
        root="http://otherhost.org/ripple/69240/">
    <owner>Alice Appleby</owner>
    <IOU-accept-limit>150.00</IOU-accept-limit>
  </node>
</account>

If either IOU-accept-limit or IOU-issue-limit is absent from either node element, it is assumed to be unlimited.

The balance can be given at either node. If Ryan owed Alice $22, the balance would be given as either -22.00 at Ryan's node, or +22.00 at Alice's. With the credit limits in this example, Alice's balance could be anywhere from -100 to 50, and Ryan's between -50 and 100.

Invoking an HTTP GET on the account URL at one of the two nodes by the other returns this full "state-of-the-account" document.

To make modifications to the account, POST to the other node's account URL the account XML piece to be modified with the attribute action="modify". Note that you can only change the information inside your own node's element.

It is perfectly acceptable, thanks to the wonders of XML, to post redundant or unchanged information. For example, every message in this section could consist of the entire account XML known up to that point, instead of just being the new, changed information. This might even be helpful to continuously verify that the data is correct.

Accessing an Account Using Different Units

The parties to an account might want to allow their account to be used in transactions involving different units. The example account in this section is denominated in Canadian dollars and so Ryan and Alice can participate in CAD transactions. They may also with to participate in US dollar transactions, by agreeing on

To set a multiplier for converting to USD:

<account-modify id="03089206-27ef-4e8c-ade6-ecfe45b0414c">
  <account id="a52b27e0-f3c7-11d8-9669-0800200c9a66">
    <units uri="urn:ripple:units:CAD">
      <convert-to>
        <units uri="urn:ripple:units:USD">
        <multiplier href="http://currencyxchange.net/CADUSD/"/>
      </convert-to>
    </units>
  </account>
</account-modify>

The multiplier element gives a URL in the href attribute that holds a plain text representation of the current value of the Canadian dollar is USD, accessible by HTTP GET. (This service could be provided by a Ripple server.) The multiplier can also be given as a number, for example:

<units uri="urn:ripple:units:ozAu">
  <convert-to>
    <units uri="urn:ripple:units:gAu">
    <multiplier>28.35</multiplier>
  </convert-to>
</units>

This is more useful when the conversion rate is constant.

Correcting Inconsistencies Between Nodes

Should the account data become inconsistent between nodes, due to technical error or human disagreement (where one node owner decides to unilaterally change the balance, say, if his software allows it). In most cases, where there is disagreement on a numerical value (credit offered, accepted, or balance), the account can continue to function normally, except that occasionally a transaction may be rejected by the other node for stepping over its version of the limits.

In the case of technical error, it would be desirable to enable the nodes' software be collaborate on suggesting a solution. See the Error Handling Section below.

Acquiring Root URLs

The question remains, how does the first node get the other node's root URL to make the original offer of credit and get the account creation process started? Ideally, the user would be able to click on a link on a website or in an email and have their Ripple client automatically begin creating the account. Otherwise, the user could copy and paste the root URL into their Ripple client, or save and import the other node's entire root document XML.

In any case, Ripple does not have a mechanism for discovering someone's node. They must explicitly tell you. Ripple is not meant for making new friends, it's for increasing the usefulness of the trust in existing friendships. If you want to connect with a friend, email them, or better yet, phone them or ask them in person.

Digitally signing the root document helps ensure its authenticity.

Digital Signatures

The XML Digital Signature syntax (http://www.w3.org/TR/xmldsig-core/) allows XML documents to be signed without upsetting the processing of the rest of the document. This, along with XML Key Management (http://www.w3.org/TR/xkms2/), allows Ripple clients to integrate digital identification and authentication schemes such as PGP and X.509 quite easily. All human-attended communication stemming from a node could be signed by the owner's personal key stored on a different machine from the node software.

This signing is not required, but recommended. For simplicity, signatures should be of the "enveloped" type (signing the entire XML document by placing the Signature element inside the root element and specifying the Enveloped Signature Transform):

http://www.w3.org/TR/xmldsig-core/#sec-EnvelopedSignature

Transactions

The Ripple payment process has four stages: initialization between the payer and recipient, searching for paths of trusted intermediaries between the payer and recipient, the freezing of the credit along paths once found, and passing of IOUs along those paths.

This section will deal with transactions involving only one type of currency units. For an example of how Ripple deals with unit conversion, see the next section.

Initialization

To begin a transaction, the payer must first acquire the recipient's root URL and then POST an initialization message:

<transaction id="f0a393a0-fdf0-11d8-9669-0800200c9a66"
             action="init"
             xmlns="urn:ripple:01:transaction">
    <recv>
        <units uri="urn:ripple:units:CAD"/>
        <amount>20.00</amount>
    </recv>
    <payer>
        <node id="e679e1a0-f719-11d8-9669-0800200c9a66"
              root="http://myhost.org/ripple/rfugger/">
            <transaction-URL>transactions/f0a393a0-fdf0-11d8-9669-0800200c9a66/</transaction-URL>
        </node>
        <name>Ryan Fugger</name>
        <email>ryan@fugger.org</email>
    </payer>
    <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyValue>
<DSAKeyValue>
<P>imW6KYBPYXAf6itSAuYs1aLPfs8/vBEiusv/pl1XMiuMvB7vyiJgSj8/NT
X/rVXv8rbCRjvYFX3x5/53f4hc6HKz7JQI4qqB7Fl5N86zp+BsQxNQ4tzous9S2H
Td2/zdTwVsvO+H9l3FahmVp/m2IHE4W27JYoF49qP10=</P>
<Q>v/xzWqjRviekk2rMW3wpYspT9Us=</Q>
<G>UIyzUDlLe6uCCgF4Rh98fiKZvg64UJ4FM5L+WbCSMmVsFN06fTwxy3naPPOCzzou fsHv/Bve2gvrDvd078oXWJJf9A44pIZnJkdjEhm2RsDFpXNq0tPKZFcjVsdmqg4M
X6YNuwpvZuTwSoDG5u1QMN0mmH9gmbIT3j9x4MO+7EY=</G>
<Y>On+KBJE3q1TRhG9RspNX01VI5C0VzSy4N/QyC4YzEENoq3GJkKHIYq+grq9ZqV9x
g2Geo/3mqhdcENOtYRmWEfOZJj18oukD6TNceYRZ4HjHjK3WY3wK2OV6QOly+k3f
xgEQpP/7IlCka5YICLuHXrbqjn5b0XcK9L2GDtWOyjs=</Y>
</DSAKeyValue>
</KeyValue>
</KeyInfo> <routing> <hint-sha1>3jB0TfWiCox09KMBZqANNQRlnrI=</hint-sha1> <hint-sha1>zCPpNAjt1WiHKGyubF6hDoOxCTM=</hint-sha1> <hint-sha1>b+eNIcx387EAhR5I2JgLrB/zCNE=</hint-sha1> <hint-sha1>5AGftpCgLuAyUo+yoN+JNAMFpwU=</hint-sha1> </routing> <note>Order no.8204</note> </transaction>

Some explanation is necessary here. Like other Ripple objects, each transaction has a UUID. The recv element specifies the units and the amount to be received at the recipient's end of the transaction. (There is a related pay element that similarly specifies the amount to be paid at the originating end which will be discussed in the next section.)

The KeyInfo element is from the XML Digital Signature specification (http://www.w3.org/TR/xmldsig-core/), and is the key to be used to authenticate the commit or rollback message that ends the transaction. This can be generated anew for each transaction, or reused so long as the private key remains secret. Any kind of key supported by the XML-DSIG specification will do, although for the sake of implementation simplicity, everyone might as well just use DSA keys. It does not have to be the payer's personal key.

The routing element lists hint keywords or their hashes which are used by potential intermediary nodes to help decide where to route path-search queries. See below for more on keywords and searching. (The hint keywords in this example are "Canada", "British Columbia", "Vancouver", and "http://myhost.org/ripple/rfugger/" converted to uppercase and SHA-1 hashed.)

The payer element conveys information about the payer to the recipient, especially the payer's root URL and the transaction-URL to be used for communication regarding this transaction. The payer's node element is required, the name and email optional. The note is for any other information relevant to the transaction.

The recipient's node software automatically (without human input) decides to accept or reject the payment. Much like the replies to an offer of credit, the rejection goes:

<transaction id="f0a393a0-fdf0-11d8-9669-0800200c9a66"
             action="reject"
             xmlns="urn:ripple:01:transaction"/>

And the acceptance goes:

<transaction id="f0a393a0-fdf0-11d8-9669-0800200c9a66"
             action="accept"
             xmlns="urn:ripple:01:transaction">
    <recipient>
        <node id="c6170b02-408d-11d6-94dd-1168a31df780"
              root="http://recipient.org/adler/">
            <transaction-URL>tx/f0a393a0-fdf0-11d8-9669-0800200c9a66/</transaction-URL>
        </node>
    </recipient>
</transaction>

 

******** Under construction...

Routing Hint Keywords

******* how do nodes discover their neighbours' keywords?

Authenticating the Transaction Commit or Rollback Message

 

Unit Conversion

Error Handling

**** Give reasons for rejecting accounts & transactions.

SSL/TLS Errors

HTTP Error Codes

Other Considerations

Spam

The note element used in both credit offers and payments might be abused by spammers. It might be necessary to remove it in future versions of the protocol, and reject any messages containing a note element.

Payment Priority

Integrating Ripple Into E-Commerce