“初链”主网上线解析之“初始化”
從2017年11月啟動至今,經過歷時近一年的研究、開發與測試,初鏈主網Beta版于新加坡時間2018年09月28日08:00正式上線,在此之前,07:56分PBFT委員會第一次共識出塊和TrueChain fPOW創世區塊被挖出。
前言:本人從12年畢業一直做的是web后臺開發,雖然一直關注區塊鏈開發但一直少有真正了解其中開發細節的欲望。因為一方面炒幣的新聞實在是層出不窮,個人內心往往會把所有宣稱區塊鏈開發的公司都當作在炒作,出于對炒作的鄙視所以不愿意與之有什么瓜葛。另一方面我對devops的工作思路和微服務的概念很喜歡所以就放在對docker和k8s之類的技術上,并不想再多開一個技能分支免得啥都學不會。一年下來,終于看到炒幣者出現了相當規模的失利退場,而我對整個devops的思路也理清覺得差不多可以把區塊鏈開發的技能樹點亮了,所以進行了一些線下meetup了解一下現在區塊鏈發展的狀態。機緣巧合之下了解了“初鏈”這個目前基于以太坊虛擬機的區塊鏈方案。而“初鏈”社區對開發者是很友好的了,剛進群就給了40RMB的紅包激勵開發者對“初鏈”進行技術解析。由于本人對區塊鏈開發也是相當基礎對一些區塊鏈的概念僅僅是了解機制的程度,所以解析難免有失誤,獻丑的地方希望大家不吝賜教。
 本著只講自己看懂了的部分進行講解的原則,下面我就把我對“初鏈”beta版本的上線中對“初鏈”初始化的部分進行講解,而其中水果鏈的快鏈和慢鏈之間的問題不是我現在能夠理解的,且待后期學會之后慢慢道來。(說個題外話,“初鏈”這個名字真是絕了,中文真是博大精深^_^,聽起來就像初戀,初又有初始的意思,可能是希望區塊鏈開發都從初鏈開始吧,而且英文的truechain也是諧音又有“真實,認真”的意思,反正咋解釋都很不錯!)
 講一下查看代碼的思路:根據log了解初鏈初始化到運行時所標記的內容->根據標記內容到源碼中對應找到方法->把所有方法倒推到最起始的位置->理解代碼的運行機制梳理出運行邏輯
感謝程序員寫代碼時提示的不同log內容,希望log能更加友好一點。
 我們先看系統log提示了什么內容
 ?
 
Sanitizing cache to Go's GC limits 
Maximum peer count 
Committee Node info: Starting peer-to-peer node 
Allocated cache and file handles 
Initialised chain configuration 
Initialised chain configuration 
Initialising Truechain protocol 
Loaded most recent local header 
Loaded most recent local full block 
Loaded most recent local fast block 
Loaded most recent local Fastheader 
Loaded most recent local full Fastblock 
Loaded most recent local fast Fastblock 
Loaded local transaction journal 
Regenerated local transaction journal 
InitNodeInfo 
init mineFruit 
Starting P2P networkingget committee .. 
IPC endpoint opened 
Transaction pool price threshold updated
start miner --miner start function
Starting mining operation
RLPx listener up 
singleloop start.
Committee Info 
Committee member: 
start to mine 
FetchFastBlock 
FetchFastBlock 
FetchFastBlock 
FetchFastBlock 
FetchFastBlock 
FetchFastBlock 
 
?
如log所示,最后已經開始挖礦了。
運行之前還要執行一個命令$ getrue init path/to/genesis.json
 {
 ? "config": {
 ? ? "chainId": 10,
 ? ? "homesteadBlock": 0,
 ? ? "eip155Block": 0,
 ? ? "eip158Block": 0
 ? },
 ? "alloc":{
 ? ? "0xbd54a6c8298a70e9636d0555a77ffa412abdd71a" : { "balance" : 90000000000000000000000},
 ? ? "0x3c2e0a65a023465090aaedaa6ed2975aec9ef7f9" : { "balance" : 10000000000000000000000}
 ? },
 ? "committee":[
 ? ? {
 ? ? ? "address": "0x76ea2f3a002431fede1141b660dbb75c26ba6d97",
 ? ? ? "publickey": "0x04044308742b61976de7344edb8662d6d10be1c477dd46e8e4c433c1288442a79183480894107299ff7b0706490f1fb9c9b7c9e62ae62d57bd84a1e469460d8ac1"
 ? ? }
 ? ]
 ,
 ? "coinbase" ? : "0x0000000000000000000000000000000000000000",
 ? "difficulty" : "0x100",
 ? "extraData" ?: "",
 ? "gasLimit" ? : "0x2fefd8",
 ? "nonce" ? ? ?: "0x0000000000000042",
 ? "mixhash" ? ?: "0x0000000000000000000000000000000000000000000000000000000000000000",
 ? "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
 ? "timestamp" ?: "0x00"
 }
 此配置加載后在->/params/config.go中配置到初始化配置中
 其中config指定了相關協議機制的升級區塊所在的高度,簽名算法是homestead ->eip155 -> eip158,所以從homesteadBlock之前區塊都通過homestead相關算法機制來驗證,homesteadBlock 到 eip155Block之間的用eip155算法來驗證,依次類推(此段為摘抄,實際沒懂,只看到注釋中有關TheDao的配置)
 alloc:不懂
 committee:委員會地址和公鑰---意思是選擇一個自己支持的委員
 coinbase:自己的收益地址
 difficulty:挖礦難度
 extraData:額外數據
 gasLimit:以太坊要求的設置交易支出
 nonce:交易只能處理一次的計數器
 mixhash:與nonce組合判斷計算是否足夠
 parentHash:父區塊哈希值
 timestamp:時間戳
 這些配置都是以太坊需要的配置項。
 初始化文件加載之后就可以開始執行挖礦加入節點的操作了。
 再此之前根據Truechain的基本架構進行梳理,本文將對其中調用邏輯進行解析并闡釋前后調用關系以求講清楚整個加載過程和運行前做的事情。
 ?
 
項目加載配置流程(根據初鏈架構圖推導得出)
1.配置底層服務
 2.配置levelDB數據庫
 3.配置區塊鏈及共識算法
 4.提供api接口接收智能合約部署
 運行參數說明
 
$ getrue?
--nodiscover //---Disables the peer discovery mechanism:關閉發現節點的機能
--singlenode //---sing node model:單節點運行模式
--bft //---無此參數
--mine //---Enable mining:激活挖礦
--etherbase 0x8a45d70f096d3581866ed27a5017a4eeec0db2a1 //---Public address for block mining rewards:收益所在的公共地址(創世塊的地址)---如果不是創世塊就把自己的地址寫進去?
--bftkeyhex "c1581e25937d9ab91421a3e1a2667c85b0397c75a195e643109938e987acecfc" //---committee generate privatekey as hex (for testing):委員會生成的私有鑰匙
--bftip "192.168.68.43"//---committee node ip:委員會節點ip
--bftport 10080 //---committee node port:委員會節點端口 
下面根據源碼調用順序做一個梳理
init->geture->makeFullNode->makeConfigNode
 ? | ? ? | ? ? ? ?|->RegisterEthService->stack.Register
 ? | ? ? | ? ? ? ?| ? ? ?|->etrue.New->NewSnailBlockChain->loadLastState(慢鏈)
 ? | ? ? | ? ? ? ?| ? ? ? ? ? ?|->NewPbftAgent->InitNodeInfo(初始化委員會節點)
 ? | ? ? | ? ? ? ?| ? ? ? ? ? ?|->miner.New(挖水果)
 ? | ? ? | ? ? ? ?| ? ? ? ? ? ?|->NewBlockChain->loadLastState(快鏈)
 ? | ? ? | ? ? ? ?| ? ? ? ? ? ?|->core.NewTxPool->pool.loop(掃描交易)
 ? | ? ? | ? ? ? ?| ? ? ? ? ? ? ? ? ? ? |->pool.journal.rotate(重新生成交易流)
 ? | ? ? | ? ? ? ?|->SetNodeConfig(設置節點)->SetP2PConfig(設置p2p網絡)
 ? | ? ? | ? ? ? ?| ? ?| ? ? ? ? ? ? ? ?|->setNodeKey
 ? | ? ? | ? ? ? ?| ? ?| ? ? ? ? ? ? ? ?|->setNAT(設置NAT穿透)
 ? | ? ? | ? ? ? ?| ? ?| ? ? ? ? ? ? ? ?|->setListenAddress(監聽地址)
 ? | ? ? | ? ? ? ?| ? ?| ? ? ? ? ? ? ? ?|->setBootstrapNodes(啟動網絡節點)
 ? | ? ? | ? ? ? ?| ? ?|->setIPC(IPC協議支持)
 ? | ? ? | ? ? ? ?| ? ?|->setHTTP(http協議支持)
 ? | ? ? | ? ? ? ?| ? ?|->setWS(webservice程序支持)
 ? | ? ? | ? ? ? ?| ? ?|->setNodeUserIdent(用戶節點標記)
 ? | ? ? | ? ? ? ?|->SetTruechainConfig->setEtherbase(設置初鏈地址)
 ? | ? ? | ? ? ? ?| ? ? ? ?|->setGPO(設置gpo)
 ? | ? ? | ? ? ? ?| ? ? ? ?|->setTxPool(交易樹)
 ? | ? ? | ? ? ? ?| ? ? ? ?|->setEthash(區塊驗證)
 ? | ? ? | ? ? ? ?| ? ? ? ?|->setBftCommitteeKey(委員會鑰匙)
 ? | ? ? | ? ? ? ?|->SetShhConfig
 ? | ? ? |->startNode->utils.StartNode
 ? | ? ? ? ? |->accounts.WalletEvent
 ? | ? ? ? ? |->utils.MiningEnabledFlag.Name---讀取命令行參數mine
 ? | ? ? ? ? |->truechain.TxPool().SetGasPrice---從配置文件中讀取
 ? | ? ? ? ? |->truechain.StartMining->s.miner.Start(開始挖礦)
 ? | ? ? ? ? |->StartMinine->Etherbase->common.Address---讀取地址后生成收益節點的配置
 ? | ? ? ? ? |->stack.Start->getCommittee(獲取委員會節點)
 ? | ? ? ? ? ? ? ? ? ? |->startRPC->startIPC->StartIPCEndpoint->ServeListener(啟動p2p節點并提供rpc服務)
 ? |->importCommand->MakeChain->MakeChainDatabase->OpenDatabase->NewLDBDatabase(LevelDB在此初始化)
?
根據以上一節代碼樹結構我們可以看到每個方法調用順序以及最終啟動挖礦的整個流程,下面對一些關鍵代碼節點進行分解說明
 ?
 
->cmd/geture/main.go中
 ?
 
// startNode boots up the system node and all registered protocols, after which
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
// miner.
func startNode(ctx *cli.Context, stack *node.Node)func getrue(ctx *cli.Context) error {node := makeFullNode(ctx)startNode(ctx, node)node.Wait()return nil
}func init() {// Initialize the CLI app and start Getrueapp.Action = getrue...
}
 
main中執行的只是讀取參數,重點在init(golang 的init方法會在包加載的時候提前運行)方法中進行getrue命令的執行,其中我們看到了對全節點的初始化配置,開啟節點運行的配置等內容
 ?
->cmd/geture/config.go
func makeFullNode(ctx *cli.Context) *node.Node {stack, cfg := makeConfigNode(ctx)utils.RegisterEthService(stack, &cfg.Etrue)if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {utils.RegisterDashboardService(stack, &cfg.Dashboard, gitCommit)}// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev modeshhEnabled := enableWhisper(ctx)shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DeveloperFlag.Name)if shhEnabled || shhAutoEnabled {if ctx.GlobalIsSet(utils.WhisperMaxMessageSizeFlag.Name) {cfg.Shh.MaxMessageSize = uint32(ctx.Int(utils.WhisperMaxMessageSizeFlag.Name))}if ctx.GlobalIsSet(utils.WhisperMinPOWFlag.Name) {cfg.Shh.MinimumAcceptedPOW = ctx.Float64(utils.WhisperMinPOWFlag.Name)}utils.RegisterShhService(stack, &cfg.Shh)}// Add the Truechain Stats daemon if requested.if cfg.Ethstats.URL != "" {utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)}return stack }啟動全節點配置注冊系統參數(RegisterDashboardService)等工作
 ?
->/cmd/utils/flags.go
// SetNodeConfig applies node-related command line flags to the config. func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {SetP2PConfig(ctx, &cfg.P2P)setIPC(ctx, cfg)setHTTP(ctx, cfg)setWS(ctx, cfg)setNodeUserIdent(ctx, cfg)... }func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {setNodeKey(ctx, cfg)setNAT(ctx, cfg)setListenAddress(ctx, cfg)setBootstrapNodes(ctx, cfg)...2---log.Info("Maximum peer count", "ETRUE", ethPeers, "LES", lightPeers, "total", cfg.MaxPeers)...if ctx.GlobalBool(DeveloperFlag.Name) {// --dev mode can't use p2p networking.cfg.MaxPeers = 0cfg.ListenAddr = ":0"cfg.NoDiscovery = truecfg.DiscoveryV5 = false} }在代碼中為SetP2PConfig()方法,而此方法是SetNodeConfig所調加載各種協議:IPC,HTTP,WS,也就是p2p網絡的底層設置。
->/cmd/utils/flags.go
func SetTruechainConfig(ctx *cli.Context, stack *node.Node, cfg *etrue.Config){...setEtherbase(ctx, ks, cfg)setGPO(ctx, &cfg.GPO)setTxPool(ctx, &cfg.TxPool)setEthash(ctx, cfg)...ctx.GlobalIsSet(...)...setBftCommitteeKey(ctx, cfg) }在這里我們看到對配置文件genesis.json內參數的讀取并設置,
-> /node/node.go
 ?
 
func (n *Node) Start() error {n.lock.Lock()defer n.lock.Unlock()...n.serverConfig = n.config.P2Pn.serverConfig.PrivateKey = n.config.NodeKey()n.serverConfig.Name = n.config.NodeName()n.serverConfig.Logger = n.log...for _, constructor := range n.serviceFuncs {//把前面的p2p網絡配置進來}if err := n.startRPC(services); err != nil {//開啟了rpc調用服務for _, service := range services {service.Stop()}running.Stop()return err}
}
 
以上代碼內部對節點工作進行布置,啟動rpc對外服務,配置p2p網絡
-> /ethdb/database.go
func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) {logger := log.New("database", file)db, err := leveldb.OpenFile(file, &opt.Options{OpenFilesCacheCapacity: handles,BlockCacheCapacity: cache / 2 * opt.MiB,WriteBuffer: cache / 4 * opt.MiB, // Two of these are used internallyFilter: filter.NewBloomFilter(10),}) }此方法在調用樹中可看到是在makechain中加載的,其中就是把數據庫文件數據加載出來使用。
-> /eture/backend.go
func New(ctx *node.ServiceContext, config *Config) (*Truechain, error) {chainDb, err := CreateDB(ctx, config, "chaindata")chainConfig, genesisHash, genesisErr := fastchain.SetupGenesisBlock(chainDb, config.FastGenesis)snailConfig, snailHash, snailErr := chain.SetupGenesisBlock(chainDb, config.SnailGenesis)etrue.snailblockchain, err = chain.NewSnailBlockChain(chainDb, snailCacheConfig, etrue.chainConfig, etrue.engine, vmConfig)etrue.txPool = core.NewTxPool(config.TxPool, etrue.chainConfig, etrue.blockchain)etrue.snailPool = core.NewSnailPool(etrue.chainConfig, etrue.blockchain, etrue.snailblockchain, etrue.engine)etrue.election = NewElction(etrue.blockchain, etrue.snailblockchain, etrue.config)etrue.snailblockchain.Validator().SetElection(etrue.election, etrue.blockchain)ethash.SetElection(etrue.election)ethash.SetSnailChainReader(etrue.snailblockchain)etrue.election.SetEngine(etrue.engine)coinbase, _ := etrue.Etherbase()etrue.agent = NewPbftAgent(etrue, etrue.chainConfig, etrue.engine, etrue.election, coinbase)etrue.miner = miner.New(etrue, etrue.chainConfig, etrue.EventMux(), etrue.engine, etrue.election, etrue.Config().MineFruit, etrue.Config().NodeType)etrue.miner.SetExtra(makeExtraData(config.ExtraData)) }啟動一個生成一個Truechain實體,代碼注釋為(?New creates a new Truechain object (including the initialisation of the common Truechain object)
其中我們看到前面加載的內容以及選舉和快鏈慢鏈的內容加載配置引擎、加載拜占庭委員會等工作,最后開始miner.New開始挖礦,到這里,我們可以把truechain的架構圖拿出來對照一番了,
上圖我們看到底層是P2P網絡,LevelDB數據庫,密碼學算法,分片優化,其中P2P網絡和levelDB是很明顯在上述代碼中的,初始化部分暫時分析到這里,還有很多知識點沒有講到,而且每個部分都只是先點到,后續每個模塊要進行拆分分析以求達到更深的理解。
總結
在閱讀源碼的時候確實是有點摸頭不著腦的,區塊鏈的知識我也是剛剛接觸,其中大部分的實現和調用如果不參照log來的化根本無法下手進行分析,閱讀完比特幣白皮書,以太坊白皮書和初鏈白皮書以及黃皮書之后比較清晰的感覺得到初鏈底層架構的巧妙設計能夠讓兩種不同的共識機制實現取長補短。而作為個人開發者來說要想入手開發掌握肯定就要把里面的源碼吃透,否則就是一些概念的倒騰對我這種希望以實戰為主的開發者來說真是有點“看得著吃不著”的感覺,而經過這次分析呢總算是理解了底層的一些架構,希望接下來的學習能夠讓我更加理解區塊鏈開發。
另:初鏈大力支持社區力量加入,我也可以邀請大家加入,希望你在看到這個文章后加入初鏈社區,加群后可以把我當作推薦人,就當作你看著篇文章所得的利益共享吧^_^這也是區塊鏈希望實現的自己擁有自己的付出換回成果的理念啊!推薦人:我是魚餌。謝謝大家閱讀。
 ?
?
?
總結
以上是生活随笔為你收集整理的“初链”主网上线解析之“初始化”的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 单目深度估计梳理(2) -- 多任务篇
- 下一篇: 【Android】将图片转为xml文件
