使用 Go 進行工業區塊鏈開發。 第1部分

四個月來,我一直在從事一個名為「基於區塊鏈的政府和工業部門資料保護和管理工具的開發」的專案。
現在我想向大家介紹一下我是如何開始這個專案的,現在我將詳細描述程式碼。

使用 Go 進行工業區塊鏈開發。 第1部分

這是系列文章的第一篇文章。 這裡我描述一下伺服器和協定。 事實上,讀者甚至可以編寫自己的這些區塊鏈元素版本。

這是第二部分 ——關於區塊鏈和交易資料結構,以及實作與資料庫互動的套件。

去年,在數位突破黑客馬拉鬆上,他們提出了一個想法,利用分散式帳本技術為工業和數位經濟打造一個有用的系統;創新援助基金也為開發提供了一筆資助(我應該單獨寫一篇)關於補助金的文章,適用於剛開始創業的人),現在按順序進行。

使用Go語言進行開發,儲存區塊的資料庫是LevelDB。
主要部分是協定、伺服器(運行 TCP 和 WebSocket - 第一個用於同步區塊鏈,第二個用於連接客戶端、從 JavaScript 發送交易和命令等)。

如前所述,該區塊鏈主要用於自動化和保護供應商和客戶之間或兩者之間的產品交換。 這些人並不急於互相信任。 但任務不僅僅是創建一個帶有內建計算器的“支票簿”,而是一個能夠自動執行產品生命週期中出現的大部分日常任務的系統。 按照區塊鏈的慣例,負責此事的字節碼儲存在交易的輸入和輸出中(交易本身儲存在區塊中,LevelDB 中的區塊以 GOB 格式預先編碼)。 首先,我們來談談協定和伺服器(又稱節點)。

該協議並不復雜,其要點是切換到加載一些資料的模式,通常是區塊或交易,以響應特殊的命令行,並且還需要交換庫存,以便節點知道它是誰連接到以及它們如何處理事務(為同步會話而連接的節點也稱為“鄰居”,因為它們的IP 已知並且它們的狀態資料儲存在記憶體中)。

在 Go 程式設計師的理解中,資料夾(Linux 稱之為目錄)被稱為包,因此在該目錄中包含 Go 程式碼的每個檔案的開頭,他們會寫 package_name_where_this_file 所在的位置。 否則,您將無法將套件提供給編譯器。 嗯,對於那些了解這種語言的人來說,這不是什麼秘密。 這些是包:

  • 網路通訊(伺服器、客戶端、協定)
  • 儲存和傳輸資料的結構(區塊、交易)
  • 資料庫(區塊鏈)
  • 共識
  • 堆疊虛擬機器 (xvm)
  • 輔助(加密、型別)現在就這些了。

這是github的鏈接

這是一個教育版本,缺乏進程間互動和幾個實驗組件,但結構與正在開發的結構相對應。 如果您在評論中有什麼建議,我很樂意在進一步的開發中考慮。 現在對伺服器進行解釋 協議.

我們先看伺服器。

伺服器子例程充當資料伺服器,使用協定包中的資料結構在 TCP 協定之上運行。

該例程使用以下套件: 服務器, 協議, 類型。 在包裝本身 tcp_server.go 包含資料結構 服務.

type Serve struct {
	Port string
	BufSize int
	ST *types.Settings
}

它可以接受以下參數:

  • 交換資料的網路端口
  • JSON 格式的伺服器設定檔
  • 在調試模式下運行的標誌(私有區塊鏈)

進步:

  • 從 JSON 檔案讀取配置
  • 檢查調試模式標誌:如果設置,則不會啟動網路同步調度程序並且不會加載區塊鏈
  • 初始化配置資料結構並啟動伺服器

服務器

  • 依照協定進行TCP伺服器的啟動和網路互動。
  • 它有一個 Serve 資料結構,由連接埠號碼、緩衝區大小和指向該結構的指標組成 類型.設定
  • Run方法啟動網路互動(監聽給定連接埠上的傳入連接,當接收到新連接時,其處理將轉移到新執行緒中的私有句柄方法)
  • В 處理 來自連接的資料被讀入緩衝區,轉換為字串表示形式並傳遞給 協議.選擇
  • 協議.選擇 回報 導致 或導致錯誤。 導致 然後轉移到 協議.解釋返回 內部 - 類型對象 解釋數據,或導致處理選擇結果時出錯
  • 然後執行switch intrpr.指令[0] 它檢查以下之一: 結果、inv、錯誤 還有一個部分 默認
  • 在該部分 導致 透過值找到開關 intrpr.指令[1] 檢查值 緩衝區長度 и 版本 (每種情況都會呼叫對應的函數)

功能 取得版本 и 緩衝區長度 都在文件裡 srvlib.go 伺服器包

GetVersion(conn net.Conn, version string)

它只是列印到控制台並將參數中傳遞的版本傳送到客戶端:

conn.Write([]byte("result:" + version))

.
功能

BufferLength(conn net.Conn, intrpr *protocol.InterpreteData)

載入區塊、交易或其他特定數據,如下所示:

  • 將協定中指定的需要接受的資料類型列印到控制台:
    fmt.Println("DataType:", intrpr.Commands[2])
  • 讀取值 內部體 到數字變數 緩衝區長度
  • 建立一個緩衝區 新緩衝區 指定尺寸:
    make([]byte, buf_len)
  • 發送一個好的回應:
    conn.Write([]byte("result:ok"))
  • 從讀取流完全填充緩衝區:
    io.ReadFull(conn, newbuf)

    .

  • 將緩衝區的內容列印到控制台
    fmt.Println(string(newbuf))

    和讀取的位元組數

    fmt.Println("Bytes length:", n)
  • 發送一個好的回應:
    conn.Write([]byte("result:ok"))

伺服器包中的方法被配置為使用包中的函數處理接收到的數據 協議.

協議

協定充當表示網路交換中的資料的手段。

選擇(str字串)(字串,錯誤) 將伺服器接收的資料進行初步處理,接收資料的字串表示形式作為輸入,並傳回準備好的字串 口譯員:

  • 使用以下命令將輸入字串分為頭部和主體 ReqParseN2(str)
  • head 被分割成元素並使用 ReqParseHead(head) 放入指令切片中
  • В 開關(命令[0]) 選擇接收到的命令(cmd、金鑰、位址 或該部分被觸發 默認)
  • cmd中檢查2條指令 switch(命令[1]) — 長度 и 取得版本.
  • 長度 檢查資料類型 命令[2] 並將其保存在 數據類型
  • 檢查 身體 包含一個字串值
    len(body) < 1
  • 回傳回應字串:
    "result:bufferlength:" + datatype + "/" + body
  • 取得版本 回傳一個字串
    return "result:version/auto"

口譯員

包含InterpreteData結構體,並對從傳回的資料進行二次處理 最優質選擇 字串和物件的形成 解釋數據.

type InterpreteData struct {
	Head string
	Commands []string
	Body string
	IsErr bool
	ErrCode int 
	ErrMessage string
}

功能

Interprete(str string) (*InterpreteData, error)

接受一個字串 導致 並創建並傳回對該物件的引用 解釋數據.

進步:

  • 同樣 最優質選擇 頭部和身體被提取使用 ReqParseN2(str)
  • head 被分成元素使用 ReqParseHead(頭)
  • 物件已初始化 解釋數據 並返回指向它的指標:

res := &InterpreteData{
	Head: head,
	Commands: commands,
	Body: body,
}
return res, nil

此物件用於 伺服器.go 包主.

客戶端

客戶端包包含的功能 TCP連線 и TCP回應數據.

功能

TCPConnect(s *types.Settings, data []byte, payload []byte)

工作原理如下:

  • 與傳遞的設定物件中指定的連接建立連接
    net.Dial("tcp", s.Host + ":" + s.Port)
  • 傳輸data參數中傳入的資料:
    conn.Write(data)
  • 答案已讀
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    並列印在控制台上

    fmt.Println(string(resp[:n]))
  • 如果轉移 有效載荷 然後傳遞它
    conn.Write(payload)

    並讀取伺服器回應,將其列印到控制台

功能

 TCPResponseData(conn net.Conn, bufsiz int) ([]byte, int, error)

建立指定大小的緩衝區,讀取伺服器回應並傳回該緩衝區和讀取的位元組數以及錯誤物件。

客戶端子程序

用於向節點伺服器發送命令,以及取得簡要統計和測試。

可以接受以下參數:JSON 格式的設定檔、要作為字串傳送到伺服器的資料、要傳送到負載的檔案的路徑、節點調度程序模擬標誌、作為數值傳輸的資料類型。

  • 獲取配置
    st := types.ParseConfig(*config)
  • 如果emu標誌被傳遞,則開始 調度器
  • 如果提供了指示檔案路徑的 f 標誌,那麼我們將其資料載入到 數據庫 並將內容傳送到伺服器
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • 如果未指定文件,則僅傳送標誌中的數據 -d:
    client.TCPConnect(st, []byte(*data), nil)

所有這些都是顯示協定結構的簡化表示。 在開發過程中,必要的功能被添加到其結構中。

在第二部分中,我將討論區塊和事務的資料結構,在第3 部分中,我將討論用於從JavaScript 連接的WebSocket 伺服器,在第4 部分中,我將討論同步調度程序,然後是處理來自輸入和輸出的字節碼、密碼學和輸出池。

來源: www.habr.com

添加評論