{"content":{"title":"vneus daemon代码解析","body":"# venus启动\r\n\r\nvenus deamon启动时主要调用两个`app/node.go`的两个方法。其中一个是用`(b *Builder) build()`生成`node`对象，这个生成方法会调用` (builder *RPCBuilder) AddServices(services ...RPCService)`方法把各种api服务添加到`node`对象，后面调用`(node *Node) RunRPCAndWait()`方法启动对外服务。服务接口有rest api和rpc两种形式。每个服务都是一个submodule，定义在`app/submodule`目录下，`***_submodule.go`定义类，`***_api.go`类实现接口逻辑。\r\n\r\n另一个是`Start()`方法来启动`syncer，mpool，paychan,network, eth`五个模块。\r\n```\r\n/ Start boots up the node.\r\nfunc (node *Node) Start(ctx context.Context) error {\r\n\t.......\r\n\r\n\t// start syncer module to receive new blocks and start sync to latest height\r\n\terr = node.syncer.Start(syncCtx)\r\n\tif err != nil {\r\n\t\treturn err\r\n\t}\r\n\r\n\t// Start mpool module to receive new message\r\n\terr = node.mpool.Start(syncCtx)\r\n\tif err != nil {\r\n\t\treturn err\r\n\t}\r\n\r\n\terr = node.paychan.Start(ctx)\r\n\tif err != nil {\r\n\t\treturn err\r\n\t}\r\n\r\n\t// network should start late,\r\n\terr = node.network.Start(syncCtx)\r\n\tif err != nil {\r\n\t\treturn err\r\n\t}\r\n\r\n\tif err := node.eth.Start(ctx); err != nil {\r\n\t\treturn fmt.Errorf(\"failed to start eth module %v\", err)\r\n\t}\r\n}\r\n```\r\n其中`syncer`的启动会产生一个goroutine，for循环不停的接受订阅的信息，仅仅是block信息，为什么这样设计呐？tipset的同步逻辑放在了别的包来实现。\r\n```\r\n// Start starts the syncer submodule for a node.\r\nfunc (syncer *SyncerSubmodule) Start(ctx context.Context) error {\r\n\t.......\r\n\t// process incoming blocks\r\n\tgo func() {\r\n\t\tfor {\r\n\t\t\treceived, err := syncer.BlockSub.Next(ctx)\r\n\t\t\tif err != nil {\r\n\t\t\t\tif ctx.Err() != context.Canceled {\r\n\t\t\t\t\tlog.Errorf(\"error reading message from topic %s: %s\", syncer.BlockSub.Topic(), err)\r\n\t\t\t\t}\r\n\t\t\t\treturn\r\n\t\t\t}\r\n\r\n\t\t\tif err := syncer.handleIncomingBlocks(ctx, received); err != nil {\r\n\t\t\t\thandlerName := runtime.FuncForPC(reflect.ValueOf(syncer.handleIncomingBlocks).Pointer()).Name()\r\n\t\t\t\tif err != context.Canceled {\r\n\t\t\t\t\tlog.Debugf(\"error in handler %s for topic %s: %s\", handlerName, syncer.BlockSub.Topic(), err)\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}()\r\n\r\n\terr = syncer.ChainModule.Start(ctx)\r\n\tif err != nil {\r\n\t\treturn err\r\n\t}\r\n\r\n\treturn syncer.ChainSyncManager.Start(ctx)\r\n}\r\n\r\n```\r\n而其中的`ChainModule.Start()`方法最主要调用一个`(c *ChainFork) preMigrationWorker()`来提前进行状态数据的迁移。作为一个刚刚从以太坊系转过来的人，感觉好惊讶，每次启动都先检查一次状态迁移（状态数据修改）。\r\n`syncer.ChainSyncManager.Start(ctx)`而这个方法会启动两个gorotine来处理信息的同步。\r\n```\r\n// Start launches the business logic for the syncing subsystem.\r\nfunc (d *Dispatcher) Start(syncingCtx context.Context) {\r\n\tgo d.processIncoming(syncingCtx)\r\n\r\n\tgo d.syncWorker(syncingCtx)\r\n}\r\n```\r\n具体的sync协议我们另外一篇来讲解。\r\n\r\n`mpool.Start()`方法会订阅msg，验证msg合法后放进mpool。\r\n\r\n`node.paychan.Start(ctx)`这个方法好像不是开启一个goroutine不停的处理事情，也许和chan不能一直存在的原因。\r\n\r\n` node.network.Start(syncCtx)`方法主要作用是连接peer，是通过` (pmgr *PeerMgr) doExpand(ctx context.Context)`方法来实现的。\r\n\r\n`node.eth.Start(ctx)`启动两个goroutine。\r\n```\r\nfunc (em *EthSubModule) Start(_ context.Context) error {\r\n\tif err := em.ethEventAPI.Start(em.ctx); err != nil {\r\n\t\treturn err\r\n\t}\r\n\r\n\treturn em.ethAPIAdapter.start(em.ctx)\r\n}\r\n```\r\n一个用于事件订阅的goroutine，一个是开启msg接受的goroutine，接收到消息后放入mpool也许？需要更多确认。\r\n\r\n# 执行区块，进行状态迁移\r\n\r\n`venus-miner`服务获得出块权后会通过http请求来调用`app/submodule/app/submodule/ming/ming_api.go: (miningAPI *MiningAPI) minerCreateBlock()`接口。这个接口主要是调用`pkg/statemanager/state_manager.go:(s *Stmgr) RunStateTransition()`来执行父区块，得到父区块的receipt root和state root。最主要进行状态迁移的函数是：`pkg/consensus/expected.go:(c *Expected) RunStateTransition()`方法，这个方法会调用`pkg/consensus/processor.go:(p *DefaultProcessor) ApplyBlocks()`来执行区块，方法的内部再调用`pkg/fvm/fvm.go:(fvm *FVM) ApplyMessage()`来执行区块内的交易，真正执行交易的是`extern/filecoin-ffi/fvm.go:(f *FVM) ApplyMessage()`方法，这个方法会通过cgo调用rust的执行方法。但是这里要小心，cgo调用还是不是ref-fvm库的方法，还是filecoin-ffi库中的rust方法。\r\n以上也只是把父区块执行一下而已，后面还需进行gas费用计算，reward奖励，签名的事情，才能完整的产生一个block。主要这里是不需要从mpool里面选择交易进行区块填充的，在venus-miner调用`minerCreateBlock`接口前，已经通过调用`app/submodule/mpool/mpool_api.go:(a *MessagePoolAPI) MpoolSelects()`接口从venus node选择了要打包的msg。为什么要这样设计啊？直接创建区块时，一起选择交易不是更有效率吗？就不需要来回传输了？"},"author":{"user":"https://learnblockchain.cn/people/808","address":null},"history":"QmdRYYNjP3nx9d4JL6uhrXFJ84397GjSKdDZ6tFPmemoLE","timestamp":1682495954,"version":1}