Skip to content

Commit

Permalink
Merge pull request #2 from gnosisguild/committee-flow
Browse files Browse the repository at this point in the history
  • Loading branch information
auryn-macmillan authored Jun 27, 2024
2 parents 70ee44c + ca901de commit cd44ea1
Show file tree
Hide file tree
Showing 12 changed files with 431 additions and 143 deletions.
18 changes: 9 additions & 9 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 @@ -163,21 +163,21 @@ contract Enclave is IEnclave, OwnableUpgradeable {
inputValidator: inputValidator,
outputVerifier: outputVerifier,
committeePublicKey: hex"",
inputs: new bytes[](0),
ciphertextOutput: hex"",
plaintextOutput: hex""
});
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 @@ -189,14 +189,13 @@ contract Enclave is IEnclave, OwnableUpgradeable {
E3 memory e3 = getE3(e3Id);
require(e3.expiration == 0, E3AlreadyActivated(e3Id));

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

// TODO: this should be based on the duration requested, not the current max duration.
e3s[e3Id].expiration = block.timestamp + maxDuration;
e3s[e3Id].committeePublicKey = committeePublicKey;
e3s[e3Id].committeePublicKey = publicKey;

emit E3Activated(e3Id, e3.expiration, e3.committeePublicKey);

Expand All @@ -219,7 +218,8 @@ contract Enclave is IEnclave, OwnableUpgradeable {
bytes memory input;
(input, success) = e3.inputValidator.validate(msg.sender, data);
require(success, InvalidInput());
// TODO: do we need to store or accumulate the inputs? Probably yes.
// TODO: probably better to accumulate inputs, rather than just dumping them in storage.
e3s[e3Id].inputs.push(input);
emit InputPublished(e3Id, input);
}

Expand Down
54 changes: 28 additions & 26 deletions packages/evm/contracts/interfaces/ICyphernodeRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,59 @@ 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
);

/// @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 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);
event CommitteePublished(uint256 indexed e3Id, bytes publicKey);

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

/// @notice This function should be called by the Enclave contract to select a node committee.
/// @notice This event MUST be emitted when a cyphernode is added to the registry.
event CyphernodeAdded(address indexed node);

/// @notice This event MUST be emitted when a cyphernode is removed from the registry.
event CyphernodeRemoved(address indexed node);

function isCyphernodeEligible(address cyphernode) external returns (bool);

/// @notice Initiates the committee selection process for a specified E3.
/// @dev This function MUST revert when not called by the Enclave contract.
/// @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 filter The address of the filter responsible for the committee selection process.
/// @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);

/// @notice Publishes the public key resulting from the committee selection process.
/// @dev This function MUST revert if not called by the previously selected filter.
/// @param e3Id ID of the E3 for which to select the committee.
/// @param publicKey The public key generated by the selected committee.
function publishCommittee(
uint256 e3Id,
bytes calldata proof,
bytes calldata publicKey
) external;

/// @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 getCommitteePublicKey(
function committeePublicKey(
uint256 e3Id
) external view returns (bytes memory publicKey);
) external view returns (bytes memory);
}
1 change: 1 addition & 0 deletions packages/evm/contracts/interfaces/IE3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct E3 {
IInputValidator inputValidator;
IOutputVerifier outputVerifier;
bytes committeePublicKey;
bytes[] inputs;
bytes ciphertextOutput;
bytes plaintextOutput;
}
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.

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

interface IRegistryFilter {
function requestCommittee(
uint256 e3Id,
uint32[2] calldata threshold
) external returns (bool success);
}
101 changes: 50 additions & 51 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 { IRegistryFilter } from "../interfaces/IRegistryFilter.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,19 +16,22 @@ contract CyphernodeRegistryOwnable is ICyphernodeRegistry, OwnableUpgradeable {

address public enclave;

mapping(uint256 e3 => Committee committee) public committees;
mapping(address nodeId => Node node) public nodes;
mapping(address cyphernode => bool isEnabled) public isEnabled;

mapping(uint256 e3Id => IRegistryFilter filter) public requests;
mapping(uint256 e3Id => bytes publicKey) public publicKeys;

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

error CommitteeAlreadyExists();
error CommitteeAlreadyRequested();
error CommitteeAlreadyPublished();
error CommitteeDoesNotExist();
error NoPublicKeyPublished();
error CommitteeNotPublished();
error CyphernodeNotEnabled(address node);
error OnlyEnclave();

////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -78,36 +67,39 @@ 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;
require(
requests[e3Id] == IRegistryFilter(address(0)),
CommitteeAlreadyRequested()
);
requests[e3Id] = IRegistryFilter(filter);

emit CommitteeRequested(e3Id, pools, threshold);
IRegistryFilter(filter).requestCommittee(e3Id, threshold);
emit CommitteeRequested(e3Id, filter, threshold);
success = true;
}

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()
);
committee.nodes = _nodes;
committee.merkleRoots = merkleRoots;
committee.publicKey = publicKey;

emit CommitteeSelected(e3Id, _nodes, merkleRoots, publicKey);
bytes calldata,
bytes calldata publicKey
) external {
// only to be published by the filter
require(address(requests[e3Id]) == msg.sender, CommitteeDoesNotExist());

// for (uint256 i = 0; i < cyphernodes.length; i++) {
// require(
// isEnabled[cyphernodes[i]] == true,
// CyphernodeNotEnabled(cyphernodes[i])
// );
// }

publicKeys[e3Id] = publicKey;
emit CommitteePublished(e3Id, publicKey);
}

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

function addCyphernode(address node) external onlyOwner {
isEnabled[node] = true;
emit CyphernodeAdded(node);
}

function removeCyphernode(address node) external onlyOwner {
isEnabled[node] = false;
emit CyphernodeRemoved(node);
}

function isCyphernodeEligible(address node) external view returns (bool) {
return isEnabled[node];
}

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

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

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

0 comments on commit cd44ea1

Please sign in to comment.