Back to library

Truledger in Plain English

Bill St. Clair



Truledger is an anonymous, digitally-signed vault and trading system. Like Loom, it allows anyone to issue assets (digital currencies). Unlike Loom, which relies entirely on (very good) obscurity for security, Truledger's digital signatures allow the server and the customer to prove to each other that they agreed at a particular time on their balances. It does this while allowing destruction of transaction history for closed trades. Truledger will initially provide server-based trading. Eventually, it will provide digital checks and bearer certificates. These, however, WILL require permanent storage of transaction history (unless they expire).

doc/db.txt gives a terse description of the Truledger server database and protocol. This page attempts to render the protocol in plain English.

Truledger uses public key cryptography to sign all messages passed back and forth between its web interface and the Truledger server. Digital signatures are a virtually unforgeable way to ensure that a message was written by its purported author. Truledger uses OpenSSL for its public key cryptography. You probably use OpenSSL every time you visit a secure web site, https://somewhere.com/, as does the web server. I didn't roll my own. Just used the same tried and true technology that secures the web. You can read more about public key cryptography, digital signatures, and hashing here and here.

I'm going to use four actors in the scenarios that follow. "Server" is the name of the Truledger server. "Bob" and "Sue" are two customers, who will trade with each other. "Spammer" is a third customer, unknown to Bob or Sue.

Scenario: Opening an Account

Sue (via email or IM): Hey, Bob. Check out Truledger. Go to Truledger.com, download the client, and install it on your computer. Then create a private key, and send me your ID, and I'll give you some usage tokens so that you can create an account.

Bob (via email or IM): Thanks, Sue! I installed the Truledger client and created a private key. Here's my ID.

Sue (via her Truledger client): Hey server, Here's a new request number. Give me a transaction number please.
Signed: Sue

Server: Here's a new transaction number.
Signed: Server

Sue: Hey server, here's that transaction number you gave me. Please spend 50 usage tokens to Bob's ID, with a message of "Hey Bob. Welcome to Truledger!" I'm paying 2 usage tokens as a transaction fee, which I'll get back when Bob accepts the spend. My balance after this transaction will be 1025 usage tokens. My outbox hash after this transaction will be X.
Signed: Sue

Server: I processed your spend of 50 usage tokens to Bob's ID. I agree that the transaction fee at the time of this transaction is 2 usage tokens, and that your balance after this transaction is 1025 usage tokens. I agree with you on your outbox hash.
Signed: Server

Sue (via email or IM): OK, Bob. I've given you 50 usage tokens. You should now be able to create an account at Truledger.com. Send me a message via Truledger when you've registered.

Bob (via his Truledger client): Hello server. Here's my ID and my public key. What's your ID and public key?
Signed: Bob

Server: Here's my ID and public key.
Signed: Server

Bob: Here's my ID and my public key, please create an account for me.
Signed: Bob

Server: I've registered your ID and public key. Somebody gave you enough tokens to register. Welcome to Truledger.
Signed: Server


In order to sign a message, you need to have a private key. In order to verify the signature on a message, you need to have the corresponding public key. Truledger identifies customers by the hash of their public key, their ID. The ID is a 40-character string of numbers and the letters A to F, the hexadecimal representation of a 160-bit number. You identify your account to the Truledger client with a passphrase, which is used to encrypt your private key on your disk. You'll only need to copy and paste your ID when you want to tell a trading partner how to send you money for the first time, or to seed your account with usage tokens, as Sue did for Bob.

Usage tokens are an idea from Patrick Chkoreff's Loom system. They are a way to charge for the server's resources. You have to buy storage for your account balances, and lease temporary storage for transactions. Usage tokens are the "currency" used to do this. Truledger also supports fees in other asset types, for server management that wants to make more than the sale of usage tokens. Truledger uses the file system as a database. One file in the Truledger database costs one usage token. Files vary in size, but are usually about 8K, mostly signatures.

Note that Bob had to send his public key to the server twice, once when he requested the server's public key, and again when he registered. Every message going to and from Truledger is digitally signed. It's only possible to verify a digital signature if you know the public key of the signer. A new customer's public key isn't in the database until after he's registered, so the first two messages, in which the new customer gets the server's public key, so that he can verify the server's signatures, and the registration request, both need to include the customers's public key, so that the signatures on those two messages can themselves be verified. After registration is complete, subsequent messages need to carry only the ID; the public key can then be looked up in the database.

Actual messages sent (with the signature that goes with each parenthesized item omitted):

Sue: (<suesid>,gettime,<serverid>,<req#>)
Server: (<serverid>,time,<suesid>,<time#>)
Sue: (<suesid>,spend,<serverid>,<time#>,<bobsid>,<tokenid>,50,Hey Bob. Welcome to Truledger!).
        (<suesid>,tranfee,<serverid>,<time#>,<tokenid>,2).
        (<suesid>,balance,<serverid>,<time#>,<tokenid>,1025).
        (<suesid>,outboxhash,<serverid>,<time#>,X)
Server: (<serverid>,@spend,(<suesid>,spend,<serverid>,<time#>,<bobsid>,<tokenid>,50,Hey Bob. Welcome to Truledger!)).
        (<serverid>,@tranfee,(<suesid>,tranfee,<serverid>,<time#>,<tokenid>,2)).
        (<serverid>,@balance,(<suesid>,balance,<serverid>,<time#>,<tokenid>,1025)).
        (<serverid>,@outboxhash,(<suesid>,outboxhash,<serverid>,<time#>,X))
Bob: (<bobsid>,serverid,<pubkey>)
Server: (<serverid>,register,<serverid>,<pubkey>,Truledger)
Bob: (<bobsid>,register,<serverid>,<pubkey>,Bob)
Server: (<serverid>,@register,(<bobsid>,register,<serverid>,<pubkey>,Bob))

Scenario: Receiving Assets

Bob: Hello server. Here's my ID and a new request number. What's in my inbox?
Signed: Bob

Server: Your inbox contains a 50 usage token spend from Sue with a message of "Hey Bob. Welcome to Truledger!" It also contains a 10 usage token charge from the server with a message of "Registration Fee". Here are two transaction numbers you can use to accept these spends and do a spend yourself.
Signed: Server

Bob: Here's my ID and the first of the transaction numbers you gave me. Accept the spend from Sue with a message of, "Thanks, Sue. I'm excited about Truledger!" Accept the server charge. My balance after this transaction will be 39 usage tokens.
Signed: Bob

Server: I've processed the spend from Sue and the server charge. I agree that your balance after this transaction is 39 usage tokens.
Signed: Server


One possible attack on an electronic server can be for someone to replay an intercepted message. Unless the protocol protects against that, this can cause problems. Except for the serverid request, the registration request, and a request for a customer's last request number, every information request must be accompanied by a request number that is larger than the customer's last used request number, and every transaction must be accompanied by a transaction number that is given out by the server. The server maintains a counter, which it increments each time someone asks for a transaction number. This makes requests that reveal information or initiate transactions impossible to replay without a customer's passphrase and private key. In the Truledger world, your passphrase and your private key are your identity. Guard them well.

Another possible replay attack is to intercept a message for one server and send it to another. Customers could protect against this by having different IDs, hence different public/private key pairs, for different servers. But it will be very convenient to use the same ID. Your friends will recognize you, and you'll have only one passphrase to remember. So the server's ID is included in almost every request. Requests meant for another server will not work.

You're probably wondering why Bob's balance after the transaction is 39, instead of 40, usage tokens. He got 50 usage tokens from Sue, and paid the server 10 usage tokens for his registration fee. The additional usage token is the price of the new file used to store the usage token balance. Storage costs usage tokens. Loom charges 1 usage token for each 16 bytes of storage. I considered charging per byte, but decided that charging per file was easier to handle, though not quite as fair. It only makes sense if message sizes are limited, of course. If you were allowed megabyte messages, then Truledger would have to charge per byte, or per kilobyte.

Actual messages sent:

Bob: (<bobsid>,getinbox,<serverid>,<req#>)
Server: (<serverid,@getinbox,(<bobsid>,getinbox,<serverid>,<req#>)).
        (<serverid>,inbox,<time3#>,(<suesid>,spend,<serverid>,<time#>,<bobsid>,<tokenid>,50,Hey Bob. Welcome to Truledger!)).
        (<serverid>,inbox,<time4#>,(<serverid>,spend,<serverid>,<time2#>,<bobsid>,<tokenid>,-10,Registration Fee)).
        (<serverid>,time,<bobsid>,<time5#>).
        (<serverid>,time,<bobsid>,<time6#>)
Bob: (<bobsid>,processinbox,<serverid>,<time5#>,<time3#>|<time4#>).
       (<bobsid>,spend|accept,<serverid>,<suesid>,<time#>,Thanks Sue. I'm excited about Truledger!).
       (<bobsid>,spend|accept,<serverid>,<serverid>,<time2#>).
       (<bobsid>,balance,<serverid>,<time5#>,<tokenid>,39)
Server: (<serverid>,@processinbox,(<bobsid>,processinbox,<serverid>,<time5#>,<time3#>|<time4#>)).
        (<serverid>,@spend|accept,(<bobsid>,spend|accept,<serverid>,<suesid>,<time#>,Thanks Sue. I'm excited about Truledger!)).
        (<serverid>,@spend|accept,(<bobsid>,spend|accept,<serverid>,<serverid>,<time2#>)).
        (<serverid>,@balance,(<bobsid>,balance,<serverid>,<time5#>,<tokenid>,39))

Scenario: Closing a Transaction

Sue: Hello server. Here's my ID and a new request number. What's in my inbox?
Signed: Sue

Server: Your inbox contains an acceptance from Bob of your 50 usage token spend with a message of, "Thanks Sue. I'm excited about Truledger!" Here are two transaction numbers you can use to close that transaction and do a new spend.
Signed: Server

Sue: Here's my ID and the first of the transaction numbers you gave me. Clear the spend to Bob. My balance after this transaction will be 1027 usage tokens. My outbox hash after this transaction will be Y.
Signed: Sue

Server: I've cleared the spend to Bob (and refunded the usage tokens that were leasing the outbox and inbox locations). I agree that your balance after this transaction is 1027 usage tokens. And I agree that your outbox hash after this transaction is Y.
Signed: Server


There are three main parts of a Truledger account: the balances, the outbox, and the inbox. Value is stored in all three places. When you do a spend, your balance for the spent asset is debited, and the spend request is stored in your outbox and in the recipients inbox. You are charged two usage tokens to lease the new outbox and inbox files. When the recipient accepts the spend, his balance for the spent asset is credited, the spend notice is removed from his inbox, and a spend acceptance notice is added to your inbox. When you acknowledge his acceptance of the spend, the spend request is removed from your outbox, the acceptance notice is removed from your inbox, and the two usage tokens you paid to lease those files is credited to your balance. This three step process is necessary, because the server can't modify your balances without your signed permission, and it can't modify the recipient's balances without his signed permission.

You may be wondering what an "outbox hash" is. Your balances plus your outbox represent the part of your account that you and the server have agreed upon. Your inbox is changed without your knowledge, but the server needs your signed permission to change your outbox (do a spend) or your balances. Since your outbox might get large, instead of sending the entire contents back and forth every time you do a spend, or acknowledge the acceptance (or rejection) of a spend by a recipient, you compute a hash of your outbox, and send that instead, and the server responds with an acknowledgement of that outbox hash (thank you to Patrick Chkoreff for this idea).

Actual messages sent:

Sue: (<suesid>,getinbox,<serverid>,<req2#>)
Server: (<serverid,@getinbox,(<suesid>,getinbox,<serverid>,<req2#>)).
        (<serverid>,inbox,<time7#>,(<bobsid>,spend|accept,<serverid>,<suesid>,<time#>,Thanks Sue. I'm excited about Truledger!)).
        (<serverid>,time,<bobsid>,<time8#>).
        (<serverid>,time,<bobsid>,<time9#>)
Sue: (<suesid>,processinbox,<serverid>,<time8#>,<time7#>).
       (<suesid>,balance,<serverid>,<time8#>,<tokenid>,1027).
       (<suesid>,outboxhash,<serverid>,<time8#>,Y)
Server: (<serverid>,@processinbox,(<suesid>,processinbox,<serverid>,<time8#>,<time7#>)).
        (<serverid>,@balance,(<suesid>,balance,<serverid>,<time8#>,<tokenid>,1027)).
        (<serverid>,@outboxhash,(<suesid>,outboxhash,<serverid>,<time8#>,Y))

Scenario: Preventing Spam

Spammer (likely via an automated client): Hey server. Here's a new request number. Give me a transaction number please.
Signed: Spammer

Server: Here's a new transaction number.
Signed: Server

Spammer: Hey server. Here's that transaction number you gave me. Please spend 0 usage tokens to Bob's ID, with a message of, "Go all night. Visit BuyViagra.com." I'm paying 2 usage tokens as a transaction fee, which I'll get back when Bob accepts the spend. My balance after this transaction will be 2425 usage tokens. My outbox hash after this transaction will be Z.
Signed: Spammer

Server: I processed your spend of 0 usage tokens to Bob's ID. I agree that the transaction fee at the time of this transaction is 2 usage tokens, and that your balance after this transaction is 2425 usage tokens. I agree with you on your outbox hash.
Signed: Server

Bob: Hello server. Here's my ID and a new request number. What's in my inbox?
Signed: Bob

Server: Your inbox contains a 0 usage token spend from Spammer with a message of, "Go all night. Visit BuyViagra.com." Here are two transaction numbers you can use to accept these spends and do a spend yourself.
Signed: Server

Bob: Here's my ID and the first of the transaction numbers you gave me. Reject the spend from Spammer with a message of, "Thanks for the tokens", and give me the two usage tokens he paid to send that spam. My balance after this transaction will be 41 usage tokens.
Signed: Bob

Server: I've rejected the spend from Spammer. I agree that your balance after this transaction is 41 usage tokens.
Signed: Server


Spends can be rejected. The amount spent goes back to the spender, but the recipient pockets the transaction fee. Zero spends use Truledger as a simple messaging service. But not a free one, unless the recipient wants the message. IMHO, spam exists largely because it is nearly free to send email. In a system where each spam message costs 2 usage tokens, cheap but not free, I doubt it will be much of a problem. Time will tell.

Actual messages sent:

Spammer: (<spammersid>,gettime,<serverid>,<req#>)
Server: (<serverid>,time,<spammersid>,<time10#>)
Spammer: (<spammersid>,spend,<serverid>,<time10#>,<bobsid>,<tokenid>,0,Go all night. Visit BuyViagra.com.).
        (<spammersid>,tranfee,<serverid>,<time#>,<tokenid>,2).
        (<spammersid>,balance,<serverid>,<time#>,<tokenid>,2425).
        (<spammersid>,outboxhash,<serverid>,<time#>,Z)
Server: (<serverid>,@spend,(<spammersid>,spend,<serverid>,<time#>,<bobsid>,<tokenid>,0,Go all night. Visit BuyViagra.com.)).
        (<serverid>,@tranfee,(<spammersid>,tranfee,<serverid>,<time#>,<tokenid>,2)).
        (<serverid>,@balance,(<spammersid>,balance,<serverid>,<time#>,<tokenid>,2425)).
        (<serverid>,@outboxhash,(<spammersid>,outboxhash,<serverid>,<time#>,Z))
Bob: (<bobsid>,getinbox,<serverid>,<req2#>)
Server: (<serverid,@getinbox,(<bobsid>,getinbox,<serverid>,<req2#>)).
        (<serverid>,inbox,<time11#>,(<spammersid>,spend,<serverid>,<time10#>,<bobsid>,<tokenid>,0,Go all night. Visit BuyViagra.com.)).
        (<serverid>,time,<bobsid>,<time12#>).
        (<serverid>,time,<bobsid>,<time13#>)
Bob: (<bobsid>,processinbox,<serverid>,<time12#>,<time11#>).
       (<bobsid>,spend|reject,<serverid>,<spammersid>,<time12#>,<time10#>,Thanks for the tokens).
       (<bobsid>,balance,<serverid>,<time12#>,<tokenid>,41)
Server: (<serverid>,@processinbox,(<bobsid>,processinbox,<serverid>,<time12#>,<time11#>)).
        (<serverid>,@spend|reject,(<bobsid>,spend|accept,<serverid>,<spammersid>,<time12#>,<time10#>,Thanks for the tokens)).
        (<serverid>,@balance,(<bobsid>,balance,<serverid>,<time12#>,<tokenid>,41))

Scenario: Issuing Assets

Bob: Hey server. Here's a new request number. Give me a transaction number please.
Signed: Bob

Server: Here's a new transaction number.
Signed: Server

Bob: Hey server. Here's that transaction number you gave me. Please register a new asset named "Bob GoldGrams". It has a scale of 7 and a precision of 3. Its ID is <bobggid>. My balance after this transaction will be 39 usage tokens and -1 <bobggid>.

Server: I have registered the new "Bob GoldGrams" asset. I agree that your balance after this transaction is 39 usage tokens and -1 <bobggid>.


Like Loom, Truledger allows customers to create their own asset types. Then, if they can convince them to do so, other customers can trade in that asset type. The ID of a Truledger asset is the sha1 hash of the creator's ID, its scale, its precision, and its name. But the message the customer signs to create the asset, and that the server signs to acknowledge creation, also contains the serverid. This allows the asset to be registered at multiple servers, with the same ID, but makes each particular registration specific to a specific server. Hence, it makes sense for an asset issuer to provide a service of transferring his holdings of his asset between servers at which he's registered; it's self-evident that Bob GoldGrams at Server A are the same asset as Bob GoldGrams at server B.

I plan to support transfer of asset issuance, but I haven't figured out the intricacies yet.

Like Loom, all amounts in Truledger are stored as integers. The scale value controls where the decimal point goes in the real-world representation of that value: move it left by scale places. The precision controls the minimum number of decimal places that are printed. So with a scale of 7 and a precision of 3, the value 12000000 will be printed by Truledger clients as 1.200, and the minimum value for Bob's new currency is 0.0000001, one ten-millionth of a gram of gold, or 0.000003 at 30/gram: 3 ten-thousandths of a cent. Hello micropayments.

Also like Loom, the sum of all the amounts, in accounts and outboxes, for one asset type is -1. There is one negative balance, owned by the issuer, who can spend as much as he wants, and a bunch of positive balances, and outbox entries. A -1 balance in the issuer's account means there are no outstanding balances or outbox entries in that asset, so that's where Bob's Bob GoldGrams balance begins. Users of an asset have to trust the issuer when he tells them, outside of Truledger, that his asset is backed by something of real value, and that he'll never issue more of the virtual asset than he has in the backing commodity. Well, unless he wants to act like a country, and issue fiat currency that is backed by his Full Faith and Credit and nothing else. Good luck getting people to go for that.

Actual messages sent:

Bob: (<bobsid>,gettime,<serverid>,<req3#>)
Server: (<serverid>,time,<bobsid>,<time13#>)
Bob: (<bobsid>,asset,<serverid>,<bobggid>,7,3,Bob GoldGrams).
       (<bobsid>,balance,<serverid>,<time13#>,<tokenid>,39).
       (<bobsid,balance,<serverid>,<time13#>,<bobggid>,-1)
Server: (<serverid>,#asset,(<bobsid>,asset,<serverid>,<bobggid>,7,3,Bob GoldGrams)).
       (<serverid>,#balance,(<bobsid>,balance,<serverid>,<time13#>,<tokenid>,39)).
       (<serverid>,#balance,(<bobsid,balance,<serverid>,<time13#>,<bobggid>,-1))

Scenario: Multiple Sub-Accounts

Bob: Hey server. Here's a new request number. Give me a transaction number please.
Signed: Bob

Server: Here's a new transaction number.
Signed: Server

Bob: Hey server. Here's that transaction number you gave me. Make a zero spend to myself. My balance after the transaction will be 38 usage tokens, -311.0347681 Bob GoldGrams in my default sub-account, and 311.034768 Bob GoldGrams in my "Gun Safe" sub-account.

Server: I did that spend. I agree with you that after the transaction, your balance is 38 usages tokens, -311.0347681 Bob GoldGrams in your default account, and 311.034768 Bob GoldGrams in your "Gun Safe" sub-account.

Bob: Hey server. Here's a new request number. Give me a transaction number please.
Signed: Bob

Server: Here's a new transaction number.
Signed: Server

Bob: Hey server. Here's that transaction number you gave me. Please spend 2.4056304 Bob GoldGrams to Sue, with a message of, "Well, I finally issued my new currency, backed by Krugerands in my gun safe. I'm giving you a gram in thanks for turning me on to Truledger, and 1.4056304 grams for 36 Capulin Coffee Units, which you said you'd sell me, so I can buy more of Daniel Fourwinds' fine coffee that we relished at your house the other day. I used a gold price of $796.60/ounce, Kitco's bid price this morning." My transaction fee will be 2 usage tokens. My balance after this transaction will be 36 usage tokens, and 309.6291376 Bob GoldGrams in my "Gun Safe" sub-account. My outbox hash after this transaction will be A.
Signed: Bob

Server: I have done your spend of 2.4056304 Bob GoldGrams to Sue, with your message, and a transaction fee of 2 usage tokens. I agree that your balance after this transaction is 36 usage tokens, and 309.6291376 Bob GoldGrams in your "Gun Safe" sub-account. I agree on the outbox hash.
Signed: Server


Truledger supports splitting up your balances into multiple "sub-accounts". Much like a conventional server gives you a checking account and a savings account, you can use these sub-accounts to help manage your assets. You can have as many of them as you wish, limited only by having usage tokens to pay for the files.

Bob has decided to keep track of his Bob GoldGrams assets with a "Gun Safe" sub-account, with a balance recording how many grams of the gold in his safe have not yet been put into circulation. He seeds it with 10 ounces of gold, the part of his holdings that he's willing to sell. Then he spends some of it to Sue, asking for enough Capulin Coffee Units for two pounds of Capulin Coffee ($17.95 per pound, shipped).

Note that you don't have to mention all of your balances with every spend. You only mention the balances that change. Also notice that there's no transaction fee for spends to yourself. Moving assets between your sub-accounts costs only tokens for new files. No need for the outbox and inbox stuff.

Actual messages sent:

Bob: (<bobsid>,gettime,<serverid>,<req4#>)
Server: (<serverid>,time,<bobsid>,<time14#>)
Bob: (<bobsid>,spend,<serverid>,<time14#>,<bobsid>,<bobggid>,0).
        (<bobsid>,balance,<serverid>,<time14#>,<bobggid>,-3110347681).
        (<bobsid>,balance,<serverid>,<time14#>,<bobggid>,3110347680,Gun Safe)
Server: (<serverid>,@spend,(<bobsid>,spend,<serverid>,<time14#>,<bobsid>,<bobggid>,0)).
        (<serverid>,@balance,(<bobsid>,balance,<serverid>,<time14#>,<bobggid>,-3110347681)).
        (<serverid>,@balance,(<bobsid>,balance,<serverid>,<time14#>,<bobggid>,3110347680,Gun Safe))
Bob: (<bobsid>,gettime,<serverid>,<req5#>)
Server: (<serverid>,time,<bobsid>,<time15#>)
Bob: (<bobsid>,spend,<serverid>,<time15#>,<suesid>,<bobggid>,24056304,Well\, I
      finally issued my new currency\, backed by Krugerands in my gun
      safe. I'm giving you a gram in thanks for turning me on to Truledger\,
      and 1.4056304 grams for 36 Capulin Coffee Units\, which you said you'd
      sell me\, so I can buy more of Daniel Fourwinds' fine coffee that we
      relished at your house the other day. I used a gold price of
      $796.60/ounce, Kitco's bid price this morning.).
       (<bobsid>,tranfee,<serverid>,<time15#>,<tokenid>,2).
       (<bobsid>,balance,<serverid>,<time15#>,<bobggid>,3096291376,Gun Safe).
       (<bobsid>,outboxhash,<serverid>,<time15#>,A)
Server: (<serverid>,@spend,(<bobsid>,spend,<serverid>,<time15#>,<suesid>,<bobggid>,24056304,Well\, I
       finally issued my new currency\, backed by Krugerands in my gun
       safe. I'm giving you a gram in thanks for turning me on to Truledger\,
       and 1.4056304 grams for 36 Capulin Coffee Units\, which you said you'd
       sell me\, so I can buy more of Daniel Fourwinds' fine coffee that we
       relished at your house the other day. I used a gold price of
       $796.60/ounce, Kitco's bid price this morning.)).
       (<serverid>,@tranfee,(<bobsid>,tranfee,<serverid>,<time15#>,<tokenid>,2)).
       (<serverid>,@balance,(<bobsid>,balance,<serverid>,<time15#>,<bobggid>,3096291376,Gun Safe)).
       (<serverid>,@outboxhash,(<bobsid>,outboxhash,<serverid>,<time15#>,A))

Scenario: Getting Information

To be done

Scenario: Cancelling a Spend

To be done


Copyright © 2008 Bill St. Clair, All Rights Reserved.

Back to library