Skip to content

Commit

Permalink
CapabilityRegistry: addDON (#13103)
Browse files Browse the repository at this point in the history
* Add CapabilityConfiguration struct

* Add CapabilityConfiguration and DON structs

* --wip-- [skip CI]

* implement add don on capability registry

* verify DON nodes unique

* add check for whether or not a node supports a capability

* update check for nodes supporting capability

* fix tests

* address PR feedback

* pack node struct

* check that capability is not deprecated when adding dons

* update node struct

* address nits

* add notice tag

---------

Co-authored-by: Christopher Dimitri Sastropranoto <[email protected]>
  • Loading branch information
DeividasK and cds95 authored May 13, 2024
1 parent f72c899 commit 54f7c9c
Show file tree
Hide file tree
Showing 14 changed files with 708 additions and 169 deletions.
5 changes: 5 additions & 0 deletions .changeset/tame-rules-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

#internal regen geth wrappers for capability registry
5 changes: 5 additions & 0 deletions contracts/.changeset/silly-bags-applaud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chainlink/contracts": patch
---

implement add don for capability regsitry
240 changes: 205 additions & 35 deletions contracts/src/v0.8/keystone/CapabilityRegistry.sol

Large diffs are not rendered by default.

179 changes: 179 additions & 0 deletions contracts/src/v0.8/keystone/test/CapabilityRegistry_AddDONTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

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

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

contract CapabilityRegistry_AddDONTest is BaseTest {
event DONAdded(uint256 donId, bool isPublic);

uint32 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");
bytes private constant CONFIG = bytes("onchain-config");

function setUp() public override {
BaseTest.setUp();

s_capabilityRegistry.addNodeOperators(_getNodeOperators());
s_capabilityRegistry.addCapability(s_basicCapability);
s_capabilityRegistry.addCapability(s_capabilityWithConfigurationContract);

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

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

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

nodes[1] = CapabilityRegistry.NodeParams({
nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID,
p2pId: P2P_ID_TWO,
signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS,
hashedCapabilityIds: nodeTwoCapabilityIds
});

changePrank(NODE_OPERATOR_ONE_ADMIN);
s_capabilityRegistry.addNodes(nodes);

changePrank(ADMIN);
}

function test_RevertWhen_CalledByNonAdmin() public {
changePrank(STRANGER);
vm.expectRevert("Only callable by owner");
bytes32[] memory nodes = new bytes32[](1);
CapabilityRegistry.CapabilityConfiguration[]
memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1);

capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({
capabilityId: s_basicHashedCapabilityId,
config: CONFIG
});
s_capabilityRegistry.addDON(nodes, capabilityConfigs, true);
}

function test_RevertWhen_NodeDoesNotSupportCapability() public {
bytes32[] memory nodes = new bytes32[](1);
nodes[0] = P2P_ID_TWO;
CapabilityRegistry.CapabilityConfiguration[]
memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1);
capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({
capabilityId: s_capabilityWithConfigurationContractId,
config: CONFIG
});
vm.expectRevert(
abi.encodeWithSelector(
CapabilityRegistry.NodeDoesNotSupportCapability.selector,
P2P_ID_TWO,
s_capabilityWithConfigurationContractId
)
);
s_capabilityRegistry.addDON(nodes, capabilityConfigs, true);
}

function test_RevertWhen_CapabilityDoesNotExist() public {
bytes32[] memory nodes = new bytes32[](1);
CapabilityRegistry.CapabilityConfiguration[]
memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1);
capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({
capabilityId: s_nonExistentHashedCapabilityId,
config: CONFIG
});
vm.expectRevert(
abi.encodeWithSelector(CapabilityRegistry.CapabilityDoesNotExist.selector, s_nonExistentHashedCapabilityId)
);
s_capabilityRegistry.addDON(nodes, capabilityConfigs, true);
}

function test_RevertWhen_DuplicateCapabilityAdded() public {
bytes32[] memory nodes = new bytes32[](1);
nodes[0] = P2P_ID;

CapabilityRegistry.CapabilityConfiguration[]
memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](2);
capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({
capabilityId: s_basicHashedCapabilityId,
config: CONFIG
});
capabilityConfigs[1] = CapabilityRegistry.CapabilityConfiguration({
capabilityId: s_basicHashedCapabilityId,
config: CONFIG
});

vm.expectRevert(
abi.encodeWithSelector(CapabilityRegistry.DuplicateDONCapability.selector, 1, s_basicHashedCapabilityId)
);
s_capabilityRegistry.addDON(nodes, capabilityConfigs, true);
}

function test_RevertWhen_DeprecatedCapabilityAdded() public {
bytes32 capabilityId = s_basicHashedCapabilityId;
s_capabilityRegistry.deprecateCapability(capabilityId);

bytes32[] memory nodes = new bytes32[](1);
nodes[0] = P2P_ID;

CapabilityRegistry.CapabilityConfiguration[]
memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1);
capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({capabilityId: capabilityId, config: CONFIG});

vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.CapabilityIsDeprecated.selector, capabilityId));
s_capabilityRegistry.addDON(nodes, capabilityConfigs, true);
}

function test_RevertWhen_DuplicateNodeAdded() public {
bytes32[] memory nodes = new bytes32[](2);
nodes[0] = P2P_ID;
nodes[1] = P2P_ID;

CapabilityRegistry.CapabilityConfiguration[]
memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1);
capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({
capabilityId: s_basicHashedCapabilityId,
config: CONFIG
});
vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.DuplicateDONNode.selector, 1, P2P_ID));
s_capabilityRegistry.addDON(nodes, capabilityConfigs, true);
}

function test_AddDON() public {
bytes32[] memory nodes = new bytes32[](1);
nodes[0] = P2P_ID;

CapabilityRegistry.CapabilityConfiguration[]
memory capabilityConfigs = new CapabilityRegistry.CapabilityConfiguration[](1);
capabilityConfigs[0] = CapabilityRegistry.CapabilityConfiguration({
capabilityId: s_basicHashedCapabilityId,
config: CONFIG
});

vm.expectEmit(true, true, true, true, address(s_capabilityRegistry));
emit DONAdded(1, true);
s_capabilityRegistry.addDON(nodes, capabilityConfigs, true);

(
uint32 id,
bool isPublic,
bytes32[] memory donNodes,
CapabilityRegistry.CapabilityConfiguration[] memory donCapabilityConfigs
) = s_capabilityRegistry.getDON(1);
assertEq(id, 1);
assertEq(isPublic, true);
assertEq(donCapabilityConfigs.length, 1);
assertEq(donCapabilityConfigs[0].capabilityId, s_basicHashedCapabilityId);
assertEq(s_capabilityRegistry.getDONCapabilityConfig(1, s_basicHashedCapabilityId), CONFIG);

assertEq(donNodes.length, 1);
assertEq(donNodes[0], P2P_ID);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {CapabilityRegistry} from "../CapabilityRegistry.sol";
contract CapabilityRegistry_AddNodesTest is BaseTest {
event NodeAdded(bytes32 p2pId, uint256 nodeOperatorId);

uint256 private constant TEST_NODE_OPERATOR_ONE_ID = 0;
uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 0;
uint256 private constant TEST_NODE_OPERATOR_TWO_ID = 1;

function setUp() public override {
Expand All @@ -20,16 +20,16 @@ contract CapabilityRegistry_AddNodesTest is BaseTest {

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

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

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

vm.expectRevert(CapabilityRegistry.AccessForbidden.selector);
Expand All @@ -38,16 +38,16 @@ contract CapabilityRegistry_AddNodesTest is BaseTest {

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

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

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

vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.InvalidNodeSigner.selector));
Expand All @@ -56,17 +56,18 @@ contract CapabilityRegistry_AddNodesTest is BaseTest {

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

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

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

s_capabilityRegistry.addNodes(nodes);

vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.InvalidNodeP2PId.selector, P2P_ID));
Expand All @@ -75,16 +76,16 @@ contract CapabilityRegistry_AddNodesTest is BaseTest {

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

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

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

vm.expectRevert(abi.encodeWithSelector(CapabilityRegistry.InvalidNodeP2PId.selector, bytes32("")));
Expand All @@ -93,89 +94,92 @@ contract CapabilityRegistry_AddNodesTest is BaseTest {

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

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

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

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

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

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

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

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

function test_AddsNode() public {
function test_AddsNodeParams() public {
changePrank(NODE_OPERATOR_ONE_ADMIN);

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

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

vm.expectEmit(address(s_capabilityRegistry));
emit NodeAdded(P2P_ID, TEST_NODE_OPERATOR_ONE_ID);
s_capabilityRegistry.addNodes(nodes);

CapabilityRegistry.Node memory node = s_capabilityRegistry.getNode(P2P_ID);
(CapabilityRegistry.NodeParams memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID);
assertEq(node.nodeOperatorId, TEST_NODE_OPERATOR_ONE_ID);
assertEq(node.p2pId, P2P_ID);
assertEq(node.supportedHashedCapabilityIds.length, 2);
assertEq(node.supportedHashedCapabilityIds[0], s_basicHashedCapabilityId);
assertEq(node.supportedHashedCapabilityIds[1], s_capabilityWithConfigurationContractId);
assertEq(node.hashedCapabilityIds.length, 2);
assertEq(node.hashedCapabilityIds[0], s_basicHashedCapabilityId);
assertEq(node.hashedCapabilityIds[1], s_capabilityWithConfigurationContractId);
assertEq(configCount, 1);
}

function test_OwnerCanAddNodes() public {
changePrank(ADMIN);

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

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

vm.expectEmit(address(s_capabilityRegistry));
emit NodeAdded(P2P_ID, TEST_NODE_OPERATOR_ONE_ID);
s_capabilityRegistry.addNodes(nodes);

CapabilityRegistry.Node memory node = s_capabilityRegistry.getNode(P2P_ID);
(CapabilityRegistry.NodeParams memory node, uint32 configCount) = s_capabilityRegistry.getNode(P2P_ID);
assertEq(node.nodeOperatorId, TEST_NODE_OPERATOR_ONE_ID);
assertEq(node.p2pId, P2P_ID);
assertEq(node.supportedHashedCapabilityIds.length, 2);
assertEq(node.supportedHashedCapabilityIds[0], s_basicHashedCapabilityId);
assertEq(node.supportedHashedCapabilityIds[1], s_capabilityWithConfigurationContractId);
assertEq(node.hashedCapabilityIds.length, 2);
assertEq(node.hashedCapabilityIds[0], s_basicHashedCapabilityId);
assertEq(node.hashedCapabilityIds[1], s_capabilityWithConfigurationContractId);
assertEq(configCount, 1);
}
}
Loading

0 comments on commit 54f7c9c

Please sign in to comment.