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

KS-129: Implement modify DON #13242

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
5 changes: 5 additions & 0 deletions .changeset/cool-readers-try.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

#internal add modify DON function to capability registry
5 changes: 5 additions & 0 deletions contracts/.changeset/quiet-guests-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chainlink/contracts": patch
---

Add update DON function to capability registry
129 changes: 83 additions & 46 deletions contracts/src/v0.8/keystone/CapabilityRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,11 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
/// @param signer The node's signer address
event NodeUpdated(bytes32 p2pId, uint256 nodeOperatorId, bytes32 signer);

/// @notice This event is emitted when a new DON is created
/// @param donId The ID of the newly created DON
/// @param isPublic True if the newly created DON is public
event DONAdded(uint256 donId, bool isPublic);

/// @notice This event is emitted when a DON is removed
/// @param donId The ID of the removed DON
event DONRemoved(uint32 donId);
/// @notice This event is emitted when a DON's config is set
/// @param donId The ID of the DON the config was set for
/// @param configCount The number of times the DON has been
/// configured
event ConfigSet(uint32 donId, uint32 configCount);

/// @notice This error is emitted when a DON does not exist
/// @param donId The ID of the nonexistent DON
Expand Down Expand Up @@ -550,44 +547,29 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
bool isPublic
) external onlyOwner {
uint32 id = s_donId;

s_dons[id].id = id;
s_dons[id].isPublic = isPublic;

uint32 configCount = 1;

DONCapabilityConfig storage donCapabilityConfig = s_dons[id].config[configCount];

for (uint256 i; i < nodes.length; ++i) {
bytes32 nodeP2PId = nodes[i];
if (donCapabilityConfig.nodes.contains(nodeP2PId)) revert DuplicateDONNode(id, nodeP2PId);

donCapabilityConfig.nodes.add(nodeP2PId);
}

for (uint256 i; i < capabilityConfigurations.length; ++i) {
CapabilityConfiguration calldata configuration = capabilityConfigurations[i];
bytes32 capabilityId = configuration.capabilityId;

if (!s_hashedCapabilityIds.contains(capabilityId)) revert CapabilityDoesNotExist(capabilityId);
if (s_deprecatedHashedCapabilityIds.contains(capabilityId)) revert CapabilityIsDeprecated(capabilityId);

if (donCapabilityConfig.capabilityConfigs[capabilityId].length > 0)
revert DuplicateDONCapability(id, capabilityId);

for (uint256 j; j < nodes.length; ++j) {
bytes32 nodeP2PId = nodes[j];
if (!s_nodes[nodeP2PId].supportedCapabilityIds[s_nodes[nodeP2PId].configCount].contains(capabilityId))
revert NodeDoesNotSupportCapability(nodeP2PId, capabilityId);
}

donCapabilityConfig.capabilityIds.push(capabilityId);
donCapabilityConfig.capabilityConfigs[capabilityId] = configuration.config;
}
_setDONConfig(id, 1, nodes, capabilityConfigurations, isPublic);
++s_donId;
}

s_dons[id].configCount = configCount;
/// @notice Updates a DON's configuration. This allows
/// the admin to reconfigure the list of capabilities supported
/// by the DON, the list of nodes that make up the DON as well
/// as whether or not the DON can accept external workflows
/// @param nodes The nodes making up the DON
/// @param capabilityConfigurations The list of configurations for the
/// capabilities supported by the DON
/// @param isPublic True if the DON is can accept external workflows
function updateDON(
uint32 donId,
bytes32[] calldata nodes,
CapabilityConfiguration[] calldata capabilityConfigurations,
bool isPublic
) external onlyOwner {
uint32 configCount = s_dons[donId].configCount;
if (configCount == 0) revert DONDoesNotExist(donId);
_setDONConfig(donId, ++configCount, nodes, capabilityConfigurations, isPublic);
++s_donId;
emit DONAdded(id, isPublic);
}

/// @notice Removes DONs from the Capability Registry
Expand All @@ -600,19 +582,20 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
// DON config count starts at index 1
if (don.configCount == 0) revert DONDoesNotExist(donId);
delete s_dons[donId];
emit DONRemoved(donId);
emit ConfigSet(donId, 0);
}
}

/// @notice Gets DON's data
/// @param donId The DON ID
/// @return uint32 The DON ID
/// @return uint32 The DON's config count
/// @return bool True if the DON is public
/// @return bytes32[] The list of node P2P IDs that are in the DON
/// @return CapabilityConfiguration[] The list of capability configurations supported by the DON
function getDON(
uint32 donId
) external view returns (uint32, bool, bytes32[] memory, CapabilityConfiguration[] memory) {
) external view returns (uint32, uint32, bool, bytes32[] memory, CapabilityConfiguration[] memory) {
uint32 configCount = s_dons[donId].configCount;

DONCapabilityConfig storage donCapabilityConfig = s_dons[donId].config[configCount];
Expand All @@ -627,7 +610,13 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
});
}

return (s_dons[donId].id, s_dons[donId].isPublic, donCapabilityConfig.nodes.values(), capabilityConfigurations);
return (
s_dons[donId].id,
configCount,
s_dons[donId].isPublic,
donCapabilityConfig.nodes.values(),
capabilityConfigurations
);
}

/// @notice Returns the DON specific configuration for a capability
Expand All @@ -638,4 +627,52 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
uint32 configCount = s_dons[donId].configCount;
return s_dons[donId].config[configCount].capabilityConfigs[capabilityId];
}

/// @notice Sets the configuration for a DON
/// @param donId The ID of the DON to set the configuration for
/// @param configCount The number of times the DON has been configured
/// @param nodes The nodes making up the DON
/// @param capabilityConfigurations The list of configurations for the
/// capabilities supported by the DON
/// @param isPublic True if the DON is can accept external workflows
function _setDONConfig(
uint32 donId,
uint32 configCount,
bytes32[] calldata nodes,
CapabilityConfiguration[] calldata capabilityConfigurations,
bool isPublic
) internal {
s_dons[donId].isPublic = isPublic;

DONCapabilityConfig storage donCapabilityConfig = s_dons[donId].config[configCount];
for (uint256 i; i < nodes.length; ++i) {
Copy link
Contributor Author

@cds95 cds95 May 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking a bit on how to optimize this. Currently the code blindly updates the DON's nodes and supported capability configs even if the function parameters are the same as what's already in storage. One idea I had was to store the hash of nodes and capabilityConfigs and compare them against the hash of the params passed in.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a ticket to test this out during the gas optimization stage?

bytes32 nodeP2PId = nodes[i];
if (donCapabilityConfig.nodes.contains(nodeP2PId)) revert DuplicateDONNode(donId, nodeP2PId);

donCapabilityConfig.nodes.add(nodeP2PId);
}

for (uint256 i; i < capabilityConfigurations.length; ++i) {
CapabilityConfiguration calldata configuration = capabilityConfigurations[i];
bytes32 capabilityId = configuration.capabilityId;

if (!s_hashedCapabilityIds.contains(capabilityId)) revert CapabilityDoesNotExist(capabilityId);
if (s_deprecatedHashedCapabilityIds.contains(capabilityId)) revert CapabilityIsDeprecated(capabilityId);

if (donCapabilityConfig.capabilityConfigs[capabilityId].length > 0)
revert DuplicateDONCapability(donId, capabilityId);

for (uint256 j; j < nodes.length; ++j) {
bytes32 nodeP2PId = nodes[j];
if (!s_nodes[nodeP2PId].supportedCapabilityIds[s_nodes[nodeP2PId].configCount].contains(capabilityId))
revert NodeDoesNotSupportCapability(nodeP2PId, capabilityId);
}

donCapabilityConfig.capabilityIds.push(capabilityId);
donCapabilityConfig.capabilityConfigs[capabilityId] = configuration.config;
}

s_dons[donId].configCount = configCount;
emit ConfigSet(donId, configCount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import {BaseTest} from "./BaseTest.t.sol";
import {CapabilityRegistry} from "../CapabilityRegistry.sol";

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

uint32 private constant DON_ID = 1;
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");
Expand Down Expand Up @@ -158,22 +159,24 @@ contract CapabilityRegistry_AddDONTest is BaseTest {
});

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

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

assertEq(donNodes.length, 1);
assertEq(donNodes.length, nodes.length);
assertEq(donNodes[0], P2P_ID);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {BaseTest} from "./BaseTest.t.sol";
import {CapabilityRegistry} from "../CapabilityRegistry.sol";

contract CapabilityRegistry_RemoveDONsTest is BaseTest {
event DONRemoved(uint32 donId);
event ConfigSet(uint32 donId, uint32 configCount);

uint32 private constant DON_ID = 1;
uint32 private constant TEST_NODE_OPERATOR_ONE_ID = 0;
Expand Down Expand Up @@ -81,16 +81,18 @@ contract CapabilityRegistry_RemoveDONsTest is BaseTest {
uint32[] memory donIDs = new uint32[](1);
donIDs[0] = DON_ID;
vm.expectEmit(true, true, true, true, address(s_capabilityRegistry));
emit DONRemoved(DON_ID);
emit ConfigSet(DON_ID, 0);
s_capabilityRegistry.removeDONs(donIDs);

(
uint32 id,
uint32 configCount,
bool isPublic,
bytes32[] memory donNodes,
CapabilityRegistry.CapabilityConfiguration[] memory donCapabilityConfigs
) = s_capabilityRegistry.getDON(DON_ID);
assertEq(id, 0);
assertEq(configCount, 0);
assertEq(isPublic, false);
assertEq(donCapabilityConfigs.length, 0);
assertEq(s_capabilityRegistry.getDONCapabilityConfig(DON_ID, s_basicHashedCapabilityId), bytes(""));
Expand Down
Loading
Loading