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

Registry Spec #2

Merged
merged 7 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 4 additions & 4 deletions packages/evm/contracts/Enclave.sol
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ contract Enclave is IEnclave, OwnableUpgradeable {
////////////////////////////////////////////////////////////

function request(
address[] memory pools, // TODO: should we allow for multiple pools?
address filter,
uint32[2] calldata threshold,
// TODO: do we also need a start block/time? Would it be possible to have computations where inputs are
//published before the request is made? This kind of assumes the cypher nodes have already been selected
Expand Down Expand Up @@ -169,15 +169,15 @@ contract Enclave is IEnclave, OwnableUpgradeable {
e3s[e3Id] = e3;

require(
cyphernodeRegistry.selectCommittee(e3Id, pools, threshold),
cyphernodeRegistry.requestCommittee(e3Id, filter, threshold),
CommitteeSelectionFailed()
);
// TODO: validate that the selected pool accepts both the computation and execution modules.

emit E3Requested(
e3Id,
e3s[e3Id],
pools,
filter,
computationModule,
executionModule
);
Expand All @@ -190,7 +190,7 @@ contract Enclave is IEnclave, OwnableUpgradeable {
require(e3.expiration == 0, E3AlreadyActivated(e3Id));

bytes memory committeePublicKey = cyphernodeRegistry
.getCommitteePublicKey(e3Id);
.committeePublicKey(e3Id);
// Note: This check feels weird
require(committeePublicKey.length > 0, CommitteeSelectionFailed());

Expand Down
32 changes: 32 additions & 0 deletions packages/evm/contracts/interfaces/ICommitteeCoordinator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.8.26;

interface ICommitteeCoordinator {
/// @notice This event MUST be emitted when a committee is selected for an E3.
/// @param e3Id ID of the E3 for which the committee was selected.
/// @param threshold The M/N threshold for the committee.
event CommitteeRequested(uint256 indexed e3Id, uint32[2] threshold);

/// @notice This event MUST be emitted when a committee is selected for an E3.
/// @param e3Id ID of the E3 for which the committee was selected.
/// @param publicKey Public key of the committee.
event CommitteeAssembled(uint256 indexed e3Id, bytes publicKey);

/// @notice This function should be called by the Enclave contract to select a node committee.
/// @param e3Id ID of the E3 for which to select the committee.
/// @param threshold The M/N threshold for the committee.
/// @return success True if committee selection was successfully initiated.
function requestCommittee(
uint256 e3Id,
uint32[2] calldata threshold
) external returns (bool success);

/// @notice This function should be called by the Enclave contract to get the public key of a committee.
/// @dev This function MUST revert if no committee has been requested for the given E3.
/// @dev This function MUST revert if the committee has not yet published a public key.
/// @param e3Id ID of the E3 for which to get the committee public key.
/// @return publicKey The public key of the committee.
function committeePublicKey(
uint256 e3Id
) external view returns (bytes memory);
}
38 changes: 15 additions & 23 deletions packages/evm/contracts/interfaces/ICyphernodeRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,40 @@ pragma solidity >=0.8.26;
interface ICyphernodeRegistry {
/// @notice This event MUST be emitted when a committee is selected for an E3.
/// @param e3Id ID of the E3 for which the committee was selected.
/// @param pools Addresses of the pools of nodes from which the committee was selected.
/// @param filter Address of the contract that will coordinate committee selection.
/// @param threshold The M/N threshold for the committee.
event CommitteeRequested(
uint256 indexed e3Id,
address[] pools,
address filter,
uint32[2] threshold
);

/// @notice This event MUST be emitted when a committee is selected for an E3.
/// @param e3Id ID of the E3 for which the committee was selected.
/// @param nodes Addresses of the nodes in the committee.
/// @param merkleRoots Merkle roots of the nodes in the committee.
/// @param publicKey Public key of the committee.
event CommitteeSelected(
uint256 indexed e3Id,
address[] nodes,
bytes32[] merkleRoots,
bytes publicKey
);
event CommitteeSelected(uint256 indexed e3Id, bytes publicKey);

/// @notice This event MUST be emitted when a node is added to the registry.
/// @param nodeId ID of the node.
/// @param node Address of the node.
event NodeAdded(uint256 indexed nodeId, address indexed node);
/// @notice This event MUST be emitted when a filter is added to the registry.
event FilterAdded(address indexed filter);
Copy link
Member Author

Choose a reason for hiding this comment

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

I wonder if additing filters is necessary?

Given that we talked about making this feature permissionless, it seems that the only real benefit here is that it might make it easier for applications/fontends to know and display available filters?

Copy link
Contributor

@cristovaoth cristovaoth Jun 7, 2024

Choose a reason for hiding this comment

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

Right. Yeah we should keep it permissionless. Explicitly adding and emitting the event feels right somehow. What you point out is one scenario scenario.

You have any concrete thoughts on whether include/remove?


/// @notice This event MUST be emitted when a node is removed from the registry.
/// @param nodeId ID of the node.
/// @param node Address of the node.
event NodeRemoved(uint256 indexed nodeId, address indexed node);
/// @notice This event MUST be emitted when a filter is removed from the registry.
event FilterRemoved(address indexed filter);

/// @notice This event MUST be emitted when `encalve` is set.
/// @param enclave Address of the enclave contract.
event EnclaveSet(address indexed enclave);

function addFilter(address filter) external;

function removeFilter(address filter) external;

/// @notice This function should be called by the Enclave contract to select a node committee.
/// @param e3Id ID of the E3 for which to select the committee.
/// @param pools IDs of the pool of nodes from which to select the committee.
/// @param threshold The M/N threshold for the committee.
/// @return success True if committee selection was successfully initiated.
function selectCommittee(
function requestCommittee(
uint256 e3Id,
address[] memory pools,
address filter,
uint32[2] calldata threshold
) external returns (bool success);

Expand All @@ -54,7 +46,7 @@ interface ICyphernodeRegistry {
/// @dev This function MUST revert if the committee has not yet published a public key.
/// @param e3Id ID of the E3 for which to get the committee public key.
/// @return publicKey The public key of the committee.
function getCommitteePublicKey(
function committeePublicKey(
uint256 e3Id
) external view returns (bytes memory publicKey);
) external view returns (bytes memory);
}
8 changes: 4 additions & 4 deletions packages/evm/contracts/interfaces/IEnclave.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ interface IEnclave {
/// @notice This event MUST be emitted when an Encrypted Execution Environment (E3) is successfully requested.
/// @param e3Id ID of the E3.
/// @param e3 Details of the E3.
/// @param pool Address of the pool of nodes from which the Cypher Node committee was selected.
/// @param filter Address of the pool of nodes from which the Cypher Node committee was selected.
/// @param computationModule Address of the Computation module selected.
/// @param executionModule Address of the execution module selected.
event E3Requested(
uint256 e3Id,
E3 e3,
address[] pool,
address filter,
IComputationModule indexed computationModule,
IExecutionModule indexed executionModule
);
Expand Down Expand Up @@ -87,7 +87,7 @@ interface IEnclave {

/// @notice This function should be called to request a computation within an Encrypted Execution Environment (E3).
/// @dev This function MUST emit the E3Requested event.
/// @param pools IDs of the pool of nodes from which to select the committee.
/// @param filter IDs of the pool of nodes from which to select the committee.
/// @param threshold The M/N threshold for the committee.
/// @param duration The duration of the computation in seconds.
/// @param computationModule Address of the computation module.
Expand All @@ -97,7 +97,7 @@ interface IEnclave {
/// @return e3Id ID of the E3.
/// @return e3 The E3 struct.
function request(
address[] memory pools,
address filter,
uint32[2] calldata threshold,
uint256 duration,
IComputationModule computationModule,
Expand Down
7 changes: 0 additions & 7 deletions packages/evm/contracts/interfaces/INodePool.sol

This file was deleted.

78 changes: 28 additions & 50 deletions packages/evm/contracts/registry/CyphernodeRegistryOwnable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,12 @@
pragma solidity >=0.8.26;

import { ICyphernodeRegistry } from "../interfaces/ICyphernodeRegistry.sol";
import { ICommitteeCoordinator } from "../interfaces/ICommitteeCoordinator.sol";
import {
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract CyphernodeRegistryOwnable is ICyphernodeRegistry, OwnableUpgradeable {
struct Committee {
address[] nodes;
uint32[2] threshold;
address[] pools;
bytes32[] merkleRoots;
bytes publicKey;
}

struct Node {
bool eligible;
// Number of duties the node has not yet completed.
// Incremented each time a duty is added, decremented each time a duty is completed.
uint256 outstandingDuties;
}

////////////////////////////////////////////////////////////
// //
// Storage Variables //
Expand All @@ -30,15 +16,17 @@ contract CyphernodeRegistryOwnable is ICyphernodeRegistry, OwnableUpgradeable {

address public enclave;

mapping(uint256 e3 => Committee committee) public committees;
mapping(address nodeId => Node node) public nodes;
mapping(address filter => bool enabled) public filters;

mapping(uint256 e3 => ICommitteeCoordinator coordinator) public requests;

////////////////////////////////////////////////////////////
// //
// Errors //
// //
////////////////////////////////////////////////////////////

error CommitteeAlreadyRequested();
error CommitteeAlreadyExists();
error CommitteeAlreadyPublished();
error CommitteeDoesNotExist();
Expand Down Expand Up @@ -78,36 +66,21 @@ contract CyphernodeRegistryOwnable is ICyphernodeRegistry, OwnableUpgradeable {
// //
////////////////////////////////////////////////////////////

function selectCommittee(
function requestCommittee(
uint256 e3Id,
address[] memory pools,
address filter,
uint32[2] calldata threshold
) external onlyEnclave returns (bool success) {
Committee storage committee = committees[e3Id];
require(committee.threshold.length == 0, CommitteeAlreadyExists());
committee.threshold = threshold;
committee.pools = pools;
success = true;

emit CommitteeRequested(e3Id, pools, threshold);
}

function publishCommittee(
uint256 e3Id,
address[] memory _nodes,
bytes32[] memory merkleRoots,
bytes memory publicKey
) external onlyOwner {
Committee storage committee = committees[e3Id];
require(
keccak256(committee.publicKey) == keccak256(hex""),
CommitteeAlreadyPublished()
requests[e3Id] == ICommitteeCoordinator(address(0)),
CommitteeAlreadyRequested()
);
committee.nodes = _nodes;
committee.merkleRoots = merkleRoots;
committee.publicKey = publicKey;
requests[e3Id] = ICommitteeCoordinator(filter);

emit CommitteeSelected(e3Id, _nodes, merkleRoots, publicKey);
ICommitteeCoordinator(filter).requestCommittee(e3Id, threshold);

emit CommitteeRequested(e3Id, filter, threshold);
success = true;
}

////////////////////////////////////////////////////////////
Expand All @@ -121,23 +94,28 @@ contract CyphernodeRegistryOwnable is ICyphernodeRegistry, OwnableUpgradeable {
emit EnclaveSet(_enclave);
}

function addFilter(address filter) external {
filters[filter] = true;
emit FilterAdded(filter);
}

function removeFilter(address filter) external onlyOwner {
filters[filter] = false;
emit FilterRemoved(filter);
}

////////////////////////////////////////////////////////////
// //
// Get Functions //
// //
////////////////////////////////////////////////////////////

function getCommitteePublicKey(
function committeePublicKey(
uint256 e3Id
) external view returns (bytes memory publicKey) {
publicKey = committees[e3Id].publicKey;
require(publicKey.length > 0, NoPublicKeyPublished());
}
require(requests[e3Id] != ICommitteeCoordinator(address(0)));

function getCommittee(
uint256 e3Id
) external view returns (Committee memory committee) {
committee = committees[e3Id];
require(committees[e3Id].threshold.length > 0, CommitteeDoesNotExist());
publicKey = requests[e3Id].committeePublicKey(e3Id);
require(publicKey.length > 0, NoPublicKeyPublished());
}
}
Loading
Loading