package syncnode import ( "context" "fmt" "sync/atomic" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-node/rollup/event" "github.com/ethereum-optimism/optimism/op-service/locks" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/superevents" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) // SyncNodesController manages a collection of active sync nodes. // Sync nodes are used to sync the supervisor, // and subject to the canonical chain view as followed by the supervisor. type SyncNodesController struct { logger log.Logger id atomic.Uint64 controllers locks.RWMap[eth.ChainID, *locks.RWMap[*ManagedNode, struct{}]] eventSys event.System emitter event.Emitter backend backend depSet depset.DependencySet } var _ event.AttachEmitter = (*SyncNodesController)(nil) // NewSyncNodesController creates a new SyncNodeController func NewSyncNodesController(l log.Logger, depset depset.DependencySet, eventSys event.System, backend backend) *SyncNodesController { return &SyncNodesController{ logger: l, depSet: depset, eventSys: eventSys, backend: backend, } } func (snc *SyncNodesController) AttachEmitter(em event.Emitter) { snc.emitter = em } func (snc *SyncNodesController) OnEvent(ev event.Event) bool { return false } func (snc *SyncNodesController) Close() error { snc.controllers.Range(func(chainID eth.ChainID, controllers *locks.RWMap[*ManagedNode, struct{}]) bool { controllers.Range(func(node *ManagedNode, _ struct{}) bool { node.Close() return true }) return true }) return nil } // AttachNodeController attaches a node to be managed by the supervisor. // If noSubscribe, the node is not actively polled/subscribed to, and requires manual ManagedNode.PullEvents calls. func (snc *SyncNodesController) AttachNodeController(chainID eth.ChainID, ctrl SyncControl, noSubscribe bool) (Node, error) { if !snc.depSet.HasChain(chainID) { return nil, fmt.Errorf("chain %v not in dependency set: %w", chainID, types.ErrUnknownChain) } // lazy init the controllers map for this chain snc.controllers.Default(chainID, func() *locks.RWMap[*ManagedNode, struct{}] { return &locks.RWMap[*ManagedNode, struct{}]{} }) controllersForChain, _ := snc.controllers.Get(chainID) node := NewManagedNode(snc.logger, chainID, ctrl, snc.backend, noSubscribe) nodeID := snc.id.Add(1) name := fmt.Sprintf("syncnode-%s-%d", chainID, nodeID) snc.eventSys.Register(name, node, event.DefaultRegisterOpts()) controllersForChain.Set(node, struct{}{}) anchor, err := ctrl.AnchorPoint(context.Background()) if err != nil { return nil, fmt.Errorf("failed to get anchor point: %w", err) } snc.emitter.Emit(superevents.AnchorEvent{ ChainID: chainID, Anchor: anchor, }) node.Start() return node, nil }