Arno

Arno

Building a blockchain based on the UTXO model from scratch using golang (Part 1: Implementing the simplest blockchain)

The method to truly understand the underlying principles of blockchain is to write a bottom layer. The development difficulty of the UTXO model blockchain is relatively simple. After the development is completed, you can try the account model.

I won't write about what blockchain is, as well as UTXO model and account model. There are many online resources available. Before writing, you can first understand the basic knowledge of blockchain.

Development environment: goland+go1.20
The project's GitHub address: lighteningchain
Mirror address: lighteningchain(1)

Create Project#

Create a project using goland, name it lighteningchain, and initialize the project with go mod init lighteningchainInsert image description here

Block Structure#

Each block should contain header (head) information to summarize the block, and then store important data to be saved in the data storage area (body) of the block.
First, define the block structure and the blockchain structure.

type Block struct {
	Timestamp int64
	Hash      []byte // The block hash is its ID
	PrevHash  []byte
	Data      []byte
}

type BlockChain struct {
	Blocks []*Block
}

Calculate Hash#

After having the structures, calculating the hash of the block is also essential.

func (b *Block) SetHash() {
	information := bytes.Join([][]byte{Int64ToByte(b.Timestamp), b.PrevHash, b.Data}, []byte{})
	hash := sha256.Sum256(information) // The sha256 package implements the SHA224 and SHA256 hash algorithms defined in FIPS 180-4.
	b.Hash = hash[:]
}

func Int64ToByte(num int64) []byte {
	var buf = make([]byte, 8)
	binary.BigEndian.PutUint64(buf, uint64(num))
	return buf
}

The information variable is the concatenated byte string of the block's attributes. bytes.Join can be used to concatenate multiple byte strings, and the second parameter is the separator when concatenating byte strings, which is set to []byte{} here, which means empty.

Create Block and Blockchain#

Okay, the preparation work for the blockchain is done, now we can start creating blocks and linking them together.
First, define the block creation function.

func CreateBlock(prevhash []byte, data []byte) *Block {
	block := Block{time.Now().Unix(), []byte{}, prevhash, data}
	block.SetHash() // Calculate the hash after all data is added
	return &block
}

But each block needs to be linked to the hash of the previous block. What about the first block? This is where the genesis block comes in.

func GenesisBlock() *Block {
	genesisWords := "HelloWorld!"
	return CreateBlock([]byte{}, []byte(genesisWords))
}

Add the created blocks to the blockchain.

func (bc *BlockChain) AddBlock(data string) {
	newBlock := CreateBlock(bc.Blocks[len(bc.Blocks)-1].Hash, []byte(data))
	bc.Blocks = append(bc.Blocks, newBlock)
}

Now the entire blockchain is built. Let's run and test it. Oh, don't forget to initialize it.

func CreateBlockChain() *BlockChain {
	myBlockchain := BlockChain{}
	myBlockchain.Blocks = append(myBlockchain.Blocks, GenesisBlock())
	return &myBlockchain
}

Write main Function to Run and Test the Blockchain#

Now we can run the blockchain, write a few instances ourselves, and then output them to see.

func main() {
	blockchain := CreateBlockChain()
	time.Sleep(time.Second)
	blockchain.AddBlock("This is first Block after Genesis")
	time.Sleep(time.Second)
	blockchain.AddBlock("This is second!")
	time.Sleep(time.Second)
	blockchain.AddBlock("Awesome!")
	time.Sleep(time.Second)

	for num, block := range blockchain.Blocks {
		fmt.Printf("number:%d Timestamp: %d\n", num, block.Timestamp)
		fmt.Printf("number:%d hash: %x\n", num, block.Hash)
		fmt.Printf("number:%d Previous hash: %x\n", num, block.PrevHash)
		fmt.Printf("number:%d data: %s\n", num, block.Data)
	}
}

Click run, the output is as follows:

number:0 Timestamp: 1677568251
number:0 hash: 5af650b22cc85f225c2c42850714be9466408025dfec7191436d7efa7a716433
number:0 Previous hash: 
number:0 data: HelloWorld!
number:1 Timestamp: 1677568252
number:1 hash: 3b2eafc41f8c0bd319fd2e7fd44ff2d15215ab931e6a599dc83b60f055653cb8
number:1 Previous hash: 5af650b22cc85f225c2c42850714be9466408025dfec7191436d7efa7a716433
number:1 data: This is first Block after Genesis
number:2 Timestamp: 1677568253
number:2 hash: 1be2cf3334f59a54a33e9c28957ecb836f17d731f6731bc7540066f99c2338c7
number:2 Previous hash: 3b2eafc41f8c0bd319fd2e7fd44ff2d15215ab931e6a599dc83b60f055653cb8
number:2 data: This is second!
number:3 Timestamp: 1677568254
number:3 hash: 460d954e05c98774b58426372f61997bed92ce7b4e2effb749b7bc2f0c19bdf3
number:3 Previous hash: 1be2cf3334f59a54a33e9c28957ecb836f17d731f6731bc7540066f99c2338c7
number:3 data: Awesome!

Process finished with the exit code 0

Everything is normal!

Summary#

In this chapter, we built a very simple blockchain to deepen our understanding of the data structure of blocks and blockchains. It can be seen that the current project does not conform to the principle of high cohesion and low coupling, so the next chapter will optimize it and introduce the Proof of Work (PoW) consensus mechanism.

References#

Go Blockchain Tutorial Part 1: Hello, Blockchain

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.