A simple cryptocurrency built out of curiosity. Not for real-world use.
- classic blockchain structure
- parallel SHA256-based proof-of-work computation
- signed transactions using Ed25519
- miners are rewarded with a coinbase transaction per block
- balance based account model
- peer-to-peer networking on top of HTTP
- no transaction rewards (miners have no incentive to include transactions in blocks)
- no difficulty scaling (increasing amount of mining nodes will lead to rapid inflation)
- forks are not resolved (nodes just keep accepting valid blocks ad infinitum)
- peer-to-peer communication is unencrypted
- blockchain is not persisted to disk or compressed in any manner
- everything will probably implode if you actually run this in production
Miners need to have an Ed25519 public key in order to receive coinbase transactions, i.e. the reward transaction for mining a block. To do that, you can generate a new random key pair first:
go run cmd/keygen/keygen.go
This will generate a key pair in your current directory, which will be used by default.
The node needs to be configured with a few environment variables if you want it to communicate with other nodes in the network:
export NODE_BIND_HOST=127.0.0.1:8000 # The address the HTTP server binds to
export NODE_ADVERTISED_HOST=some-public-ip:8000 # The address other nodes can reach your node with
Additionally you can define a seed host, which is used to synchronize the blockchain when the node initially starts:
export NODE_SEED_HOST=some-other-node:8080
Once you have your keys and you have configured the node, you can compile the app and start mining for blocks:
go build cmd/node/node.go
./node
...
2021/06/10 17:52:57 Adding genesis block: Block 0 000002be9afbfdaa977028a51d10bd590f9b56b03c3f570b8723e3809dc439ba transactions: 0
2021/06/10 17:52:57 Listening for API requests at localhost:8080
2021/06/10 17:53:13 🎉 Found valid block: Block 1 000002b6ba6b836c75c90bcbf938fafd75a0f5f933fd0b12fe18532477b2cc69 transactions: 1
If you want to run two nodes on the same host just for fun and to see how the peer-to-peer communication works, you can open two terminals (or use tmux or whatever) and run this in the first one:
export NODE_BIND_HOST=localhost:8000
./node
This will start the first node, which will immediately start mining for new blocks. Then you can start a second one using a different port. The second node will use the first one as a seed host, i.e. the second node will first synchronize any blocks the first node has mined before the two will start competing for the next block in the blockchain:
export NODE_BIND_HOST=localhost:8080
export NODE_SEED_HOST=localhost:8000
./node
...
2021/06/10 17:53:16 Adding genesis block: Block 0 000002be9afbfdaa977028a51d10bd590f9b56b03c3f570b8723e3809dc439ba transactions: 0
2021/06/10 17:53:16 Syncing blockchain via localhost:8080
2021/06/10 17:53:16 Listening for API requests at localhost:8000
2021/06/10 17:53:16 Remote node found valid block: Block 1 000002b6ba6b836c75c90bcbf938fafd75a0f5f933fd0b12fe18532477b2cc69 transactions: 1
2021/06/10 17:53:16 Node @ localhost:8080 sent greeting
2021/06/10 17:53:20 Remote node found valid block: Block 2 000003ef948b69e2167da552dade695ebda52cf0433964f96b10406b333ef333 transactions: 1
2021/06/10 17:53:37 🎉 Found valid block: Block 3 000003ad1c34b165cab51bf21d135bfc7783b51fca144aeb4b2204302c08292b transactions: 1
2021/06/10 17:53:42 Remote node found valid block: Block 4 0000021d3a4fff9de706de98a4dbc9233b9083fffbbf63db8f14adae7e15c20c transactions: 1
2021/06/10 17:53:57 Remote node found valid block: Block 5 0000077c4b9a8eea9d64a427175d7c26f2d115ee76358d7688e74e4edb4a49eb transactions: 1
2021/06/10 17:53:59 Remote node found valid block: Block 6 0000047082767c1f362c88bf0b98386c3a2d78f4c79bd66c20e511626c133141 transactions: 1
2021/06/10 17:54:06 🎉 Found valid block: Block 7 00000561aa27b01b5f709269cffb651a0fb7fe69aa31ee7b207e6067235b3b18 transactions: 1
2021/06/10 17:54:31 🎉 Found valid block: Block 8 000006dba152ad81e4f9ee69e05368ac63091ecd2173edb54b0154695abc859e transactions: 1
You can query a node for the state of the blockchain. For example, to get the latest block in the blockchain known by the node:
curl localhost:8080/api/v1/blockchain/ --silent | jq '.[-1]'
{
"number": 16,
"time": "2021-06-10T14:58:27.730607Z",
"transactions": [
{
"sender": null,
"receiver": "gBg426L2kNWAE1WFz+Jd+GmlcQ4XUabIqLvAAxz9OgI=",
"amount": 10,
"nonce": 0,
"time": "2021-06-10T14:58:22.083545Z",
"signature": null
}
],
"nonce": 640205,
"previousHash": "AAAFkZjtFxv+jUoTTqrzcp8XC/9TmDPi2OM5VxpTgUA=",
"hash": "AAADdOoIKvXfBAxx2RK9nIYIwOcgDfkjzEFXRycEHKc="
}
The transaction in the block above is a coinbase transaction, thus it has neither a valid signature nor a sender.