这篇文章在之前的基础上,解决多个节点网络内,如何生成块、如何通信、如何广播消息等。流程
Step 1
Step 2
Step 3
之后你可以重复上面的步骤,使得每个节点都创建TCP server并监听(不同的)端口以便其他节点来连接。通过这样的流程你将建立一个简化的模拟的(本地的)P2P网络,当然你也可以将节点的代码编译后,将二进制程序部署到云端。 开始coding吧设置与导入依赖参考之前第一篇文章,我们使用相同的计算 hash 的函数、验证块数据的函数等。 设置 在工程的根目录创建一个
通过
通过
之后创建 导入 接着我们导入所有的依赖: package main 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 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 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 handles incoming concurrent Blocks var bcServer chan Block 注:Channel 是 Go 语言中很重要的特性之一,它使得我们以流的方式读写数据,特别是用于并发编程。通过这里[2]可以更深入地学习 Channel。 接下来我们声明 func main { err := godotenv.Load if err != nil { log.Fatal(err) } bcServer = make(chan []Block) // create genesis block t := time.Now genesisBlock := Block{0, t.String, 0, '', ''} spew.Dump(genesisBlock) Blockchain = append(Blockchain, genesisBlock) } 接着创建 TCP server 并监听端口: // start TCP and serve TCP server server, err := net.Listen('tcp', ':'+os.Getenv('ADDR')) if err != nil { log.Fatal(err) } defer server.Close 需要注意这里的 for { conn, err := server.Accept if err != nil { log.Fatal(err) } go handleConn(conn) } 通过这个无限循环,我们可以接受其他节点的 TCP 链接,同时通过 接下来是“处理请求”这个重要函数,其他节点可以创建新的块并通过 TCP 连接发送出来。在这里我们依然像第一篇文章一样,以 BPM 来作为示例数据。
func handleConn(conn net.Conn) { io.WriteString(conn, 'Enter a new BPM:') scanner := bufio.NewScanner(conn) // take in BPM from stdin and add it to blockchain after // conducting necessary validation go func { for scanner.Scan { bpm, err := strconv.Atoi(scanner.Text) if err != nil { log.Printf('%v not a number: %v', scanner.Text, err) continue } newBlock, err := generateBlock( Blockchain[len(Blockchain)-1], bpm) if err != nil { log.Println(err) continue } if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) { newBlockchain := append(Blockchain, newBlock) replaceChain(newBlockchain) } bcServer <->-> io.WriteString(conn, '\nEnter a new BPM:') } } defer conn.Close } 我们创建一个 scanner,并通过 通过 TCP 链接将最新的链广播出去时,我们需要:
// simulate receiving broadcast go func { for { time.Sleep(30 * time.Second) output, err := json.Marshal(Blockchain) if err != nil { log.Fatal(err) } io.WriteString(conn, string(output)) } } for _ = range bcServer { spew.Dump(Blockchain) } 整个 现在让我们来启动整个程序,
就像我们预期的,首先创建了“创世块”,接着启动了 TCP server 并监听9000端口。 接着我们打开一个新的终端,连接到那个端口。(我们用不同颜色来区分)
接下来我们输入一个BPM值: 接着我们从第一个终端(节点)中能看到(依据输入的BPM)创建了新的块。 我们等待30秒后,可以从其他终端(节点)看到广播过来的最新的链。 下一步到目前为止,我们为这个例子添加了简单的、本地模拟的网络能力。当然,肯定有读者觉得这不够有说服力。但本质上来说,这就是区块链的网络层。它能接受外部数据并改变内在数据的状态,又能将内在数据的最新状态广播出去。 接下来你需要学习的是一些主流的共识算法,比如 PoW (Proof-of-Work) 和 PoS (Proof-of-Stake) 等。当然,我们会继续在后续的文章中将共识算法添加到这个例子中。 下一篇文章再见! 参考链接 [1] https://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&mid=2653549361&idx=1&sn=019f54713891cf33ef3bef3b24773a96&chksm=813a62a9b64debbfdd24a8507bb974048a4456e5b0a2d5f685fb3bdf40366a25764c5df8afec&scene=21 [2] https:///channels/ [3] https://blog./defer-panic-and-recover [4] https://github.com/mycoralhealth/blockchain-tutorial/blob/master/networking/main.go |
|