Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: totalAlerts legacy #156

Merged
merged 2 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions contracts/src/core/MachServiceManagerRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,26 @@
pragma solidity =0.8.12;

import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
import {IMachServiceManager} from "../interfaces/IMachServiceManager.sol";
import {ITotalAlerts, ITotalAlertsLegacy} from "../interfaces/IMachServiceManager.sol";
import {ZeroAddress, AlreadyAdded, NotAdded} from "../error/Errors.sol";

/// @title MachServiceManagerRegistry
/// @notice This contract allows the owner to register service managers for specific rollup chain IDs and check if they have active alerts.
contract MachServiceManagerRegistry is OwnableUpgradeable {
// Mapping of rollup chain ID to service manager
mapping(uint256 => IMachServiceManager) public serviceManagers;
mapping(uint256 => address) public serviceManagers;

/// @notice Emitted when a service manager is registered
/// @param rollupChainId The rollup chain ID
/// @param serviceManager The registered service manager
/// @param sender The address that registered the service manager
event ServiceManagerRegistered(uint256 indexed rollupChainId, IMachServiceManager serviceManager, address sender);
event ServiceManagerRegistered(uint256 indexed rollupChainId, address serviceManager, address sender);

/// @notice Emitted when a service manager is deregistered
/// @param rollupChainId The rollup chain ID
/// @param serviceManager The deregistered service manager
/// @param sender The address that deregistered the service manager
event ServiceManagerDeregistered(uint256 indexed rollupChainId, IMachServiceManager serviceManager, address sender);
event ServiceManagerDeregistered(uint256 indexed rollupChainId, address serviceManager, address sender);

/// @notice Initializes the contract and sets the deployer as the owner
function initialize() external initializer {
Expand All @@ -39,7 +39,7 @@ contract MachServiceManagerRegistry is OwnableUpgradeable {
/// @param rollupChainId_ The rollup chain ID
/// @param serviceManager_ The service manager to be registered
/// @dev Reverts if the service manager address is zero or already registered
function registerServiceManager(uint256 rollupChainId_, IMachServiceManager serviceManager_) external onlyOwner {
function registerServiceManager(uint256 rollupChainId_, address serviceManager_) external onlyOwner {
if (address(serviceManager_) == address(0)) {
revert ZeroAddress();
}
Expand All @@ -54,7 +54,7 @@ contract MachServiceManagerRegistry is OwnableUpgradeable {
/// @param rollupChainId_ The rollup chain ID
/// @param serviceManager_ The service manager to be deregistered
/// @dev Reverts if the service manager is not already registered
function deregisterServiceManager(uint256 rollupChainId_, IMachServiceManager serviceManager_) external onlyOwner {
function deregisterServiceManager(uint256 rollupChainId_, address serviceManager_) external onlyOwner {
if (serviceManagers[rollupChainId_] != serviceManager_) {
revert NotAdded();
}
Expand All @@ -66,6 +66,15 @@ contract MachServiceManagerRegistry is OwnableUpgradeable {
/// @param rollupChainId_ The rollup chain ID
/// @return True if there are active alerts, false otherwise
function hasActiveAlerts(uint256 rollupChainId_) external view returns (bool) {
return serviceManagers[rollupChainId_].totalAlerts(rollupChainId_) > 0;
address target = serviceManagers[rollupChainId_];
if (target == address(0)) {
return false;
}

try ITotalAlerts(target).totalAlerts(rollupChainId_) returns (uint256 totalAlerts) {
return totalAlerts > 0;
} catch (bytes memory reason) {
return ITotalAlertsLegacy(target).totalAlerts() > 0;
}
}
}
11 changes: 11 additions & 0 deletions contracts/src/interfaces/IMachServiceManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ import {IServiceManager} from "eigenlayer-middleware/interfaces/IServiceManager.
import {BLSSignatureChecker} from "eigenlayer-middleware/BLSSignatureChecker.sol";
import {IMachOptimism} from "../interfaces/IMachOptimism.sol";

interface ITotalAlertsLegacy {
// Legacy
/// @notice Returns the length of total alerts
function totalAlerts() external view returns (uint256);
}

interface ITotalAlerts {
/// @notice Returns the length of total alerts
function totalAlerts(uint256 rollupChainId) external view returns (uint256);
}

/**
* @title Interface for the MachServiceManager contract.
* @author Altlayer, Inc.
Expand Down
111 changes: 80 additions & 31 deletions contracts/test/MachServiceManagerRegistry.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,46 @@ import "../src/interfaces/IMachServiceManager.sol";
import "../src/core/MachServiceManagerRegistry.sol";
import "../src/error/Errors.sol";

contract MockServiceManager is ITotalAlerts {
uint256 public alerts;

function setAlerts(uint256 _alerts) public {
alerts = _alerts;
}

function totalAlerts(uint256) external view override returns (uint256) {
return alerts;
}
}

contract MockServiceManagerLegacy is ITotalAlertsLegacy {
uint256 public alerts;

function setAlerts(uint256 _alerts) public {
alerts = _alerts;
}

function totalAlerts() external view override returns (uint256) {
return alerts;
}
}

contract MachServiceManagerRegistryTest is Test {
MachServiceManagerRegistry registry;
IMachServiceManager mockServiceManager;
MockServiceManager mockServiceManager;
MockServiceManagerLegacy mockServiceManagerLegacy;

event ServiceManagerRegistered(uint256 indexed rollupChainId, IMachServiceManager serviceManager, address sender);
event ServiceManagerDeregistered(uint256 indexed rollupChainId, IMachServiceManager serviceManager, address sender);
event ServiceManagerRegistered(uint256 indexed rollupChainId, address serviceManager, address sender);
event ServiceManagerDeregistered(uint256 indexed rollupChainId, address serviceManager, address sender);

function setUp() public {
// Deploy the registry
registry = new MachServiceManagerRegistry();
registry.initialize();

// Mocking the IMachServiceManager interface
mockServiceManager = IMachServiceManager(address(101));
// Deploy the mock service managers
mockServiceManager = new MockServiceManager();
mockServiceManagerLegacy = new MockServiceManagerLegacy();

// Ensure registry is owned by this test contract for testing purposes
registry.transferOwnership(address(this));
Expand All @@ -30,9 +56,9 @@ contract MachServiceManagerRegistryTest is Test {
uint256 rollupChainId = 1;

vm.expectEmit();
emit ServiceManagerRegistered(rollupChainId, mockServiceManager, address(this));
emit ServiceManagerRegistered(rollupChainId, address(mockServiceManager), address(this));

registry.registerServiceManager(rollupChainId, mockServiceManager);
registry.registerServiceManager(rollupChainId, address(mockServiceManager));

assertEq(
address(registry.serviceManagers(rollupChainId)), address(mockServiceManager), "Service manager mismatch"
Expand All @@ -43,16 +69,16 @@ contract MachServiceManagerRegistryTest is Test {
uint256 rollupChainId = 1;

vm.expectRevert(ZeroAddress.selector);
registry.registerServiceManager(rollupChainId, IMachServiceManager(address(0)));
registry.registerServiceManager(rollupChainId, address(0));
}

function test_RegisterServiceManager_RevertIfAlreadyAdded() public {
uint256 rollupChainId = 1;

registry.registerServiceManager(rollupChainId, mockServiceManager);
registry.registerServiceManager(rollupChainId, address(mockServiceManager));

vm.expectRevert(AlreadyAdded.selector);
registry.registerServiceManager(rollupChainId, mockServiceManager);
registry.registerServiceManager(rollupChainId, address(mockServiceManager));
}

function test_RegisterServiceManager_RevertIfNotOwner() public {
Expand All @@ -62,52 +88,75 @@ contract MachServiceManagerRegistryTest is Test {
registry.transferOwnership(address(0xdead));

vm.expectRevert("Ownable: caller is not the owner");
registry.registerServiceManager(rollupChainId, mockServiceManager);
registry.registerServiceManager(rollupChainId, address(mockServiceManager));
}

function test_HasActiveAlerts() public {
uint256 rollupChainId = 1;

// Mocking the behavior of totalAlerts to return a non-zero value
vm.mockCall(
address(mockServiceManager),
abi.encodeWithSelector(IMachServiceManager.totalAlerts.selector, rollupChainId),
abi.encode(1)
);
// Set alerts to a non-zero value
mockServiceManager.setAlerts(1);

registry.registerServiceManager(rollupChainId, mockServiceManager);
registry.registerServiceManager(rollupChainId, address(mockServiceManager));

bool hasAlerts = registry.hasActiveAlerts(rollupChainId);

assertTrue(hasAlerts, "Expected to have active alerts");
}

function test_HasActiveAlerts_NoServiceManager() public {
bool hasAlerts = registry.hasActiveAlerts(99); // no service manager registerd for the rollup ID 99
assertFalse(hasAlerts, "Expected to have no active alerts");
}

function test_HasActiveAlerts_NoAlerts() public {
uint256 rollupChainId = 1;

// Mocking the behavior of totalAlerts to return zero
vm.mockCall(
address(mockServiceManager),
abi.encodeWithSelector(IMachServiceManager.totalAlerts.selector, rollupChainId),
abi.encode(0)
);
// Set alerts to zero
mockServiceManager.setAlerts(0);

registry.registerServiceManager(rollupChainId, mockServiceManager);
registry.registerServiceManager(rollupChainId, address(mockServiceManager));

bool hasAlerts = registry.hasActiveAlerts(rollupChainId);

assertFalse(hasAlerts, "Expected to have no active alerts");
}

function test_HasActiveAlerts_LegacyAlerts() public {
uint256 rollupChainId = 1;

// Set alerts to a non-zero value (simulating legacy alerts)
mockServiceManagerLegacy.setAlerts(1);

registry.registerServiceManager(rollupChainId, address(mockServiceManagerLegacy));

bool hasAlerts = registry.hasActiveAlerts(rollupChainId);

assertTrue(hasAlerts, "Expected to have active alerts in legacy mode");
}

function test_HasActiveAlerts_LegacyNoAlerts() public {
uint256 rollupChainId = 1;

// Set alerts to zero (simulating no legacy alerts)
mockServiceManagerLegacy.setAlerts(0);

registry.registerServiceManager(rollupChainId, address(mockServiceManagerLegacy));

bool hasAlerts = registry.hasActiveAlerts(rollupChainId);

assertFalse(hasAlerts, "Expected to have no active alerts in legacy mode");
}

function test_DeregisterServiceManager() public {
uint256 rollupChainId = 1;

registry.registerServiceManager(rollupChainId, mockServiceManager);
registry.registerServiceManager(rollupChainId, address(mockServiceManager));

vm.expectEmit();
emit ServiceManagerDeregistered(rollupChainId, mockServiceManager, address(this));
emit ServiceManagerDeregistered(rollupChainId, address(mockServiceManager), address(this));

registry.deregisterServiceManager(rollupChainId, mockServiceManager);
registry.deregisterServiceManager(rollupChainId, address(mockServiceManager));

assertEq(address(registry.serviceManagers(rollupChainId)), address(0), "Service manager should be deregistered");
}
Expand All @@ -116,19 +165,19 @@ contract MachServiceManagerRegistryTest is Test {
uint256 rollupChainId = 1;

vm.expectRevert(NotAdded.selector);
registry.deregisterServiceManager(rollupChainId, mockServiceManager);
registry.deregisterServiceManager(rollupChainId, address(mockServiceManager));
}

function test_DeregisterServiceManager_RevertIfNotOwner() public {
uint256 rollupChainId = 1;

// Register a service manager
registry.registerServiceManager(rollupChainId, mockServiceManager);
registry.registerServiceManager(rollupChainId, address(mockServiceManager));

// Transfer ownership to another address for this test
registry.transferOwnership(address(0xdead));

vm.expectRevert("Ownable: caller is not the owner");
registry.deregisterServiceManager(rollupChainId, mockServiceManager);
registry.deregisterServiceManager(rollupChainId, address(mockServiceManager));
}
}