前言
学了两个月左右的区块链理论知识,学来学去总是不深入,总是觉得迷迷糊糊,于是我打算自己造一个小型私有的区块链。当然这一个项目我也是有借鉴前人视频的,因为项目这个东西其实单靠一个人的力量根本搞不动的,只好借鉴前人的视频来深化自己的基础内容,同时也增加自己的项目经验。
环境
编程语言:go语言
测试环境以及运行环境:windows11
理论知识
区块链:存数据的块+hash指针链。
hash:哈希,类似于一个数学函数f(x),其输出空间f(x)为2^256次方,而输入空间x是无限的。所以有一定几率会有hash碰撞。
hash碰撞:就是两个不同的输入,却有两个相同的输出。但是专家说这个几率比bigbang还小,专家把它称为collision resistance。此外专家还提出了其余两个hash的性质:hiding , puzzle friendly。
collision resistance:耐撞,就是hash可以尽量避免碰撞的意思。
hiding:单向的,不可逆转的,即知道x可以推f(x),但是知道f(x)不可以推x。
puzzle friendly:谜一般的存在,就是这玩意很难快速通过x找出f(x),只能一个个不同的x尝试,所以这也成为了pow!
pow:proof of work,工作量证明,说白了:你家电脑开着挖矿计算f(x),电表转了多少w(或者 尝试x的次数),这就是你的工作量证明。进而v神提出了某太坊(也是区块链),想要改变某特币中pow为pos。
pos:proof of state,权益证明,说白了:有钱就有权!
UTXO:未花费的交易输出,就是一个数据结构存放了未花费的交易,这样就不用从当前区块往前查找自己获得的比特币以及自己花费的比特币来计算自己余额。
全节点:保存区块链一切的信息
轻节点:只保留与自身相关的交易信息,等到需要验证的时候,去全节点进行验证交易信息是否伪造篡改。
交易信息:transaction,它是矿工将区块链上相关的转账交易等进行打包,生成merkle tree,然后通过merkle tree获得根值的hash值。
merkle tree:默克尔树,哈希树,叶节点链接交易信息,并且逐层取hash,直到获得根hash,将其存储block header(区块头),如下图所示。
挖矿:用电获得算力,用算力计算hash值,查看hash是否小于区块链系统指定的target区间。这个hash值是block header中的信息进行concat(链接)的字节,然后将这些字节进行计算获得hash(当然nonce是可变的,所以可以使得每次输出的hash不同,从而找到符合的hash值)
初步实现pow的框架
初步实现:能算hash,让其达到target区间内的hash。
1.实现单个的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.实现一整条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.实现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
}
大致的流程如下: