Use Go language to build a small blockchain from scratch (1)

Preface

I have learned the theoretical knowledge of blockchain for about two months, but I never got to the bottom of it and felt confused. So I planned to build a small private blockchain myself. Of course, I also drew on predecessors’ videos for this project, because the project cannot be accomplished by one person alone, so I had to draw on predecessors’ videos to deepen my basic content and at the same time increase my project experience. .

environment

Programming language: go language
Test environment and running environment: windows11

theoretical knowledge

theoretical knowledge
Blockchain: block + hash pointer chain to store data.
Hash: Hash, similar to a mathematical function f(x), whose output space f(x) is 2^256th power, and the input space x is infinite. So there is a certain probability that there will be hash collision.
Hash collision: two different inputs, but two identical outputs. But experts said that the probability is smaller than bigbang, experts called it for collision resistance. In additionexperts also proposed the other two properties of hash: hiding and puzzle friendly.
Collision resistance: Collision resistance means that hash can avoid collisions as much as possible.
hiding: one-way, irreversible, that is, knowing x can push f(x), but knowing f(x) cannot push x.
Puzzle friendly: The existence of this puzzle is that it is difficult to quickly find f(x) through x. You can only try different x one by one, so this also becomes pow!
pow: proof of work, proof of work, to put it bluntly: your computer is running to mine and calculate f(x), how many times w does the meter turn (or the number of times you try x), this is Your proof of work. Then God V proposed a certain Ethereum (also a blockchain) and wanted to change the POW in a certain special currency to POS.
pos: proof of state, proof of equity, to put it bluntly: if you have money, you have power!
UTXO: Unspent transaction output is a data structure that stores unspent transactions, so that you don’t have to look forward from the current block for the Bitcoins you obtained and the Bitcoins you spent. Calculate your own balance.
Insert image description here
Full node: Saves all information in the blockchain.
Light node: Only keeps transaction information related to itself. When verification is needed, go to the full node. Verify whether the transaction information is forged or tampered with.
Transaction information: transaction, which is when miners package related transfer transactions on the blockchain, generate a merkle tree, and then obtain the hash value of the root value through the merkle tree.
Merkle tree: Merkle tree, hash tree, leaf node links transaction information, and takes hash layer by layer until the root hash is obtained, and stores it in the block header (block header), as shown below shown.
Insert image description here
Mining: Use electricity to obtain computing power, use the computing power to calculate the hash value, and check whether the hash is smaller than the target interval specified by the blockchain system. This hash value is the bytes used to concat (link) the information in the block header, and then calculate these bytes to obtain the hash (of course the nonce is variable, so the hash output can be made different each time, so as to find a consistent hash value)

Initial implementation of the POW framework

Preliminary implementation: can calculate hash, so that it reaches the hash within the target range.

1. Implement a single block (block)

package block

import (
	"bytes"
	"crypto/sha256"
	"strconv"
	"time"
)

type Block struct {
    
    
	//区块 包含 高度 prevhash thishash merkleTree(交易数据) timestamp nonce
	Height        int64
	PrevBlockHash []byte
	Data          []byte
	thisBlockHash []byte
	Timestamp     int64
	Nonce         int64
}

// 创建新的区块
func CreateBlock(data string, height int64, prevBlockHash []byte) *Block {
    
    
	block := new(Block)
	block.Height = height
	block.Data = []byte(data)
	block.Timestamp = time.Now().Unix()
	block.PrevBlockHash = prevBlockHash
	block.thisBlockHash = nil
	block.Nonce = 0
	//pow:proof of work,工作量的证明,返回hash和nonce
	pow := NewProofOfWork(block)
	//开始挖矿
	hash, nonce := pow.Start()
	//更新两者
	block.thisBlockHash = hash[:]
	block.Nonce = nonce
	return block
}

// 形成hash值
func (block *Block) SetHash() {
    
    
	//高度字节化
	height := Int64ToByte(block.Height)
	//timeStamp := Int64ToByte(block.Timestamp)
	//时间戳字节化
	timeString := strconv.FormatInt(block.Timestamp, 2)
	timeStamp := []byte(timeString)
	//拼接所有属性
	blockBytes := bytes.Join([][]byte{
    
    height, block.PrevBlockHash, block.Data, timeStamp, block.thisBlockHash}, []byte{
    
    })
	//形成hash
	hash := sha256.Sum256(blockBytes)
	//类型转换
	block.thisBlockHash = hash[:]
}

// 生成创世块
func CreateGenesisBlock(data string) *Block {
    
    
	genesisBlock := new(Block)
	//创世区块的长度为1,无前hash指针
	genesisBlock.PrevBlockHash = []byte{
    
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	genesisBlock.Data = []byte(data)
	genesisBlock.Height = 1
	genesisBlock.Timestamp = time.Now().Unix()
	genesisBlock.SetHash()
	return genesisBlock
}

2. Implement an entire blockchain (blockchain)

package block

type BlockChain struct {
    
    
	//存储区块
	Blocks []*Block
}

// 创建带有创世区块的区块链
func CreateBCWithGB() *BlockChain {
    
    
	genesisBlock := CreateGenesisBlock("创世区块")
	return &BlockChain{
    
    []*Block{
    
    genesisBlock}}
}

// 往区块链添加新的区块
func (bc *BlockChain) AppendBlock(data string, height int64, preHash []byte) {
    
    
	newBC := CreateBlock(data, height, preHash)
	bc.Blocks = append(bc.Blocks, newBC)
	//newBC := block.CreateBlock(data, int64(len(bc.Blocks)), bc.Blocks[len(bc.Blocks)-1].thisBlockHash)

}

3. Implement proof of work (proof of work)

package block

import (
	"bytes"
	"crypto/sha256"
	"fmt"
	"math/big"
)

// 代表该目标区域的hash中前16位为0
const targetZeroBits = 30

type ProofOfWork struct {
    
    
	block  *Block   //当前要验证的区块
	target *big.Int //挖矿难度(目标区域)
}

// 拼接数据,返回字节数组,以便形成hash
func (pow *ProofOfWork) prepData(nonce int64) []byte {
    
    
	data := bytes.Join(
		[][]byte{
    
    
			pow.block.PrevBlockHash,
			pow.block.Data,
			Int64ToByte(pow.block.Timestamp),
			Int64ToByte(int64(targetZeroBits)),
			Int64ToByte(int64(nonce)),
			Int64ToByte(int64(pow.block.Height))}, []byte{
    
    })
	return data
}

// 启动工作量证明(挖矿!)
func (pow *ProofOfWork) Start() ([]byte, int64) {
    
    
	//1.block属性拼接成字节数组
	//2.生成hash
	//3.判断hash有效性
	nonce := 0
	var hashInt big.Int //用于存储当前所生成的hash值
	var hash [32]byte
	for {
    
    
		prep := pow.prepData(int64(nonce))
		hash = sha256.Sum256(prep)
		fmt.Println(hash)
		hashInt.SetBytes(hash[:])
		fmt.Println(hash)
		if pow.target.Cmp(&hashInt) == 1 {
    
    
			break
		}
		nonce = nonce + 1
	}
	return hash[:], int64(nonce)
}

// 判断hash是否有效
func (pow *ProofOfWork) isValid() bool {
    
    
	var hashInt big.Int
	hashInt.SetBytes(pow.block.thisBlockHash)
	if pow.target.Cmp(&hashInt) == -1 {
    
    
		return false
	}
	return true
}

// 创建工作量证明的对象(矿工)
func NewProofOfWork(block *Block) *ProofOfWork {
    
    
	pow := new(ProofOfWork)
	//创建一个初始值为1的target
	target := big.NewInt(1)
	//左移256-targetZeroBits,这样就可以得到它的目标区域target
	target = target.Lsh(target, 256-targetZeroBits)
	pow.target = target
	pow.block = block
	return pow
}

The general process is as follows:
Insert image description here

Guess you like

Origin blog.csdn.net/qq_46255801/article/details/132024670