Skip to content

Lucre and Denominations

burdges edited this page Nov 28, 2011 · 1 revision

Lucre does not use a "Cut and Choose" protocol like I was expecting from my classical understanding of Chaum's work.

My original understanding was that the client sent N prototokens to the server, all with a different serial number, but the same face value. These tokens were blinded, so the server was forced to ask the client for the unblinding factors, one-by-one. The server thus opened and verified all of the tokens--except for one, which was signed and returned. Upon return, the client unblinds the last token. Now it is ready for spending, but the server has no idea what the serial number is.

This is what I was expecting, so I actually designed the classes to support this. OTToken does not merely store its own state as a prototoken (before it is signed.) Rather, it stores its own state as a LIST of prototokens (before it is signed.) This list can be sent to the server as a single OTToken, so the server can receive it and execute the cut-and-choose protocol.

This is where things broke down. I was originally going to code this so I could set the token up with ANY face amount, and then add denominations later for extra anonymity. But I realized there was no way to put the AMOUNT of the coin inside the signed portion. Only the Lucre data itself, of any digital cash note, is blinded, signed, unblinded, and verified. But face value of the note is not stored in the Lucre data itself. There was no way to put an amount inside the Lucre data. So there was no way to blind the amount.

THEREFORE, I WAS FORCED to wrack my brains and google old archives, and eventually came to denominations as the solution.

For any digital asset type, (that is, for any currency contract that is uploaded to the server by a user who is creating an issuer account or any basket currency), the server creates MULTIPLE keys--one for each denomination. If I upload a currency contract for Chuck-E-Cheese game tokens, a new asset type ID is created (as well as a new issuer account, if I am the signer on the contract). At the same time, a new and corresponding MINT is also created, with a different public/private minting key pair FOR EACH DENOMINATION.

Actually, the mint is not created just then, because it would freeze the server up while the keys were being generated. (They take a long time.) So instead, I made a utility called "otcreatemint" that runs in the transaction/ folder and creates the mint for you. Try it, it's easy to use. You won't need to use it until you issue some new asset types.

If you search the transaction/ code for "if (0)" you will find a section where you can comment one line, and uncomment another line, and the server will ITSELF create any new and needed minting keys whenever it boots up. But be warned: this can take a long time for each asset type. If 1000 currencies have just been issued, it's going to be a while, which is why I put it in a separate utility.

A future version of the server will fire off the otcreatemint utility in a separate process. For now, it's just proof-of-concept , so you have to run it by hand.

There are two versions of any mint, on the server side: mints/ASSET_TYPE_ID and mints/ASSET_TYPE_ID.public

If a user wants to withdraw cash, his wallet must send a getMint message to the server first. The server will look up the public mint file based on the asset type ID, and return it in a response. The wallet saves the mint file on the client side. This contains the public keys for each of the denominations. Any withdrawal will consist of a purse of tokens, the least number of which, of the appropriate denominations, are necessary to fulfill the withdrawal request. (In the future, there will be some limit placed on the number of tokens allowed here. At some point, it's too expensive for the server to process, plus the message size can get out of hand.)

So Chuck-E-Cheese game tokens would have its own mint file, stored in mints/ASSET_TYPE_ID, with a key pair for 1, for 5, for 10, for 25, 100, 500, 1000, 2000, 10000, 100000. That is, a penny, nickel, dime, quarter, dollar, 5 dollars, 10 dollars, 20 dollars, a hundred dollars, and a thousand dollars. In the future, these will be described in the currency contract by the issuer, and the server will simply obey the contract. But right now, for testing purposes, those specific denominations are HARD-CODED, and they are automatic for all asset types.

If it weren't for denominations, you would literally have to pull 100,000 tokens out of the bank for a $1,000 withdrawal, which is not feasible. I also didn't bother with larger denominations or custom amounts after this, because I realized that the small denominations themselves add greatly to the anonymity of the total cash base.

CUT AND CHOOSE -- NOT NECESSARY

The server KNOWS the denomination of each coin, since that portion of the note is NOT BLINDED. The wallet had to use the appropriate public key for each denomination in order to generate the blinded request. The server therefore uses the CORRESPONDING KEY, from the private mint file, to sign that token, based on the appropriate denomination. Because of this, the server KNOWS that only the SAME KEY, of the right denomination, of the right asset type, can possibly verify the signature later. But the server still does not know the ID itself, of the token, since that portion WAS blinded safely in the Lucre data.

So it turned out that denominations were necessary, but that cut-and-choose was not necessary. This means that the user must request the server for a copy of the mint before he can withdraw cash in any specific asset type (including basket currencies.)

This also means that the server can have expiring and rotating Mint files, useful for expiring tokens and therefore keeping the spent token database manageable.

This also means that the server can give different token keys to one user that it gives to another, thus breaking untraceability. ONE EASY VERIFICATION MECHANISM TO PREVENT THIS is for users to COMPARE the public mint files that they store in their wallets. This is the sort of client-side stuff that I haven't really explored deeply enough in thought (yet). If the wallets are also anonymous network nodes and/or clients, then it seems they are all connected anyway, so why wouldn't they share public mint files as a way of keeping the servers clean? The nodes would derive a benefit from this. Any user COULD do this himself, in fact. Just create multiple user accounts from multiple VPN locations, and compare the public minting keys.


any chance you could write a sequence diagram for how this process works?

In Open Transactions, when the client software withdraws cash, it constructs blinded prototokens and sends them to the server. The server debits the asset account, signs the prototokens, and then sends those tokens back to the client, who unblinds them. (Now they are now ready to spend.)

If you are an anonymous user connecting over Tor, then this step will occur through one of the server's exchange accounts, over an HTTPS interface specially set up for cash exchanges. (Meaning, you don't even need to have an account.)

Later, when the token is spent, the payee deposits it into his own account at the server. (Or exchanges it.) When he does, the server sees the Token ID for the first time; it has no way of tracing it back to the withdrawal. The server then stores the token ID into a spent token database, in order to prevent double-spending of the token.

You can see the full progression of the blinding code if you look here:

  1. WITHDRAW (CREATE) PROTOTOKENS OTClient.cpp: else if (OTClient::notarizeWithdrawal == requestedCommand) // NOTARIZE WITHDRAWAL

  2. SIGN PROTOTOKENS OTServer.cpp:
    void OTServer::NotarizeWithdrawal(OTPseudonym & theNym, OTAccount theAccount, OTTransaction & tranIn, OTTransaction & tranOut)

  3. UNBLIND TOKENS OTClient.cpp:
    void OTClient::ProcessWithdrawalResponse(OTTransaction & theTransaction, OTServerConnection & theConnection, OTMessage & theReply)

[The tokens are ready to spend now, and untraceable. Later, the client spends some tokens, by passing them to another wallet user. That payee deposits the tokens to verify them (perhaps immediately withdrawing again, if he just wants to exchange them for new ones.) The server has no way of knowing where the tokens came from, since they were blinded when they were issued. The server also has no way of knowing if the tokens represent a new payment to me from someone else, or if I am just exchanging and re-exchanging tokens that I already had, perhaps to fix an approaching expiration date. Below this point, that payee is now the client, since he now has the tokens...]

  1. DEPOSIT TOKENS (IN PURSE) OTClient.cpp: else if (OTClient::notarizePurse == requestedCommand) // NOTARIZE PURSE (deposit)

  2. VERIFY TOKENS OTServer.cpp:
    void OTServer::NotarizeDeposit(OTPseudonym & theNym, OTAccount & theAccount, OTTransaction & tranIn, OTTransaction & tranOut)

Also I suggest reading the OTToken and OTMint classes to see the actual Lucre calls being made.

Clone this wiki locally