mirror of
https://github.com/luxfi/evmgpu.git
synced 2026-10-30 16:57:11 +00:00
133 lines
4.3 KiB
Go
133 lines
4.3 KiB
Go
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package uptime
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/luxfi/evmgpu/plugin/evm/validators/uptime/interfaces"
|
|
log "github.com/luxfi/log"
|
|
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/math/set"
|
|
)
|
|
|
|
var errPausedDisconnect = errors.New("paused node cannot be disconnected")
|
|
|
|
type pausableManager struct {
|
|
*Manager
|
|
pausedVdrs set.Set[ids.NodeID]
|
|
// connectedVdrs is a set of nodes that are connected to the manager.
|
|
// This is used to immediately connect nodes when they are unpaused.
|
|
connectedVdrs set.Set[ids.NodeID]
|
|
}
|
|
|
|
// NewPausableManager takes a Manager and returns a PausableManager
|
|
func NewPausableManager(manager *Manager) interfaces.PausableManager {
|
|
return &pausableManager{
|
|
pausedVdrs: make(set.Set[ids.NodeID]),
|
|
connectedVdrs: make(set.Set[ids.NodeID]),
|
|
Manager: manager,
|
|
}
|
|
}
|
|
|
|
// Connect connects the node with the given ID to the uptime.Manager
|
|
// If the node is paused, it will not be connected
|
|
func (p *pausableManager) Connect(nodeID ids.NodeID) error {
|
|
p.connectedVdrs.Add(nodeID)
|
|
if !p.IsPaused(nodeID) && !p.Manager.IsConnected(nodeID) {
|
|
return p.Manager.Connect(nodeID)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Disconnect disconnects the node with the given ID from the uptime.Manager
|
|
// If the node is paused, it will not be disconnected
|
|
// Invariant: we should never have a connected paused node that is disconnecting
|
|
func (p *pausableManager) Disconnect(nodeID ids.NodeID) error {
|
|
p.connectedVdrs.Remove(nodeID)
|
|
if p.Manager.IsConnected(nodeID) {
|
|
if p.IsPaused(nodeID) {
|
|
// We should never see this case
|
|
return errPausedDisconnect
|
|
}
|
|
return p.Manager.Disconnect(nodeID)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsConnected returns true if the node with the given ID is connected to this manager
|
|
// Note: Inner manager may have a different view of the connection status due to pausing
|
|
func (p *pausableManager) IsConnected(nodeID ids.NodeID) bool {
|
|
return p.connectedVdrs.Contains(nodeID)
|
|
}
|
|
|
|
// OnValidatorAdded is called when a validator is added.
|
|
// If the node is inactive, it will be paused.
|
|
func (p *pausableManager) OnValidatorAdded(_ ids.ID, nodeID ids.NodeID, _ uint64, isActive bool) {
|
|
if !isActive {
|
|
err := p.pause(nodeID)
|
|
if err != nil {
|
|
log.Error("failed to handle added validator %s: %s", nodeID, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// OnValidatorRemoved is called when a validator is removed.
|
|
// If the node is already paused, it will be resumed.
|
|
func (p *pausableManager) OnValidatorRemoved(_ ids.ID, nodeID ids.NodeID) {
|
|
if p.IsPaused(nodeID) {
|
|
err := p.resume(nodeID)
|
|
if err != nil {
|
|
log.Error("failed to handle validator removed %s: %s", nodeID, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// OnValidatorStatusUpdated is called when the status of a validator is updated.
|
|
// If the node is active, it will be resumed. If the node is inactive, it will be paused.
|
|
func (p *pausableManager) OnValidatorStatusUpdated(_ ids.ID, nodeID ids.NodeID, isActive bool) {
|
|
var err error
|
|
if isActive {
|
|
err = p.resume(nodeID)
|
|
} else {
|
|
err = p.pause(nodeID)
|
|
}
|
|
if err != nil {
|
|
log.Error("failed to update status for node %s: %s", nodeID, err)
|
|
}
|
|
}
|
|
|
|
// IsPaused returns true if the node with the given ID is paused.
|
|
func (p *pausableManager) IsPaused(nodeID ids.NodeID) bool {
|
|
return p.pausedVdrs.Contains(nodeID)
|
|
}
|
|
|
|
// pause pauses uptime tracking for the node with the given ID
|
|
// pause can disconnect the node from the uptime.Manager if it is connected.
|
|
func (p *pausableManager) pause(nodeID ids.NodeID) error {
|
|
// Ensure the node exists in the manager even if not connected yet
|
|
// This allows pre-tracking uptime to be credited when StartTracking is called
|
|
p.Manager.EnsureExists(nodeID)
|
|
p.pausedVdrs.Add(nodeID)
|
|
if p.Manager.IsConnected(nodeID) {
|
|
// If the node is connected, then we need to disconnect it from
|
|
// manager
|
|
// This should be fine in case tracking has not started yet since
|
|
// the inner manager should handle disconnects accordingly
|
|
return p.Manager.Disconnect(nodeID)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// resume resumes uptime tracking for the node with the given ID
|
|
// resume can connect the node to the uptime.Manager if it was connected.
|
|
func (p *pausableManager) resume(nodeID ids.NodeID) error {
|
|
p.pausedVdrs.Remove(nodeID)
|
|
if p.connectedVdrs.Contains(nodeID) && !p.Manager.IsConnected(nodeID) {
|
|
return p.Manager.Connect(nodeID)
|
|
}
|
|
return nil
|
|
}
|