在上一篇博客 [乙太坊源码学习] 乙太坊node的启动过程 中,我们在其中提到了 Ethereum Backend的启动过程,大体总结一下就是:
(1)以太坊节点注册Ethereum Backend的构造函数
(2)以太坊节点在启动的过程中,调用构造函数,生成Backend,然后启动它。
这是一个很简略和过程,下面我们就详细的学习一下Ethereum Backend及它的启动过程。
1. Ethereum结构体代码
type Ethereum struct { config *Config //这是配置信息,下面再细说 chainConfig *params.ChainConfig //这是区块链的配置信息 // Channel for shutting down the service shutdownChan chan bool // 关闭该线程的标志位,通过goroutin+channel的形式实现。当需要关闭该线程时,只需要往该channel中发送一个bool值就行了 stopDbUpgrade func() error // 这是一个函数句柄,用于关闭该线程时,提示关闭数据库写入的 // Handlers txPool *core.TxPool //交易池,用来缓存未处理的交易的 blockchain *core.BlockChain //区块链对象 protocolManager *ProtocolManager //协议管理器 lesServer LesServer //暂时不知道是做什么的 // DB interfaces chainDb ethdb.Database // 数据库对象 eventMux *event.TypeMux //事件的收听器和分发器 engine consensus.Engqine //共识算法的引擎,目前用的pow accountManager *accounts.Manager //账户管理器 bloomRequests chan chan *bloombits.Retrieval // 缓存bloom数据接收请求的管道。Channel receiving bloom data retrieval requests bloomIndexer *core.ChainIndexer // 对Bloom数据创建索引的工具。 Bloom indexer operating during block imports ApiBackend *EthApiBackend //Backend接口的封装体,其实里面就是一堆函数 miner *miner.Miner //矿工 gasPrice *big.Int //gas价格 etherbase common.Address //矿工收取奖励的地址 networkId uint64 //P2P网络的id netRPCService *ethapi.PublicNetAPI //P2P 服务一些接口的封装体,其实就是一些简单的函数 lock sync.RWMutex // 对本结构体进行改写的时候,需要加锁。Protects the variadic fields (e.g. gas price and etherbase) }
通过这个结构体所包含的成员变量来看,Ethereum Backend的功能覆盖了账号管理、共识、挖矿、协议管理、数据库写入等等比较核心的功能环节,可见它是乙太坊的核心程序。
Ethereum Backend其实实现了node/service.go中的Service接口,该接口有三个方法:
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 }
Etherem实现了4个方法,以提供Service的功能。这几个方法,我们稍后会渐渐提到。
2. Ethereum的创建方法New
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { log.Info(">>>>>>>>> Will create backend with config: ", "config", config) log.Info(">>>>>>>>> Will create backend with context: ", "context", ctx) _,file,line,_ := runtime.Caller(2) log.Info(">>>>>>>>>> backend.New() Caller: ", "file", file, "line", line) if config.SyncMode == downloader.LightSync { return nil, errors.New("can't run eth.Ethereum in light sync mode, use les.LightEthereum") } if !config.SyncMode.IsValid() { return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode) } chainDb, err := CreateDB(ctx, config, "chaindata") if err != nil { return nil, err } stopDbUpgrade := upgradeDeduplicateData(chainDb) chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis) log.Info(">>>>>>>>> Genesis Block created: ", "genesis", genesisHash) if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { return nil, genesisErr } log.Info("Initialised chain configuration", "config", chainConfig) eth := &Ethereum{ config: config, chainDb: chainDb, chainConfig: chainConfig, eventMux: ctx.EventMux, accountManager: ctx.AccountManager, engine: CreateConsensusEngine(ctx, &config.Ethash, chainConfig, chainDb), shutdownChan: make(chan bool), stopDbUpgrade: stopDbUpgrade, networkId: config.NetworkId, gasPrice: config.GasPrice, etherbase: config.Etherbase, bloomRequests: make(chan chan *bloombits.Retrieval), bloomIndexer: NewBloomIndexer(chainDb, params.BloomBitsBlocks), } log.Info("Initialising Ethereum protocol", "versions", ProtocolVersions, "network", config.NetworkId) log.Info(">>>>>>>>> Backend created: ", "backend", eth) if !config.SkipBcVersionCheck { bcVersion := core.GetBlockChainVersion(chainDb) if bcVersion != core.BlockChainVersion && bcVersion != 0 { return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d). Run geth upgradedb.\n", bcVersion, core.BlockChainVersion) } core.WriteBlockChainVersion(chainDb, core.BlockChainVersion) } var ( vmConfig = vm.Config{EnablePreimageRecording: config.EnablePreimageRecording} cacheConfig = &core.CacheConfig{Disabled: config.NoPruning, TrieNodeLimit: config.TrieCache, TrieTimeLimit: config.TrieTimeout} ) log.Info(">>>>>>>>> VM config created: ") log.Info(">>>>>>>>> Cache config created: ", "cacheconfig", cacheConfig) eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, eth.chainConfig, eth.engine, vmConfig) log.Info(">>>>>>>>> Blockchain created: ") if err != nil { return nil, err } // Rewind the chain in case of an incompatible config upgrade. if compat, ok := genesisErr.(*params.ConfigCompatError); ok { log.Warn("Rewinding chain to upgrade configuration", "err", compat) eth.blockchain.SetHead(compat.RewindTo) core.WriteChainConfig(chainDb, genesisHash, chainConfig) } eth.bloomIndexer.Start(eth.blockchain) if config.TxPool.Journal != "" { config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal) log.Info(">>>>>>>>> TxPool Journal created: ", "journal", config.TxPool.Journal) } eth.txPool = core.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain) log.Info(">>>>>>>>> Txpool created: ", "txpool", eth.txPool) if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil { return nil, err } eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine) eth.miner.SetExtra(makeExtraData(config.ExtraData)) log.Info(">>>>>>>>> Miner created: ", "miner", eth.miner) eth.ApiBackend = &EthApiBackend{eth, nil} gpoParams := config.GPO if gpoParams.Default == nil { gpoParams.Default = config.GasPrice } eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams) log.Info(">>>>>>>>> Backend create finished: ", "backend", eth) return eth, nil }
未完待续