This is a creation in Article, where the information may have evolved or changed.
Blockchain is currently the hottest topic, the vast number of readers have heard of bitcoin, and perhaps a smart contract, I believe we all want to know how this all work. This article is to help you use the Go language to implement a simple blockchain, with less than 200 lines of code to reveal the principle of the blockchain! High-availability architectures will also continue to introduce more Blockchain articles, please click on the above blue "high-availability architecture" concern.
"With less than 200 lines of go code, you can implement your own blockchain!" "Does that sound interesting?" What better way to learn and practice than to develop a blockchain of your own? Then let's do it together!
Because we are a technology company in the Medical and health field, we use the heartbeat data of human peace (BPM heart rate) as the sample data in this article. Let's start by counting the number of heartbeats you have in a minute, and then note that this number may be used in the next section.
Through this article, you will be able to:
Create your own Blockchain
Understand how hash functions keep the integrity of the blockchain
How to create and add a new block
How multiple nodes compete for building blocks
View the entire chain through a browser
All the other basics about blockchain
However, the consensus algorithm articles such as the Work Proof algorithm (PoW) and the Equity Proof algorithm (PoS) will not be covered. At the same time, in order to make you more aware of the blockchain and block additions, we have simplified the process of network interaction, such as the "full network broadcast" process and other content will be added in the next article.
Let's get started!
Set up
Let's say you already have a little bit of Go language development experience. After installing and configuring the Go development environment, we also want to obtain some of the following dependencies:
Go get Github.com/davecgh/go-spew/spew
Spew can help us see both struct and slice data structures directly in the console.
Go get Github.com/gorilla/mux
Gorilla's MUX package is very popular and we use it to write Web handler.
Go get github.com/joho/godotenv
Godotenv can help us read the. env configuration file in the project root so that we don't have to hardcode the configuration of HTTP port into code. For example, like this:
addr=8080
Next, we create a main.go file. After most of our work revolves around this file, let me start coding it!
Import dependency
We import all the dependency packages in a declarative way:
Package Main
Import (
"Crypto/sha256"
"Encoding/hex"
"Encoding/json"
"IO"
"Log"
"Net/http"
"OS"
"Time"
"Github.com/davecgh/go-spew/spew"
"Github.com/gorilla/mux"
"Github.com/joho/godotenv"
)
Data model
We then define a struct that represents the data model for each block that makes up the blockchain:
Type Block struct {
Index int
Timestamp string
BPM int
Hash String
Prevhash String
}
Index is the position of this block in the entire chain
Timestamp is obviously the timestamp when the block was generated.
Hash is the hash value generated by this block through the SHA256 algorithm.
Prevhash represents the SHA256 hash value of the previous block
BPM beats the number of heartbeats per minute, which is the heart rate. Do you remember what the article said at the beginning?
Next, we define a structure that represents the entire chain, and the simplest representation is the slice of a Block:
var Blockchain []block
We use the hashing algorithm (SHA256) to determine and maintain the correct order of blocks and blocks in the chain, ensuring that the Prevhash value of each block equals the hash value in the previous block, so that the chain is built in the correct block order:
Hashing and building blocks
Why do we need a hash? There are two main reasons:
Uniquely identifies data in space-saving premises. Hashing is calculated using the entire block of data, in our case, the entire block of data is computed by SHA256 into a fixed-length, non-forged string.
Maintain the integrity of the chain. By storing the hash value of the previous block, we are able to ensure that each block is in the correct order in the chain. Any tampering with the data will change the hash value and also destroy the chain. For example, in the area of health care we are engaged in, for example, if a malicious third party modifies the value of a "person's life insurance" for the sake of adjusting the price of a "human", the entire chain becomes untrustworthy.
We then write a function to calculate the SHA256 hash value for the given data:
Func Calculatehash (block block) string {
Record: = String (block. Index) + block. Timestamp + string (block. BPM) + block. Prevhash
H: = sha256. New ()
H.write ([]byte (record))
Hashed: = H.sum (nil)
Return hex. Encodetostring (Hashed)
}
The Calculatehash function takes a block that calculates the SHA256 hash value from the index,timestamp,bpm in the block, as well as the Prevhash value. Next we'll be able to carry a function that generates blocks:
Func Generateblock (Oldblock block, BPM Int) (block, error) {
var newblock Block
T: = time. Now ()
Newblock.index = Oldblock.index + 1
Newblock.timestamp = t.string ()
NEWBLOCK.BPM = BPM
Newblock.prevhash = Oldblock.hash
Newblock.hash = Calculatehash (Newblock)
Return Newblock, Nil
}
Where index is incremented from the index of the given previous piece, the timestamp is passed directly through time. Now () function, the hash value is calculated from the preceding Calculatehash function, and Prevhash is the hash value of the previous block given.
Check Block
Having done with the block generation, we need to have a function to help us determine if a block has been tampered with. Check the Index to see if this block is incremented correctly, check whether the hash of the Prevhash and the previous block is consistent, and then calculatehash check whether the hash value of the current block is correct. With these steps we can write a check function:
Func isblockvalid (Newblock, Oldblock Block) bool {
If oldblock.index+1! = Newblock.index {
return False
}
If Oldblock.hash! = Newblock.prevhash {
return False
}
If Calculatehash (newblock)! = Newblock.hash {
return False
}
return True
}
In addition to the check block, we also encounter a problem: Two nodes are generated blocks and added to their respective chains, then we should be based on whom? Here's the details we left to the next article, here first let's remember a principle: always choose the longest chain.
In general, a longer chain means that its data (state) is updated, so we need a function
can help us to change the local expiration chain into the latest chain:
Func Replacechain (newblocks []block) {
If Len (newblocks) > Len (Blockchain) {
Blockchain = Newblocks
}
}
At this point, we basically complete all the important functions. Next, we need a convenient and intuitive way to view our chain, including data and status. Viewing Web pages through a browser can be the most appropriate way!
WEB Services
I guess you must be familiar with traditional Web services and development, so this part of you is sure to look at it.
With the Gorilla/mux package, we first write a function to initialize our Web service:
Func run () error {
MUX: = Makemuxrouter ()
HTTPADDR: = os. Getenv ("ADDR")
Log. Println ("Listening on", OS. Getenv ("ADDR"))
S: = &http. server{
ADDR: " :" + httpaddr,
Handler: Mux,
ReadTimeout: Ten * time. Second,
WriteTimeout: Ten * time. Second,
Maxheaderbytes:1 << 20,
}
If err: = S.listenandserve (); Err! = Nil {
return err
}
return Nil
}
The port number is obtained by the above mentioned. Env to add some basic configuration parameters, this Web service can already listen and serve!
Then we'll define the different endpoint and the corresponding handler. For example, for a GET request for "/" We can view the entire chain, and the "/" POST request can create blocks.
Func makemuxrouter () http. Handler {
Muxrouter: = Mux. Newrouter ()
Muxrouter.handlefunc ("/", Handlegetblockchain). Methods ("GET")
Muxrouter.handlefunc ("/", Handlewriteblock). Methods ("POST")
Return Muxrouter
}
Handler for GET requests:
Func Handlegetblockchain (w http. Responsewriter, R *http. Request) {
Bytes, err: = json. Marshalindent (Blockchain, "", "" )
If err! = Nil {
http. Error (W, err. Error (), HTTP. Statusinternalservererror)
Return
}
Io. WriteString (w, string (bytes))
}
To simplify, we return the entire chain directly in JSON format, and you can access localhost:8080 or 127.0.0.1:8080 in the browser (8080 here is the port number you defined in. env ADDR).
The handler of the post request is slightly more complicated, so let's define the payload of the POST request first:
Type Message struct {
BPM int
}
And look at the implementation of handler:
Func Handlewriteblock (w http. Responsewriter, R *http. Request) {
var m Message
Decoder: = JSON. Newdecoder (R.body)
If err: = decoder. Decode (&M); Err! = Nil {
Respondwithjson (W, R, http. Statusbadrequest, R.body)
Return
}
Defer R.body.close ()
Newblock, err: = Generateblock (Blockchain[len (Blockchain)-1], M.BPM)
If err! = Nil {
Respondwithjson (W, R, http. Statusinternalservererror, M)
Return
}
If Isblockvalid (Newblock, Blockchain[len (Blockchain)-1]) {
Newblockchain: = Append (Blockchain, Newblock)
Replacechain (Newblockchain)
Spew. Dump (Blockchain)
}
Respondwithjson (W, R, http. statuscreated, Newblock)
}
The payload defined above can be used in our POST request body, for example:
{"BPM": 75}
Do you remember the Generateblock function we wrote earlier? It accepts a "previous block" parameter, and a BPM value. Post handler can obtain the BPM value in the request body after accepting the request, then can generate a new block with the help of generating block function and check block
In addition, you can also:
Use spew. Dump This function can be very beautiful and easy to read the way the struct, slice and other data printed in the console, convenient for us to debug.
When testing a POST request, you can use the POSTMAN chrome plugin, which is more intuitive and convenient than curl.
After the POST request has been processed, we need to return a response from the client, regardless of whether the block was created successfully or not:
Func Respondwithjson (w http. Responsewriter, R *http. Request, code int, payload interface{}) {
Response, Err: = json. Marshalindent (Payload, "", "" )
If err! = Nil {
W.writeheader (http. Statusinternalservererror)
W.write ([]byte ("HTTP 500:internal Server Error")
Return
}
W.writeheader (Code)
W.write (response)
}
It's almost done.
Next, we "assemble" the functions of the blockchain, the Web service, into the function:
Func Main () {
ERR: = godotenv. Load ()
If err! = Nil {
Log. Fatal (ERR)
}
Go func () {
T: = time. Now ()
Genesisblock: = block{0, t.string (), 0, "", ""}
Spew. Dump (Genesisblock)
Blockchain = Append (Blockchain, Genesisblock)
}()
Log. Fatal (Run ())
}
The Genesisblock (Genesis block) Here is the most important part of the main function, which initializes the blockchain, after all, the Prevhash of the first block is empty.
Oh, yes! Completed the
You can get the full code from here: [Github repo] (https://github.com/mycoralhealth/blockchain-tutorial/blob/master/main.go)
Let's start it up:
Go Run main.go
In the terminal, we can see the log information that the Web server started and print out the information of the Genesis Block:
Then we open the browser and visit the localhost:8080 address, and we can see that the page shows the current entire blockchain information (of course, there is only one Genesis block):
Next, we'll send some POST requests via POSTMAN:
Refresh the page just now, there are a few more blocks in the chain, exactly what we just generated, and you can see that the order and hash values of the blocks are correct.
Next
We have just completed a block chain of our own, although very simple (the burrow), but it has block generation, hash calculation, block check and other basic capabilities. You can then continue to learn more about other important chunks of the blockchain, such as proof of effort, consensus algorithms such as proof of entitlement, or smart contracts, dapp, side-chaining, and so on.
Currently this implementation does not include any peer-to network content, we will supplement this section in the next article, of course, we encourage you to practice on this basis!
RELATED LINKS
Special Recommendation:
Bitcoin, Ethereum, ERC20, PoW, PoS, smart contracts, lightning networks ...
Want to get to know and discuss these topics in depth? High-availability architecture in the Knowledge Planet (small circle) to create a blockchain learning group, a joint learning blockchain including digital currency frontier technology, Welcome to click the link to join.
Blockchain Learning Group
this author coral Health, translated by Jiatong. Please specify the source, technical original and architectural practice articles, welcome to the public number menu "Contact us" to contribute.