This page explains some technical details on the interaction between cryptocurrency and game engine. It is assumed that the reader is familiar with main principles of Bitcoin transactions — inputs, outputs, signatures, scripts.
Bitcoin transactions can be of two types: coinbase and normal.
Coinbase transaction has no inputs and N outputs.
Total amount of outputs must not exceed block reward + fees from other transactions in the same block. Every output has a script (usually a Bitcoin address).
Technically, coinbase tx always has 1 dummy input (which can store supplementary info, such as additional nonce value to ensure unique tx hash), but it is never treated as a real input, i.e. it does not link back to any previous transaction.
Normal transactions have M inputs and N outputs.
Every input must be signed with accordance to the output script of corresponding previous transaction. Total amount of outputs must not exceed total amount of inputs (if it's lower, the difference becomes fee).
Namecoin added special transactions to hold name/value pairs. We use this type of transactions for creating players and sending moves. General form of transaction is the following.
Name-in and name-out do not have to be the first input/output, they can be ordered arbitrarily.
There are 3 name operations which produce slightly different transactions:
- name_new: has no name_in. Name_out contains salted hash of name.
- name_firstupdate: name_in must link to the output of name_new transaction. Name_out must contain name, salt and value.
- name_update: name_in must link to name_firstupdate or name_update. Name_out must contain name and value.
Operations are defined by a constant op in the script. For example, OP_NAME_NEW = 1.
name_new is used to announce a name and prevent its squatting by someone who intercepts it in the tx pool while it's not in the block yet. In Namecoin the delay is 12 blocks (2 hours) before name_firstupdate can be applied. In Huntercoin it's just 2 blocks (2 minutes), since name squatting should not be much of a problem.
The parameters (op, hash, name, salt, value) are stored in output scripts as vectors of bytes, followed by OP_DROP operations.
OP_NAME_NEW << hash << OP_2DROP << ... OP_NAME_FIRSTUPDATE << vchName << vchRand << vchValue << OP_2DROP << OP_2DROP << ... OP_NAME_UPDATE << vchName << vchValue << OP_2DROP << OP_DROP << ...
All scripts are followed by normal Bitcoin script containing either pubkey or address:
... << pubkey << OP_CHECKSIG ... << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG
Locked coin is the price of creating a name. It is allocated in name_new and carried over in updates. Additional inputs are generally used to pay tx fee. Additional outputs are used to get change, if inputs exceed the desired fee. It is possible, but uncommon, to send coins to someone inside a name transactions.
To transfer a name to someone you just change the address in the name-out. Ownership of the name is claimed by the posession of private key to the name-out. Thus only the owner can perform updates on the name.
In Namecoin, if name is not updated in 36000 blocks (~8 months), it becomes expired. In this case someone else can register the same name.
Name transactions are identified by special magic number written into the version field of tx. In the game name transactions are used to create and move characters. Names do not expire (however, if player is killed, his name can be taken again). Value field is JSON-encoded move.
There are also game transactions, which allow interaction between the currency and the game. Game transactions are deterministically created by the game engine using game state (which, in turn, was deterministically computed from all player moves since genesis block till current block). Game transactions do not need their inputs signed and they bypass most checks.
Game transactions are marked by special magic number written into the version field of tx, plus they are stored in separate arrays and form a separate merkle tree in the block.
In Huntercoin there are currently two types of game transactions explained below.
Player death transaction
The idea is simple — the game just spends locked coin of the player. In terms of transaction graph, the coin is destroyed. The game then puts equal amount onto the map tile where the player was killed, allowing others to collect the loot.
When such transaction is generated, killer name(s) are put into the signature field of the tx. This is to assist the GUI when transaction details are queried.
In the current implementation at most one transaction of this type is created per block, listing all killed players in the inputs.
Player reward transaction
This transaction acts like a second coinbase. It creates coins out of nothing and sends to player(s). Game engine ensures that the same amount was subtracted from the game.
In the current implementation at most one transaction of this type is created per block, listing all rewards in the outputs.
Technically, N dummy inputs are created, ordered according to outputs. Each dummy input contains some info on the reward. This may be used to show reward info in the GUI, plus it ensures a unique tx hash. This is important, as otherwise receiving the same reward amount by same player(s) will create a duplicate tx hash, rendering it unspendable.
We added maturity interval of 120 blocks to reward transactions. This is to neglect the effect of orphaned blocks (in case of chain fork). Normal transactions are easily rebroadcasted, but coinbase/reward transactions can change their hash. Thus we need to be sure that such transactions can only be spent when chain fork is unlikely. This is the same reasoning that was behind the coinbase maturity interval in Bitcoin.
Game transactions are appended to the block and saved to disk. They are never sent through the network. Instead, they are deterministically recreated on each peer from the game state. They are spendable like normal transactions.
Currently M ≤ 2, since all player deaths and rewards are combined. We may change this in future.