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

Rename type -> id #13008

Merged
merged 1 commit into from
May 2, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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