官术网_书友最值得收藏!

3.3 bytomd守護進程的初始化實現

bytomd守護進程的Cobra流程與bytomcli過程非常相似,所以在此略去,后續主要對bytomd守護進程重要內容進行深入分析。在這里我們看一下bytomd預處理過程中使用到的代碼文件結構,命令如下:

        $ tree cmd/bytomd/
        cmd/bytomd/
        ├—— bytomd
        ├—— commands
        |   ├—— init.go         節點網絡初始化相關
        |   ├—— root.go         節點root目錄相關
        |   ├—— run_node.go    節點守護進程相關
        |   └—— version.go     節點版本參數相關
        ├—— main.go             bytomd入口函數

bytomd守護進程啟動時,會根據不同的命令行flag參數,初始化不同的模塊,最終以守護進程的方式運行。有關bytomd守護進程所有的運行工作都在node.NewNode(config)的具體實現中。下面介紹具體的實現過程。

3.3.1 Node對象

Node對象說明如下。

? cmn.BaseService:服務管理。

? config:當前節點的全局配置。

? syncManager:區塊和交易同步管理。

? wallet:本地錢包管理。

? accessTokens:token管理,用戶訪問憑證。

? api:API Server接口服務。

? chain:本地區塊鏈管理對象。

? txfeed:當前版本中該功能未使用。

? cpuMiner:CPU挖礦管理對象。

? miningPool:礦池管理對象。

? miningEnable:是否啟用挖礦模式。

node.NewNode(config)整個過程是為了創建Node對象,Node對象是整個bytomd所有模塊運行的基礎。

cmn.BaseService是tendermint框架的一個服務管理模塊,在這里我們可以把Node作為一個服務,對該服務進行OnStart/OnStop/IsRunning等操作管理。tendermint框架可以保證這些操作不會被重復執行多次。

3.3.2 配置初始化

在執行node.NewNode(config)之前,config的默認配置就已經定義好了。在深入node. NewNode(config)分析之前,我們需要先了解默認配置都有哪些。

首先,bytomd守護進程聲明一個config全局變量,表示整個bytomd守護進程的配置信息。進程啟動時config對象被賦予一個默認的配置參數。

默認的配置參數分有6個,每個針對不同的模塊。下面對配置進行說明。我們將默認參數歸納為三塊:Base基礎配置、P2P網絡配置、其他配置。

1. Base基礎配置

BaseConfig用于配置bytomd節點所需的基礎參數,包括數據目錄、日志、監聽地址等相關參數。

部分參數從配置文件中獲取默認值,比如ApiAddress參數,它的tag是api_addr。我們可以從config/toml.go中獲取默認值:

2. P2P網絡配置

P2PConfig用于配置bytomd P2P通信協議中使用的參數,包括本機監聽端口、通信節點超時、地址簿等相關參數。

注意,在比特幣中,節點會采用DNS的方式來詢問種子節點,進而查詢到其他節點的IP地址。而在比原鏈中,種子節點是IP地址,一般會硬編碼到代碼里。技術細節我們會在后面的第10章詳細講解。

3.其他配置

WalletConfig用于配置bytomd本地錢包使用的參數,包括是否啟用本地錢包和更新等相關參數。

在bytomd守護進程聲明config = DefaultConfig()之后,init()函數實現了config對象中各屬性的賦值。具體實現代碼如下:

在init()函數中定義了很多不同類型的flag參數,并將flag的參數值綁定到config對象上,比如:

        runNodeCmd.Flags().Bool("mining", config.Mining, "Enable mining")

這條語句的含義為:

? 定義一個Bool類型的flag參數。

? 該flag的名稱為mining。

? 該flag的賦值對象為config.Mining。

? 該flag的描述信息為Enable mining。

至此,bytomd守護進程所需要的配置信息初始化完畢,程序運行真正進入初始階段。下面對此進行深入分析。

3.3.3 創建文件鎖

在比原鏈中,一份數據目錄(--root參數指定)只能同時由一個bytomd守護進程讀寫,因為LevelDB高性能鍵值數據庫是單進程模式,如果多個進程同時讀寫一份數據,會造成數據不一致的情況。因此,需要使用文件鎖可以保證同一時間一個進程讀寫一份數據目錄,代碼如下:

bytomd啟動時,lockDataDirectory函數使用flock在RootDir目錄下創建一個LOCK文件。如果bytomd進程在一個文件的inode上加了鎖,那么再次啟動bytomd進程則會對errors.New中的內容報錯并退出進程。flock的作用是檢測進程是否已經存在。

flock主要有3種操作類型。

? LOCK_SH:共享鎖,多個進程使用同一把鎖用于讀鎖。

? LOCK_EX:排他鎖,同時只允許一個進程使用,一般用于寫鎖。

? LOCK_UN:釋放鎖。

如果深入研究flock包的函數,我們可以看到,這里使用了LOCK_EX鎖,即同時只允許一個進程使用,代碼示例如下:

3.3.4 初始化網絡類型

比原鏈的三種網絡模式,分別是mainnet主網、testnet測試網和solonet單機模式。

其中initActiveNetParams函數根據用戶傳入的chain_id,初始化網絡類型。consensus. ActiveNetParams對象保存了當前使用的網絡模式。在比原鏈代碼中會經常引用consensus. ActiveNetParams對象,用來識別當前節點連接的網絡類型。

ActiveNetParams默認使用主網。MainNetParams中的參數說明如下。

? Bech32HRPSegwit:隔離見證,是一種協議升級,我們會在后面第5章講解。

? Checkpoints:檢查點,指定一個高度,以及與這個高度相匹配的hash值,用于快速同步時驗證區塊的正確性。通常在主網升級時,會將歷史的塊信息硬編碼在Checkpoints中。

Checkpoints檢查點有兩種作用:第一是防止分叉,如果有人試圖從檢查點之前的區塊進行分叉,當前節點不會接受這個分叉;也用于保護網絡不受全網51%的算力攻擊,因為攻擊者不可能逆轉檢查點之前的交易。第二是用于節點間的快速同步,我們將在第10章中詳細講解。

3.3.5 初始化數據庫(持久化存儲)

創建一條公鏈,需要將鏈上的所有數據(包含塊信息、交易信息等)存儲在本地鍵值數據庫中。在比原鏈中使用LevelDB來存儲鏈上數據,代碼如下:

dbm使用tendermint框架的db管理庫。dbm.NewDB返回一個DB對象,DB對象提供了數據庫接口和許多方法實現,包括使用內存映射、文件系統目錄結構、GO中LevelDB等的實現。

dbm.NewDB返回一個DB對象,需要傳入三個參數:db的名稱,db使用的鍵值數據庫(默認為LevelDB), db數據存儲的路徑。leveldb.NewStore函數返回一個Store對象,即比原鏈對LevelDB進行了封裝,在LevelDB的基礎上增加了區塊緩存(cache)、區塊驗證、區塊狀態、區塊查詢等功能。

3.3.6 初始化交易池

當交易被廣播到網絡中并且被礦工接收到時,礦工會將接收到的交易加入到本地的TxPool交易池中,TxPool對象的作用是管理本地交易池。交易池相當于一個緩沖區,它并不是無限大。默認情況下比原鏈中交易池最大可以存儲10000筆交易。如果超出這個閾值,則會返回"transaction pool reach the max number"提示。

protocol.NewTxPool()返回一個TxPool實例對象。此處我們只介紹交易池初始化部分,交易池實現原理的代碼將在6.10節中深入剖析。

3.5.7 創建一條本地區塊鏈

當節點第一次啟動時,判斷本地持久化存儲的狀態,當狀態為初始化時會初始化本地的區塊鏈。區塊鏈的第一個區塊(創世區塊)會被加入到區塊高度為0的地方。代碼如下:

protocol.NewChain返回一個Chain對象,NewChain需要接收兩個參數:Store區塊鏈的存儲對象,TxPool交易池。Chain對象管理著比原鏈的整個區塊鏈條。代碼如下:

NewChain函數的執行可分為下面幾個步驟:

1)實例化Chain對象。

2)store.GetStoreStatus獲取本地區塊鏈的存儲狀態,如果狀態為nil則說明區塊鏈未被初始化。執行initChainStatus初始化本地區塊鏈,該函數初始化創世區塊(第一個區塊)并添加到本地鏈上。

3)store.LoadBlockIndex加載塊索引,從數據庫中讀取所有Block Header信息并緩存在內存中,目的是加速訪問區塊頭信息。

4)c.index.SetMainChain,設置當前節點已同步的最新區塊。

5)go c.blockProcesser(),啟動一個go rutine,用于更新本地區塊鏈上的區塊信息。

3.3.8 初始化本地錢包

默認情況下比原鏈節點會啟用本地錢包功能。代碼實例如下:

在比原鏈的節點啟動時,上述代碼流程主要邏輯為:

1)創建加密機hsm對象,hsm對象管理keystore文件,該文件是存儲私鑰的一種格式(JSON)。keystore是一串代碼,本質上是加密后的私鑰,需配合錢包的密碼來使用。

2)創建錢包數據庫。

3)創建賬戶管理對象。

4)創建資產管理對象。

5)實例化Wallet對象。

6)RescanBlocks掃描本地所有區塊,觸發錢包更新操作。

3.3.9 初始化網絡同步管理

P2P通信模塊主要由SyncManager管理,SyncManager負責節點業務層信息的同步工作,即區塊和交易信息的同步。代碼如下:

主要參數說明如下:

? newBlockCh:通道用于新挖掘出的區塊進行快速廣播給其他節點。通道大小為1024。

? netsync.NewSyncManager:實例化syncManager同步管理對象,它管理節點與節點之間的區塊、交易信息同步。

? newPoolTxListenner:啟動一個go routine,監聽交易池中的交易,將交易發送給syncManager同步管理對象或本地錢包。

詳細實現機制將在第10章進行講解。

3.3.10 初始化Pprof性能分析工具

Pprof是GO語言標準庫中自帶的性能分析工具。用于內存分析、CPU分析、代碼追蹤等,還可以生成性能分析圖表。(詳細參考https://golang.org/pkg/net/ http/pprof/)。在比原鏈中默認不啟用該功能,可以使用--prof_laddr參數啟動代碼性能分析功能,代碼示例如下:

3.3.11 初始化CPU挖礦功能

在比原鏈節點源碼中,只提供了CPU設備的挖礦功能,以目前全網的算力來看,CPU設備挖礦幾乎挖不到BTM幣了。目前主流的挖礦設備,有比特大陸定制的挖礦芯片或各大礦池使用GPU設備挖礦。挖礦和礦池細節將在第13章中詳細解讀。代碼實例如下:

其中,simd參數用于Tenaority CPU指令的優化。

主站蜘蛛池模板: 竹山县| 林芝县| 瑞丽市| 洞头县| 凤城市| 门头沟区| 淅川县| 武定县| 色达县| 岳池县| 临清市| 池州市| 五家渠市| 河津市| 内丘县| 香河县| 轮台县| 横山县| 通海县| 松滋市| 洪湖市| 女性| 昌都县| 武陟县| 双江| 崇州市| 东乡县| 镇坪县| 铁力市| 桑植县| 元朗区| 夏津县| 仁化县| 温宿县| 宁化县| 天门市| 京山县| 合作市| 双城市| 卢氏县| 监利县|