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  }