This is a write-up explaining the proof-of-work based blockchain project that leverages asynchronous programming to achieve concurrency, high efficiency in CPU usage, and scalability.
To show how, I will explain how a block is added to the blockchain to explain the complete flow and the asynchronous components I have used in this project.
Before that, it is essential to understand the [Node](https://github.com/RajeshRk18/Tutorials/blob/747808f06916d1b151f120330129f57341728e09/blockchain/src/lib/src/node.rs#L122) struct.
Node consists of fields such as;
- Address (which is the IPv4 address of the node)
- Mempool (where the user submitted transactions reside)
- State (which is the blockchain)
- Miner (which consists of a Miner thread handle and a mpsc(Multi-Producer Single Consumer) block communication channel)
A node is instantiated using the command,
```shell
cargo run --bin node
```
Along with that, Node also instantiates the query server using the command,
```shell
cargo run --bin query
```
Lets deconstruct how a block is constructed.
There is no way for a client to publish transaction. Such functionality cannot be added due to the time constraints for project submission. Transactions are [randomly generated](https://github.com/RajeshRk18/Tutorials/blob/747808f06916d1b151f120330129f57341728e09/blockchain/src/lib/src/node.rs#L183) by the node and added to the mempool.
After that, [Node::run_miner](https://github.com/RajeshRk18/Tutorials/blob/747808f06916d1b151f120330129f57341728e09/blockchain/src/lib/src/node.rs#L145) gets the transaction from the mempool and the `Node` spawns a thread for mining the new block.
`Node` waits for the mining thread to receive the mined block. It does so by having a mpsc channel in the `Miner`. When the mining thread mines a new block, it sends through the `mpsc::sender`. `Node::run` method constantly polls whether the `mpsc::receiver` got a block using `tokio::select!` macro.
Below is an example of serialized block;
```
{
"block_header": {
"index": 1,
"previous_hash": "0x2F6B8D7E4C9A315FD1E823B69C0A4FBE",
"timestamp": 1618577150,
"current_hash": "0x7C5A812E9359D146AF7D5C76B7F983ED",
"coinbase_txn": {
"amount": 10,
"validator": "127.0.0.1:1730"
},
"merkle_root": "0x9A2E5F4138D7B0CE19AB6D27A8FE3C20",
"nonce": 123456,
"difficulty": 1
},
"body": {
"txn_data": [
{
"id": "0x9A2E5F4138D7B0CE19AB6D27A8FE3C20",
"sender": "0xC3A9D2F4B16780E59C1FAE78D0256B34",
"receiver": "0xF85E9B17D264CA3B062A4F91C7D3E8B0",
"amount": 100
}
]
}
}
```
> Why `tokio::select!`?
Imagine there are many asynchronous tasks in addition to mining such as receiving payload from peer, or client, indexing, etc,.`select!` macro polls every future sequentially and it stops polling once one of the futures are ready. By looping the `select!`, it helps us handle many asynchronous tasks.
> Also, Miner struct has `Joinhandle`. Why we need that? Imagine we have a real world peer-to-peer blockchain. Now every miner works on finding nonce that is below the target level. If a peer mined the block, he will send it over the stream(`TcpStream`) to the node. Node already has separate thread for mining blocks. So, node must have the ability to abort the task and start mining new block. This aborting and restarting the miner task is performed by `Miner::stop_and_restart` method.
Now, back to the flow.
Once the node receives the block, it updates the state and writes the new block to the rocksDB in separate threads.
Blockchain state stored in the database can be used to provide data on querying. Query can be made either with transaction id, or block number.
Querying by transaction ID,
```shell
cargo run --bin query_client -- txn <Transaction ID>
```
Response will be as follows;
```
{
"sender": "0x6F43B291F8A0",
"receiver": "0xE2D9C817B4F5",
"value": 100,
"block_index": 42,
"block_hash": "0xABCD1234EF5678",
"block_root": "0x9876FEDCBA4321",
"validator": "127.0.0.1:1730"
}
```
Querying by Block number,
```shell
cargo run --bin query_client -- block <Block Number>
```
Response will be as follows;
```
{
"index": 1,
"timestamp": 1618577150,
"block_hash": "0x7C5A812E9359D146AF7D5C76B7F983ED",
"coinbase_txn": {
"amount": 10,
"validator": "127.0.0.1:1730"
},
"merkle_root": "0x9A2E5F4138D7B0CE19AB6D27A8FE3C20",
"txns": [
{
"sender": "0x6F43B291F8A0",
"receiver": "0xE2D9C817B4F5",
"value": 100,
"block_index": 42,
"block_hash": "0xABCD1234EF5678",
"block_root": "0x9876FEDCBA4321",
"validator": "127.0.0.1:1730"
},
{
"sender": "0xA0B1C2D3E4F5",
"receiver": "0x5E6F7A8B9C1D",
"value": 200,
"block_index": 43,
"block_hash": "0x1234567890ABCDEF",
"block_root": "0xFEDCBA0987654321",
"validator": "127.0.0.1:1730"
}
]
}
```