Implement gambling

This commit is contained in:
2025-12-13 23:41:07 +01:00
parent fd6b5019ac
commit 872e44e317
10 changed files with 954 additions and 171 deletions

View File

@@ -2,6 +2,8 @@
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:
@@ -93,6 +95,7 @@ If a payment transaction is included, the following happens additionally:
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:
@@ -182,13 +185,13 @@ Technically, gambling happens in the following steps:
## 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.
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.
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

View File

@@ -11,6 +11,13 @@ Node to Node communication happens over IPv6 only.
When starting a node process, it tries to claim udp port 62039. If this port is not free, any other port is chosen.
### Version fields
Every packet starts with a 4-byte header containing two version fields. (A 2-byte "protocol version" and a 2-byte "capable version".)
Participants should fill both fields with 0 when sending messages. When receiving a messages with "protocol version" != 0, the message should be ignored. The "capable version" must not be checked for incoming messages, so messages are processed regardless of the value they contain as "capable version".
This is the current behaviour for all "protocol version 0" participants and should allow to extend the protocol in the future, if this becomes necessary.
## Node behaviour
### Peers
@@ -43,14 +50,19 @@ Each node keeps a list of up to 1024 open transactions. (That are valid but not
The list is sorted by the following criteria:
- transaction fee, decreasing
- sent or played amount, decreasing
- sender pubkey, increasing
- id, increasing
These sorting criteria form a 3-tuple.
These sorting criteria form a 4-tuple.
Only one transaction per (sender pubkey, id) tuple stays in the list. If a transaction with the same tuple but greater transaction fee is received, it replaces the current transaction. If the transaction fee is equal or smaller, the new transaction is ignored.
Only one transaction per (sender pubkey, id) tuple stays in the list. If a transaction with the same (sender pubkey, id) tuple but greater transaction fee or same fee but greater amount is received, it replaces the current transaction. In other cases (smaller fee or neither the fee nor the amount increased), the new transaction is ignored.
If one sender created multiple transactions, it must have a large enough balance for all transactions, otherwise the excess ones (as defined by the sorting criteria above) are removed from the list.
By choosing different ids, one sender can put multiple transactions into the list.
Each sender must have a large enough balance for all transactions, otherwise the excess ones (as defined by the sorting criteria above) are removed from the list.
At most one (the next pending) gambling reveal transaction can be part of the list and will be the first one, before all other transactions.
If the list grows above 1024 entries, a node may either remove excess ones or keep them in a local list. Within the Node to Node communication, only the first 1024 entries will be synced.
@@ -60,13 +72,17 @@ The hash of each open transaction is a SHA256, calculated over the following dat
| content | length |
|---|---|
| previous hash | 32 |
| is reveal transaction (bool) | 1 |
| transaction fee (BE) | 8 |
| sender pubkey | 32 |
| amount (BE) | 8 |
| sender / player pubkey | 32 |
| id | 4 |
The "previous hash" consists of 32 nullbytes for the first transaction in the list. For all other entries, it is the calculated hash value of the open transaction entry directly before in the list.
If the list contains less than 1024 entries, set "transaction fee", "sender pubkey" and "id" to nullbytes for the hash calculation of all following entries.
The field "is reveal transaction" is 0x01 for the first transaction if it is a reveal transaction and 0x00 for all other transactions. Reveal transactions have "transaction fee", "amount", "sender / player pubkey" and "id" set to nullbytes.
If the list contains less than 1024 entries, set "is reveal transaction", "transaction fee", "amount", "sender / player pubkey" and "id" to nullbytes for the hash calculation of all following entries.
## Node to Node message packet formats
@@ -76,7 +92,7 @@ If the list contains less than 1024 entries, set "transaction fee", "sender pubk
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 0 (BE) | 1 |
| type = 0 (dec) | 1 |
| difficulty sum of second last known block (BE) | 32 |
| hash value of open transaction # 1023 | 32 |
| partner IPv6 | 16 |
@@ -92,7 +108,7 @@ partner IPv6 and partner port may be nullbytes (no partner included).
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 1 (BE) | 1 |
| type = 1 (dec) | 1 |
| block hash | 32 |
A block request is sent from node A to node B in order to transfer a block from node B to node A.
@@ -106,8 +122,8 @@ If "block hash" consists of 32 nullbytes, node A wants node B to send the newest
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 2 (BE) | 1 |
| block | 292 |
| type = 2 (dec) | 1 |
| block | 293 |
A "block transfer" message is sent back in response to a "block request" message.
@@ -117,7 +133,7 @@ A "block transfer" message is sent back in response to a "block request" message
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 3 (BE) | 1 |
| type = 3 (dec) | 1 |
| list position (0 <= x < 1024) (BE) | 2 |
### open transaction list hash response
@@ -126,7 +142,7 @@ A "block transfer" message is sent back in response to a "block request" message
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 4 (BE) | 1 |
| type = 4 (dec) | 1 |
| list position (0 <= x < 1024) (BE) | 2 |
| "open transaction" hash value | 32 |
@@ -136,7 +152,7 @@ A "block transfer" message is sent back in response to a "block request" message
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 5 (BE) | 1 |
| type = 5 (dec) | 1 |
| list position (0 <= x < 1024) (BE) | 2 |
### open transaction response
@@ -145,9 +161,9 @@ A "block transfer" message is sent back in response to a "block request" message
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 6 (BE) | 1 |
| type = 6 (dec) | 1 |
| list position (0 <= x < 1024) (BE) | 2 |
| transaction | 148 |
| transaction | 149 |
## Client to Node message packet formats
@@ -157,8 +173,8 @@ A "block transfer" message is sent back in response to a "block request" message
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 7 (BE) | 1 |
| padding (nullbytes) | 252 |
| type = 7 (dec) | 1 |
| padding (nullbytes) | 253 |
The node should answer to a "Mining task request" with a "Mining task response"
@@ -168,8 +184,8 @@ The node should answer to a "Mining task request" with a "Mining task response"
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 8 (BE) | 1 |
| transaction (optional) | 148 |
| type = 8 (dec) | 1 |
| transaction | 149 |
| previous hash | 32 |
| timestamp (unix time in seconds, BE) | 8 |
| difficulty sum (BE) | 32 |
@@ -181,23 +197,130 @@ The miner fills "nonce", "message" and "miner pubkey" on its own.
When a miner finds a block, it sends a "block transfer" message to the node.
### Payment request (Client -> Node)
### Transaction request (Client -> Node)
| content | size (bytes) |
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 9 (BE) | 1 |
| transaction | 148 |
| type = 9 (dec) | 1 |
| transaction | 149 |
The node should answer to a "Payment request" with a "Payment request received" if the contained transaction is formally correct (see "validity / transaction" in the blockchain specification).
The transaction can be of any type. (Payment, gambling, gambling reveal)
A response is always sent back in this case, even if the transaction cannot be applied to the blockchain right now.
The node should answer to a "Transaction request" with a "Transaction request received" if the contained transaction is formally correct (see "validity / transaction" in the blockchain specification).
### Payment request received (Node -> Client)
To validate a gambling reveal transaction, the node will first ask the client for the associated proof (see "Associated revealing proof request" and "Associated revealing proof response" messages) before sending back a "Transaction request received" response.
A response is always sent back for valid transaction data structures, even if the transaction cannot be applied to the blockchain (e.g. because the sender used the transaction id before or has not enough money). For gambling reveal transactions, the request is confirmed if the proof was received, the proof hash matches and all numbers are correctly in range. (0 < I < n/2) The check if the reveal proof matches the oldest not yet revealed commitment block, is done after confirming with "Transaction request received".
### Transaction request received (Node -> Client)
| content | size (bytes) |
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 10 (BE) | 1 |
| type = 10 (dec) | 1 |
### Reveal mining task request (Client -> Node)
| content | size (bytes) |
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 11 (dec) | 1 |
| padding (nullbytes) | 32 |
The node should answer to a "Reveal mining task request" with a "Reveal mining task response"
### Reveal mining task response (Node -> Client)
| content | size (bytes) |
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 12 (dec) | 1 |
| commitment hash | 32 |
The commitment hash is either 32 nullbytes ("nothing to do for reveal miners at the moment") or it contains the hash of the oldest not yet revealed gambling commitment block.
See blockchain.md / section Gambling for the mathematical background what to do with the commitment hash "H".
When the miner is done forming a proof, he is expected to create a gambling reveal transaction and send it to a node using a "Transaction request" message. It should also be ready to receive and answer "Associated revealing proof request" messages from that node.
## General packet formats
These packets can be used in both situations (For Client <-> Node communication and for Node <-> Node communication.)
### Ping
| content | size (bytes) |
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 13 (dec) | 1 |
| ASCII string "R u carrotcoin?" | 15 |
| nonce (arbitrary value) | 8 |
The ASCII string "R u carrotcoin?" is fixed and messages with a different content in this part of the message should be ignored.
When receiving a valid Ping message, a Pong message with the same nonce value must be sent back to the sender.
### Pong
| content | size (bytes) |
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 14 (dec) | 1 |
| ASCII string "I m carrotcoin!" | 15 |
| nonce (same value as in the Ping) | 8 |
An answer message to a Ping request. See Ping for implementation details.
### Associated revealing proof request
As described in the blockchain specification, a reveal transaction requires an "associated revealing proof" to be fully validated.
There are 3 possible situations when a participant A transfers a reveal transaction to a participant B:
1. A reveal mining client (A) finished calculating the proof and sends the transaction to some node (B).
2. A node (A) further spreads this transaction to another node (B).
3. A node (A) transfers a block with a reveal transaction to another node (B).
In each case, if B does not know the associated revealing proof, it needs to ask A for it with the following message:
| content | size (bytes) |
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 15 (dec) | 1 |
| parts bitfield | 1 |
| associated revealing proof (hash) | 32 |
The associated revealing proof described in the blockchain has a total length of 5376 bytes. For transmission, it is equally divided into 6 parts with 896 bytes each. These parts are numbered from "part 0" (the first 896 bytes) to "part 5" (the last 896 bytes).
A sha256 hash value of the entire 5376 byte datastructure is the identifier that is used inside the transaction and used in this request to identify the proof being requested.
The "parts bitfield" describes which parts should be sent back. 0x01 means "part 0", 0x02 means "part 1", ..., 0x20 means "part 5". To request all 6 parts (which might be a typical request), the "parts bitfield" will have value 0x3f. If a retransmission is needed, the "parts bitfield" can describe exactly, which parts are still missing.
When node A sent a reveal transation to node B, it should answer such associated revealing proof requests from B with all required parts, each part transmitted as an individual UDP packet of type "Associated revealing proof response".
### Associated revealing proof response
| content | size (bytes) |
|---|---|
| protocol version = 0 (BE) | 2 |
| capable version = 0 (BE) | 2 |
| type = 16 (dec) | 1 |
| part number | 1 |
| associated revealing proof (hash) | 32 |
| proof partial data | 896 |
This message is sent back as response to an associated revealing proof request. See above for implementation details.
The "part number" describes where this part belongs to and has a value from 0 to 5 (inclusive).
The "associated revealing proof (hash)" identifies the entire 5376-byte proof and is the same as in the request.
The "proof partial data" contains the actual information (1/6 of the entire proof in each message).