This is a creation in Article, where the information may have evolved or changed.
The series of articles I have put on GitHub: blockchain-tutorial, Updates will be on GitHub, and may not be synced here. If you want to run the code directly, you can clone the tutorial repository on GitHub and go to the SRC directory to execute make
.
Introduction
Blockchain is one of the most revolutionary technologies of the 21st century, it is still in the growing stage, and there is still a lot of potential has not yet emerged. Essentially, a blockchain is just a distributed database. What makes it unique, however, is that the blockchain is an open database, not a private database, which means that every person who uses it has a full or partial copy. A new record can be added to the database only after the other database administrator has consented to it. In addition, it is the blockchain that makes crypto currency and smart contracts a reality.
In this series of articles, we will implement a simplified version of the blockchain, based on which to build a simplified version of the cryptocurrency.
Block
Let's talk about "chunks" in the blockchain. In a blockchain, chunks are stored for valid information. For example, bitcoin block storage of valid information is the bitcoin transaction, transaction information is also the essence of all cryptocurrency. In addition, chunks contain technical information, such as the version, the current timestamp, and the hash of the previous chunk.
In this article, we will not implement a blockchain as described in the Bitcoin technical specification, but instead implement a simplified version of the blockchain, which contains only a few key pieces of information. It looks like this:
type Block struct { Timestamp int64 Data []byte PrevBlockHash []byte Hash []byte}
- Timestamp is the current timestamp, which is the time the chunk was created.
- data is the actual and valid information for chunk storage.
- Prevblockhash Stores the hash of the previous block.
- Hashing is the hash of the current block.
In the bitcoin technical specification,Timestamp, prevblockhash, Hash is the chunk header (block header), the chunk header is a separate data structure. The transaction, that is, data here, is another separate data structure. For the sake of brevity, I mixed the two together.
So how do we calculate the hash? How the hash is computed is a very important part of the blockchain. It is this feature that makes the blockchain safe. Calculating a hash is a very difficult operation to compute. Even on high-speed computers, it takes some time (that's why people buy the GPU to dig bitcoins). This is an intentional architectural design that deliberately makes it difficult to add new chunks, so it's hard to make changes once the chunks are added. In the next few articles of this series, we will discuss and implement this mechanism.
Currently, we take only a few fields of the block structure (Timestamp, Data, and Prevblockhash), connect them to each other, and then calculate a SHA-256 hash on the concatenated results. Let's SetHash
complete this task in the method:
func (b *Block) SetHash() { timestamp := []byte(strconv.FormatInt(b.Timestamp, 10)) headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, timestamp}, []byte{}) hash := sha256.Sum256(headers) b.Hash = hash[:]}
Next, in accordance with the Convention of Golang, we will implement a function to simplify the creation of a chunk:
func NewBlock(data string, prevBlockHash []byte) *Block { block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}} block.SetHash() return block}
This is all part of the chunk!
Block chain
Let's implement a blockchain. In essence, a blockchain is simply a database with a specific structure, and is a list of ordered, back-up connections. This means that chunks are stored in the order in which they are inserted, and each block is connected to the previous block. This structure allows us to quickly get the latest blocks on the chain and efficiently retrieve a block from the hash.
In Golang, this structure can be implemented by an array and map: The array stores an ordered hash (the array in the Golang is ordered), the map storage Hask-block pair (Golang, map is unordered). But in the basic prototype phase, we only used the array, because it does not need to be hashed to get the block.
type Blockchain struct { blocks []*Block}
This is our first blockchain! I never thought it would be so easy.
Now, let's add a block to it:
func (bc *Blockchain) AddBlock(data string) { prevBlock := bc.blocks[len(bc.blocks)-1] newBlock := NewBlock(data, prevBlock.Hash) bc.blocks = append(bc.blocks, newBlock)}
Complete! But is that really the case?
In order to add a new block, we have to have an existing block, but now our chain is empty and a block is not! Therefore, in any blockchain, there must be at least one block. Such a block, the first block in the chain, is often called the Genesis block (Genesisblocks). Let's implement a method to create a Genesis block:
func NewGenesisBlock() *Block { return NewBlock("Genesis Block", []byte{})}
Now, we can implement a function to create a blockchain with a Genesis block:
func NewBlockchain() *Blockchain { return &Blockchain{[]*Block{NewGenesisBlock()}}}
To check if one of our blockchain is working as expected:
func main() { bc := NewBlockchain() bc.AddBlock("Send 1 BTC to Ivan") bc.AddBlock("Send 2 more BTC to Ivan") for _, block := range bc.blocks { fmt.Printf("Prev. hash: %x\n", block.PrevBlockHash) fmt.Printf("Data: %s\n", block.Data) fmt.Printf("Hash: %x\n", block.Hash) fmt.Println() }}
Output:
Prev. hash:Data: Genesis BlockHash: aff955a50dc6cd2abfe81b8849eab15f99ed1dc333d38487024223b5fe0f1168Prev. hash: aff955a50dc6cd2abfe81b8849eab15f99ed1dc333d38487024223b5fe0f1168Data: Send 1 BTC to IvanHash: d75ce22a840abb9b4e8fc3b60767c4ba3f46a0432d3ea15b71aef9fde6a314e1Prev. hash: d75ce22a840abb9b4e8fc3b60767c4ba3f46a0432d3ea15b71aef9fde6a314e1Data: Send 2 more BTC to IvanHash: 561237522bb7fcfbccbc6fe0e98bbbde7427ffe01c6fb223f7562288ca2295d1
Summarize
We created a very simple blockchain prototype: It was just a series of chunks of an array, each associated with the previous block. The real blockchain is much more complicated than this. Adding new blocks in our blockchain is very simple and fast, but in a real blockchain, adding a new block requires a lot of work: you have to go through a very heavy calculation (this mechanism is called the workload proof) to get the power to add a new block. Also, blockchain is a distributed database that does not have a single decision maker. Therefore, a new block must be confirmed and agreed by the other participants in the network (this mechanism is called consensus (consensus)). One more thing, our blockchain doesn't have any deals!
In the next article we will cover each of these features.
- Source code covered in this article: part_1
- Chunk hashing algorithm: Https://en.bitcoin.it/wiki/Bl ...
Original:
Building Blockchain in Go. Part 1:basic Prototype