一、区块链介绍
区块链是一种基于密码学保障和P2P网络的分布式记账技术。在如今Web3.0时代下,去中心化的潮流已经势不可挡,区块链技术更是其中的一项关键技术。
区块链的三大技术
- 分布式账本
- 共识机制
- 密码学
- 智能合约
本文实现的区块链系统的共识机制是最为常见和经典的PoW(工作量证明),其他的共识机制还有PoS(股权证明)、DPoS(委托权益证明)和BFT(拜占庭容错算法)等。
二、准备工作
Go环境安装
官网地址:golang.org/dl/ 。
国内打不开的可以打开这个地址:golang.google.cn/dl/
根据自己的系统选择合适的安装包,安装后复制安装所在路径到环境变量中
在终端输入检查是否安装成功:
go version
编译环境
Visual Studio Code、Goland等开发环境
VSCode需安装拓展(在拓展搜索Go,第一个即是)
三、一个基本的区块链
区块链的结构是一串单向的链表,一个区块的由上一个区块的哈希值和本区块的哈希值形成连接,这样确保了链上的任一区块的改变都将会被检测到
我们来构建一个基本的区块:
import(
"fmt"
"crypto/sha256"
"encoding/hex"
"strconv"
)
type Block struct {
previousHash string
hash string
metadata []string
timestamp string
}
func Hash(s string)(string) {
hash := sha256.Sum256([]byte(s))
return hex.EncodeToString(hash[:])
}
func calculateHash(block *Block)(string) {
str := block.timestamp + strconv.Itoa(block.nonce)
return Hash(str)
}
为了计算哈希值,我们引用了Go基本库中的crypto库,我们定义一个名为Block的结构体,储存了上一个区块的哈希值、本区块的哈希值和时间戳。
随后创建一个链:
var latestPointer *string
type BlockChain struct {
chain []Block
}
func createGenesisBlock(c *BlockChain) {
var genesisBlock Block
genesisBlock.timestamp = "01/01/2023"
genesisBlock.metadata = []string {"null"}
genesisBlock.hash = calculateHash(&genesisBlock)
genesisBlock.previousHash = ""
genesisBlock.nonce = 0
c.chain = append(c.chain, genesisBlock)
}
func addBlock(timestamp string, c *BlockChain){
if len(c.chain) == 0 {
createGenesisBlock(c)
}
latesPointer = &c.chain[len(c.chain)-1].hash
var newBlock Block
newBlock.timestamp = timestamp
newBlock.hash = calculateHash(&newBlock)
newBlock.previousHash = *latesPointer
newBlock.metadata = metaData
c.chain = append(c.chain, newBlock)
latesPointer = &c.chain[len(c.chain)-1].hash
}
在BlockChian构造体中,我们用一个数组储存传递过来的区块,并且我们定义一个全局的用于储存上一个区块的哈希值的地址,我们还设置了两个方法:
- createGenesisBlock方法用于创建创世块,是区块链的首个区块。(小知识:中本聪于UTC时间2009年1月13日18:15:05生成了世界上第一个区块)
- addBlock方法用于添加块到链上,首先检测链是否为空,否则创建创世块。更新上一个区块的哈希值到latesPointer中,设置区块的时间戳、哈希值和上一个区块的哈希值,添加到链上,最后再重新更新一下指针。
总结:
我们实现一个基本的区块链,但这个区块链是不安全的。
我们在下一步将解决这两个问题:
- 一致性问题:如何确保添加到链上的区块是安全的(不是恶意修改的信息)
- 垃圾信息:所有人都可以在短时间内快速的将区块上传到链上,若有一个节点发送大量的区块将导致系统的崩溃。
四、PoW工作量证明
什么是PoW:
PoW可以理解成我们熟知的挖矿,计算机分配给每个节点一个数学问题,每个节点需计算出正确答案才可以上传区块,并且每个节点都可以验证这个答案是正确的。其目的在于使作恶者作恶的成本大于获得利益,而作恶者要想赶在其他所有节点计算出正确答案,需要拥有全世界51%的算力,这所需的成本大于其获得利益,有效的解决了我们在上一节提出的两个问题。
实现方法:
我们先来修改一下Block结构体中的成员
type Block struct {
previousHash string
hash string
metadata []string
timestamp string
nonce int \\我们添加一个成员属性nonce
}
func calculateHash(block *Block) string {
str := calculateRootHash(pendingTransaction) + block.timestamp + strconv.Itoa(block.nonce)
return Hash(str)
}
同时也需要一个方法来进行所谓的挖矿:
func mineBlock(b *Block,difficulty int){
for true {
if b.hash[:difficulty] == strings.Repeat("0",difficulty){
break
}else{
b.nonce += 1
b.hash = calculateHash(b)
}
}
fmt.Println("Block mined: "+ b.hash)
}
通过设置difficulty来控制计算难度,每次挖矿就是计算机在不断尝试直至得出符合条件的答案。
随后我们将其应用到区块链上,完整代码为:
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"strconv"
"strings"
)
var latesPointer *string
type Block struct {
previousHash string
hash string
metadata []string
timestamp string
nonce int
}
type BlockChain struct {
chain []Block
}
func calculateHash(block *Block) string {
str := calculateRootHash(pendingTransaction) + block.timestamp + strconv.Itoa(block.nonce)
return Hash(str)
}
func mineBlock(b *Block,difficulty int){
for true {
if b.hash[:difficulty] == strings.Repeat("0",difficulty){
break
}else{
b.nonce += 1
b.hash = calculateHash(b)
}
}
fmt.Println("Block mined: "+ b.hash)
}
func createGenesisBlock(c *BlockChain){
var genesisBlock Block
genesisBlock.timestamp = "01/01/2023"
genesisBlock.metadata = []string {"null"}
genesisBlock.hash = calculateHash(&genesisBlock)
genesisBlock.previousHash = ""
genesisBlock.nonce = 0
c.chain = append(c.chain, genesisBlock)
}
func addBlock(timestamp,metaData string, c *BlockChain){
if pendingTransaction != nil {
if len(c.chain) == 0 {
createGenesisBlock(c)
}
latesPointer = &c.chain[len(c.chain)-1].hash
var newBlock Block
newBlock.nonce = 0
newBlock.timestamp = timestamp
newBlock.hash = calculateHash(&newBlock)
newBlock.metadata = metaData
newBlock.previousHash = *latesPointer
mineBlock(&newBlock,5)
c.chain = append(c.chain, newBlock)
latesPointer = &c.chain[len(c.chain)-1].hash
pendingTransaction = nil
}
}
问题
我们创建了一个有基本功能的区块链,但是将区块上传到区块链所需时间较久(比特币一般是十分钟),如果在这期间有多个节点上传区块将造成堵塞。
五、事务管理
为了解决上一节提出的问题,我们可以增加一个待处理事务的流程,同时增加交易类
var pendingTransaction []string
type Transcation struct {
fromAdress string
toAdress string
amount int
}
func addTransaction(t *Transcation){
//验证交易信息
transaction := t.fromAdress + t.toAdress + strconv.Itoa(t.amount)
pendingTransaction = append(pendingTransaction, transaction)
}
在新增了一个交易结构体后,为了对区块计算哈希值的方法继续改进,更好的增强区块链的安全性,我们先计算待处理事务的默克尔树。同时我们将每个交易信息做Base64编码
func calculateRootHash(pt []string)(string){
for len(pt)>1{
if len(pt)%2 != 0{
pt = append(pt, "")
}
var ram []string
for i := 0; i < len(pt)/2; i++ {
ram = append(ram, Hash(pt[2*i]+pt[(2*i)+1]))
}
pt = ram
}
return pt[0]
}
func basa64(pd []string)([]string){
var str []string
for i := 0; i < len(pd); i++{
code := base64.StdEncoding.EncodeToString([]byte(pd[i]))
str = append(str,code)
}
return str
}
最终,我们的addBlock方法如下:
func addBlock(timestamp string, c *BlockChain){
if pendingTransaction != nil { //确保添加块时待处理事务不为空
if len(c.chain) == 0 {
createGenesisBlock(c)
}
latesPointer = &c.chain[len(c.chain)-1].hash
var newBlock Block
newBlock.nonce = 0
newBlock.timestamp = timestamp
newBlock.hash = calculateHash(&newBlock)
newBlock.metadata = basa64(pendingTransaction) //储存base64编码后的元数据
newBlock.previousHash = *latesPointer
mineBlock(&newBlock,5)
c.chain = append(c.chain, newBlock)
latesPointer = &c.chain[len(c.chain)-1].hash
pendingTransaction = nil
}
}
完整的源代码如下:
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"strconv"
"encoding/base64"
"strings"
)
var pendingTransaction []string
type Transcation struct {
fromAdress string
toAdress string
amount int
}
type Block struct {
previousHash string
hash string
metadata []string
timestamp string
nonce int
}
type BlockChain struct {
chain []Block
}
func Hash(s string)(string){
hash := sha256.Sum256([]byte(s))
return hex.EncodeToString(hash[:])
}
func addTransaction(t *Transcation){
transaction := t.fromAdress + t.toAdress + strconv.Itoa(t.amount)
pendingTransaction = append(pendingTransaction, transaction)
}
func calculateRootHash(pt []string)(string){
for len(pt)>1{
if len(pt)%2 != 0{
pt = append(pt, "")
}
var ram []string
for i := 0; i < len(pt)/2; i++ {
ram = append(ram, Hash(pt[2*i]+pt[(2*i)+1]))
}
pt = ram
}
return pt[0]
}
func calculateHash(block *Block) string {
str := calculateRootHash(pendingTransaction) + block.timestamp + strconv.Itoa(block.nonce)
return Hash(str)
}
func basa64(pd []string)([]string){
var str []string
for i := 0; i < len(pd); i++{
code := base64.StdEncoding.EncodeToString([]byte(pd[i]))
str = append(str,code)
}
return str
}
func createGenesisBlock(c *BlockChain){
var genesisBlock Block
genesisBlock.timestamp = "01/01/2023"
genesisBlock.metadata = []string {"null"}
genesisBlock.hash = calculateHash(&genesisBlock)
genesisBlock.previousHash = ""
genesisBlock.nonce = 0
c.chain = append(c.chain, genesisBlock)
}
func mineBlock(b *Block,difficulty int){
for true {
if b.hash[:difficulty] == strings.Repeat("0",difficulty){
break
}else{
b.nonce += 1
b.hash = calculateHash(b)
}
}
fmt.Println("Block mined: "+ b.hash)
}
var latesPointer *string
func addBlock(timestamp string, c *BlockChain){
if pendingTransaction != nil
if len(c.chain) == 0 {
createGenesisBlock(c)
}
latesPointer = &c.chain[len(c.chain)-1].hash
var newBlock Block
newBlock.nonce = 0
newBlock.timestamp = timestamp
newBlock.hash = calculateHash(&newBlock)
newBlock.metadata = basa64(pendingTransaction)
newBlock.previousHash = *latesPointer
mineBlock(&newBlock,5)
c.chain = append(c.chain, newBlock)
latesPointer = &c.chain[len(c.chain)-1].hash
pendingTransaction = nil
}
}
声明
该代码实现的区块链仅仅实现了最为基本的功能,最重要的P2P网络和密码学保障功能还没实现。本人水平浅薄,代码写的烂,在此抛砖引玉,欢迎大家在评论区指教和批评,感谢您的阅读。