废话不多说,开始跟我用golang搭建个迷你区块链:
首先,引入包:
package main
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"fmt"
"math"
"math/big"
"time"
)
设置一些常量,工作量证明难度系数dif,搜索上界INT64_MAX:
const (
dif = 20
INT64_MAX = math.MaxInt64
)
然后我们设计一个简单的区块体和区块链体,每个区块呢有前个区块哈希,本区块哈希,数据,链长,时间戳和随机数:
type Block struct {
PrevHash []byte
Hash []byte
Data string
Height int64
Timestamp int64
Nonce int
}
type BlockChain struct {
Blocks []Block
}
实现一段工作量证明函数,这个工作量证明呢以后聊共识算法会细讲的,也就是这里实现了不可篡改(reigns最近被raft血虐的,TAT):
func IntToHex(num int64) []byte {
buff := new(bytes.Buffer)
err := binary.Write(buff, binary.BigEndian, num)
if err != nil {
panic(err)
}
return buff.Bytes()
}
func ProofOfWork(b Block, dif int) ([]byte, int) {
target := big.NewInt(1)
target.Lsh(target, uint(256-dif))
nonce := 0
for ; nonce < INT64_MAX; nonce++ {
check := bytes.Join(
[][]byte{b.PrevHash,
[]byte(b.Data),
IntToHex(b.Height),
IntToHex(b.Timestamp),
IntToHex(int64(nonce))},
[]byte{})
hash := sha256.Sum256(check)
var hashInt big.Int
hashInt.SetBytes(hash[:])
if hashInt.Cmp(target) == -1 {
return hash[:], nonce
}
}
return []byte(""), nonce
}
然后呢这段函数实现生成创世块,也就是链头啦:
func GenesisBlock(data string) BlockChain {
var bc BlockChain
bc.Blocks = make([]Block, 1)
bc.Blocks[0] = Block{
PrevHash: []byte(""),
Data: data,
Height: 1,
Timestamp: time.Now().Unix(),
}
bc.Blocks[0].Hash, bc.Blocks[0].Nonce = ProofOfWork(bc.Blocks[0], dif)
return bc
}
这段呢是生成新的区块,也就是俗称的挖矿,当然了真实的挖矿可比这个复杂多了:
func GenerateBlock(bc *BlockChain, data string) {
prevBlock := bc.Blocks[len(bc.Blocks)-1]
block := Block{
PrevHash: prevBlock.Hash,
Data: data,
Height: prevBlock.Height + 1,
Timestamp: time.Now().Unix(),
}
block.Hash, block.Nonce = ProofOfWork(block, dif)
bc.Blocks = append(bc.Blocks, block)
}
再写个打印区块函数:
func Print(bc BlockChain) {
for _, i := range bc.Blocks {
fmt.Printf("PrevHash: %x\n", i.PrevHash)
fmt.Printf("Hash: %x\n", i.Hash)
fmt.Println("Block's Data: ", i.Data)
fmt.Println("Current Height: ", i.Height)
fmt.Println("Timestamp: ", i.Timestamp)
fmt.Println("Nonce: ", i.Nonce)
}
}
好啦,各个函数都写完了,不多不少99行。
写个main函数看看吧~
来,reigns开始挖矿,先挖个创世块,给alice转账2刀,然后再给alice转3刀,看看这几次次操作怎么记录在区块链中吧:
func main() {
blockchain := GenesisBlock("i am reigns")
GenerateBlock(&blockchain, "send 2$ to alice")
GenerateBlock(&blockchain, "send 3$ to alice")
Print(blockchain)
}
输出结果:
由于创世块没有前面的区块,所以他的PrevHash就是空,记录给alice转账2刀的这个区块,它的PrevHash就是创世块了,后面的以此类推,得到一个不可篡改链。
也可以发现啊,区块生成并不是一下子执行完成的,三个区块用了接近7秒钟,这就是工作量证明算法起的作用了,防止恶意节点不劳而获,每个新的区块都需要付出一定计算力才能得到,当然这里没有设置很高的困难度(dif = 20),也没有根据时间动态增加难度,比特币网络中一般设置的挖矿难度都需要10分钟左右的,想想2009年的10分钟和2019的10分钟,难度提了多少~
怎么样,是不是了解了区块链的基本结构呢,当然了成熟的链使用的数据存储结构肯定不会是一个字符串,比如比特币用的是默克尔树,交易也不会这么随便就成功,都需要依靠一个叫做智能合约的东东进行约束的。
好啦,今天就讲这么多。
如果对区块链感兴趣,可以关注下面这个公众号哦,推送的全是区块链干货~