1. 程式人生 > >200行Go程式碼實現一個區塊鏈之二——區塊生成與網路通訊

200行Go程式碼實現一個區塊鏈之二——區塊生成與網路通訊

上一篇文章中,我們向大家展示瞭如何通過精煉的Go程式碼實現一個簡單的區塊鏈。如何計算每個塊的 Hash 值,如何驗證塊資料,如何讓塊連結起來等等,但是所有這些都是跑在一個節點上的。文章釋出後,讀者反響熱烈,紛紛留言讓我快點填坑(網路部分),於是就誕生了這第二篇文章。

這篇文章在之前的基礎上,解決多個節點網路內,如何生成塊、如何通訊、如何廣播訊息等。

流程


  • 第一個節點建立“創始區塊”,同時啟動 TCP server並監聽一個埠,等待其他節點連線。

Step 1

  • 啟動其他節點,並與第一個節點建立TCP連線(這裡我們通過不同的終端來模擬其他節點)

  • 建立新的塊

Step 2

  • 第一個節點驗證新生成塊

  • 驗證之後廣播(鏈的新狀態)給其他節點

Step 3

  • 所有的節點都同步了最新的鏈的狀態

之後你可以重複上面的步驟,使得每個節點都建立TCP server並監聽(不同的)埠以便其他節點來連線。通過這樣的流程你將建立一個簡化的模擬的(本地的)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 檔案。

匯入
接著我們匯入所有的依賴:

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 結構體,並宣告一個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
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)

// 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()

需要注意這裡的 defer server.Close(),它用來之後關閉連結,可以從這裡[3]瞭解更多 defer 的用法。

for {
conn, err := server.Accept()
if err != nil {
log.Fatal(err)
}
go handleConn(conn)
}

通過這個無限迴圈,我們可以接受其他節點的 TCP 連結,同時通過 go handleConn(conn) 啟動一個新的 go routine(譯者注:Rob Pike 不認為go routine 是協程,因此沒有譯為協程)來處理請求。

接下來是“處理請求”這個重要函式,其他節點可以建立新的塊並通過 TCP 連線傳送出來。在這裡我們依然像第一篇文章一樣,以 BPM 來作為示例資料。

  • 客戶端通過 stdin 輸入 BPM

  • 以 BPM 的值來建立塊,這裡會用到前面的函式:generateBlockisBlockValid,和 replaceChain

  • 將新的鏈放在 channel 中,並廣播到整個網路

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 <- Blockchain
io.WriteString(conn, "\nEnter a new BPM:")
}
}()

defer conn.Close()
}

我們建立一個 scanner,並通過 for scanner.Scan() 來持續接收連線中發來的資料。為了簡化,我們把 BPM 數值轉化成字串。bcServer <- Blockchain 是表示我們將新的鏈寫入 channel 中。

通過 TCP 連結將最新的鏈廣播出去時,我們需要:

  • 將資料序列化成 JSON 格式

  • 通過 timer 來定時廣播

  • 在控制檯中打印出來,方便我們檢視鏈的最新狀態

// 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)
}

整個 handleConn 函式差不多就完成了,通過這裡[4]可以獲得完整的程式碼。

有意思的地方

現在讓我們來啟動整個程式,
go run main.go


就像我們預期的,首先建立了“創世塊”,接著啟動了 TCP server 並監聽9000埠。

接著我們開啟一個新的終端,連線到那個埠。(我們用不同顏色來區分)
nc localhost 9000


接下來我們輸入一個BPM值:



接著我們從第一個終端(節點)中能看到(依據輸入的BPM)建立了新的塊。


我們等待30秒後,可以從其他終端(節點)看到廣播過來的最新的鏈。

下一步

到目前為止,我們為這個例子添加了簡單的、本地模擬的網路能力。當然,肯定有讀者覺得這不夠有說服力。但本質上來說,這就是區塊鏈的網路層。它能接受外部資料並改變內在資料的狀態又能將內在資料的最新狀態廣播出去。

接下來你需要學習的是一些主流的共識演算法,比如 PoW (Proof-of-Work) 和 PoS (Proof-of-Stake) 等。當然,我們會繼續在後續的文章中將共識演算法新增到這個例子中。

下一篇文章再見!

相關推薦

200Go程式碼實現一個區塊——區塊生成網路通訊

在上一篇文章中,我們向大家展示瞭如何通過精煉的Go程式碼實現一個簡單的區塊鏈。如何計算每個塊的 Hash 值,如何驗證塊資料,如何讓塊連結起來等等,但是所有這些都是跑在一個節點上的。文章釋出後,讀者反響熱烈,紛紛留言讓我快點填坑(網路部分),於是就誕生了這第二篇文章。這篇文章

200go程式碼實現區塊

你可以學到什麼 建立自己的blockchain 理解雜湊演算法是怎樣保證blockchain的完整性 理解新塊是如何被新增的 如何解決多個節點競爭問題 通過瀏覽器來檢視你的blockchain 寫新的blocks 對blockchain有基本的瞭解

200python程式碼實現一個類似django的簡單模板引擎

之前使用django覺得模板就是通過正則等方法把裡面的標籤替換成變數,其實裡面還大有學問。 後面看到一些模板引擎的原理,覺得很酷,比如下面這個教程講的,這是500lines系列裡的一篇文章。 教程原文地址:http://aosabook.org/en/500L/a-tem

5Python程式碼實現一個簡單的線上英漢詞典

# -*- coding: utf-8 -*- import urllib, urllib2, simplejson while True: url = 'http://fanyi.youdao.com/openapi.do?key=79379998&keyfro

帶大家用40python程式碼實現一個疫情地圖

最近兩個月,因為新冠病毒無情的肆虐,相信會給每個中國人的記憶中畫上重重的一筆。到今天為止,疫情形勢依然十分嚴峻,雖然除湖北外的其他省份已經連續十一天確診人數下降,但是接下來還有將近至少1.6億的人口遷徙,這無疑還存在一定的風險。 相信很多人和我一樣,早上起床的第一件事情都是檢視疫情資料,一般的資料都配有中國確

200PYTHON程式碼實現貪吃蛇

# 200行Python程式碼實現貪吃蛇 話不多說,最後會給出全部的程式碼,也可以從[這裡](https://github.com/NemoHoHaloAi/Game/tree/master/YummySnake)Fork,正文開始; 目前實現的功能列表: 1. 貪吃蛇的控制,通過上下左右方向鍵; 2.

區塊初識區塊

通過 才有 礦池 什麽是 技術 才會 怎麽 目前 能力   大家下午好,這裏是鏈客區塊鏈技術問答社區,給大家講一講剛接觸區塊鏈的萌新需要了解什麽。 首先得明白幾個概念:區塊鏈,比特幣,中心化,去中心化,挖礦 區塊鏈和比特幣 區塊鏈是啥呢?區塊鏈和比特幣有啥關系呢? 比特

200go語言程式碼自建一個區塊 體驗挖礦樂趣

談談區塊鏈: 挖礦的目的:通過挖礦證明算力,防止他人作弊,自己又能獲得獎勵【給自己加錢】。 挖礦的過程:將網上別的合法且最新的使用者交易同步過來,加入到區塊,然後加隨機數雜湊後 與系統給出的值去比較。若符合條件則表示挖礦成功,挖礦成功了還不行,要趕緊告訴別人,我挖成功了,別讓其他人搶風

只用200Go代碼寫一個自己的區塊

itl OS qpi master method 最長 png log 得出 區塊鏈是目前最熱門的話題,廣大讀者都聽說過比特幣,或許還有智能合約,相信大家都非常想了解這一切是如何工作的。這篇文章就是幫助你使用 Go 語言來實現一個簡單的區塊鏈,用不到 200 行代碼來揭示

200Go代碼實現自己的區塊——區塊生成網絡通信

type hash lazy avi present lte () cti 裏的 在第一篇文章[1]中,我們向大家展示了如何通過精煉的Go代碼實現一個簡單的區塊鏈。如何計算每個塊的 Hash 值,如何驗證塊數據,如何讓塊鏈接起來等等,但是所有這些都是跑在一個節點上的。文章

用120Java程式碼一個自己的區塊

區塊鏈是目前最熱門的話題,廣大讀者都聽說過比特幣,或許還有智慧合約,相信大家都非常想了解這一切是如何工作的。這篇文章就是幫助你使用 Java 語言來實現一個簡單的區塊鏈,用不到 120 行程式碼來揭示區塊鏈的原理! “用不到120行 Java 程式碼就能實現一個

100程式碼實現一個vue分頁元件

今天用vue來實現一個分頁元件,總體來說,vue實現比較簡單,樣式部分模仿了elementUI。所有程式碼的原始碼可以再github上下載的到:下載地址 先來看一下實現效果: 點選檢視效果 整體思路 我們先看一下使用到的檔案的目錄: 我們在 pageComponentsTe

不到50程式碼實現一個能對請求併發數做限制的通用RequestDecorator

使用場景 在開發中,我們可能會遇到一些對非同步請求數做併發量限制的場景,比如說微信小程式的request併發最多為5個,又或者我們需要做一些批量處理的工作,可是我們又不想同時對伺服器發出太多請求(可能會對伺服器造成比較大的壓力)。這個時候我們就可以對請求併發數進行限制,並且使用排隊機制讓請求有序的傳送出去。

【Android】三程式碼實現一個輪播BannerView

自己簡單封裝了一個帶hint的輪播ViewPager,用來展示app首頁的Banner,先看效果圖吧。 ezgif-1-437f7aee24.gif dependencies(依賴) compile 'com.coldmoqiuli:banners:1.0.0'

一個 11 Python 程式碼實現的神經網路

概要:直接上程式碼是最有效的學習方式。這篇教程通過由一段簡短的 python 程式碼實現的非常簡單的例項來講解 BP 反向傳播演算法。 程式碼如下: Python 1234567891011X=np.array([

[tensorflow應用路]10程式碼實現一個完整的SVM分類器

SVM是一種常用的機器學習分類器模型,其原理為最大化類間隔(被稱為支援向量),來達到分類的目的。它是一種有監督的模型。 SVM原理簡述 SVM通過預測值y′=wx+by′=wx+b與真實值yy之間的差值實現最大間隔分類。即 wx+b⩾1,y=1wx+b

【我的區塊路】- go實現區塊中常見的各類演算法

咳咳,為什麼要出這一篇文章呢?首先,這段時間本人在找工作,然後被問到了各類演算法的底層細節,有些確實很懵逼。這裡做個總結,也順便給大家歸納歸納一下! 上主題: 橢圓曲線加密: 我們先來說一說最常用的 ECC 吧,ECC 就是 Elliptic Curve Crypt

不到100程式碼實現一個簡單的推薦系統

一個好的推薦系統推薦的精度必然很高,能夠真的發現使用者的潛在需求或喜好,提高購物網詀的銷量,讓視訊網站發現使用者喜歡的收費電影… 可是要實現一個高精度的推薦系統不是那麼容易的,netflix曾經懸賞高額獎金尋找能給其推薦系統的精確度提高10%的人,可見各個公司對推薦系統的

200 Python 程式碼做了一個換臉程式

def transformation_from_points(points1, points2): points1 = points1.astype(numpy.float64) points2 = points2.astype(numpy.float64) c1 = numpy.

3程式碼實現一個簡易版promise

前言 作為一個後端過來的同學,剛入門前端的時候,被js種種「反人類」的概念折騰的死去活來的. 其中一個印象比較深刻的,就是promise,感覺實在太難理解了…所有就有了寫個簡單的promise的想法. 希望能幫助到一些跟我一樣,感覺promise很難理解的