// SPDX-License-Identifier: MIT pragma solidity 0.8.24; import {CapabilitiesRegistry} from "../CapabilitiesRegistry.sol"; import {INodeInfoProvider} from "../interfaces/INodeInfoProvider.sol"; import {BaseTest} from "./BaseTest.t.sol"; contract CapabilitiesRegistry_UpdateNodesTest is BaseTest { function setUp() public override { BaseTest.setUp(); changePrank(ADMIN); CapabilitiesRegistry.Capability[] memory capabilities = new CapabilitiesRegistry.Capability[](2); capabilities[0] = s_basicCapability; capabilities[1] = s_capabilityWithConfigurationContract; s_CapabilitiesRegistry.addNodeOperators(_getNodeOperators()); s_CapabilitiesRegistry.addCapabilities(capabilities); CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](2); hashedCapabilityIds[0] = s_basicHashedCapabilityId; hashedCapabilityIds[1] = s_capabilityWithConfigurationContractId; nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY, hashedCapabilityIds: hashedCapabilityIds }); changePrank(NODE_OPERATOR_ONE_ADMIN); s_CapabilitiesRegistry.addNodes(nodes); nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_TWO_ID, p2pId: P2P_ID_TWO, signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS, encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY_TWO, hashedCapabilityIds: hashedCapabilityIds }); changePrank(NODE_OPERATOR_TWO_ADMIN); s_CapabilitiesRegistry.addNodes(nodes); } function test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() public { changePrank(STRANGER); CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS, encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY_TWO, hashedCapabilityIds: hashedCapabilityIds }); vm.expectRevert(abi.encodeWithSelector(CapabilitiesRegistry.AccessForbidden.selector, STRANGER)); s_CapabilitiesRegistry.updateNodes(nodes); } function test_RevertWhen_CalledByAnotherNodeOperatorAdmin() public { changePrank(NODE_OPERATOR_TWO_ADMIN); CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_TWO_ID, p2pId: P2P_ID, signer: NEW_NODE_SIGNER, encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY_TWO, hashedCapabilityIds: hashedCapabilityIds }); vm.expectRevert(abi.encodeWithSelector(CapabilitiesRegistry.AccessForbidden.selector, NODE_OPERATOR_TWO_ADMIN)); s_CapabilitiesRegistry.updateNodes(nodes); } function test_RevertWhen_NodeDoesNotExist() public { changePrank(NODE_OPERATOR_ONE_ADMIN); CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: INVALID_P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY, hashedCapabilityIds: hashedCapabilityIds }); vm.expectRevert(abi.encodeWithSelector(INodeInfoProvider.NodeDoesNotExist.selector, INVALID_P2P_ID)); s_CapabilitiesRegistry.updateNodes(nodes); } function test_RevertWhen_P2PIDEmpty() public { changePrank(NODE_OPERATOR_ONE_ADMIN); CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: bytes32(""), signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY, hashedCapabilityIds: hashedCapabilityIds }); vm.expectRevert(abi.encodeWithSelector(INodeInfoProvider.NodeDoesNotExist.selector, bytes32(""))); s_CapabilitiesRegistry.updateNodes(nodes); } function test_RevertWhen_SignerAddressEmpty() public { changePrank(NODE_OPERATOR_ONE_ADMIN); CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: bytes32(""), encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY, hashedCapabilityIds: hashedCapabilityIds }); vm.expectRevert(abi.encodeWithSelector(CapabilitiesRegistry.InvalidNodeSigner.selector)); s_CapabilitiesRegistry.updateNodes(nodes); } function test_RevertWhen_EncryptionPublicKeyEmpty() public { changePrank(NODE_OPERATOR_ONE_ADMIN); CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, encryptionPublicKey: bytes32(""), hashedCapabilityIds: hashedCapabilityIds }); vm.expectRevert(abi.encodeWithSelector(CapabilitiesRegistry.InvalidNodeEncryptionPublicKey.selector, bytes32(""))); s_CapabilitiesRegistry.updateNodes(nodes); } function test_RevertWhen_NodeSignerAlreadyAssignedToAnotherNode() public { changePrank(NODE_OPERATOR_ONE_ADMIN); CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS, encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY_TWO, hashedCapabilityIds: hashedCapabilityIds }); vm.expectRevert(CapabilitiesRegistry.InvalidNodeSigner.selector); s_CapabilitiesRegistry.updateNodes(nodes); } function test_RevertWhen_UpdatingNodeWithoutCapabilities() public { changePrank(NODE_OPERATOR_ONE_ADMIN); CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](0); nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY, hashedCapabilityIds: hashedCapabilityIds }); vm.expectRevert(abi.encodeWithSelector(CapabilitiesRegistry.InvalidNodeCapabilities.selector, hashedCapabilityIds)); s_CapabilitiesRegistry.updateNodes(nodes); } function test_RevertWhen_AddingNodeWithInvalidCapability() public { changePrank(NODE_OPERATOR_ONE_ADMIN); CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_nonExistentHashedCapabilityId; nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY, hashedCapabilityIds: hashedCapabilityIds }); vm.expectRevert(abi.encodeWithSelector(CapabilitiesRegistry.InvalidNodeCapabilities.selector, hashedCapabilityIds)); s_CapabilitiesRegistry.updateNodes(nodes); } function test_RevertWhen_RemovingCapabilityRequiredByWorkflowDON() public { // SETUP: addDON CapabilitiesRegistry.CapabilityConfiguration[] memory capabilityConfigs = new CapabilitiesRegistry.CapabilityConfiguration[](1); capabilityConfigs[0] = CapabilitiesRegistry.CapabilityConfiguration({ capabilityId: s_basicHashedCapabilityId, config: BASIC_CAPABILITY_CONFIG }); bytes32[] memory nodeIds = new bytes32[](2); nodeIds[0] = P2P_ID; nodeIds[1] = P2P_ID_TWO; // SETUP: updateNodes CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); // DON requires s_basicHashedCapabilityId but we are swapping for // s_capabilityWithConfigurationContractId hashedCapabilityIds[0] = s_capabilityWithConfigurationContractId; nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY, hashedCapabilityIds: hashedCapabilityIds }); uint32 workflowDonId = 1; // Operations changePrank(ADMIN); s_CapabilitiesRegistry.addDON(nodeIds, capabilityConfigs, true, true, 1); vm.expectRevert( abi.encodeWithSelector( CapabilitiesRegistry.CapabilityRequiredByDON.selector, s_basicHashedCapabilityId, workflowDonId ) ); s_CapabilitiesRegistry.updateNodes(nodes); } function test_RevertWhen_RemovingCapabilityRequiredByCapabilityDON() public { // SETUP: addDON CapabilitiesRegistry.CapabilityConfiguration[] memory capabilityConfigs = new CapabilitiesRegistry.CapabilityConfiguration[](1); capabilityConfigs[0] = CapabilitiesRegistry.CapabilityConfiguration({ capabilityId: s_basicHashedCapabilityId, config: BASIC_CAPABILITY_CONFIG }); bytes32[] memory nodeIds = new bytes32[](2); nodeIds[0] = P2P_ID; nodeIds[1] = P2P_ID_TWO; // SETUP: updateNodes CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); // DON requires s_basicHashedCapabilityId but we are swapping for // s_capabilityWithConfigurationContractId hashedCapabilityIds[0] = s_capabilityWithConfigurationContractId; nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY, hashedCapabilityIds: hashedCapabilityIds }); uint32 capabilitiesDonId = 1; // Operations changePrank(ADMIN); s_CapabilitiesRegistry.addDON(nodeIds, capabilityConfigs, true, false, 1); vm.expectRevert( abi.encodeWithSelector( CapabilitiesRegistry.CapabilityRequiredByDON.selector, s_basicHashedCapabilityId, capabilitiesDonId ) ); s_CapabilitiesRegistry.updateNodes(nodes); } function test_CanUpdateParamsIfNodeSignerAddressNoLongerUsed() public { changePrank(NODE_OPERATOR_ONE_ADMIN); CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; // Set node one's signer to another address nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: bytes32(abi.encodePacked(address(6666))), encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY, hashedCapabilityIds: hashedCapabilityIds }); s_CapabilitiesRegistry.updateNodes(nodes); // Set node two's signer to node one's signer changePrank(NODE_OPERATOR_TWO_ADMIN); nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_TWO_ID, p2pId: P2P_ID_TWO, signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY_TWO, hashedCapabilityIds: hashedCapabilityIds }); s_CapabilitiesRegistry.updateNodes(nodes); CapabilitiesRegistry.NodeInfo memory node = s_CapabilitiesRegistry.getNode(P2P_ID_TWO); assertEq(node.signer, NODE_OPERATOR_ONE_SIGNER_ADDRESS); } function test_UpdatesNodeParams() public { changePrank(NODE_OPERATOR_ONE_ADMIN); CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NEW_NODE_SIGNER, encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY, hashedCapabilityIds: hashedCapabilityIds }); vm.expectEmit(address(s_CapabilitiesRegistry)); emit CapabilitiesRegistry.NodeUpdated(P2P_ID, TEST_NODE_OPERATOR_ONE_ID, NEW_NODE_SIGNER); s_CapabilitiesRegistry.updateNodes(nodes); CapabilitiesRegistry.NodeInfo memory node = s_CapabilitiesRegistry.getNode(P2P_ID); assertEq(node.nodeOperatorId, TEST_NODE_OPERATOR_ONE_ID); assertEq(node.p2pId, P2P_ID); assertEq(node.signer, NEW_NODE_SIGNER); assertEq(node.hashedCapabilityIds.length, 1); assertEq(node.hashedCapabilityIds[0], s_basicHashedCapabilityId); assertEq(node.configCount, 2); } function test_OwnerCanUpdateNodes() public { changePrank(ADMIN); CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); bytes32[] memory hashedCapabilityIds = new bytes32[](1); hashedCapabilityIds[0] = s_basicHashedCapabilityId; nodes[0] = CapabilitiesRegistry.NodeParams({ nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, p2pId: P2P_ID, signer: NEW_NODE_SIGNER, encryptionPublicKey: TEST_ENCRYPTION_PUBLIC_KEY, hashedCapabilityIds: hashedCapabilityIds }); vm.expectEmit(address(s_CapabilitiesRegistry)); emit CapabilitiesRegistry.NodeUpdated(P2P_ID, TEST_NODE_OPERATOR_ONE_ID, NEW_NODE_SIGNER); s_CapabilitiesRegistry.updateNodes(nodes); CapabilitiesRegistry.NodeInfo memory node = s_CapabilitiesRegistry.getNode(P2P_ID); assertEq(node.nodeOperatorId, TEST_NODE_OPERATOR_ONE_ID); assertEq(node.p2pId, P2P_ID); assertEq(node.signer, NEW_NODE_SIGNER); assertEq(node.hashedCapabilityIds.length, 1); assertEq(node.hashedCapabilityIds[0], s_basicHashedCapabilityId); assertEq(node.configCount, 2); } }