一什乙,geth makefile 以及編譯邏輯
上篇提到用 make geth 來(lái)編譯geth客戶(hù)端。我們來(lái)看看make file做了什么:
build/env.sh go run build/ci.go install ./cmd/geth
@echo "Done building."
@echo "Run \"$(GOBIN)/geth\" to launch geth."
執(zhí)行了env.sh
# Create fake Go workspace if it doesn't exist yet.
workspace="$PWD/build/_workspace"
root="$PWD"
ethdir="$workspace/src/github.com/ethereum"
if [ ! -L "$ethdir/go-ethereum" ]; then
mkdir -p "$ethdir"
cd "$ethdir"
ln -s ../../../../../. go-ethereum
cd "$root"
fi
# Set up the environment to use the workspace.
GOPATH="$workspace"
export GOPATH
# Run the command inside the workspace.
cd "$ethdir/go-ethereum"
PWD="$ethdir/go-ethereum"
里面做了兩件事情
1,ln -s命令在build/_workspace/ 目錄上生成了go-etherum的一個(gè)文件鏡像廓脆,不占用磁盤(pán)空間逊拍,與源文件同步更新
2爱咬,把工作目錄 workspace加入GOPATH環(huán)境變量
跟蹤進(jìn)ci.go 關(guān)鍵函數(shù)
func doInstall(cmdline []string)
這個(gè)方法拼接了完整的geth 編譯命令:
go install -ldflags -X main.gitCommit= 722bac84fa503199b9c485c1a2bfba03bc487d -v ./cmd/geth
二,geth 客戶(hù)端main函數(shù) 以及geth的啟動(dòng)入口
geth啟動(dòng)命令:
build/bin/geth --datadir =./data/00 --networkid 1 --fast --cache = 1024 --etherbase“[your preferred account]”console >>geth.log
--datadir設(shè)置區(qū)塊鏈數(shù)據(jù)存放路徑
--networkid 網(wǎng)絡(luò)設(shè)置啟動(dòng)的區(qū)塊鏈網(wǎng)路默認(rèn)值是1表示以太坊公司份汗,0,2,3表示測(cè)試網(wǎng)路盈电,大于4表示本地私有網(wǎng)路
--fast同步方式,默認(rèn)為fast要選擇完全同步使用命令--syncmode full
--cache設(shè)置緩存大斜睢(最小16 MB /數(shù)據(jù)庫(kù)強(qiáng)制)(默認(rèn)值:128)
console表示啟動(dòng)控制臺(tái) >>geth.log將控制臺(tái)顯示內(nèi)容輸出到文件geth.log中去
geth的源碼入口main()函數(shù)在/cmd/geth/main.go
// /cmd/geth/main.go
func main() {
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func geth(ctx *cil.Context) error {
node := makeFullNode(ctx)
startNode(ctx, node)
node.Wait()
return nil
}
main.go / main() - > app.go / Run() - > app.go / HandleAction() - > main.go / geth()
通過(guò)這幾個(gè)函數(shù)跳轉(zhuǎn)來(lái)到了GETH啟動(dòng)入口匆帚。
從啟動(dòng)入口可以看到第一個(gè)啟動(dòng)的模塊是node模塊,通過(guò)makeFullNode函數(shù)來(lái)創(chuàng)建一個(gè)節(jié)點(diǎn)對(duì)象(在ETH里node可以認(rèn)為是以太坊全網(wǎng)的一個(gè)節(jié)點(diǎn)旁钧,也可以認(rèn)為是一個(gè)以太坊終端)吸重。
func makeFullNode(ctx *cli.Context) *node.Node {
stack, cfg := makeConfigNode(ctx)
utils.RegisterEthService(stack, &cfg.Eth)
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 mode
shhEnabled := 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 Ethereum Stats daemon if requested.
if cfg.Ethstats.URL != "" {
utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
}
return stack
}
func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
// Load defaults.
cfg := gethConfig{
Eth: eth.DefaultConfig,
Shh: whisper.DefaultConfig,
Node: defaultNodeConfig(),
Dashboard: dashboard.DefaultConfig,
}
// Load config file.
if file := ctx.GlobalString(configFileFlag.Name); file != "" {
if err := loadConfig(file, &cfg); err != nil {
utils.Fatalf("%v", err)
}
}
// Apply flags.
utils.SetNodeConfig(ctx, &cfg.Node)
stack, err := node.New(&cfg.Node)
if err != nil {
utils.Fatalf("Failed to create the protocol stack: %v", err)
}
utils.SetEthConfig(ctx, stack, &cfg.Eth)
if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) {
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
}
utils.SetShhConfig(ctx, stack, &cfg.Shh)
utils.SetDashboardConfig(ctx, &cfg.Dashboard)
return stack, cfg
}
makeConfigNode做了兩件事 1,函數(shù)獲取以太坊相關(guān)服務(wù)(Eth Node Shh DashBoard)的默認(rèn)配置 2歪今,通過(guò)Node的默認(rèn)配置來(lái)創(chuàng)建一個(gè)Node嚎幸。返回node對(duì)象和cfg
makeFullNode把創(chuàng)建eth service、創(chuàng)建DashBoard service彤委、創(chuàng)建Shh service以及創(chuàng)建ethStats service注冊(cè)到Node
從這里我們可以看出來(lái)eth DashBoard Shh ethStats都是從node.Service接口派生出來(lái)的鞭铆,它們的實(shí)例化對(duì)象需要實(shí)現(xiàn)node.Service所有接口,這在以后相關(guān)模塊的分析中會(huì)遇到
type Service interface {
// Protocols retrieves the P2P protocols the service wishes to start.
Protocols() []p2p.Protocol
// APIs retrieves the list of RPC descriptors the service provides
APIs() []rpc.API
// Start is called after all services have been constructed and the networking
// layer was also initialized to spawn any goroutines required by the service.
Start(server *p2p.Server) error
// Stop terminates all goroutines belonging to the service, blocking until they
// are all terminated.
Stop() error
}
Protocols() 返回service要啟動(dòng)的P2P 協(xié)議列表
APIs() 返回service提供的RPC接口
Start() 啟動(dòng)已經(jīng)初始化的service
Stop() 停止service所有的goroutines,并阻塞線程知道所有g(shù)oroutines都終止
接下來(lái)調(diào)用startNode來(lái)啟動(dòng)
func startNode(ctx *cli.Context, stack *node.Node) {
// Start up the node itself
utils.StartNode(stack)
// Unlock any account specifically requested
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
passwords := utils.MakePasswordList(ctx)
unlocks := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
for i, account := range unlocks {
if trimmed := strings.TrimSpace(account); trimmed != "" {
unlockAccount(ctx, ks, trimmed, i, passwords)
}
}
// Register wallet event handlers to open and auto-derive wallets
events := make(chan accounts.WalletEvent, 16)
stack.AccountManager().Subscribe(events)
go func() {
// Create an chain state reader for self-derivation
rpcClient, err := stack.Attach()
if err != nil {
utils.Fatalf("Failed to attach to self: %v", err)
}
stateReader := ethclient.NewClient(rpcClient)
// Open any wallets already attached
for _, wallet := range stack.AccountManager().Wallets() {
if err := wallet.Open(""); err != nil {
log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err)
}
}
// Listen for wallet event till termination
for event := range events {
switch event.Kind {
case accounts.WalletArrived:
if err := event.Wallet.Open(""); err != nil {
log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err)
}
case accounts.WalletOpened:
status, _ := event.Wallet.Status()
log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status)
if event.Wallet.URL().Scheme == "ledger" {
event.Wallet.SelfDerive(accounts.DefaultLedgerBaseDerivationPath, stateReader)
} else {
event.Wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader)
}
case accounts.WalletDropped:
log.Info("Old wallet dropped", "url", event.Wallet.URL())
event.Wallet.Close()
}
}
}()
// Start auxiliary services if enabled
if ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name) {
// Mining only makes sense if a full Ethereum node is running
if ctx.GlobalBool(utils.LightModeFlag.Name) || ctx.GlobalString(utils.SyncModeFlag.Name) == "light" {
utils.Fatalf("Light clients do not support mining")
}
var ethereum *eth.Ethereum
if err := stack.Service(eereum); err != nil {
utils.Fatalf("Ethereum service not running: %v", err)
}
// Use a reduced number of threads if requested
if threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name); threads > 0 {
type threaded interface {
SetThreads(threads int)
}
if th, ok := ethereum.Engine().(threaded); ok {
th.SetThreads(threads)
}
}
// Set the gas price to the limits from the CLI and start mining
ethereum.TxPool().SetGasPrice(utils.GlobalBig(ctx, utils.GasPriceFlag.Name))
if err := ethereum.StartMining(true); err != nil {
utils.Fatalf("Failed to start mining: %v", err)
}
}
}
首先node start自己车遂。node將之前注冊(cè)的所有service交給p2p.Server, 然后啟動(dòng)p2p.Server對(duì)象封断,Server對(duì)象會(huì)逐個(gè)啟動(dòng)每個(gè)Service。
解鎖賬號(hào)舶担,并注冊(cè)錢(qián)包反饋事件坡疼。
啟動(dòng)RPC。(RPC 提供一種能通過(guò)網(wǎng)絡(luò)或者其他I/O連接訪問(wèn)的能力衣陶,將在后續(xù)章節(jié)分析)
如果配置支持挖礦柄瑰,則啟動(dòng)挖礦。
node.Wait()阻塞住程序剪况,直到node stop教沾。
三,小結(jié)
Node就好像一個(gè)組裝工廠译断,把以太坊相關(guān)功能裝配起來(lái)授翻,連接了以太坊的前端和后端,啟動(dòng)RPC供遠(yuǎn)程調(diào)用孙咪,啟動(dòng)了P2P server跟網(wǎng)絡(luò)中的其他節(jié)點(diǎn)建立聯(lián)系堪唐,開(kāi)始了console 供命令行操作。
Node模塊并沒(méi)有做任何跟區(qū)塊鏈實(shí)質(zhì)相關(guān)的事情翎蹈,甚至它都不直接創(chuàng)建以太坊相關(guān)模塊淮菠,而讓它們封裝成一個(gè)個(gè)的service,每個(gè)service自身自滅荤堪。就好比一個(gè)賣(mài)場(chǎng)合陵,各個(gè)商家都可以來(lái)賣(mài)東西蒜埋,但我不關(guān)心你們的死活窖维,你們的死活也不影響我。
這給我們擴(kuò)展以太坊客戶(hù)端的功能提供一個(gè)思路轧邪,我們可以把擴(kuò)展功能封裝成一個(gè)Service寇荧,塞給Node举庶。