Skip to content

Commit

Permalink
Rename type -> id
Browse files Browse the repository at this point in the history
  • Loading branch information
HenryNguyen5 committed May 2, 2024
1 parent 9ca4f04 commit 01ff8fe
Show file tree
Hide file tree
Showing 27 changed files with 333 additions and 301 deletions.
5 changes: 5 additions & 0 deletions .changeset/bright-queens-joke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

#internal Keystone - rename type -> id
5 changes: 5 additions & 0 deletions contracts/.changeset/old-seas-doubt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chainlink/contracts": patch
---

#internal Keystone - rename type to id
116 changes: 66 additions & 50 deletions contracts/src/v0.8/keystone/CapabilityRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
bytes32 p2pId;
/// @notice The signer address for application-layer message verification.
address signer;
/// @notice The list of capability IDs this node supports. This list is
/// @notice The list of hashed capability IDs this node supports. This list is
/// never empty and all capabilities are guaranteed to exist in the
/// CapabilityRegistry.
bytes32[] supportedCapabilityIds;
bytes32[] supportedHashedCapabilityIds;
}

// CapabilityResponseType indicates whether remote response requires
Expand All @@ -45,10 +45,16 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
}

struct Capability {
// Capability type, e.g. "data-streams-reports"
// The `labelledName` is a partially qualified ID for the capability.
//
// Given the following capability ID: {name}:{label1_key}_{label1_value}:{label2_key}_{label2_value}@{version}
// Then we denote the `labelledName` as the `{name}:{label1_key}_{label1_value}:{label2_key}_{label2_value}` portion of the ID.
//
// Ex. id = "data-streams-reports:chain:ethereum@1.0.0"
// labelledName = "data-streams-reports:chain:ethereum"
//
// bytes32(string); validation regex: ^[a-z0-9_\-:]{1,32}$
// Not "type" because that's a reserved keyword in Solidity.
bytes32 capabilityType;
bytes32 labelledName;
// Semver, e.g., "1.2.3"
// bytes32(string); must be valid Semver + max 32 characters.
bytes32 version;
Expand Down Expand Up @@ -92,8 +98,8 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {

/// @notice This error is thrown when trying to add a node without
/// capabilities or with capabilities that do not exist.
/// @param capabilityIds The IDs of the capabilities that are being added.
error InvalidNodeCapabilities(bytes32[] capabilityIds);
/// @param hashedCapabilityIds The IDs of the capabilities that are being added.
error InvalidNodeCapabilities(bytes32[] hashedCapabilityIds);

/// @notice This event is emitted when a new node is added
/// @param p2pId The P2P ID of the node
Expand All @@ -104,15 +110,15 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
/// exists.
error CapabilityAlreadyExists();

/// @notice This error is thrown when a capability with the provided ID is
/// @notice This error is thrown when a capability with the provided hashed ID is
/// not found.
/// @param capabilityId The ID used for the lookup.
error CapabilityDoesNotExist(bytes32 capabilityId);
/// @param hashedCapabilityId The hashed ID used for the lookup.
error CapabilityDoesNotExist(bytes32 hashedCapabilityId);

/// @notice This error is thrown when trying to deprecate a capability that
/// is already deprecated.
/// @param capabilityId The ID of the capability that is already deprecated.
error CapabilityAlreadyDeprecated(bytes32 capabilityId);
/// @param hashedCapabilityId The hashed ID of the capability that is already deprecated.
error CapabilityAlreadyDeprecated(bytes32 hashedCapabilityId);

/// @notice This error is thrown when trying to add a capability with a
/// configuration contract that does not implement the required interface.
Expand All @@ -138,16 +144,22 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
event NodeOperatorUpdated(uint256 nodeOperatorId, address indexed admin, string name);

/// @notice This event is emitted when a new capability is added
/// @param capabilityId The ID of the newly added capability
event CapabilityAdded(bytes32 indexed capabilityId);
/// @param hashedCapabilityId The hashed ID of the newly added capability
event CapabilityAdded(bytes32 indexed hashedCapabilityId);

/// @notice This event is emitted when a capability is deprecated
/// @param capabilityId The ID of the deprecated capability
event CapabilityDeprecated(bytes32 indexed capabilityId);
/// @param hashedCapabilityId The hashed ID of the deprecated capability
event CapabilityDeprecated(bytes32 indexed hashedCapabilityId);

mapping(bytes32 => Capability) private s_capabilities;
EnumerableSet.Bytes32Set private s_capabilityIds;
EnumerableSet.Bytes32Set private s_deprecatedCapabilityIds;
/// @notice Set of hashed capability IDs.
/// A hashed ID is created by the function `getHashedCapabilityId`.
EnumerableSet.Bytes32Set private s_hashedCapabilityIds;
/// @notice Set of deprecated hashed capability IDs,
/// A hashed ID is created by the function `getHashedCapabilityId`.
///
/// Deprecated capabilities are skipped by the `getCapabilities` function.
EnumerableSet.Bytes32Set private s_deprecatedHashedCapabilityIds;

/// @notice Mapping of node operators
mapping(uint256 nodeOperatorId => NodeOperator nodeOperator) private s_nodeOperators;
Expand Down Expand Up @@ -227,14 +239,15 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
NodeOperator memory nodeOperator = s_nodeOperators[node.nodeOperatorId];
if (msg.sender != nodeOperator.admin) revert AccessForbidden();

bool nodeExists = s_nodes[node.p2pId].supportedCapabilityIds.length > 0;
bool nodeExists = s_nodes[node.p2pId].supportedHashedCapabilityIds.length > 0;
if (nodeExists || bytes32(node.p2pId) == bytes32("")) revert InvalidNodeP2PId(node.p2pId);

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

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

s_nodes[node.p2pId] = node;
Expand All @@ -250,9 +263,8 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
}

function addCapability(Capability calldata capability) external onlyOwner {
bytes32 capabilityId = getCapabilityID(capability.capabilityType, capability.version);

if (s_capabilityIds.contains(capabilityId)) revert CapabilityAlreadyExists();
bytes32 hashedId = getHashedCapabilityId(capability.labelledName, capability.version);
if (s_hashedCapabilityIds.contains(hashedId)) revert CapabilityAlreadyExists();

if (capability.configurationContract != address(0)) {
if (
Expand All @@ -263,63 +275,67 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
) revert InvalidCapabilityConfigurationContractInterface(capability.configurationContract);
}

s_capabilityIds.add(capabilityId);
s_capabilities[capabilityId] = capability;
s_hashedCapabilityIds.add(hashedId);
s_capabilities[hashedId] = capability;

emit CapabilityAdded(capabilityId);
emit CapabilityAdded(hashedId);
}

/// @notice Deprecates a capability by adding it to the deprecated list
/// @param capabilityId The ID of the capability to deprecate
function deprecateCapability(bytes32 capabilityId) external onlyOwner {
if (!s_capabilityIds.contains(capabilityId)) revert CapabilityDoesNotExist(capabilityId);
if (s_deprecatedCapabilityIds.contains(capabilityId)) revert CapabilityAlreadyDeprecated(capabilityId);

s_deprecatedCapabilityIds.add(capabilityId);
emit CapabilityDeprecated(capabilityId);
/// @param hashedCapabilityId The ID of the capability to deprecate
function deprecateCapability(bytes32 hashedCapabilityId) external onlyOwner {
if (!s_hashedCapabilityIds.contains(hashedCapabilityId)) revert CapabilityDoesNotExist(hashedCapabilityId);
if (s_deprecatedHashedCapabilityIds.contains(hashedCapabilityId))
revert CapabilityAlreadyDeprecated(hashedCapabilityId);

s_deprecatedHashedCapabilityIds.add(hashedCapabilityId);
emit CapabilityDeprecated(hashedCapabilityId);
}

function getCapability(bytes32 capabilityID) public view returns (Capability memory) {
return s_capabilities[capabilityID];
/// @notice This function returns a Capability by its hashed ID. Use `getHashedCapabilityId` to get the hashed ID.
function getCapability(bytes32 hashedId) public view returns (Capability memory) {
return s_capabilities[hashedId];
}

/// @notice Returns all capabilities. This operation will copy capabilities
/// to memory, which can be quite expensive. This is designed to mostly be
/// used by view accessors that are queried without any gas fees.
/// @return Capability[] An array of capabilities
function getCapabilities() external view returns (Capability[] memory) {
bytes32[] memory capabilityIds = s_capabilityIds.values();
bytes32[] memory hashedCapabilityIds = s_hashedCapabilityIds.values();

// Solidity does not support dynamic arrays in memory, so we create a
// fixed-size array and copy the capabilities into it.
Capability[] memory capabilities = new Capability[](capabilityIds.length - s_deprecatedCapabilityIds.length());
Capability[] memory capabilities = new Capability[](
hashedCapabilityIds.length - s_deprecatedHashedCapabilityIds.length()
);

// We need to keep track of the new index because we are skipping
// deprecated capabilities.
uint256 newIndex;

for (uint256 i; i < capabilityIds.length; ++i) {
bytes32 capabilityId = capabilityIds[i];
for (uint256 i; i < hashedCapabilityIds.length; ++i) {
bytes32 hashedCapabilityId = hashedCapabilityIds[i];

if (!s_deprecatedCapabilityIds.contains(capabilityId)) {
capabilities[newIndex] = getCapability(capabilityId);
if (!s_deprecatedHashedCapabilityIds.contains(hashedCapabilityId)) {
capabilities[newIndex] = getCapability(hashedCapabilityId);
newIndex++;
}
}

return capabilities;
}

/// @notice This functions returns a Capability ID packed into a bytes32 for cheaper access
/// @notice This functions returns a capability id that has been hashed to fit into a bytes32 for cheaper access
/// @return bytes32 A unique identifier for the capability
function getCapabilityID(bytes32 capabilityType, bytes32 version) public pure returns (bytes32) {
return keccak256(abi.encodePacked(capabilityType, version));
function getHashedCapabilityId(bytes32 labelledName, bytes32 version) public pure returns (bytes32) {
return keccak256(abi.encodePacked(labelledName, version));
}

/// @notice Returns whether a capability is deprecated
/// @param capabilityId The ID of the capability to check
/// @param hashedCapabilityId The hashed ID of the capability to check
/// @return bool True if the capability is deprecated, false otherwise
function isCapabilityDeprecated(bytes32 capabilityId) external view returns (bool) {
return s_deprecatedCapabilityIds.contains(capabilityId);
function isCapabilityDeprecated(bytes32 hashedCapabilityId) external view returns (bool) {
return s_deprecatedHashedCapabilityIds.contains(hashedCapabilityId);
}
}
18 changes: 9 additions & 9 deletions contracts/src/v0.8/keystone/test/BaseTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,37 @@ contract BaseTest is Test, Constants {
CapabilityConfigurationContract internal s_capabilityConfigurationContract;
CapabilityRegistry.Capability internal s_basicCapability;
CapabilityRegistry.Capability internal s_capabilityWithConfigurationContract;
bytes32 internal s_basicCapabilityId;
bytes32 internal s_basicHashedCapabilityId;
bytes32 internal s_capabilityWithConfigurationContractId;
bytes32 internal s_nonExistentCapabilityId;
bytes32 internal s_nonExistentHashedCapabilityId;

function setUp() public virtual {
vm.startPrank(ADMIN);
s_capabilityRegistry = new CapabilityRegistry();
s_capabilityConfigurationContract = new CapabilityConfigurationContract();

s_basicCapability = CapabilityRegistry.Capability({
capabilityType: "data-streams-reports",
labelledName: "data-streams-reports",
version: "1.0.0",
responseType: CapabilityRegistry.CapabilityResponseType.REPORT,
configurationContract: address(0)
});
s_capabilityWithConfigurationContract = CapabilityRegistry.Capability({
capabilityType: "read-ethereum-mainnet-gas-price",
labelledName: "read-ethereum-mainnet-gas-price",
version: "1.0.2",
responseType: CapabilityRegistry.CapabilityResponseType.OBSERVATION_IDENTICAL,
configurationContract: address(s_capabilityConfigurationContract)
});

s_basicCapabilityId = s_capabilityRegistry.getCapabilityID(
s_basicCapability.capabilityType,
s_basicHashedCapabilityId = s_capabilityRegistry.getHashedCapabilityId(
s_basicCapability.labelledName,
s_basicCapability.version
);
s_capabilityWithConfigurationContractId = s_capabilityRegistry.getCapabilityID(
s_capabilityWithConfigurationContract.capabilityType,
s_capabilityWithConfigurationContractId = s_capabilityRegistry.getHashedCapabilityId(
s_capabilityWithConfigurationContract.labelledName,
s_capabilityWithConfigurationContract.version
);
s_nonExistentCapabilityId = s_capabilityRegistry.getCapabilityID("non-existent-capability", "1.0.0");
s_nonExistentHashedCapabilityId = s_capabilityRegistry.getHashedCapabilityId("non-existent-capability", "1.0.0");
}

function _getNodeOperators() internal view returns (CapabilityRegistry.NodeOperator[] memory) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,13 @@ contract CapabilityRegistry_AddCapabilityTest is BaseTest {
function test_AddCapability_NoConfigurationContract() public {
s_capabilityRegistry.addCapability(s_basicCapability);

bytes32 capabilityId = s_capabilityRegistry.getCapabilityID(bytes32("data-streams-reports"), bytes32("1.0.0"));
CapabilityRegistry.Capability memory storedCapability = s_capabilityRegistry.getCapability(capabilityId);
bytes32 hashedCapabilityId = s_capabilityRegistry.getHashedCapabilityId(
bytes32("data-streams-reports"),
bytes32("1.0.0")
);
CapabilityRegistry.Capability memory storedCapability = s_capabilityRegistry.getCapability(hashedCapabilityId);

assertEq(storedCapability.capabilityType, s_basicCapability.capabilityType);
assertEq(storedCapability.labelledName, s_basicCapability.labelledName);
assertEq(storedCapability.version, s_basicCapability.version);
assertEq(uint256(storedCapability.responseType), uint256(s_basicCapability.responseType));
assertEq(storedCapability.configurationContract, s_basicCapability.configurationContract);
Expand All @@ -59,13 +62,13 @@ contract CapabilityRegistry_AddCapabilityTest is BaseTest {
function test_AddCapability_WithConfiguration() public {
s_capabilityRegistry.addCapability(s_capabilityWithConfigurationContract);

bytes32 capabilityId = s_capabilityRegistry.getCapabilityID(
bytes32(s_capabilityWithConfigurationContract.capabilityType),
bytes32 hashedCapabilityId = s_capabilityRegistry.getHashedCapabilityId(
bytes32(s_capabilityWithConfigurationContract.labelledName),
bytes32(s_capabilityWithConfigurationContract.version)
);
CapabilityRegistry.Capability memory storedCapability = s_capabilityRegistry.getCapability(capabilityId);
CapabilityRegistry.Capability memory storedCapability = s_capabilityRegistry.getCapability(hashedCapabilityId);

assertEq(storedCapability.capabilityType, s_capabilityWithConfigurationContract.capabilityType);
assertEq(storedCapability.labelledName, s_capabilityWithConfigurationContract.labelledName);
assertEq(storedCapability.version, s_capabilityWithConfigurationContract.version);
assertEq(uint256(storedCapability.responseType), uint256(s_capabilityWithConfigurationContract.responseType));
assertEq(storedCapability.configurationContract, s_capabilityWithConfigurationContract.configurationContract);
Expand Down

0 comments on commit 01ff8fe

Please sign in to comment.