WebAssembly to run blockchain using Go - Part 2
In this article I aim at making an attempt to demystify blockchain by presenting an implementation using Go programming language.
In part 1 of this article, the application has been presented as a scenario between two actors Alice and Bob.
Objectives of this article
- Gain an insight over the inner-workings of blockchain.
- How to create a blockchain in Go.
- Consider the distributed aspect of the app carrying out the blockchain operations.
Blockchain can be seen as a distributed database in which consensus algorithms are provided to allow adding new entries while these entries are ensured to be valid without the need of a central authority to ensure that.
Proof of Work (PoW)
PoW is required to be a hard problem so that one node can not consistently solve it hence delivers a proof for group consensus.
Suppose : event that one node finds the nonce that solves the hash in a time not exceeding ten minutes.
Assume for one node we have : probability of event taking place = which is a very insignificant probability.
Let's now define : event that at least one node solves the hash within the ten minutes in a pool of n number of nodes, accordingly .
Substituting for two values of n number of nodes in the network ,
Therefore, it is concluded that with a big enough number of contributing nodes, the value jumps significantly from 0.001 to 0.6323 to nearly one!
It is worth noting that the probability model that describes this scenario should be more complex than that. But the point is to make sense of the fact that the likelihood of getting the block created becomes significant if many nodes exist in the network hence it prevents a malicious node from introducing fake transactions into the chain.
It is required to have a level of control on the time in which the nodes take to create a new block. A harder PoW in turn requires more time, hence we need to control the difficulty of PoW. In this article difficulty diff
is the number of leading zeros in which the hash is required to have for the block to be considered valid.
Mining
Mining is the Work in Proof of Work as nodes loop through nonce values until the valid hash is found.
In this application nonce values looped through are not incrementally increasing. This to allow different nodes in the network to go through different nonces.
In this article a SHA256 hash is used in an array byte represented in little-endian.
What's little endian vs big endian !
Endiannes is about the order in which bytes of a datatype is placed in memory. For little endian, the least significant byte of a datatype is placed first in memory followed by the more signficant bytes until reaching the most significant one while big endian follows the opposite order.
If we want to enforce the leading zeros condition upon the hex 0xAB0300, it is first converted to 0x0003AB from which we count the number of leading zeros.
Code
Now let's get into the exciting job, in the previous part of this article, a walkthrough on the usage of the application is presented along with the WebAssembly bindings written. In this part, blockchain and the networking portion in it is presented.
Transaction
First I define what a transaction is
In a real blockchain application transaction most proabably is required to contain more attributes but the bare minimum which I choose to present is to have the sender From
of the transaction, the reciever To
and the amount of crypsys Amount
being transferred. I also allow the transaction to be represented in json format since it is being propagated through network.
Block
The block fields:
Block |
---|
id |
previous hash |
nonce |
payload (transaction) |
difficulty |
timestamp |
this block hash |
Represented in the struct of this code from the file block.go which belongs to package blockchain.
For this snippet,
I create a struct for the part of the block to be hashed which is basically all the attributes of the block except for the hash
. In lines 11-15 , these fields are filled then converted to a string. Then in line 18 the string is hashed and copied into the hash
attribute of the created block instance.
As for function hashValid
, checking the validity of the hash is simply checking the number of leading zeros.
But considering the endiannes things shall require a bit more work than just counting zeros. I start by taking the last 8 bytes of the hash array that is under check. It is worth noting that taking only those 8 bytes is not functionally wrong as long as the difficulty is no more than 64 (8*8) bits .
In line 7 I shift the last byte of the array to take the place of the most significant byte of a new uint64 number (hashAsInt
) while the first byte of array forms the least significant byte of the new number. The rest of array bytes form the remaining bytes of hashAsInt
accordingly in that order. After finishing that for
loop in lines 6-8, I am retreiving back the most significant 64-bits of the hash in the form in which they existed before getting represented in little-endian as array. Hence now we can validate those bytes accordingly whether they pass difficulty check in lines 9-10 or not.
Blockchain
In this file blockchain.go, the blockchain structure and methods are defined.
For this application Blockchain
struct contains only one attribute chain
which is an array of Block
.
The first function NewBlockchain()
constructs an instance of Blockchain
. It first invokes GetObserver()
where this initializes the underlying networking operations. By creating a new blockchain the first block is created rightaway where in blockchain terms it is called "genesis block" and it does not contain any relevant transaction info but its creation is essential for other blocks to follow.
In line 25 I set a callback, this callback is later called by the underlying network when a message is received by the node notifying it that there is a new transaction requested. The receiving node then might contribute by mining the transaction or ignore it and just add the new block. In this article it is assumed that nodes always mine transaction when they are asked for it. In line 9 the listener()
converts the serialized message msg
received into a predefined struct.
After that it invokes a new thread that starts mining to create a new block in line 11 while the transaction information of the block to be created is just extracted priorly from msg
. At the end, the chain is mutated by adding that new block to it.
RequestTransaction()
method insitigates the creation of new transactions. At first it sends a message to the network in line 35 to notify other nodes about a newly requested block that needs to be created then it tries to mine for that new block by itself.
Networking
Node struct reflects the networking aspect of the blockchain as it comprises features of the network.
First are the destination ip address toIP
and port toPort
of the running server in file tracker.js which is written in nodejs. Attribute response
holds messages received from the tracker. While ws
points at the websocket connection. Finally, newTransactionCallback
is a callback that is called once the node received a request for a new transaction to be created.
At this point I fill the Connect()
procedure first to establish connection with tracker then to register event listeners.
First I get the websocket object from the javascript global scope running on the web browser.
From this object, New()
is invoked passing the url to connect to as an argument, in this case the url is "ws://127.0.0.1:8081/ws"
In line 9, I register the necessary event listener that is called once a message is received. The Go func callback is wrapped inside a js.FuncOf() which converts Go func into a function to be used by javascript.
This is also mentioned in part 1 of this article. The message is received and parsed then it branches to two cases either it be a transaction request or be it a response. In the first case the newTransactionCallback
is invoked on the transaction, this in turn invokes the listener()
in blockchain.go. response
holds the value of a nonce during the mining process which solves the nonce to achieve the required hash. This is the value which is being checked upon when mine()
executes in block.go.
I wish you felt excited about blockchains after reading this article, hopefully the code in my repository helps you in your subequent Go blockchain projects.