Skip to content. | Skip to navigation

Personal tools


You are here: Home / Members / jhb / the original protocol

the original protocol

This is the opencoin protocol in its written form. The current python library uses a sligtly modified version

Plain Text icon protocol.txt — Plain Text, 15 kB (16121 bytes)

File contents

OpenCoin Project                                            N. Toedtmann                                         J. H. Baach
Category: Draft                                                 M. Ryden

                      OpenCoin Formats and Protocol

Status of this Memo

   This draft is work in heavy progress. Do not consider it's content
   stable in any sense as long as this note is present. Get in touch
   with [1] and fetch a recent copy [2].

Copyright Notice

   Copyright (c) N. Toedtmann, J. H. Baach, M. Ryden (2008).


   This document describes the OpenCoin protocol which seeks to
   implement David Chaum's concept of "untraceable payments" [3].


   - licence of this document (GNU FDL, CC-BY-SA, Public Domain)?
   - "Introduction", including scope of protocol
   - JSON, 7-bit ASCII
   - define token/certificate format, encryption padding
   - define validity of certificates and tokens
   - add note on randomness
   - add HANDSHAKE, CONTINUE, GOODBYE; warning that GOODBYE will disappear
   - throw out reduntant "TRANSFER_TOKEN" explanatoins
   - add authentication and authorization, at least for "target"
     when minting
   - add mandatory trusted channel (Bluetooth, TLS)
   - reformat this into RFC-XML
   - add warning on differences to scientific notation

Table of Contents

   1. Introduction
      1.1  Object of the OpenCoin protocol
      1.2  Limited scope of the OpenCoin protocol
      1.3  General Layout of the OpenCoin protocol
      1.4  Encoding of messages, tokens and certificates
   2. General guidelines
   3. The OpenCoin protocol
      3.1.  Issuer setup
      3.2.  Wallet setup
      3.3.  Wallet creates blanks
      3.3.5."TRANSFER_TOKEN": A generic wallet-issuer request
      3.4.  Wallet send minting request to issuer  
      3.5.  Wallet gets token back
      3.6.  Wallet to wallet
      3.7.  Redeeming tokens
   4. References

1. Introduction

1.1 Object of the OpenCoin protocol

The OpenCoin protocol aims to implement David Chaum's concept of "untraceable 
payments" [3]. The general procedure is this:

* Minting
  * A payer creates a yet unsigned, 'blank' token according to the rules 
    published by the issuer. It includes a serial number.
  * He obfuscates this blank, yielding the 'blind'. He send the blind to the
    issuer and request signing with a special minting key.
  * If the issuer's requirements for minting (which may include a payment) 
    are met, he signs the payer's  blind with the nominated minting key.
  * The payer fetches the signed blind from the issuer and 'unblinds'. The 
    result is a token including a valid signature from the issuer.

* Spending
    A payer sends the token to a payee. The payee verifies that the token is
    valid according to the issuer's rules (format, data, signature, ...). In
    the standard online case, he also checks it against the issuer's double 
    spending database (DSDB). He tells the payer if he accepts the token.

* Redemption
    The payee sends the token to the issuer. The issuer verifies that the 
    token is valid and checks it against his DSDB. If he accepts the token, 
    he adds its serial number to the DSDB. He may offer the payee something 
    in exchange for the token (like a payment).

In the standard case of online payment, spending and redemption are actually 
entwined to one simultanious operation.

Tokens include a reference to this protocol, a reference to the issuer, a 
denomination, a random serial and the mint's signature over this data. The
minting key used to sign the token is deticated to mint exclusivly tokens 
of this denomination.

This protocol is designed such that tokens are unforgable and untracable:

* Unforgeability/balance
  Without knowledge of the issuer's private minting keys, no combination of 
  payers and payees can successfully redeem tokens of a total denomination 
  higher than the total denomination of tokens minted by the issuer for them. 
  In Particular, no one (except the issuer) can produce N+1 valid tokens 
  from N valid tokens ('one-more-forgery').

* Untraceability
  No combination of the issuer and a set of payees are able to correlate 
  blinds and tokens of a payer just by looking at them (but maybe by traffic

1.2 Limited scope of the OpenCoin protocol


1.3 General Layout of the OpenCoin protocol

The OpenCoin protocol typically involves three parties: the issuer, a sender/
payer (Alice) and a receiver/payee (Bob). We call the OpenCoin user agents of 
payer and payee 'wallets'. The issuer consists of four parts:
* The 'master key holder' (MHK) generates and keeps the master key pair 
  and signes and publishes the 'currency description document' (CDD) and 
  all the certificates.
* The mint generates and keeps the minting keys and signes blinds.
* The 'double spending database' (DSDB) keeps track of the serials of 
  tokens which got redeemed.
* The 'issuer service' (IS) is the public interface of the issuer on the

The participants send each other messages in request/response pairs. The 
universal scheme is this:

     * session initiation *

   -- [ HANDSHAKE, DATA ] -->
  <-- [ HANDSHAKE, null ] --

   -- [ REQUEST_1, DATA ] -->
  <-- [ RESPONSE_1,DATA ] --

   -- [ REQUEST_2, DATA ] -->
  <-- [ RESPONSE_2,DATA ] --

   -- [ CONTINUE,  null ] -->
  <-- [ CONTINUE,  null ] --

          * pause *

   -- [ REQUEST_3, DATA ] -->
  <-- [ RESPONSE_3,DATA ] --

   -- [ REQUEST_4, DATA ] -->
  <-- [ RESPONSE_4,DATA ] --

   -- [ GOODBYE,   null ] -->
  <-- [ GOODBYE,   null ] --

     * session termination *

The standard case involves three sessions:

   Payer Alice  --[minting]--------------------------------->  Issuer
   Payer Alice  --[spending]-->  Payee Bob  --[redemption]-->  Issuer

the latter two usually happening at the same time ('online case').

1.4 Encoding of messages, tokens and certificates


2. General guidelines


3. The OpenCoin protocol
3.1 Issuer setup

* issuer generates master key pair (ALG,pM,sM)

* issuer sets up "currency description document" = CDD (like a root certificate)

     standard version             =
     currency identifier          =
     short currency identifier    = OC 
     issuer service location      = opencoin://
     denominations                = strlist(1, 2, 5, 10, 20, 50, 100, 200, 500, 1000)  #list of strings seperated by commas
     issuer cipher suite          = HASH-ALG, SIGN-ALG, BLINDING-ALG
     issuer public master key     = base64(pM)
     issuer                       = base64(hash(pM))
     base64(sig(sM,hash(content part)))

   (question: is the "short currency identifier" needed?)
   (question: "not use after")
   (future: add additionial signatures, e.g. from wallet software vendors (set up in containers already))

* issuer publishes CDD at "currency identifier" URL

* mint (regularily) creates keypairs (pP,sP) for all denominations and id(p).
  Master key holder generates keys certificate

    key identifier      = base64(hash(pP))
    currency identifier =
    denomination        = denomination
    not_before          = TIME(...)
    key_not_after       = TIME(...)
    token_not_after      = TIME(...)
    public key          = base64(pP)

    issuer              = base64(hash(pM))
    base64(sig(sM, hash(content part)))

  * CDD?

* issuer fires up issuer service (=IS) at <opencoin://>

3.2 Wallet setup

* fetch "currency description document" from issuer

3.3 Wallet creates blanks

* Wallet: fetches current public minting keys for denomination

    MINTING_KEY_PASS(keycertificate) or MINTING_KEY_FAILURE(reason)

* Wallet: creates blank according to CDD:

      standard identifier =
      currency identifier = 
      denomination        = denomination
      key identifier      = key_id(signing key)
      serial              = base64(128bit random number)

* Wallet: create random r, calculate 

    blind = blinding(r, pub_minting_key, hash(blank))
  Calculate a collision-free random transaction ID (128 bit)

  Keep (r, blank, blind) in mind. 

3.3.5 "TRANSFER_TOKEN": A generic wallet-issuer request

The atom for this transaction is a list of tokens - if one of the tokens /blanks
fail, the whole transaction fails.

* Client sends
            transaction_id, target, list_of_blinds+keyids, 
            list_of_tokens, list_of_options )

  [ WARNING: In future versions of this protocol, it wll change to
            transaction_id, list_of_options,
            target, list_of_blinds+keyids, list_of_tokens )

  to IS (issuer service), where 

  * transaction_id is a base64(random(128bit)) referencing this transaction
    e.g. for later resume after an abort.
  * list_of_option may contain variable=value pairs like "JITM=mandatory".
    It must contain the option "type", which can have three values:
    * mint    : A minting request. target is a payment reference, 
                list_of_tokens must be empty.
    * redeem  : A token redemption. target is an account reference,
                list_of_blinds+keyids must be empty.
    * exchange: A request to mint new tokens for old ones. target must be 
                empty, value of blinds must equal value of tokens.

  If at least one of the blinds or tokens is rejected, the issuer answers

            transaction_id, reason,
            list( (blind1.key_id, reason1), ... ),
            list( (token1.key_id,  reason1), ... )  )

  where "reason" may be some general failure like "500 minting not available".
  If the request is accepted with no delay, IS answers

            transaction_id, message, list_of_signed_blinds)

  (with list_of_singed_blinds empty if no minting was required)
  If minting was requested and acccepted but postponed, IS answers

        TRANSFER_TOKEN_DELAY( transaction_id, message )

  In this case, the wallet can fetch the signed blinds later by 

        TRANSFER_TOKEN_RESUME( transaction_id )

3.4 Wallet send minting request to issuer  

* Send

        TRANSFER_TOKEN_REQUEST( transaction_id, target, 
            list_of_blinds+keyids, (empty list) , list_of_options, )

  to issuer service

* Issuer: if request will not be minted (e.g., "Bad Key ID" if the key_id
  is not current):

        TRANSFER_TOKEN_REJECT( transaction_id, reason,
            list( (blind1.key_id, reason1), ... ), (empty list1)  )

  ElseIf minting is done just-in-time, IS answers

        TRANSFER_TOKEN_ACCEPT( transaction_id, message, list_of_signed_blinds)

  Else IS queues blind to the mint and tells wallet to wait

        TRANSFER_TOKEN_DELAY( transaction_id, reason )

  Session is terminated.

  In case of delayed minting, mint processes request (signs blind with key_id)
  some time later and passes "signed blind"="blind token" back to IS 

3.5 Wallet gets token back

* Wallet asks issuer service

        TRANSFER_TOKEN_RESUME( transaction_id )

* IS either rejects finally

        TRANSFER_TOKEN_REJECT( transaction_id, reason,
            list( (blind1.key_id, reason1), ... ), (empty list) )
  with reasons like "TID Unknown", "TID expired", "TID rejected", ...,
  or tells to wait longer

        TRANSFER_TOKEN_DELAY( transaction_id, reason )
  (question: what about key expiration while request is in mining queue)
  (oierw thinks: as long as the key is valid for minting when the request is made, we are good)

  or passes signed blinds to wallet Bob, must preserve order

        TRANSFER_TOKEN_ACCEPT( transaction_id, message, list_of_singed_blinds )
  Session terminates

* wallet checks if blind fits request id and if blind was correctly signed. 
  If not, delete blind and inform user (optional: inform issuer about error)
  (optional: if yes, inform issuer that he may delete the request)

* Wallet unblinds signed blind and yields token  (or reblinds)

3.6 Wallet to wallet

Alice - sends a token
Bob   - receives the token

  [ Warning: 
    The messages "SPEND_TOKEN_*" may get exchanged by "TRANSFER_TOKEN_*" of 
    type "redeem" or "spend" in future versions of this protocol. ]

* Prerequisites
  * Wallet Alice locates Wallet Bob and sets up (secure) connection
  * Alice knows how much to send and tells her Wallet
  * Wallet Alice calculates a splitting of sum into tokens (units)
    and reates a list of tokens to send
  * Wallet Alice and Wallet Bob are synchronized to UTC (within some small 
    margin of error)

* [ToDo] Handshake

* Wallet Alice announces sum of tokens she wishes to spend for a certain
  prupose=target, wallet Bob decides if it is going to accept them:

    A:      SUM_ANNOUNCE( transaction_id, sum, target )
    B:      SUM_ACCEPT( transaction_id )
        or  SUM_REJECT( transaction_id, "Reason" )

* Wallet Alice sends tokens to Wallet Bob (this time including their clear 
  serial and signature)
    A:      SPEND_TOKEN_REQUEST( transaction_id, list(token1, ...) )

* The atom for a SPEND_TOKEN_REQUEST is the entire list of tokens

* Wallet Bob checks if the sum of their values matches the announced sum, if 
  they are valid and (if the former tests do not fail) tries itself to spent
  the tokens at the issuer with a TRANSFER_TOKEN_REQUEST of type "redeem" or
  "exchange", using a new, different transaction_id. If one of these fail,
  wallet Bob rejects the request it with a reason/reasons, otherwise accepts

    B:      SPEND_TOKEN_REJECT( transaction_id, list( (tokenN, "ReasonN") ) )
        or  SPEND_TOKEN_REJECT( transaction_id, emptylist, "Reason")
        or  SPEND_TOKEN_ACCEPT( transaction_id )

  Possible reasons are "unknown", "invalid" ... [ToDo].

  In case of rejection, wallet Alice itself should immediatly exchange these 
  tokens at the issuer as an emergancy countermeasure against token theft.

  In case of acceptance, wallet Alice must delete all instances of the spent

3.7 Redeeming tokens 

* Wallet sends tokens + target to IS

            transaction_id, list_of_options, target, (empty list), list_of_tokens 

  target may be an account and is of the form:

        and so on... to be defined with relationship between IS and individual

* IS checks if tokens and target are valid
    - if minting keys are still valid (XXX token has not expired)
    - if serial is still valid (against DSDB)
    - if signature is valid

  If not, IS rejects with reason (key id unknown, token outdated, token spent, 
  signature invalid) per token or sweeping

            transaction_id, reason, (empty list), list( (token1.key_id,  reason1), ... )  )

  If tokens and target are valid, IS enters the serials of the tokens into the
  DSDB, servers the target and replies

            transaction_id, message, (empty list)

4. References

[1]         The OpenCoin project <>

[2]         The OpenCoin project, "OpenCoin protocol v1.0"

[3]         David Chaum, "Blind signatures for untraceable payments", Advances
            in Cryptology - Crypto '82, Springer-Verlag (1983), 199-203.

[RFC4086]   D. Eastlake, J. Schiller and S. Crocker, "Randomness Requirements 
            for Security", RFC 4086, June 2005

[RFC4627]   D. Crockford, "The application/json Media Type for JavaScript 
            Object Notation (JSON)", RFC 4627, July 2006