Skip to content

Commit

Permalink
implement update nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
aiagentdev-1990 committed May 2, 2024
1 parent 841fe61 commit 41cb8a0
Show file tree
Hide file tree
Showing 6 changed files with 347 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/modern-trainers-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

#internal Generate gethwrappers for capability registry changes
5 changes: 5 additions & 0 deletions contracts/.changeset/three-hotels-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chainlink/contracts": patch
---

Add function to update nodes in capability registry
38 changes: 38 additions & 0 deletions contracts/src/v0.8/keystone/CapabilityRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
/// @param nodeOperatorId The ID of the node operator that manages this node
event NodeAdded(bytes32 p2pId, uint256 nodeOperatorId);

/// @notice This event is emitted when a node is updated
/// @param p2pId The P2P ID of the node
/// @param nodeOperatorId The ID of the node operator that manages this node
/// @param signer The node's signer address
event NodeUpdated(bytes32 p2pId, uint256 nodeOperatorId, address signer);

/// @notice This error is thrown when trying to set the node's
/// signer address to zero
error InvalidNodeSigner();

/// @notice This error is thrown when trying add a capability that already
/// exists.
error CapabilityAlreadyExists();
Expand Down Expand Up @@ -255,6 +265,34 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
}
}

/// @notice Updates nodes. The node admin can update node signer addresses
/// and reconfigure the node's supported capabilities
/// @param nodes The nodes to update
function updateNodes(Node[] calldata nodes) external {
for (uint256 i; i < nodes.length; ++i) {
Node memory node = nodes[i];

NodeOperator memory nodeOperator = s_nodeOperators[node.nodeOperatorId];
if (msg.sender != nodeOperator.admin) revert AccessForbidden();

bool nodeExists = s_nodes[node.p2pId].supportedHashedCapabilityIds.length > 0;
if (!nodeExists) revert InvalidNodeP2PId(node.p2pId);

if (node.signer == address(0)) revert InvalidNodeSigner();

if (node.supportedHashedCapabilityIds.length == 0)
revert InvalidNodeCapabilities(node.supportedHashedCapabilityIds);

for (uint256 j; j < node.supportedHashedCapabilityIds.length; ++j) {
if (!s_hashedCapabilityIds.contains(node.supportedHashedCapabilityIds[j]))
revert InvalidNodeCapabilities(node.supportedHashedCapabilityIds);
}

s_nodes[node.p2pId] = node;
emit NodeUpdated(node.p2pId, node.nodeOperatorId, node.signer);
}
}

/// @notice Gets a node's data
/// @param p2pId The P2P ID of the node to query for
/// @return Node The node data
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {BaseTest} from "./BaseTest.t.sol";
import {CapabilityRegistry} from "../CapabilityRegistry.sol";

contract CapabilityRegistry_UpdateNodesTest is BaseTest {
event NodeUpdated(bytes32 p2pId, uint256 nodeOperatorId, address signer);

uint256 private constant TEST_NODE_OPERATOR_ONE_ID = 0;
uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 1;
bytes32 private constant INVALID_P2P_ID = bytes32("fake-p2p");

function setUp() public override {
BaseTest.setUp();
changePrank(ADMIN);
s_capabilityRegistry.addNodeOperators(_getNodeOperators());
s_capabilityRegistry.addCapability(s_basicCapability);
s_capabilityRegistry.addCapability(s_capabilityWithConfigurationContract);

CapabilityRegistry.Node[] memory nodes = new CapabilityRegistry.Node[](1);
bytes32[] memory hashedCapabilityIds = new bytes32[](2);
hashedCapabilityIds[0] = s_basicHashedCapabilityId;
hashedCapabilityIds[1] = s_capabilityWithConfigurationContractId;

nodes[0] = CapabilityRegistry.Node({
nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID,
p2pId: P2P_ID,
signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS,
supportedHashedCapabilityIds: hashedCapabilityIds
});

changePrank(NODE_OPERATOR_ONE_ADMIN);

s_capabilityRegistry.addNodes(nodes);
}

function test_RevertWhen_CalledByNonNodeOperatorAdmin() public {
changePrank(STRANGER);
CapabilityRegistry.Node[] memory nodes = new CapabilityRegistry.Node[](1);

bytes32[] memory hashedCapabilityIds = new bytes32[](1);
hashedCapabilityIds[0] = s_basicHashedCapabilityId;

nodes[0] = CapabilityRegistry.Node({
nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID,
p2pId: P2P_ID,
signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS,
supportedHashedCapabilityIds: hashedCapabilityIds
});

vm.expectRevert(CapabilityRegistry.AccessForbidden.selector);
s_capabilityRegistry.updateNodes(nodes);
}

function test_RevertWhen_NodeDoesNotExist() public {
changePrank(NODE_OPERATOR_ONE_ADMIN);
CapabilityRegistry.Node[] memory nodes = new CapabilityRegistry.Node[](1);

bytes32[] memory hashedCapabilityIds = new bytes32[](1);
hashedCapabilityIds[0] = s_basicHashedCapabilityId;

nodes[0] = CapabilityRegistry.Node({
nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID,
p2pId: INVALID_P2P_ID,
signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS,
supportedHashedCapabilityIds: hashedCapabilityIds
});

vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.InvalidNodeP2PId.selector, INVALID_P2P_ID));
s_capabilityRegistry.updateNodes(nodes);
}

function test_RevertWhen_P2PIDEmpty() public {
changePrank(NODE_OPERATOR_ONE_ADMIN);
CapabilityRegistry.Node[] memory nodes = new CapabilityRegistry.Node[](1);

bytes32[] memory hashedCapabilityIds = new bytes32[](1);
hashedCapabilityIds[0] = s_basicHashedCapabilityId;

nodes[0] = CapabilityRegistry.Node({
nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID,
p2pId: bytes32(""),
signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS,
supportedHashedCapabilityIds: hashedCapabilityIds
});

vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.InvalidNodeP2PId.selector, bytes32("")));
s_capabilityRegistry.updateNodes(nodes);
}

function test_RevertWhen_UpdatingNodeWithoutCapabilities() public {
changePrank(NODE_OPERATOR_ONE_ADMIN);
CapabilityRegistry.Node[] memory nodes = new CapabilityRegistry.Node[](1);

bytes32[] memory hashedCapabilityIds = new bytes32[](0);

nodes[0] = CapabilityRegistry.Node({
nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID,
p2pId: P2P_ID,
signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS,
supportedHashedCapabilityIds: hashedCapabilityIds
});
vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.InvalidNodeCapabilities.selector, hashedCapabilityIds));
s_capabilityRegistry.updateNodes(nodes);
}

function test_RevertWhen_AddingNodeWithInvalidCapability() public {
changePrank(NODE_OPERATOR_ONE_ADMIN);
CapabilityRegistry.Node[] memory nodes = new CapabilityRegistry.Node[](1);

bytes32[] memory hashedCapabilityIds = new bytes32[](1);
hashedCapabilityIds[0] = s_nonExistentHashedCapabilityId;

nodes[0] = CapabilityRegistry.Node({
nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID,
p2pId: P2P_ID,
signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS,
supportedHashedCapabilityIds: hashedCapabilityIds
});

vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.InvalidNodeCapabilities.selector, hashedCapabilityIds));
s_capabilityRegistry.updateNodes(nodes);
}

function test_UpdatesNode() public {
changePrank(NODE_OPERATOR_ONE_ADMIN);

CapabilityRegistry.Node[] memory nodes = new CapabilityRegistry.Node[](1);
bytes32[] memory hashedCapabilityIds = new bytes32[](1);
hashedCapabilityIds[0] = s_basicHashedCapabilityId;

nodes[0] = CapabilityRegistry.Node({
nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID,
p2pId: P2P_ID,
signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS,
supportedHashedCapabilityIds: hashedCapabilityIds
});

vm.expectEmit(address(s_capabilityRegistry));
emit NodeUpdated(P2P_ID, TEST_NODE_OPERATOR_ONE_ID, NODE_OPERATOR_TWO_SIGNER_ADDRESS);
s_capabilityRegistry.updateNodes(nodes);

CapabilityRegistry.Node memory node = s_capabilityRegistry.getNode(P2P_ID);
assertEq(node.nodeOperatorId, TEST_NODE_OPERATOR_ONE_ID);
assertEq(node.p2pId, P2P_ID);
assertEq(node.signer, NODE_OPERATOR_TWO_SIGNER_ADDRESS);
assertEq(node.supportedHashedCapabilityIds.length, 1);
assertEq(node.supportedHashedCapabilityIds[0], s_basicHashedCapabilityId);
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
GETH_VERSION: 1.13.8
forwarder: ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.bin b4c900aae9e022f01abbac7993d41f93912247613ac6270b0c4da4ef6f2016e3
keystone_capability_registry: ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.abi ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.bin aeb366351d69f320c610419a3e09a991bd6ea75690778835eb8f6421d1277f44
keystone_capability_registry: ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.abi ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.bin b91f09255ae292de8c05f990ef28a488a64088da6590676e21bf93409b2cde17
ocr3_capability: ../../../contracts/solc/v0.8.19/OCR3Capability/OCR3Capability.abi ../../../contracts/solc/v0.8.19/OCR3Capability/OCR3Capability.bin 9dcbdf55bd5729ba266148da3f17733eb592c871c2108ccca546618628fd9ad2

0 comments on commit 41cb8a0

Please sign in to comment.