当前位置:首页 > 知识 >

用200行Go代码实现区块链的区块生成和网络通信

在第一篇文章[1]中,我们向大家展示了如何通过精炼的Go代码实现一个简单的区块链。我们介绍了如何计算每个块的Hash值、验证块数据、让块链接起来等,但是所有这些都是跑在一个节点上的。文章发布后,读者反响热烈,纷纷留言让我快点填坑(网络部分),于是就诞生了这第二篇文章。

这篇文章在之前的基础上,解决多个节点网络内,如何生成块、如何通信、如何广播消息等。

流程:

- 第一个节点创建“创始区块”,同时启动TCPserver并监听一个端口,等待其他节点连接。 - 启动其他节点,并与第一个节点建立TCP连接(这里我们通过不同的终端来模拟其他节点) - 创建新的块 - 第一个节点验证新生成块 - 验证之后广播(链的新状态)给其他节点 - 所有的节点都同步了最新的链的状态

之后你可以重复上面的步骤,使得每个节点都创建TCPserver并监听(不同的)端口以便其他节点来连接。通过这样的流程你将建立一个简化的模拟的(本地的)P2P网络,当然你也可以将节点的代码编译后,将二进制程序部署到云端。

开始coding吧

设置与导入依赖:

参考之前第一篇文章,我们使用相同的计算hash的函数、验证块数据的函数等。

设置:

在工程的根目录创建一个.env文件,并添加配置:

ADDR=9000

通过go-spew包将链数据输出到控制台,方便我们阅读:

go get github.com/davecgh/go-spew/spew

通过godotenv包来加载配置文件:

go get github.com/joho/godotenv

之后创建main.go文件。

导入:

接着我们导入所有的依赖:

import ( "bufio" "crypto/sha256" "encoding/hex" "encoding/json" "io" "log" "net" "os" "strconv" "time" "github.com/davecgh/go-spew/spew" "github.com/joho/godotenv" )

回顾:

让我们再快速回顾下之前的重点,我们创建一个Block结构体,并声明一个Block类型的slice,Blockchain:

// Block represents each "item" in the blockchain type Block struct { Index int Timestamp string BPM int Hash string PrevHash string }

// Blockchain is a series of validated Blocks var Blockchain []Block

创建块时计算hash值的函数:

// SHA256 hashing function func calculateHash(block Block) string { record := string(block.Index) + block.Timestamp + string(block.BPM) + block.PrevHash h := sha256.New() h.Write([]byte(record)) hashed := h.Sum(nil) return hex.EncodeToString(hashed) }

创建块的函数:

// create a new block using previous block's hash func generateBlock(oldBlock Block, BPM int) (Block, error) { var newBlock Block

t := time.Now() newBlock.Index = oldBlock.Index + 1 newBlock.Timestamp = t.String() newBlock.BPM = BPM newBlock.PrevHash = oldBlock.Hash newBlock.Hash = calculateHash(newBlock)

return newBlock, nil }

验证块数据的函数:

// make sure block is valid by checking index, and comparing the hash of the previous block func isBlockValid(newBlock, oldBlock Block) bool { if oldBlock.Index+1 != newBlock.Index { return false }

if oldBlock.Hash != newBlock.PrevHash { return false }

if calculateHash(newBlock) != newBlock.Hash { return false }

return true }

确保各个节点都以最长的链为准:

// make sure the chain we're checking is longer than the current blockchain func replaceChain(newBlocks []Block) { if len(newBlocks) > len(Blockchain) { Blockchain = newBlocks } }

网络通信:

接着我们来建立各个节点间的网络,用来传递块、同步链状态等。

我们先来声明一个全局变量bcServer,以channel(译者注:channel类似其他语言中的Queue,代码中声明的是一个Block数组的channel)的形式来接受块。

// bcServer handles incoming concurrent Blocks var bcServer chan []Block

注:Channel是Go语言中很重要的特性之一,它使得我们以流的方式读写数据,特别是用于并发编程。通过这里[2]可以更深入地学习Channel。

接下来我们声明main函数,从.env加载配置,也就是端口号,然后实例化bcServer

func main() { err := godotenv.Load() if err != nil { log.Fatal(err) } bcServer = make(chan []Block)

猜你喜欢

关注我们

微信二维码

微信