rfcs/draft-tilde-coin.md

19 KiB

title number author status
Standards 2: Tilde Coin Specification 2 Austin Ewens <aewens@tilde.team> Proposed

Abstract

Digital currency system for tilde.team (aka ~team) and potentially other tilde servers in the tildeverse (aka ~verse), called tilde coin (~coin).

Tilde Town Integration

At this time, there is an existing tilde coin system in tilde.town (aka ~town) run by login. For the ~town coin system, it allows programs to facilitate the sending / receiving of ~coins which can be used to act as a middle-man for exchanging ~town coins to digital currencies on other servers.

As for the exchange rate to ~town, to facilitate a purchasing power parity (PPP) system one would need to approximate the amount of ~town coins in their economy, which can be done using the following equation:

new_coin_rate = 2.48055555 * coin_per_hour_per_user * rate_of_gambling
max = 1000 * number_of_town_users + sum_of_entries_in_tildescores
town_approx = max + new_coin_rate * time

The rate_of_gambling can be found by averaging the current value of tildegraph and sum_of_entries_in_tildescores is from /home/krowbar/Code/irc/tildescores.txt on ~team. You can then use approx and the approximate size of your server's coin market to get the exchange rate using rate = server_approx / town_approx.

Philosophy

To better understand the decision making behind how ~coin were chosen, it helps to know the philosophy behind ~coin. The core features that ~coin will try to adhere to is being secure, private, simple, not slow, and erifiable. To explain the context of those features here:

  • Secure: The system maintaining the account balance and transactions between individuals is resilient against invalid modifications from others.

  • Private: Transactions made on the ~coin network (e.g. ledger) does not make your purchase publicly available (much like if you make a cash transaction it's not automatically displayed on a public listing anywhere outline who you are, what you spent, and what it was spent on).

  • Simple: Using ~coin should not require a PhD or vast knowledge of technical concepts, spending / using a ~coin should be as easy as using cash.

  • Not Slow: Over time, transactions over the ledger should not take exponentially longer as the network grows / more transactions have been made.

  • Verifiable: Don't just trust the maintainers, the users should be able to verify that the ledger has not been altered and everything is valid.

Tilde Coin Implementation

The ~coin system is comprised of three different systems:

  • The ledger
  • The server
  • The client

The implementation of the server and client can vary, but they must adhere to their specifications below and to the system of how the ledger works to be considered a proper ~coin implementation. The following will be the broad strokes of how each of these systems work and what operations they need to provide.

The Ledger

Before explain how the ledger works, it helps to know what the ledger is. For any digital currency system there needs to be a way to determine if any tender is actually a part of the system and not just spontaneously created, and a way to do so is to log the movement of the currency when any transaction is made. This way if you want to know where the currency came from, you merely traverse through this log and it should end at where it was originally added into the system. This type of log is called a ledger.

To maintain privacy in ~coin system the ledger is to stored in an asymmetrically encrypted file, and to keep the file secure / verifiable it will be stored alongside an HMAC-SHA signed checksum of the ledger's unencrypted contents. This prevents the public from being able to view the transactions made but provides an easily means to check that the ledger was not modified using the signed checksum. The contents of the ledger will include:

  • The type of event
  • The UTC timestamp to designate when a transaction took place
  • The source of the funds being moved
  • The destination of the funds
  • How many ~coins were moved
  • A hash of the current contents of the ledger
  • The ID of the receipt of the transaction (zero-indexed, increments by one)
  • A hash of the signed receipt

With the entries in the pipe delimited format:

TYPE|UTC_TIMESTAMP|SOURCE|DESTINATION|AMOUNT|LEDGER_HASH|RECEIPT_ID|RECEIPT_HASH\n

The receipt is something provided to the user after each transaction so that they have their own personal log of what they have used their ~coins on as a means to verify that what they have as their balance according to the ledger matches up with what their receipts reflect. The receipt itself includes:

  • The UTC timestamp of the transaction
  • Who the receipt belongs to
  • Where the funds were moved
  • How much was moved
  • The hash of the contents of the ledger before the transaction took place
  • The ID of the receipt

With the receipt in the pipe delimited format:

UTC_TIMESTAMP|SOURCE|DESTINATION|AMOUNT|LEDGER_HASH|RECEIPT_ID\n

Before the receipt is finalized, it is sent to the client to be signed, sent back to the server to sign, and the doubly-signed receipt is hashed to be stored in the ledger and sent to the client for it's records. The fact that it's signed by both the client and the server provides a way to verify the authenticity of the receipt on both ends, which helps keep the ~coin system both secure and verifiable. As for the signing process, while the method can vary it's highly advised to do this using the "clearsign" methodin gpg to help adhere to the philosophy of keeping the ~coin system simple (both from a user's perspective and for implementing ~coin on other platforms). This allows the client and server to each have their own private and public keys that can be used to keep the signing process secure through the private keys as well as verifiable through the public keys.

As of now, there are many systems in place already that is keeping everything private and secure, but with the ledger always be encrypted there needs to be something else in place to help verify the information the client has in their receipts in such a way that stringing together everyone's receipts could be used to double-check the contents of the ledger and re-create it's history. This is accomplished through an append-only, publicly accessible file that contains:

  • The type of event
  • The UTC timestamp of the ledger entry
  • The receipt ID of the ledger entry
  • The amount of funds moved
  • The current hash of the ledger's contents up to that entry
  • The hash of the signed receipt from the ledger entry

With the records in the pipe delimited format:

TYPE|UTC_TIMESTAMP|RECEIPT_ID|AMOUNT|LEDGER_HASH|RECEIPT_HASH\n

Throughout the rest of this document this file will be referred to as the "public records" for the sake of clarity. This allows a user to check that the ledger hash in their receipt matches what's on the public records along with the other metadata of the receipt. So in essence, the ledger is made up of two files (the ledger itself and the public records) along with the receipts that are the by-product of transactions that can be used to verify the contents and legitimacy of the ledger.

The Server

To maintain the ledger and public records files, there needs to be a program to facilitate all of those operations. This is where the server comes in, which is only referred to as a server due to it's relationship to the clients. From an implementation standpoint, the server is to be a TCP socket server so that it can communicate to multiple clients and can be accessible either locally or remotely.

The operations the server performs is:

  • Encrypting / decrypting the ledger file
  • Maintaining the signed checksum at the end of the ledger
  • Append the public metadata of transactions to the public records
  • Generate the balance of accounts based on the contents of the ledger
  • Archive the ledger once it's reached a certain size
  • Verify the contents of the ledger (and archives) before adding transactions
  • Accept and answer requests from clients

Most of those operations are self-explanatory, but the last three require a bit of explanation. Looking at the implementation of the ledger it shares many similarities to a blockchain, and by association it would share the same performance issues that comes with the growth of the ledger's list of transactions. In short, as new transactions are added to a blockchain it takes longer each time to verify it's history before continuing, this has to do with the bottleneck of hashing contents to compare to the hashes in it's logs. The ~coin system's attempt at getting around this issue is to perform an archiving action whenever the ledger reaches a certain length. This is done by:

  1. Going through the entire ledger to generate the current state (e.g. balances of the accounts)
  2. Making a hash of the current contents of the ledger
  3. Saving the current ledger to a separate file
  4. Clearing out the current ledger
  5. Creating a new "archive" entry in the ledger.

The archive entry will include:

  • The type of event
  • The UTC timestamp of when the archive was made
  • The state the ledger was in
  • The merkle tree hash of the ledger
  • The hash of the ledger
  • The name of the archive file (using the absolute path for maximum clarity)

With the archive entry in the pipe delimited format:

TYPE|UTC_TIMESTAMP|<STATE>|MERKLE_TREE|LEDGER_HASH|ARCHIVE_FILE\n

The definitive way to quickly know if the entry being read is an archive or transactional entry is through the event type field (being either "transaction" or "archive"). Also, the state of the ledger will be in the comma separated, colon delimited format:

ACCOUNT1:AMOUNT1,ACCOUNT2:AMOUNT2,...,ACCOUNTN:AMOUNTN\n

In theory, this would mean that the slowest the ledger would be is right before the archive and while the archive is being performed, but would not become drastically slower over time as the ~coin network grows like a blockchain would. Also, it makes the task of verifying the archives by simply looking up archive file and checking to see if it's checksum matches the hash stored in the archive entry (which is significantly quicker to do than verifying each individual hash inside the archive file). The hashes within the archived ledgers would not need to be checked individually during each transaction since it's hashed would be backed by the validity of the proceeding hashes that follow it, further strengthening it's validity with each new transactions. The server could of course be requested to verify the entire ledger history in it's entirety if an audit is requested (which can be further quickened by making use of the merkle tree hash), but there's no reason to take the performance hit of doing this after each transaction.

The last responsibility of the server to review would be the acceptance and answering of requests from clients. The server should be able to respond to the following requests:

  • REGISTER: This request takes two arguments, the alias for the account and the public key for the account used for signing. It will use the public key as the account holder identifier internally that will be used for transactions and will return the receipt for the initial zero balance each account has that needs to be signed in the format described above. Do note that the account alias can be sent instead of the account holder identifier when making requests to the server.

  • WHOAMI: This request takes one argument, the alias for an account. It returns the account holder identifier (i.e. the public key) in the format:

FOUND\n
or
FOUND||ACCOUNT_HOLDER\n

Where found is either 1 (true) or 0 (false) if the alias exists. If found is 0, no account holder identifier will be sent.

  • RENAME: This request takes two arguments, the current alias of an account and the desired new alias. This will rename the existing alias, not add a second one. Do note that there is nothing stopping another user later on from taking the old alias.

  • SEND: This request takes three arguments; the account holder, the account the funds are moving to, and the funds being moved. Assuming the account has this amount of funds, it will return a receipt to be signed in the format described above.

  • SIGN: This request takes two arguments, the account holder and the signed receipt. Assuming the signed receipt is validated by the account holder's public key the signed receipt is returned in the receipt format described above.

  • BALANCE: This request takes one argument, the account holder, and provides the current balance of that account according to the ledger in the format:

ACCOUNT_HOLDER||BALANCE\n

Where balance is the floating point value of the ~coins owned by the account holder.

  • VERIFY: This request takes one argument, a receipt, and verifies that the receipt exists in the ledger and that it's information is accurate according to it's entry. The response will be in the format:
RECEIPT_ID||VALIDITY\n

Where validity is either a 1 (true) or 0 (false).

  • CONTEST: This request takes two arguments, the first being a receipt and the other being either another receipt or the line number of the public record, the idea being that one receipt is invalid and the other proves it's invalidity. The server will then check the receipt(s) and determine if there's any error present in the receipt(s) and/or public records. The ending result being the conflicts found (if any) and the options to resolve the conflicts in the following format:
CONFLICT_ID||CONFLICT_DESCRIPTION||<OPTIONS>\n

Where the options are comma separated, colon delimited as:

OPTION1_ID:OPTION1_DESCRIPTION,OPTION2_ID:OPTION2_DESCRIPTION,...,OPTIONN_ID:OPTIONN_DESCRIPTION
  • AUDIT: This request takes one argument, the account holder, and requires one other account holder to second the motion, before hand providing a ticket number for another member to use to second this motion. The ticket number response will be presented as:
TICKET_NUMBER\n

After which it will schedule in another thread to have the entire ledger (current and archives included) to be verified in it's entirety both with it's own contents and against the results in the public records. The ending result being the conflicts found (if any) and the options to resolve the conflicts in the following format:

TICKET_NUMBER||CONFLICTS\n
CONFLICT1_ID||CONFLICT1_DESCRIPTION||<OPTIONS1>\n
CONFLICT2_ID||CONFLICT2_DESCRIPTION||<OPTIONS2>\n
...
CONFLICTN_ID||CONFLICTN_DESCRIPTION||<OPTIONSN>\n

Where conflicts is the number of conflicts found and if more than 1 conflict exists it will list the ID, description, and resolve options of the conflicts. The resolve options are comma separated, colon delimited as:

OPTION1_ID:OPTION1_DESCRIPTION,OPTION2_ID:OPTION2_DESCRIPTION,...,OPTIONN_ID:OPTIONN_DESCRIPTION
  • RESOLVE: This request takes two arguments, the conflict ID and the resolve option ID. This action requires one other account holder to second the motion, before hand providing a ticket number for another member to use to second this motion. The ticket number response will be presented as:
TICKET_NUMBER\n

After which, it will schedule the following changes to be made in a separate thread. The response for the resolve being scheduled will be:

TICKET_NUMBER||SCHEDULED\n

Where schedules is either a 1 (true) or 0 (false).

  • SECOND: This request takes two arguments, the ticket number provided by the server and the account holder. Assuming the correct parameters are met, the motion will proceed.

For composing a request to the server, it should be sent in the double-pipe delimited format:

REQUEST||<OPERATION>||<ARG1>||<ARG2>||...||<ARGN>\n

The use of double pipes here vs the single pipes used everywhere else is because the arguments could be a receipt (which has single pipe delimited content). For context, here is an example of a valid request:

REQUEST||SEND||ALICE||BOB||5.0\n

Additionally, to protect against identity theft and fraud, any requests to the server that require specifying the account holder and/or moving funds will require the request to be signed by the user. This way the server can check the signature using the public key on records to know that a request being made is indeed from the account holder. For this reason, it's important that the server maintains it's own key/value pair of the alias and account holder identifier (i.e. the public key) so that this can always be accomplished without issue.

Also note that while the provided operations from the server along with the implementation details of the ledger, public records, and receipts do not allow for definitive knowledge of the contents of the encrypted ledger (as this would breach the privacy philosophy of ~coin), it does allow for a zero knowledge proof methodology of testing / verifying the contents of the encrypted ledger without exposing any private information.

The Client

While using the ~coin system can be done by hand, this would greatly go against the simplicity philosophy of the ~coin system since it would require an abundance of up-front knowledge to even get started. To mitigate against this, the ~coin system is expected to do the following:

  • Setup the private and public keys of the user (preferably using gpg)
  • Manage transactional requests with the server
  • Handle signing the receipts from the server
  • Maintain a local balance of the user's funds based on it's own ledger
  • Store the provided receipts from the server in a way where the user can query them later
  • Expose an API to communicate with third-party services

The majority is self-explanatory, as long as the implementation sends requests to the server in the correct formats (described above). Also, the communications API is not a hard requirement, but a strong recommendation to allow for third-party services to integrate with the client to allow for an easier experience for the user to utilize ~coin across other local services. The expectation of the communications API is to provide a token and secret for the third-party service to use to act on behalf of the client for both sending requests to the server and signing receipts. The token and secret allows the user to revoke the token should they want to cut off the third-party service's access to the account and the secret is to help ensure that only the specified third-party service has access to that token.

Security Note

The relationship between the third-party service and the client can work by either passing along commands from the third-party service through the API to act as a proxy through the client to act on behalf of the user, or the client can provide the third-party service with the it's public and private keys. The latter, while possible, is greatly discouraged as there is now no longer any method to definitively revoke the third-party service's access to your account and if it's security is breached it could allow attackers to also have full access to the account.

Coin Generation Methods

To be written

Federation

To be written