diff --git a/examples/optimistic-auction/script/PodAuctionConsumerDeployer.s.sol b/examples/optimistic-auction/script/PodAuctionConsumerDeployer.s.sol index a680fda4..8e69cf42 100644 --- a/examples/optimistic-auction/script/PodAuctionConsumerDeployer.s.sol +++ b/examples/optimistic-auction/script/PodAuctionConsumerDeployer.s.sol @@ -10,10 +10,11 @@ import {PodAuctionConsumer} from "../contracts/PodAuctionConsumer.sol"; contract PodAuctionConsumerDeployer is BaseDeployer { function run() public { address[] memory initialValidators = getValidatorAddresses(); + (string[] memory initialHosts, uint16[] memory initialPorts) = getValidatorHostsAndPorts(); vm.startBroadcast(); - PodRegistry podRegistry = new PodRegistry(initialValidators); + PodRegistry podRegistry = new PodRegistry(initialValidators, initialHosts, initialPorts); console.log("PodRegistry deployed:"); console.logAddress(address(podRegistry)); diff --git a/examples/optimistic-auction/test/PodAuctionConsumer.t.sol b/examples/optimistic-auction/test/PodAuctionConsumer.t.sol index 8b63b7b0..bf564412 100644 --- a/examples/optimistic-auction/test/PodAuctionConsumer.t.sol +++ b/examples/optimistic-auction/test/PodAuctionConsumer.t.sol @@ -37,16 +37,20 @@ contract PodAuctionConsumerTest is Test { function setUp() public { address[] memory initialValidators = new address[](NUMBER_OF_VALIDATORS); + string[] memory initialHosts = new string[](NUMBER_OF_VALIDATORS); + uint16[] memory initialPorts = new uint16[](NUMBER_OF_VALIDATORS); validatorPrivateKeys = new uint256[](NUMBER_OF_VALIDATORS); for (uint256 i = 0; i < NUMBER_OF_VALIDATORS; i++) { validatorPrivateKeys[i] = uint256(i + 1); initialValidators[i] = vm.addr(validatorPrivateKeys[i]); + initialHosts[i] = string.concat("validator-", vm.toString(i), ".example.org"); + initialPorts[i] = uint16(30000 + i); } vm.prank(OWNER); - podRegistry = new PodRegistry(initialValidators); + podRegistry = new PodRegistry(initialValidators, initialHosts, initialPorts); consumer = new PodAuctionConsumer(address(podRegistry), 1 ether); vm.deal(SMALLER_BIDDER, 2 ether); diff --git a/examples/solidity/script/Deploy.s.sol b/examples/solidity/script/Deploy.s.sol index 72b03f19..0533ce82 100644 --- a/examples/solidity/script/Deploy.s.sol +++ b/examples/solidity/script/Deploy.s.sol @@ -30,8 +30,9 @@ contract Deployer is BaseDeployer { if (testContracts) { address[] memory initialValidators = getValidatorAddresses(); + (string[] memory initialHosts, uint16[] memory initialPorts) = getValidatorHostsAndPorts(); - PodRegistry podRegistry = new PodRegistry(initialValidators); + PodRegistry podRegistry = new PodRegistry(initialValidators, initialHosts, initialPorts); console.log("PodRegistry deployed at:", address(podRegistry)); uint256 bondAmount = 1 ether; diff --git a/protocol/script/BaseDeployer.s.sol b/protocol/script/BaseDeployer.s.sol index 2ba95861..baa0a35f 100644 --- a/protocol/script/BaseDeployer.s.sol +++ b/protocol/script/BaseDeployer.s.sol @@ -20,4 +20,31 @@ contract BaseDeployer is Script { return initialValidators; } + + function getValidatorHostsAndPorts() internal view returns (string[] memory hosts, uint16[] memory ports) { + // Read validator endpoints from environment variable + string memory committeeHosts = vm.envString("POD_COMMITTEE_HOSTS"); + + // Split comma-separated host:port pairs + string[] memory entries = vm.split(committeeHosts, ","); + + uint256 len = entries.length; + hosts = new string[](len); + ports = new uint16[](len); + + for (uint256 i = 0; i < len; i++) { + // Split each entry on ":" (host:port) + string[] memory parts = vm.split(entries[i], ":"); + require(parts.length == 2, "invalid host:port entry"); + + hosts[i] = parts[0]; + + // Parse port string into uint16 + uint256 portVal = vm.parseUint(parts[1]); + require(portVal > 0 && portVal <= type(uint16).max, "invalid port"); + ports[i] = uint16(portVal); + } + + require(len > 0, "No validator hosts provided"); + } } diff --git a/protocol/script/PodRegistryDeployer.s.sol b/protocol/script/PodRegistryDeployer.s.sol index 3eea022c..f48d9014 100644 --- a/protocol/script/PodRegistryDeployer.s.sol +++ b/protocol/script/PodRegistryDeployer.s.sol @@ -9,12 +9,13 @@ import {PodRegistry} from "../src/PodRegistry.sol"; contract PodRegistryDeployer is BaseDeployer { function run() public { address[] memory initialValidators = getValidatorAddresses(); + (string[] memory initialHosts, uint16[] memory initialPorts) = getValidatorHostsAndPorts(); vm.startBroadcast(); - PodRegistry podRegistry = new PodRegistry(initialValidators); + PodRegistry registry = new PodRegistry(initialValidators, initialHosts, initialPorts); vm.stopBroadcast(); console.log("PodRegistry deployed:"); - console.logAddress(address(podRegistry)); + console.logAddress(address(registry)); } } diff --git a/protocol/src/PodRegistry.sol b/protocol/src/PodRegistry.sol index 38e00261..186c9e0a 100644 --- a/protocol/src/PodRegistry.sol +++ b/protocol/src/PodRegistry.sol @@ -51,6 +51,12 @@ contract PodRegistry is IPodRegistry, Ownable { */ address[] public validators; + /** + * @notice Array of endpoints in the registry. We also use `validators.length + 1` to track the 1-based + * index of the next validator to add. + */ + Endpoint[] private endpoints; + /** * @notice Bitmap of the currently active validators */ @@ -65,11 +71,19 @@ contract PodRegistry is IPodRegistry, Ownable { * @notice Initialize the registry with a set of initial validators. Only creates one snapshot * after adding all the initial validators. * @param initialValidators Array of validator addresses to initialize with + * @param initialHosts The hostnames or IPs for each validator (same length as validators) + * @param initialPorts The TCP ports for each validator (same length as validators) * @dev The contract owner will be set to msg.sender */ - constructor(address[] memory initialValidators) Ownable(msg.sender) { - for (uint8 i = 0; i < initialValidators.length; i++) { - _addValidator(initialValidators[i]); + constructor(address[] memory initialValidators, string[] memory initialHosts, uint16[] memory initialPorts) + Ownable(msg.sender) + { + uint256 len = initialValidators.length; + require(len == initialHosts.length && len == initialPorts.length, "length mismatch"); + require(len <= 255, "too many validators"); + + for (uint8 i = 0; i < len; i++) { + _addValidator(initialValidators[i], initialHosts[i], initialPorts[i]); activeValidatorBitmap |= (1 << i); } @@ -79,9 +93,11 @@ contract PodRegistry is IPodRegistry, Ownable { /** * @notice Add a validator to the registry * @param validator The address of the validator to add + * @param host The hostname or IP address of the validator + * @param port The TCP port used by the validator * @dev Internal function called by addValidator */ - function _addValidator(address validator) internal { + function _addValidator(address validator, string memory host, uint16 port) internal { if (validator == address(0)) { revert ValidatorIsZeroAddress(); } @@ -91,16 +107,26 @@ contract PodRegistry is IPodRegistry, Ownable { if (validators.length >= MAX_VALIDATOR_COUNT) { revert MaxValidatorCountReached(); } + if (bytes(host).length == 0) { + revert ValidatorInvalidHost(); + } + if (bytes(host).length > 255) { + revert ValidatorInvalidHost(); + } + if (port == 0) { + revert ValidatorInvalidPort(); + } validators.push(validator); validatorIndex[validator] = uint8(validators.length); + endpoints.push(Endpoint({host: host, port: port})); } /** * @inheritdoc IPodRegistry */ - function addValidator(address validator) external onlyOwner { - _addValidator(validator); + function addValidator(address validator, string calldata host, uint16 port) external onlyOwner { + _addValidator(validator, host, port); uint8 index = uint8(validators.length); _activateValidator(index); @@ -143,6 +169,29 @@ contract PodRegistry is IPodRegistry, Ownable { emit ValidatorUnbanned(validator); } + /** + * @inheritdoc IPodRegistry + */ + function setValidatorEndpoint(address validator, string calldata host, uint16 port) external onlyOwner { + uint8 index = validatorIndex[validator]; + if (index == 0) { + revert ValidatorDoesNotExist(); + } + if (bytes(host).length == 0) { + revert ValidatorInvalidHost(); + } + if (bytes(host).length > 255) { + revert ValidatorInvalidHost(); + } + if (port == 0) { + revert ValidatorInvalidPort(); + } + + endpoints[index - 1] = Endpoint({host: host, port: port}); + + emit ValidatorNetworkUpdated(validator, host, port); + } + /** * @inheritdoc IPodRegistry */ @@ -283,25 +332,45 @@ contract PodRegistry is IPodRegistry, Ownable { /** * @inheritdoc IPodRegistry */ - function getValidatorsAtIndex(uint256 snapshotIndex) public view returns (address[] memory) { + function getValidatorsAtIndex(uint256 snapshotIndex) + public + view + returns (address[] memory addrs, string[] memory hosts, uint16[] memory ports) + { uint256 bitmap = history[snapshotIndex].bitmap; uint8 count = _popCount(bitmap); - address[] memory subset = new address[](count); + + addrs = new address[](count); + hosts = new string[](count); + ports = new uint16[](count); + + address[] storage all = validators; uint8 j = 0; - for (uint8 i = 0; i < validators.length; i++) { + + for (uint8 i = 0; i < all.length; i++) { if (_isBitSet(bitmap, i)) { - subset[j++] = validators[i]; + address v = all[i]; + addrs[j] = v; + + Endpoint storage ep = endpoints[i]; + hosts[j] = ep.host; + ports[j] = ep.port; + + j++; } } - return subset; } /** * @inheritdoc IPodRegistry */ - function getActiveValidators() external view returns (address[] memory) { + function getActiveValidators() + external + view + returns (address[] memory addrs, string[] memory hosts, uint16[] memory ports) + { if (history.length == 0) { - return new address[](0); + return (new address[](0), new string[](0), new uint16[](0)); } return getValidatorsAtIndex(history.length - 1); @@ -310,9 +379,13 @@ contract PodRegistry is IPodRegistry, Ownable { /** * @inheritdoc IPodRegistry */ - function getActiveValidatorsAtTimestamp(uint256 timestamp) external view returns (address[] memory) { + function getActiveValidatorsAtTimestamp(uint256 timestamp) + external + view + returns (address[] memory addrs, string[] memory hosts, uint16[] memory ports) + { if (history.length == 0) { - return new address[](0); + return (new address[](0), new string[](0), new uint16[](0)); } uint256 snapshotIndex = findSnapshotIndex(timestamp); diff --git a/protocol/src/interfaces/IPodRegistry.sol b/protocol/src/interfaces/IPodRegistry.sol index 442caff4..88bae0ac 100644 --- a/protocol/src/interfaces/IPodRegistry.sol +++ b/protocol/src/interfaces/IPodRegistry.sol @@ -21,6 +21,16 @@ interface IPodRegistry { uint256 bitmap; } + /** + * @notice Network endpoint information for a validator + * @param host The hostname or IP address of the validator + * @param port The TCP port used by the validator + */ + struct Endpoint { + string host; + uint16 port; + } + /// @notice Error thrown when a validator is a zero address error ValidatorIsZeroAddress(); @@ -36,6 +46,12 @@ interface IPodRegistry { /// @notice Error thrown when a validator is not banned error ValidatorNotBanned(); + /// @notice Error thrown when a validator host is invalid + error ValidatorInvalidHost(); + + /// @notice Error thrown when a validator port is invalid + error ValidatorInvalidPort(); + /// @notice Error thrown when the caller is not a validator error CallerNotValidator(); @@ -75,6 +91,9 @@ interface IPodRegistry { /// @notice Event emitted when a validator is reactivated event ValidatorReactivated(address indexed validator); + /// @notice Event emitted when a validator network information is updated + event ValidatorNetworkUpdated(address indexed validator, string host, uint16 port); + /// @notice Event emitted when a snapshot is created event SnapshotCreated(uint256 indexed activeAsOfTimestamp, uint256 bitmap); @@ -108,9 +127,11 @@ interface IPodRegistry { * @notice Add a new validator to the registry and activate them. Modifies the current validator set * therefore creates a new snapshot. * @param validator The address of the validator to add + * @param host The hostname or IP address of the validator + * @param port The TCP port used by the validator * @dev Only callable by the contract owner */ - function addValidator(address validator) external; + function addValidator(address validator, string calldata host, uint16 port) external; /** * @notice Ban a validator from the registry. @@ -129,6 +150,14 @@ interface IPodRegistry { */ function unbanValidator(address validator) external; + /** + * @notice Update the network endpoint of an existing validator + * @param validator The address of the validator + * @param host The new hostname or IP address of the validator + * @param port The new TCP port used by the validator + */ + function setValidatorEndpoint(address validator, string calldata host, uint16 port) external; + /** * @notice Deactivate the caller's validator status. Modifies the current validator set * therefore creates a new snapshot. @@ -192,23 +221,38 @@ interface IPodRegistry { /** * @notice Get all currently active validators - * @return Array of addresses of currently active validators + * @return validators The list of addresses of currently active validators + * @return hosts The list of hostnames or IP addresses corresponding to each validator + * @return ports The list of TCP ports corresponding to each validator */ - function getActiveValidators() external view returns (address[] memory); + function getActiveValidators() + external + view + returns (address[] memory validators, string[] memory hosts, uint16[] memory ports); /** * @notice Get all validators that were active at a specific timestamp * @param timestamp The timestamp to query - * @return Array of addresses of validators active at the specified timestamp + * @return validators The list of addresses of currently active validators + * @return hosts The list of hostnames or IP addresses corresponding to each validator + * @return ports The list of TCP ports corresponding to each validator */ - function getActiveValidatorsAtTimestamp(uint256 timestamp) external view returns (address[] memory); + function getActiveValidatorsAtTimestamp(uint256 timestamp) + external + view + returns (address[] memory validators, string[] memory hosts, uint16[] memory ports); /** * @notice Get all validators at a specific snapshot * @param snapshotIndex The snapshot index to query - * @return Array of addresses of validators at the specified snapshot + * @return validators The list of addresses of currently active validators + * @return hosts The list of hostnames or IP addresses corresponding to each validator + * @return ports The list of TCP ports corresponding to each validator */ - function getValidatorsAtIndex(uint256 snapshotIndex) external view returns (address[] memory); + function getValidatorsAtIndex(uint256 snapshotIndex) + external + view + returns (address[] memory validators, string[] memory hosts, uint16[] memory ports); /** * @notice Get snapshot details at a specific index diff --git a/protocol/test/PodRegistry.t.sol b/protocol/test/PodRegistry.t.sol index 389e224d..1194c3f3 100644 --- a/protocol/test/PodRegistry.t.sol +++ b/protocol/test/PodRegistry.t.sol @@ -13,12 +13,29 @@ contract PodRegistryTest is Test { address public validator3 = address(4); address public validator4 = address(5); + string public host1 = "validator1.example.org"; + string public host2 = "validator2.example.org"; + string public host3 = "validator3.example.org"; + string public host4 = "validator4.example.org"; + + uint16 public port1 = 30303; + uint16 public port2 = 30304; + uint16 public port3 = 30305; + uint16 public port4 = 30306; + function setUp() public { address[] memory initialValidators = new address[](2); initialValidators[0] = validator1; initialValidators[1] = validator2; + string[] memory initialHosts = new string[](2); + initialHosts[0] = host1; + initialHosts[1] = host2; + + uint16[] memory initialPorts = new uint16[](2); + initialPorts[0] = port1; + initialPorts[1] = port2; vm.prank(owner); - registry = new PodRegistry(initialValidators); + registry = new PodRegistry(initialValidators, initialHosts, initialPorts); } function test_Initialization() public view { @@ -33,7 +50,7 @@ contract PodRegistryTest is Test { vm.prank(owner); vm.expectEmit(true, false, false, true); emit IPodRegistry.ValidatorAdded(validator3); - registry.addValidator(validator3); + registry.addValidator(validator3, host3, port3); assertEq(registry.getActiveValidatorCount(), 3); assertEq(registry.validatorIndex(validator3), 3); @@ -42,31 +59,62 @@ contract PodRegistryTest is Test { function test_AddValidator_RevertIfNotOwner() public { vm.expectRevert(abi.encodeWithSignature("OwnableUnauthorizedAccount(address)", address(this))); - registry.addValidator(validator3); + registry.addValidator(validator3, host3, port3); } function test_AddValidator_RevertIfZeroAddress() public { vm.prank(owner); vm.expectRevert(abi.encodeWithSignature("ValidatorIsZeroAddress()")); - registry.addValidator(address(0)); + registry.addValidator(address(0), "dummy-host", 30303); + } + + function test_AddValidator_RevertIfInvalidHost_Empty() public { + vm.prank(owner); + vm.expectRevert(abi.encodeWithSignature("ValidatorInvalidHost()")); + registry.addValidator(address(10), "", 30303); + } + + function test_AddValidator_RevertIfInvalidHost_TooLong() public { + // Build a host string of length 256 (> 255), to trigger the length check + bytes memory b = new bytes(256); + for (uint256 i = 0; i < b.length; i++) { + b[i] = bytes1("a"); + } + string memory longHost = string(b); + + vm.prank(owner); + vm.expectRevert(abi.encodeWithSignature("ValidatorInvalidHost()")); + registry.addValidator(address(11), longHost, 30303); + } + + function test_AddValidator_RevertIfInvalidPort_Zero() public { + vm.prank(owner); + vm.expectRevert(abi.encodeWithSignature("ValidatorInvalidPort()")); + registry.addValidator(address(12), "ok.example.org", 0); } function test_AddValidator_RevertIfAlreadyExists() public { vm.prank(owner); vm.expectRevert(abi.encodeWithSignature("ValidatorAlreadyExists()")); - registry.addValidator(validator1); + registry.addValidator(validator1, host1, port1); } function test_AddValidator_RevertIfMaxCountReached() public { vm.startPrank(owner); for (uint8 i = 0; i < 253; i++) { address newValidator = address(uint160(1000 + i)); - registry.addValidator(newValidator); + string memory host = string.concat("validator-", vm.toString(i), ".example.org"); + uint16 port = uint16(30000 + i); + + registry.addValidator(newValidator, host, port); } vm.expectRevert(abi.encodeWithSignature("MaxValidatorCountReached()")); - registry.addValidator(address(9999)); - vm.stopPrank(); + + address overflowValidator = address(uint160(2000)); + string memory overflowHost = "overflow.example.org"; + uint16 overflowPort = 40000; + registry.addValidator(overflowValidator, overflowHost, overflowPort); } function test_BanValidator() public { @@ -153,7 +201,7 @@ contract PodRegistryTest is Test { function test_ActiveValidatorBitmap_AfterAddBan() public { vm.prank(owner); - registry.addValidator(validator3); + registry.addValidator(validator3, host3, port3); uint256 bitmapAfterAdd = registry.activeValidatorBitmap(); assertEq(bitmapAfterAdd & (1 << (3 - 1)), (1 << (3 - 1))); @@ -246,7 +294,7 @@ contract PodRegistryTest is Test { vm.prank(owner); vm.expectEmit(true, false, false, true); emit IPodRegistry.SnapshotCreated(block.timestamp, (1 << (1 - 1)) | (1 << (2 - 1)) | (1 << (3 - 1))); - registry.addValidator(validator3); + registry.addValidator(validator3, host3, port3); assertEq(registry.getHistoryLength(), initialHistory + 1); vm.prank(owner); @@ -312,7 +360,7 @@ contract PodRegistryTest is Test { function test_ComputeWeight_HistoricalAndSnapshotValidation() public { vm.prank(owner); - registry.addValidator(validator3); // snapshot + registry.addValidator(validator3, host3, port3); // snapshot vm.warp(block.timestamp + 5); vm.prank(owner); @@ -332,7 +380,7 @@ contract PodRegistryTest is Test { function test_ComputeWeight_ExactSnapshotBlock() public { vm.prank(owner); - registry.addValidator(validator3); + registry.addValidator(validator3, host3, port3); uint256 snapshotIndex = registry.getHistoryLength() - 1; uint256 snapshotTimestamp; (snapshotTimestamp,) = registry.getSnapshotAtIndex(snapshotIndex); @@ -348,7 +396,7 @@ contract PodRegistryTest is Test { function test_ComputeWeight_RevertIfSnapshotTooOld() public { vm.prank(owner); - registry.addValidator(validator3); // snapshot at index 1 + registry.addValidator(validator3, host3, port3); // snapshot at index 1 vm.prank(owner); vm.warp(block.timestamp + 5); @@ -367,7 +415,7 @@ contract PodRegistryTest is Test { function test_ComputeWeight_RevertIfSnapshotTooNew() public { vm.prank(owner); - registry.addValidator(validator3); // snapshot at index 1 + registry.addValidator(validator3, host3, port3); // snapshot at index 1 uint256 timestampAtAdd = block.timestamp; vm.warp(block.timestamp + 5); @@ -392,7 +440,7 @@ contract PodRegistryTest is Test { } function test_ComputeWeight_NoHistoryReturnsZero() public { - PodRegistry emptyRegistry = new PodRegistry(new address[](0)); + PodRegistry emptyRegistry = new PodRegistry(new address[](0), new string[](0), new uint16[](0)); address[] memory subset = new address[](1); subset[0] = validator1; assertEq(emptyRegistry.computeWeight(subset), 0); @@ -413,7 +461,7 @@ contract PodRegistryTest is Test { // Warp time and add a new validator (creates snapshot) vm.warp(block.timestamp + 5); vm.prank(owner); - registry.addValidator(validator3); + registry.addValidator(validator3, host3, port3); uint256 timestampAtAdd = block.timestamp; // Warp time and ban validator1 (creates another snapshot) @@ -464,7 +512,7 @@ contract PodRegistryTest is Test { function test_FindSnapshotIndex_AfterLastSnapshot() public { vm.prank(owner); - registry.addValidator(validator3); + registry.addValidator(validator3, host3, port3); vm.warp(block.timestamp + 10); vm.prank(owner); @@ -482,7 +530,7 @@ contract PodRegistryTest is Test { function test_FindSnapshotIndex_ExactMatchLastSnapshot() public { vm.warp(block.timestamp + 10); vm.prank(owner); - registry.addValidator(validator3); + registry.addValidator(validator3, host3, port3); uint256 latestSnapshotTimestamp = block.timestamp; uint256 index = registry.findSnapshotIndex(latestSnapshotTimestamp); assertEq(index, registry.getHistoryLength() - 1); @@ -490,7 +538,7 @@ contract PodRegistryTest is Test { function test_GetSnapshot() public { vm.prank(owner); - registry.addValidator(validator3); + registry.addValidator(validator3, host3, port3); uint256 index = registry.getHistoryLength() - 1; (uint256 timestamp, uint256 bitmap) = registry.getSnapshotAtIndex(index); assertGt(timestamp, 0); @@ -500,29 +548,75 @@ contract PodRegistryTest is Test { function test_GetHistory() public { uint256 before = registry.getHistoryLength(); vm.prank(owner); - registry.addValidator(validator3); + registry.addValidator(validator3, host3, port3); assertEq(registry.getHistoryLength(), before + 1); } function test_GetActiveValidators() public view { - address[] memory validators = registry.getActiveValidators(); + (address[] memory validators, string[] memory hosts, uint16[] memory ports) = registry.getActiveValidators(); assertEq(validators.length, 2); + assertEq(hosts.length, 2); + assertEq(ports.length, 2); assertEq(validators[0], validator1); + assertEq(hosts[0], host1); + assertEq(ports[0], port1); assertEq(validators[1], validator2); + assertEq(hosts[1], host2); + assertEq(ports[1], port2); } function test_GetValidatorsAtIndex() public { vm.startPrank(owner); vm.warp(100); - registry.addValidator(validator3); + registry.addValidator(validator3, host3, port3); registry.banValidator(validator1); vm.warp(block.timestamp + 100); - registry.addValidator(validator4); + registry.addValidator(validator4, host4, port4); vm.stopPrank(); uint256 index = registry.findSnapshotIndex(100); - address[] memory validators = registry.getValidatorsAtIndex(index); + (address[] memory validators, string[] memory hosts, uint16[] memory ports) = + registry.getValidatorsAtIndex(index); assertEq(validators.length, 2); + assertEq(hosts.length, 2); + assertEq(ports.length, 2); assertEq(validators[0], validator2); + assertEq(hosts[0], host2); + assertEq(ports[0], port2); assertEq(validators[1], validator3); + assertEq(hosts[1], host3); + assertEq(ports[1], port3); + } + + function test_GetActiveValidators_AndUpdateEndpoint() public { + (address[] memory validators, string[] memory hosts, uint16[] memory ports) = registry.getActiveValidators(); + assertEq(validators.length, 2); + assertEq(hosts.length, 2); + assertEq(ports.length, 2); + + assertEq(validators[0], validator1); + assertEq(hosts[0], host1); + assertEq(ports[0], port1); + + assertEq(validators[1], validator2); + assertEq(hosts[1], host2); + assertEq(ports[1], port2); + + string memory newHost = "updated1.example.org"; + uint16 newPort = 31337; + + vm.prank(owner); + vm.expectEmit(true, false, false, true); + emit IPodRegistry.ValidatorNetworkUpdated(validator1, newHost, newPort); + + registry.setValidatorEndpoint(validator1, newHost, newPort); + + (validators, hosts, ports) = registry.getActiveValidators(); + assertEq(validators[0], validator1); + assertEq(hosts[0], newHost); + assertEq(ports[0], newPort); + + assertEq(validators[1], validator2); + assertEq(hosts[1], host2); + assertEq(ports[1], port2); } } diff --git a/solidity-sdk/test/PodECDSA.t.sol b/solidity-sdk/test/PodECDSA.t.sol index b2ef7ee0..96f87c55 100644 --- a/solidity-sdk/test/PodECDSA.t.sol +++ b/solidity-sdk/test/PodECDSA.t.sol @@ -16,15 +16,21 @@ contract PodECDSATest is Test { uint256[] public validatorPrivateKeys = new uint256[](128); function setUp() public { - address[] memory initialValidators = new address[](validatorPrivateKeys.length); + uint256 len = validatorPrivateKeys.length; + + address[] memory initialValidators = new address[](len); + string[] memory initialHosts = new string[](len); + uint16[] memory initialPorts = new uint16[](len); for (uint256 i = 0; i < validatorPrivateKeys.length; i++) { validatorPrivateKeys[i] = uint256(i + 1); initialValidators[i] = vm.addr(validatorPrivateKeys[i]); + initialHosts[i] = string.concat("validator-", vm.toString(i), ".example.org"); + initialPorts[i] = uint16(30000 + i); } vm.prank(OWNER); - podRegistry = new PodRegistry(initialValidators); + podRegistry = new PodRegistry(initialValidators, initialHosts, initialPorts); } function test_verify() public view { @@ -200,7 +206,7 @@ contract PodECDSATest is Test { vm.warp(medianTimestamp - 1); vm.prank(OWNER); - podRegistry.addValidator(vm.addr(0x123abc)); + podRegistry.addValidator(vm.addr(0x123abc), "validator-new.example.org", 30303); bytes memory aggregateSignature = ECDSA.aggregate_signatures(signatures); @@ -240,11 +246,11 @@ contract PodECDSATest is Test { vm.warp(medianTimestamp + 2); vm.startPrank(OWNER); - podRegistry.addValidator(vm.addr(0x123abcd)); + podRegistry.addValidator(vm.addr(0x123abcd), "validator-a.example.org", 30303); vm.warp(block.timestamp + medianTimestamp); - podRegistry.addValidator(vm.addr(0x456defa)); + podRegistry.addValidator(vm.addr(0x456defa), "validator-b.example.org", 30304); vm.warp(block.timestamp + medianTimestamp); - podRegistry.addValidator(vm.addr(0x789abcd)); + podRegistry.addValidator(vm.addr(0x789abcd), "validator-c.example.org", 30305); vm.warp(block.timestamp + medianTimestamp); vm.stopPrank();