15 KiB
Protocol of the carrotcoin cryptocurrency
The carrotcoin cryptocurrency is a bitcoin-like currency with proof-of-work, created just for learning purposes.
A special feature is its builtin option for gambling.
The currency
One carrotcoin (abbreviated cc) consists of 100 cents. Internally, only cent amounts are processed using integer fields. The recommended notation for an amount is like in this example:
42.00 cc
(This would be processed internally as 4200 cents.)
Blockchain
The network should stabilize at around 1 block every 5 minutes.
Blocks are hashed with SHA256 and this hash needs to start with a certain number of zeros (or, more precise, be below a given value depending on the difficulty)
ed25519 keys are used to validate transactions.
block datastructure
| content | size (bytes) |
|---|---|
| transaction | 149 |
| message (arbitrary) | 32 |
| miner pubkey | 32 |
| previous hash | 32 |
| timestamp (unix time in seconds, BE) | 8 |
| difficulty sum (BE) | 32 |
| nonce | 8 |
The first block has a "previous hash" value of 32 null bytes.
transaction datastructure
The first byte of a transaction describes the transaction type. The content of the following 148 bytes depend on the transaction type. The following transaction types are specified:
| transaction type | meaning |
|---|---|
| 0x00 | no transaction |
| 0x01 | payment transaction |
| 0x02 | gambling transaction |
| 0x03 | gambling reveal transaction |
no transaction
All 149 bytes of the transaction field must be nullbytes. Nothing special happens.
payment transaction
| content | size (bytes) |
|---|---|
| transaction type (= 0x01) | 1 |
| id (arbitrary) | 4 |
| sender (pubkey) | 32 |
| receiver (pubkey) | 32 |
| amount (BE, in cents) | 8 |
| transaction fee (BE, in cents, can be zero) | 8 |
| signature (signed by sender, over transaction fields before signature) | 64 |
gambling transaction
| content | size (bytes) |
|---|---|
| transaction type (= 0x02) | 1 |
| id (arbitrary) | 4 |
| player (pubkey) | 32 |
| gambling amount (BE, in cents) | 8 |
| transaction fee (BE, in cents, can be zero) | 8 |
| signature (signed by player, over transaction fields before signature) | 64 |
| padding (nullbytes) | 32 |
gambling reveal transaction
| content | size (bytes) |
|---|---|
| transaction type (= 0x03) | 1 |
| revealer (pubkey) | 32 |
| associated revealing proof (hash) | 32 |
| padding (nullbytes) | 84 |
impacts of blocks and transactions
For every block, the miner gets a reward of 1.00 cc. (100 cents)
If a payment transaction is included, the following happens additionally:
- The sender needs to pay "amount" + "transaction fee"
- The receiver gets "amount"
- The miner gets "transaction fee" on top of the reward.
If a gambling transaction is included, the following happens additionally:
- The player needs to pay "gambling amount" + "transaction fee"
- The miner gets "transaction fee" on top of the reward.
If a gambling reveal transaction is included, the following happens additionally:
- The miner gets an additional reward of 1.00 cc (So together with the general block reward, he will get 2.00 cc)
- The revealer gets 15.00 cc
- Each winning player gets twice the gambling amount he played.
You find more technical details below in the section Gambling.
calculating difficulty
The "difficulty sum" field (included in each block) is the cumulative sum of all block difficulty values. The first block has a "difficulty sum" value of 2^29.
The block difficulty value is not directly stated in each block but can be calculated by subtracting the "difficulty sum" of the previous block from the "difficulty sum" of the current block.
To calculate the block difficulty value, the following blocks are relevant:
- The current block for which to calculate the difficulty: [B-0]
- The previous block: [B-1]
- The block that is 10 steps back in the chain: [B-10]
Then apply the following formula: (The slash / means integer division)
D = [B-1].difficulty_sum - [B-10].difficulty_sum
T = [B-0].timestamp - [B-10].timestamp
calculated_difficulty = D * 3000 / 9 / T
block_difficulty = max(calculated_difficulty, 2^28)
[B-0].difficulty_sum = [B-1].difficulty_sum + block_difficulty
Note the max() operation: A difficulty can never be lower than 2^28 (=268435456). This is a strict lower bound.
For the first block, the "difficulty sum" value is set to 2^29 but its actual difficulty value is only 2^28.
Early in the chain, for calculating the "difficulty sum" of the first 10 blocks, we make assumptions for blocks "before" the first block:
- Each of these "before" blocks had a block difficulty of exactly 2^28
- The timestamp difference between each two blocks before the first block is exactly 300 seconds.
validity
payment / gambling transaction
A transaction is valid if its signature is valid and it contains an "amount" or "gambling amount" of at least 1 cent (0.01 cc).
The "signature" field must be an ed25519 signature over the concatenation of all fields before the signature and must be valid when validated with the "sender" or "player" pubkey.
payment / gambling transaction in a block
A transaction that is stored in a block must also fulfill the following criteria:
- There was never another transaction with the same ("id", "sender") pair anywhere in the block chain before. This counts across both types, so if a tuple was used for a payment transaction for example, it can not be used again, neither for another payment nor for a gambling transaction.
- Following the balances in the block chain, the "sender" or "player" key has enough money for both payments ("amount" or "playing amount" + "transaction fee") before the mining reward (1.00 cc + transaction fee) is counted.
This means: A miner cannot create a block that mines money and at the same time contains a transaction that relies on this 1.00 cc reward being available. After mining money in one block, the following block is the earliest possibility to pay this fresh mined money.
gambling reveal transaction
A gambling reveal transaction is only valid with a valid associated proof that needs to fulfill a few more conditions. More details in the section Gambling.
block
A block is valid if all of the following criteria are fulfilled:
- The "previous hash" field contains a SHA256 hash value of a previous valid block. (Or is completely filled with nullbytes, in case of the first block.)
- The first block has a public key with the following SHA256 hash value:
88023d392db35f2d3936abd0532003ae0a38b4d35e4d123a0fa28c568c7e3e2f
The public key that matches this stated hash value will be kept secret until the launch day. Then it will be released along with the first block. - The "timestamp" field is greater than the timestamp of the previous block (if there is any). Equal timestamps are not allowed.
- The "timestamp" field is less than or equal to the current time. This needs to be decided by each node based on the local clock.
- The "difficulty sum" must be precisely calculated, as specified in
calculating difficulty - The "transaction" must be valid as described above.
- The SHA256 hash of the entire block, when interpreted as an BE integer, multiplied by the block difficulty, is less than 2^256.
Seecalculating difficultywhat "block difficulty" means. (which is not directly given in the block.)
Gambling
The carrotcoin cryptocurrency has a built-in gambling feature where you play against the currency, not against a bank or casino. This means that lost money disappears while winnings are created from nowhere.
Technically, gambling happens in the following steps:
- A user places a bet by investing an arbitrary amount of money. There is a 50/50 chance of either losing it completely or doubling the amount.
- The gambling instruction is placed on the blockchain. The gambling amount is taken from the player's wallet for now.
- About once a day (more precisely every 256 blocks) there is a gambling commitment block. It mathematically defines the outcomes of all gambling bets since the last gambling commitment block. But these outcomes are not known at this point in time, they need to be revealed first by evaluating a delay-function.
- Miners can choose to perform a special "reveal mining" which means calculating the delay-function and generating a proof for the result.
- Once a reveal miner finished his calculation, the reveal information is sent to the network along with the generated proof.
- The reveal information is placed on the blockchain, the revealer gets a reward and every winning player gets his winnings.
Gambling transaction
A user can create a gambling transaction at any time to participate in the gambling process. Such transactions are described above in the section "Blockchain" / "transaction datastructure" / "gambling transaction". The id and player fields (along with other information) will be used to calculate if the player wins.
Gambling commitment blocks
To find out which block is a gambling commitment block, you need to number all blocks of the blockchain. The first block (this is the one with 32 nullbytes in the "previous hash" field) gets block number 0. From there on, just count up. (The block number of a block is the block number of its previous block plus one.)
Every block with a block number of 255 (modulo 256) is a gambling commitment block. Its hash value will also be relevant to calculate if a player wins.
Decision of winning / losing
To calculate if a player wins or loses, you first need to evaluate a delay-function based on the hash value H of the corresponding gambling commitment block. The hash value is interpreted as a 32-byte integer, big endian. The delay function to produce the reveal value R is the following where "^" means exponentiation:
R = H ^ 2 ^ 2 ^ 30 % n
The number n is the RSA modulus of the DNSSEC root signing key KSK-2024 with the following value:
n = 22152137184697602751949152182712806144286735269755991212091578018969267364854563246965161398309375840488156997640625085213985729183180764348127989435514689722834129288342499703533613231239853168289771769536412763137391054558055082146752229593979328251181647873233949602834141648160681711983351545692646009424518816069561938917629523175464947983950548802679152115205735609960641453864298194702935993896839374645356040490091081577992299773430144650589605043643969140352237968606446474316247592579560197155686719175897498255683642121505357781103123719079205647707696181709515150954235402701095586525936356219917713227143
All values modulo n that are stored, hashed, transmitted or compared need to be less than n/2. If a calculated value is greater than n/2, it must be subtracted from n to produce a value less than n/2. This is relevant for the value R above, for example.
Once the reveal value R is known, it is converted to a 256-byte datablock, big endian, and for each player the following block is hashed with SHA256:
| content | size (bytes) |
|---|---|
| id (from gambling transaction) | 4 |
| player (from gambling transaction) | 32 |
| R | 256 |
If the hash value starts with bit "0" (the first byte is something in the range 0x00 - 0x7f), the player loses. If the hash value start with bit "1" (the first byte is something in the range 0x80 - 0xff), the player wins. Winning means, twice the amount played is put back onto the players wallet. Example:
A player has 20.00 cc.
He plays 10.00 cc with a transaction fee of 0.20 cc.
The player now has 9.80 cc left.
It is later revealed that the player won this gambling round.
The player now has a balance of 29.80 cc. (The transaction fee is always lost.)
Revealing process
Reveal transactions need to be placed on the blockchain in their correct order. There is no field to indicate which gambling commitment block a reveal transaction is meant for. It must always be for the oldest not yet revealed gambling commitment block.
The following proof was taken from A Survey of Two Verifiable Delay Functions, a paper by Dan Boneh, Benedikt Bünz, and Ben Fisch. They call it "Pietrzak’s succinct argument". On page 10, the reason is given why every number must be less than n/2.
Krzysztof Pietrzak describes the proof first in Simple Verifiable Delay Functions.
Validating a reveal transaction
A gambling reveal transaction contains a hash value of an "associated revealing proof". This proof is a 5376 byte datastructure that needs to be transmitted along the block to be verified as part of the blockchain. It contains the following fields:
| content | size (bytes) |
|---|---|
| R (BE) | 256 |
| I_0 to I_19 (20 values, each BE) | 20 * 256 = 5120 |
Remember that every stored number modulo n must be less than n/2. This is also true for the values I_0 to I_19.
The values I_0 to I_19 are intermediate values that represent an interactive proof that was made non-interactive by using a hash functions. It works in the following way:
The prover wants to show that H, squared 2^i times, modulo n, results in the value R. For example with i=30, we write this in the following way:
H -> square[2^30] -> R
To prove that, he needs to provide the intermediate value I, that was the result after half the number of squaring steps. (In this example 2^29 steps.)
H -> square[2^29] -> I -> square[2^29] -> R
The four parameters H, I, R, i are now put into one datastructure and hashed using SHA256:
| content | size (bytes) |
|---|---|
| revealer (pubkey) | 32 |
| H (BE) | 256 |
| I (BE) | 256 |
| R (BE) | 256 |
| i | 1 |
The resulting hash value is then interpreted as a 256-bit number e, big endian.
The prover divided his claim into two sub-claims:
H -> square[2^29] -> I
I -> square[2^29] -> R
They are now combined into one claim using a combination function:
(H * I^e % n) -> square[2^29] -> (I * R^e % n)
You can see that this reduction step produced new start and end values H' and R':
H' = H * I^e % n
R' = I * R^e % n
This reduction step is done 20 times in total as summarized in the following table:
| i value | claim | reduced claim | I value to use |
|---|---|---|---|
| 30 | H -> square[2^30] -> R | H_1 -> square[2^29] -> R_1 | I_0 |
| 29 | H_1 -> square[2^29] -> R_1 | H_2 -> square[2^28] -> R_2 | I_1 |
| ... | ... | ... | ... |
| 12 | H_18 -> square[2^12] -> R_18 | H_19 -> square[2^11] -> R_19 | I_18 |
| 11 | H_19 -> square[2^11] -> R_19 | H_20 -> square[2^10] -> R_20 | I_19 |
The final claim (that H_20 squared 2^10 (= 1024) times results in R_20) can be verified by each node directly.
To sum it up, a node needs to verify the following for each gambling reveal transaction:
- The hash in the transaction must be the correct hash value for the associated proof block.
- The value R and each value I_0 to I_19 must be greater than zero and less than n/2.
- The final claim (H_20 -> square[2^10] -> R_20) must be true.