github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/service.go (about) 1 package gossip 2 3 import ( 4 "errors" 5 "fmt" 6 "math/big" 7 "math/rand" 8 "sync" 9 "sync/atomic" 10 "time" 11 12 "github.com/unicornultrafoundation/go-helios/hash" 13 "github.com/unicornultrafoundation/go-helios/native/dag" 14 "github.com/unicornultrafoundation/go-helios/native/idx" 15 utypes "github.com/unicornultrafoundation/go-helios/types" 16 "github.com/unicornultrafoundation/go-helios/utils/workers" 17 "github.com/unicornultrafoundation/go-u2u/accounts" 18 "github.com/unicornultrafoundation/go-u2u/common" 19 "github.com/unicornultrafoundation/go-u2u/core/types" 20 "github.com/unicornultrafoundation/go-u2u/eth/protocols/snap" 21 "github.com/unicornultrafoundation/go-u2u/event" 22 notify "github.com/unicornultrafoundation/go-u2u/event" 23 "github.com/unicornultrafoundation/go-u2u/log" 24 "github.com/unicornultrafoundation/go-u2u/node" 25 "github.com/unicornultrafoundation/go-u2u/p2p" 26 "github.com/unicornultrafoundation/go-u2u/p2p/dnsdisc" 27 "github.com/unicornultrafoundation/go-u2u/p2p/enode" 28 "github.com/unicornultrafoundation/go-u2u/p2p/enr" 29 "github.com/unicornultrafoundation/go-u2u/rpc" 30 31 "github.com/unicornultrafoundation/go-u2u/ethapi" 32 "github.com/unicornultrafoundation/go-u2u/eventcheck" 33 "github.com/unicornultrafoundation/go-u2u/eventcheck/basiccheck" 34 "github.com/unicornultrafoundation/go-u2u/eventcheck/epochcheck" 35 "github.com/unicornultrafoundation/go-u2u/eventcheck/gaspowercheck" 36 "github.com/unicornultrafoundation/go-u2u/eventcheck/heavycheck" 37 "github.com/unicornultrafoundation/go-u2u/eventcheck/parentscheck" 38 "github.com/unicornultrafoundation/go-u2u/evmcore" 39 "github.com/unicornultrafoundation/go-u2u/gossip/blockproc" 40 "github.com/unicornultrafoundation/go-u2u/gossip/blockproc/drivermodule" 41 "github.com/unicornultrafoundation/go-u2u/gossip/blockproc/eventmodule" 42 "github.com/unicornultrafoundation/go-u2u/gossip/blockproc/evmmodule" 43 "github.com/unicornultrafoundation/go-u2u/gossip/blockproc/sealmodule" 44 "github.com/unicornultrafoundation/go-u2u/gossip/blockproc/verwatcher" 45 "github.com/unicornultrafoundation/go-u2u/gossip/emitter" 46 "github.com/unicornultrafoundation/go-u2u/gossip/filters" 47 "github.com/unicornultrafoundation/go-u2u/gossip/gasprice" 48 "github.com/unicornultrafoundation/go-u2u/gossip/proclogger" 49 snapsync "github.com/unicornultrafoundation/go-u2u/gossip/protocols/snap" 50 "github.com/unicornultrafoundation/go-u2u/logger" 51 "github.com/unicornultrafoundation/go-u2u/native" 52 "github.com/unicornultrafoundation/go-u2u/utils/signers/gsignercache" 53 "github.com/unicornultrafoundation/go-u2u/utils/txtime" 54 "github.com/unicornultrafoundation/go-u2u/utils/wgmutex" 55 "github.com/unicornultrafoundation/go-u2u/valkeystore" 56 "github.com/unicornultrafoundation/go-u2u/vecmt" 57 ) 58 59 type ServiceFeed struct { 60 scope notify.SubscriptionScope 61 62 newEpoch notify.Feed 63 newEmittedEvent notify.Feed 64 newBlock notify.Feed 65 newLogs notify.Feed 66 } 67 68 func (f *ServiceFeed) SubscribeNewEpoch(ch chan<- idx.Epoch) notify.Subscription { 69 return f.scope.Track(f.newEpoch.Subscribe(ch)) 70 } 71 72 func (f *ServiceFeed) SubscribeNewEmitted(ch chan<- *native.EventPayload) notify.Subscription { 73 return f.scope.Track(f.newEmittedEvent.Subscribe(ch)) 74 } 75 76 func (f *ServiceFeed) SubscribeNewBlock(ch chan<- evmcore.ChainHeadNotify) notify.Subscription { 77 return f.scope.Track(f.newBlock.Subscribe(ch)) 78 } 79 80 func (f *ServiceFeed) SubscribeNewLogs(ch chan<- []*types.Log) notify.Subscription { 81 return f.scope.Track(f.newLogs.Subscribe(ch)) 82 } 83 84 type BlockProc struct { 85 SealerModule blockproc.SealerModule 86 TxListenerModule blockproc.TxListenerModule 87 PreTxTransactor blockproc.TxTransactor 88 PostTxTransactor blockproc.TxTransactor 89 EventsModule blockproc.ConfirmedEventsModule 90 EVMModule blockproc.EVM 91 } 92 93 func DefaultBlockProc() BlockProc { 94 return BlockProc{ 95 SealerModule: sealmodule.New(), 96 TxListenerModule: drivermodule.NewDriverTxListenerModule(), 97 PreTxTransactor: drivermodule.NewDriverTxPreTransactor(), 98 PostTxTransactor: drivermodule.NewDriverTxTransactor(), 99 EventsModule: eventmodule.New(), 100 EVMModule: evmmodule.New(), 101 } 102 } 103 104 // Service implements go-ethereum/node.Service interface. 105 type Service struct { 106 config Config 107 108 // server 109 p2pServer *p2p.Server 110 Name string 111 112 accountManager *accounts.Manager 113 114 // application 115 store *Store 116 engine utypes.Consensus 117 dagIndexer *vecmt.Index 118 engineMu *sync.RWMutex 119 emitters []*emitter.Emitter 120 txpool TxPool 121 heavyCheckReader HeavyCheckReader 122 gasPowerCheckReader GasPowerCheckReader 123 checkers *eventcheck.Checkers 124 uniqueEventIDs uniqueID 125 126 // version watcher 127 verWatcher *verwatcher.VerWarcher 128 129 blockProcWg sync.WaitGroup 130 blockProcTasks *workers.Workers 131 blockProcTasksDone chan struct{} 132 blockProcModules BlockProc 133 134 blockBusyFlag uint32 135 eventBusyFlag uint32 136 137 feed ServiceFeed 138 eventMux *event.TypeMux 139 140 gpo *gasprice.Oracle 141 142 // application protocol 143 handler *handler 144 145 u2uDialCandidates enode.Iterator 146 snapDialCandidates enode.Iterator 147 148 EthAPI *EthAPIBackend 149 netRPCService *ethapi.PublicNetAPI 150 151 procLogger *proclogger.Logger 152 153 stopped bool 154 haltCheck func(oldEpoch, newEpoch idx.Epoch, time time.Time) bool 155 156 tflusher PeriodicFlusher 157 158 bootstrapping bool 159 160 logger.Instance 161 } 162 163 func NewService(stack *node.Node, config Config, store *Store, blockProc BlockProc, 164 engine utypes.Consensus, dagIndexer *vecmt.Index, newTxPool func(evmcore.StateReader) TxPool, 165 haltCheck func(oldEpoch, newEpoch idx.Epoch, age time.Time) bool) (*Service, error) { 166 if err := config.Validate(); err != nil { 167 return nil, err 168 } 169 170 svc, err := newService(config, store, blockProc, engine, dagIndexer, newTxPool) 171 if err != nil { 172 return nil, err 173 } 174 175 svc.p2pServer = stack.Server() 176 svc.accountManager = stack.AccountManager() 177 svc.eventMux = stack.EventMux() 178 svc.EthAPI.SetExtRPCEnabled(stack.Config().ExtRPCEnabled()) 179 // Create the net API service 180 svc.netRPCService = ethapi.NewPublicNetAPI(svc.p2pServer, store.GetRules().NetworkID) 181 svc.haltCheck = haltCheck 182 183 return svc, nil 184 } 185 186 func newService(config Config, store *Store, blockProc BlockProc, engine utypes.Consensus, dagIndexer *vecmt.Index, newTxPool func(evmcore.StateReader) TxPool) (*Service, error) { 187 svc := &Service{ 188 config: config, 189 blockProcTasksDone: make(chan struct{}), 190 Name: fmt.Sprintf("Node-%d", rand.Int()), 191 store: store, 192 engine: engine, 193 blockProcModules: blockProc, 194 dagIndexer: dagIndexer, 195 engineMu: new(sync.RWMutex), 196 uniqueEventIDs: uniqueID{new(big.Int)}, 197 procLogger: proclogger.NewLogger(), 198 Instance: logger.New("gossip-service"), 199 } 200 201 svc.blockProcTasks = workers.New(new(sync.WaitGroup), svc.blockProcTasksDone, 1) 202 203 // load epoch DB 204 svc.store.loadEpochStore(svc.store.GetEpoch()) 205 es := svc.store.getEpochStore(svc.store.GetEpoch()) 206 svc.dagIndexer.Reset(svc.store.GetValidators(), es.table.DagIndex, func(id hash.Event) dag.Event { 207 return svc.store.GetEvent(id) 208 }) 209 210 // load caches for mutable values to avoid race condition 211 svc.store.GetBlockEpochState() 212 svc.store.GetHighestLamport() 213 svc.store.GetLastBVs() 214 svc.store.GetLastEVs() 215 svc.store.GetLlrState() 216 svc.store.GetUpgradeHeights() 217 svc.store.GetGenesisID() 218 netVerStore := verwatcher.NewStore(store.table.NetworkVersion) 219 netVerStore.GetNetworkVersion() 220 netVerStore.GetMissedVersion() 221 222 // create GPO 223 svc.gpo = gasprice.NewOracle(svc.config.GPO) 224 225 // create checkers 226 net := store.GetRules() 227 txSigner := gsignercache.Wrap(types.LatestSignerForChainID(new(big.Int).SetUint64(net.NetworkID))) 228 svc.heavyCheckReader.Store = store 229 svc.heavyCheckReader.Pubkeys.Store(readEpochPubKeys(svc.store, svc.store.GetEpoch())) // read pub keys of current epoch from DB 230 svc.gasPowerCheckReader.Ctx.Store(NewGasPowerContext(svc.store, svc.store.GetValidators(), svc.store.GetEpoch(), net.Economy)) // read gaspower check data from DB 231 svc.checkers = makeCheckers(config.HeavyCheck, txSigner, &svc.heavyCheckReader, &svc.gasPowerCheckReader, svc.store) 232 233 // create tx pool 234 stateReader := svc.GetEvmStateReader() 235 svc.txpool = newTxPool(stateReader) 236 237 // init dialCandidates 238 dnsclient := dnsdisc.NewClient(dnsdisc.Config{}) 239 var err error 240 svc.u2uDialCandidates, err = dnsclient.NewIterator(config.U2UDiscoveryURLs...) 241 if err != nil { 242 return nil, err 243 } 244 svc.snapDialCandidates, err = dnsclient.NewIterator(config.SnapDiscoveryURLs...) 245 if err != nil { 246 return nil, err 247 } 248 249 // create protocol manager 250 svc.handler, err = newHandler(handlerConfig{ 251 config: config, 252 notifier: &svc.feed, 253 txpool: svc.txpool, 254 engineMu: svc.engineMu, 255 checkers: svc.checkers, 256 s: store, 257 process: processCallback{ 258 Event: func(event *native.EventPayload) error { 259 done := svc.procLogger.EventConnectionStarted(event, false) 260 defer done() 261 return svc.processEvent(event) 262 }, 263 SwitchEpochTo: svc.SwitchEpochTo, 264 PauseEvmSnapshot: svc.PauseEvmSnapshot, 265 BVs: svc.ProcessBlockVotes, 266 BR: svc.ProcessFullBlockRecord, 267 EV: svc.ProcessEpochVote, 268 ER: svc.ProcessFullEpochRecord, 269 }, 270 }) 271 if err != nil { 272 return nil, err 273 } 274 275 rpc.SetExecutionTimeLimit(config.RPCTimeout) 276 277 // create API backend 278 svc.EthAPI = &EthAPIBackend{false, svc, stateReader, txSigner, config.AllowUnprotectedTxs} 279 if svc.EthAPI.allowUnprotectedTxs { 280 log.Info("Unprotected transactions allowed") 281 } 282 283 svc.verWatcher = verwatcher.New(netVerStore) 284 svc.tflusher = svc.makePeriodicFlusher() 285 286 return svc, nil 287 } 288 289 // makeCheckers builds event checkers 290 func makeCheckers(heavyCheckCfg heavycheck.Config, txSigner types.Signer, heavyCheckReader *HeavyCheckReader, gasPowerCheckReader *GasPowerCheckReader, store *Store) *eventcheck.Checkers { 291 // create signatures checker 292 heavyCheck := heavycheck.New(heavyCheckCfg, heavyCheckReader, txSigner) 293 294 // create gaspower checker 295 gaspowerCheck := gaspowercheck.New(gasPowerCheckReader) 296 297 return &eventcheck.Checkers{ 298 Basiccheck: basiccheck.New(), 299 Epochcheck: epochcheck.New(store), 300 Parentscheck: parentscheck.New(), 301 Heavycheck: heavyCheck, 302 Gaspowercheck: gaspowerCheck, 303 } 304 } 305 306 // makePeriodicFlusher makes PeriodicFlusher 307 func (s *Service) makePeriodicFlusher() PeriodicFlusher { 308 // Normally the diffs are committed by message processing. Yet, since we have async EVM snapshots generation, 309 // we need to periodically commit its data 310 return PeriodicFlusher{ 311 period: 10 * time.Millisecond, 312 callback: PeriodicFlusherCallaback{ 313 busy: func() bool { 314 // try to lock engineMu/blockProcWg pair as rarely as possible to not hurt 315 // events/blocks pipeline concurrency 316 return atomic.LoadUint32(&s.eventBusyFlag) != 0 || atomic.LoadUint32(&s.blockBusyFlag) != 0 317 }, 318 commitNeeded: func() bool { 319 // use slightly higher size threshold to avoid locking the mutex/wg pair and hurting events/blocks concurrency 320 // PeriodicFlusher should mostly commit only data generated by async EVM snapshots generation 321 return s.store.isCommitNeeded(1200, 1000) 322 }, 323 commit: func() { 324 s.engineMu.Lock() 325 defer s.engineMu.Unlock() 326 // Note: blockProcWg.Wait() is already called by s.commit 327 if s.store.isCommitNeeded(1200, 1000) { 328 s.commit(false) 329 } 330 }, 331 }, 332 wg: sync.WaitGroup{}, 333 quit: make(chan struct{}), 334 } 335 } 336 337 func (s *Service) EmitterWorld(signer valkeystore.SignerI) emitter.World { 338 return emitter.World{ 339 External: &emitterWorld{ 340 emitterWorldProc: emitterWorldProc{s}, 341 emitterWorldRead: emitterWorldRead{s.store}, 342 WgMutex: wgmutex.New(s.engineMu, &s.blockProcWg), 343 }, 344 TxPool: s.txpool, 345 Signer: signer, 346 TxSigner: s.EthAPI.signer, 347 } 348 } 349 350 // RegisterEmitter must be called before service is started 351 func (s *Service) RegisterEmitter(em *emitter.Emitter) { 352 txtime.Enabled = true // enable tracking of tx times 353 s.emitters = append(s.emitters, em) 354 } 355 356 // MakeProtocols constructs the P2P protocol definitions for `u2u`. 357 func MakeProtocols(svc *Service, backend *handler, disc enode.Iterator) []p2p.Protocol { 358 protocols := make([]p2p.Protocol, len(ProtocolVersions)) 359 for i, version := range ProtocolVersions { 360 version := version // Closure 361 362 protocols[i] = p2p.Protocol{ 363 Name: ProtocolName, 364 Version: version, 365 Length: protocolLengths[version], 366 Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { 367 // wait until handler has started 368 backend.started.Wait() 369 peer := newPeer(version, p, rw, backend.config.Protocol.PeerCache) 370 defer peer.Close() 371 372 select { 373 case <-backend.quitSync: 374 return p2p.DiscQuitting 375 default: 376 backend.wg.Add(1) 377 defer backend.wg.Done() 378 return backend.handle(peer) 379 } 380 }, 381 NodeInfo: func() interface{} { 382 return backend.NodeInfo() 383 }, 384 PeerInfo: func(id enode.ID) interface{} { 385 if p := backend.peers.Peer(id.String()); p != nil { 386 return p.Info() 387 } 388 return nil 389 }, 390 Attributes: []enr.Entry{currentENREntry(svc)}, 391 DialCandidates: disc, 392 } 393 } 394 return protocols 395 } 396 397 // Protocols returns protocols the service can communicate on. 398 func (s *Service) Protocols() []p2p.Protocol { 399 protos := append( 400 MakeProtocols(s, s.handler, s.u2uDialCandidates), 401 snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...) 402 return protos 403 } 404 405 // APIs returns api methods the service wants to expose on rpc channels. 406 func (s *Service) APIs() []rpc.API { 407 apis := ethapi.GetAPIs(s.EthAPI) 408 409 apis = append(apis, []rpc.API{ 410 { 411 Namespace: "eth", 412 Version: "1.0", 413 Service: NewPublicEthereumAPI(s), 414 Public: true, 415 }, { 416 Namespace: "eth", 417 Version: "1.0", 418 Service: filters.NewPublicFilterAPI(s.EthAPI, s.config.FilterAPI), 419 Public: true, 420 }, { 421 Namespace: "eth", 422 Version: "1.0", 423 Service: snapsync.NewPublicDownloaderAPI(s.handler.snapLeecher, s.eventMux), 424 Public: true, 425 }, { 426 Namespace: "net", 427 Version: "1.0", 428 Service: s.netRPCService, 429 Public: true, 430 }, 431 }...) 432 433 return apis 434 } 435 436 // Start method invoked when the node is ready to start the service. 437 func (s *Service) Start() error { 438 s.gpo.Start(&GPOBackend{s.store, s.txpool}) 439 // start tflusher before starting snapshots generation 440 s.tflusher.Start() 441 // start snapshots generation 442 if s.store.evm.IsEvmSnapshotPaused() && !s.config.AllowSnapsync { 443 return errors.New("cannot halt snapsync and start fullsync") 444 } 445 s.RecoverEVM() 446 root := s.store.GetBlockState().FinalizedStateRoot 447 if !s.store.evm.HasStateDB(root) { 448 if !s.config.AllowSnapsync { 449 return errors.New("fullsync isn't possible because state root is missing") 450 } 451 root = hash.Zero 452 } 453 _ = s.store.GenerateSnapshotAt(common.Hash(root), true) 454 455 // start blocks processor 456 s.blockProcTasks.Start(1) 457 458 // start p2p 459 StartENRUpdater(s, s.p2pServer.LocalNode()) 460 s.handler.Start(s.p2pServer.MaxPeers) 461 462 // start emitters 463 for _, em := range s.emitters { 464 em.Start() 465 } 466 467 s.verWatcher.Start() 468 469 if s.haltCheck != nil && s.haltCheck(s.store.GetEpoch(), s.store.GetEpoch(), s.store.GetBlockState().LastBlock.Time.Time()) { 470 // halt syncing 471 s.stopped = true 472 } 473 474 return nil 475 } 476 477 // WaitBlockEnd waits until parallel block processing is complete (if any) 478 func (s *Service) WaitBlockEnd() { 479 s.blockProcWg.Wait() 480 } 481 482 // Stop method invoked when the node terminates the service. 483 func (s *Service) Stop() error { 484 defer log.Info("U2U service stopped") 485 s.verWatcher.Stop() 486 for _, em := range s.emitters { 487 em.Stop() 488 } 489 490 // Stop all the peer-related stuff first. 491 s.u2uDialCandidates.Close() 492 s.snapDialCandidates.Close() 493 494 s.handler.Stop() 495 s.feed.scope.Close() 496 s.eventMux.Stop() 497 s.gpo.Stop() 498 // it's safe to stop tflusher only before locking engineMu 499 s.tflusher.Stop() 500 501 // flush the state at exit, after all the routines stopped 502 s.engineMu.Lock() 503 defer s.engineMu.Unlock() 504 s.stopped = true 505 506 s.blockProcWg.Wait() 507 close(s.blockProcTasksDone) 508 s.store.evm.Flush(s.store.GetBlockState()) 509 return s.store.Commit() 510 } 511 512 // AccountManager return node's account manager 513 func (s *Service) AccountManager() *accounts.Manager { 514 return s.accountManager 515 }