mirror of https://github.com/nucypher/nucypher.git
shuffle Corrdinator contract versions
parent
6533c08464
commit
4a6842b5be
|
@ -0,0 +1,216 @@
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
import "zeppelin/ownership/Ownable.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title Coordinator
|
||||||
|
* @notice Coordination layer for DKG-TDec
|
||||||
|
*/
|
||||||
|
contract Coordinator is Ownable {
|
||||||
|
|
||||||
|
// Ritual
|
||||||
|
event StartRitual(uint32 indexed ritualId, address indexed initiator, address[] nodes);
|
||||||
|
event StartTranscriptRound(uint32 indexed ritualId);
|
||||||
|
event StartAggregationRound(uint32 indexed ritualId);
|
||||||
|
// TODO: Do we want the public key here? If so, we want 2 events or do we reuse this event?
|
||||||
|
event EndRitual(uint32 indexed ritualId, address indexed initiator, RitualState status);
|
||||||
|
|
||||||
|
// Node
|
||||||
|
event TranscriptPosted(uint32 indexed ritualId, address indexed node, bytes32 transcriptDigest);
|
||||||
|
event AggregationPosted(uint32 indexed ritualId, address indexed node, bytes32 aggregatedTranscriptDigest);
|
||||||
|
|
||||||
|
// Admin
|
||||||
|
event TimeoutChanged(uint32 oldTimeout, uint32 newTimeout);
|
||||||
|
event MaxDkgSizeChanged(uint32 oldSize, uint32 newSize);
|
||||||
|
|
||||||
|
enum RitualState {
|
||||||
|
NON_INITIATED,
|
||||||
|
AWAITING_TRANSCRIPTS,
|
||||||
|
AWAITING_AGGREGATIONS,
|
||||||
|
TIMEOUT,
|
||||||
|
INVALID,
|
||||||
|
FINALIZED
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 public constant PUBLIC_KEY_SIZE = 48;
|
||||||
|
|
||||||
|
struct Participant {
|
||||||
|
address node;
|
||||||
|
bool aggregated;
|
||||||
|
bytes transcript; // TODO: Consider event processing complexity vs storage cost
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Optimize layout
|
||||||
|
struct Ritual {
|
||||||
|
uint32 id; // TODO: Redundant? ID is index of rituals array
|
||||||
|
address initiator;
|
||||||
|
uint32 dkgSize;
|
||||||
|
uint32 initTimestamp;
|
||||||
|
uint32 totalTranscripts;
|
||||||
|
uint32 totalAggregations;
|
||||||
|
bytes32 aggregatedTranscriptHash;
|
||||||
|
bool aggregationMismatch;
|
||||||
|
bytes aggregatedTranscript;
|
||||||
|
bytes1[PUBLIC_KEY_SIZE] publicKey;
|
||||||
|
Participant[] participant;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ritual[] public rituals;
|
||||||
|
|
||||||
|
uint32 public timeout;
|
||||||
|
uint32 public maxDkgSize;
|
||||||
|
|
||||||
|
constructor(uint32 _timeout, uint32 _maxDkgSize) {
|
||||||
|
timeout = _timeout;
|
||||||
|
maxDkgSize = _maxDkgSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRitualState(uint256 ritualId) external view returns (RitualState){
|
||||||
|
// TODO: restrict to ritualID < rituals.length?
|
||||||
|
return getRitualState(rituals[ritualId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRitualState(Ritual storage ritual) internal view returns (RitualState){
|
||||||
|
uint32 t0 = ritual.initTimestamp;
|
||||||
|
uint32 deadline = t0 + timeout;
|
||||||
|
if(t0 == 0){
|
||||||
|
return RitualState.NON_INITIATED;
|
||||||
|
} else if (ritual.publicKey[0] != 0x0){ // TODO: Improve check
|
||||||
|
return RitualState.FINALIZED;
|
||||||
|
} else if (ritual.aggregationMismatch){
|
||||||
|
return RitualState.INVALID;
|
||||||
|
} else if (block.timestamp > deadline){
|
||||||
|
return RitualState.TIMEOUT;
|
||||||
|
} else if (ritual.totalTranscripts < ritual.dkgSize) {
|
||||||
|
return RitualState.AWAITING_TRANSCRIPTS;
|
||||||
|
} else if (ritual.totalAggregations < ritual.dkgSize) {
|
||||||
|
return RitualState.AWAITING_AGGREGATIONS;
|
||||||
|
} else {
|
||||||
|
// TODO: Is it possible to reach this state?
|
||||||
|
// - No public key
|
||||||
|
// - All transcripts and all aggregations
|
||||||
|
// - Still within the deadline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function setTimeout(uint32 newTimeout) external onlyOwner {
|
||||||
|
emit TimeoutChanged(timeout, newTimeout);
|
||||||
|
timeout = newTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMaxDkgSize(uint32 newSize) external onlyOwner {
|
||||||
|
emit MaxDkgSizeChanged(maxDkgSize, newSize);
|
||||||
|
maxDkgSize = newSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
function numberOfRituals() external view returns(uint256) {
|
||||||
|
return rituals.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParticipants(uint32 ritualId) external view returns(Participant[] memory) {
|
||||||
|
Ritual storage ritual = rituals[ritualId];
|
||||||
|
return ritual.participant;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initiateRitual(address[] calldata nodes) external returns (uint32) {
|
||||||
|
// TODO: Validate service fees, expiration dates, threshold
|
||||||
|
require(nodes.length <= maxDkgSize, "Invalid number of nodes");
|
||||||
|
|
||||||
|
uint32 id = uint32(rituals.length);
|
||||||
|
Ritual storage ritual = rituals.push();
|
||||||
|
ritual.id = id; // TODO: Possibly redundant
|
||||||
|
ritual.initiator = msg.sender; // TODO: Consider sponsor model
|
||||||
|
ritual.dkgSize = uint32(nodes.length);
|
||||||
|
ritual.initTimestamp = uint32(block.timestamp);
|
||||||
|
|
||||||
|
address previousNode = address(0);
|
||||||
|
for(uint256 i=0; i < nodes.length; i++){
|
||||||
|
Participant storage newParticipant = ritual.participant.push();
|
||||||
|
address currentNode = nodes[i];
|
||||||
|
newParticipant.node = currentNode;
|
||||||
|
require(previousNode < currentNode, "Nodes must be sorted");
|
||||||
|
previousNode = currentNode;
|
||||||
|
// TODO: Check nodes are eligible (staking, etc)
|
||||||
|
}
|
||||||
|
// TODO: Compute cohort fingerprint as hash(nodes)
|
||||||
|
|
||||||
|
emit StartRitual(id, msg.sender, nodes);
|
||||||
|
emit StartTranscriptRound(id);
|
||||||
|
return ritual.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function postTranscript(uint32 ritualId, uint256 nodeIndex, bytes calldata transcript) external {
|
||||||
|
Ritual storage ritual = rituals[ritualId];
|
||||||
|
require(
|
||||||
|
getRitualState(ritual) == RitualState.AWAITING_TRANSCRIPTS,
|
||||||
|
"Not waiting for transcripts"
|
||||||
|
);
|
||||||
|
Participant storage participant = ritual.participant[nodeIndex];
|
||||||
|
require(
|
||||||
|
participant.node == msg.sender,
|
||||||
|
"Node not part of ritual"
|
||||||
|
);
|
||||||
|
require(
|
||||||
|
participant.transcript.length == 0,
|
||||||
|
"Node already posted transcript"
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: Validate transcript size based on dkg size
|
||||||
|
|
||||||
|
// Nodes commit to their transcript
|
||||||
|
bytes32 transcriptDigest = keccak256(transcript);
|
||||||
|
participant.transcript = transcript; // TODO: ???
|
||||||
|
emit TranscriptPosted(ritualId, msg.sender, transcriptDigest);
|
||||||
|
ritual.totalTranscripts++;
|
||||||
|
|
||||||
|
// end round
|
||||||
|
if (ritual.totalTranscripts == ritual.dkgSize){
|
||||||
|
emit StartAggregationRound(ritualId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function postAggregation(uint32 ritualId, uint256 nodeIndex, bytes calldata aggregatedTranscript) external {
|
||||||
|
Ritual storage ritual = rituals[ritualId];
|
||||||
|
require(
|
||||||
|
getRitualState(ritual) == RitualState.AWAITING_AGGREGATIONS,
|
||||||
|
"Not waiting for aggregations"
|
||||||
|
);
|
||||||
|
Participant storage participant = ritual.participant[nodeIndex];
|
||||||
|
require(
|
||||||
|
participant.node == msg.sender,
|
||||||
|
"Node not part of ritual"
|
||||||
|
);
|
||||||
|
require(
|
||||||
|
!participant.aggregated,
|
||||||
|
"Node already posted aggregation"
|
||||||
|
);
|
||||||
|
|
||||||
|
// nodes commit to their aggregation result
|
||||||
|
bytes32 aggregatedTranscriptDigest = keccak256(aggregatedTranscript);
|
||||||
|
participant.aggregated = true;
|
||||||
|
emit AggregationPosted(ritualId, msg.sender, aggregatedTranscriptDigest);
|
||||||
|
|
||||||
|
if (ritual.aggregatedTranscriptHash == bytes32(0)){
|
||||||
|
ritual.aggregatedTranscriptHash = aggregatedTranscriptDigest;
|
||||||
|
} else if (ritual.aggregatedTranscriptHash != aggregatedTranscriptDigest){
|
||||||
|
ritual.aggregationMismatch = true;
|
||||||
|
emit EndRitual(ritualId, ritual.initiator, RitualState.INVALID);
|
||||||
|
// TODO: Invalid ritual
|
||||||
|
// TODO: Consider freeing ritual storage
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ritual.totalAggregations++;
|
||||||
|
|
||||||
|
// end round - Last node posting aggregation will finalize
|
||||||
|
if (ritual.totalAggregations == ritual.dkgSize){
|
||||||
|
emit EndRitual(ritualId, ritual.initiator, RitualState.FINALIZED);
|
||||||
|
// TODO: Last node extracts public key bytes from aggregated transcript
|
||||||
|
// and store in ritual.publicKey
|
||||||
|
ritual.publicKey[0] = bytes1(0x42);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,175 +0,0 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
pragma solidity ^0.8.0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title CoordinatorV1
|
|
||||||
* @notice Coordination layer for DKG-TDec
|
|
||||||
*/
|
|
||||||
contract CoordinatorV1 {
|
|
||||||
|
|
||||||
uint32 public constant DKG_SIZE = 8;
|
|
||||||
uint32 public TIMEOUT = 9600;
|
|
||||||
|
|
||||||
event StartRitual(uint32 indexed ritualId, address[] nodes);
|
|
||||||
event StartTranscriptRound(uint32 indexed ritualId);
|
|
||||||
event StartConfirmationRound(uint32 indexed ritualId);
|
|
||||||
event RitualEnded(uint32 indexed ritualId);
|
|
||||||
|
|
||||||
event TranscriptPosted(uint32 indexed ritualId, address indexed node, bytes32 transcriptDigest);
|
|
||||||
event ConfirmationPosted(uint32 indexed ritualId, address indexed node, address[] confirmedNodes);
|
|
||||||
|
|
||||||
event TimeoutChanged(uint32 timeout);
|
|
||||||
event DkgSizeChanged(uint8 dkgSize);
|
|
||||||
|
|
||||||
enum RitualStatus {
|
|
||||||
WAITING_FOR_CHECKINS,
|
|
||||||
WAITING_FOR_TRANSCRIPTS,
|
|
||||||
WAITING_FOR_CONFIRMATIONS,
|
|
||||||
COMPLETED,
|
|
||||||
FAILED
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Find better name
|
|
||||||
struct Performance {
|
|
||||||
address node;
|
|
||||||
uint32 checkinTimestamp;
|
|
||||||
uint96 confirmedBy;
|
|
||||||
bytes32 transcript;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Ritual {
|
|
||||||
uint32 id;
|
|
||||||
uint32 initTimestamp;
|
|
||||||
uint32 totalCheckins;
|
|
||||||
uint32 totalTranscripts;
|
|
||||||
uint32 totalConfirmations;
|
|
||||||
RitualStatus status;
|
|
||||||
Performance[DKG_SIZE] performance;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ritual[] public rituals;
|
|
||||||
|
|
||||||
function numberOfRituals() external view returns(uint256){
|
|
||||||
return rituals.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPerformances(uint32 ritualId) external view returns(Performance[] memory){
|
|
||||||
Performance[] memory performances = new Performance[](rituals[ritualId].performance.length);
|
|
||||||
for(uint32 i=0; i < rituals[ritualId].performance.length; i++){
|
|
||||||
performances[i] = rituals[ritualId].performance[i];
|
|
||||||
}
|
|
||||||
return performances;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTimeout(uint32 timeout) external {
|
|
||||||
TIMEOUT = timeout;
|
|
||||||
emit TimeoutChanged(timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
function initiateRitual(address[] calldata nodes) external {
|
|
||||||
// TODO: Check for payment
|
|
||||||
// TODO: Check for expiration time
|
|
||||||
// TODO: Improve DKG size choices
|
|
||||||
require(nodes.length == DKG_SIZE, "Invalid number of nodes");
|
|
||||||
|
|
||||||
uint32 id = uint32(rituals.length);
|
|
||||||
Ritual storage ritual = rituals.push();
|
|
||||||
ritual.id = id;
|
|
||||||
ritual.initTimestamp = uint32(block.timestamp);
|
|
||||||
ritual.status = RitualStatus.WAITING_FOR_CHECKINS;
|
|
||||||
ritual.totalTranscripts = 0;
|
|
||||||
ritual.totalConfirmations = 0;
|
|
||||||
ritual.totalCheckins = 0;
|
|
||||||
|
|
||||||
address previousNode = nodes[0];
|
|
||||||
ritual.performance[0].node = previousNode;
|
|
||||||
address currentNode;
|
|
||||||
for(uint256 i=1; i < nodes.length; i++){
|
|
||||||
currentNode = nodes[i];
|
|
||||||
require(currentNode > previousNode, "Nodes must be sorted");
|
|
||||||
ritual.performance[i].node = currentNode;
|
|
||||||
previousNode = currentNode;
|
|
||||||
// TODO: Check nodes are eligible (staking, etc)
|
|
||||||
}
|
|
||||||
|
|
||||||
emit StartRitual(id, nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkIn(uint32 ritualId, uint256 nodeIndex) external {
|
|
||||||
Ritual storage ritual = rituals[ritualId];
|
|
||||||
require(ritual.status == RitualStatus.WAITING_FOR_CHECKINS, "Not waiting for check-ins");
|
|
||||||
require(ritual.performance[nodeIndex].node == msg.sender, "Node not part of ritual");
|
|
||||||
if ((uint32(block.timestamp) - ritual.initTimestamp) > TIMEOUT) {
|
|
||||||
ritual.status = RitualStatus.FAILED;
|
|
||||||
emit RitualEnded(ritualId);
|
|
||||||
revert("Ritual timed out");
|
|
||||||
}
|
|
||||||
require(ritual.performance[nodeIndex].checkinTimestamp == 0, "Node already checked in");
|
|
||||||
ritual.performance[nodeIndex].checkinTimestamp = uint32(block.timestamp);
|
|
||||||
ritual.totalCheckins++;
|
|
||||||
if (ritual.totalCheckins == DKG_SIZE){
|
|
||||||
ritual.status = RitualStatus.WAITING_FOR_TRANSCRIPTS;
|
|
||||||
emit StartTranscriptRound(ritualId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function postTranscript(uint32 ritualId, uint256 nodeIndex, bytes calldata transcript) external {
|
|
||||||
Ritual storage ritual = rituals[ritualId];
|
|
||||||
require(ritual.status == RitualStatus.WAITING_FOR_TRANSCRIPTS, "Not waiting for transcripts");
|
|
||||||
require(ritual.performance[nodeIndex].node == msg.sender, "Node not part of ritual");
|
|
||||||
if ((uint32(block.timestamp) - ritual.initTimestamp) > TIMEOUT) {
|
|
||||||
ritual.status = RitualStatus.FAILED;
|
|
||||||
emit RitualEnded(ritualId);
|
|
||||||
revert("Ritual timed out");
|
|
||||||
}
|
|
||||||
require(ritual.performance[nodeIndex].transcript == bytes32(0), "Node already posted transcript");
|
|
||||||
|
|
||||||
// Nodes commit to their transcript
|
|
||||||
bytes32 transcriptDigest = keccak256(transcript);
|
|
||||||
ritual.performance[nodeIndex].transcript = transcriptDigest;
|
|
||||||
ritual.totalTranscripts++;
|
|
||||||
if (ritual.totalTranscripts == DKG_SIZE){
|
|
||||||
ritual.status = RitualStatus.WAITING_FOR_CONFIRMATIONS;
|
|
||||||
emit StartConfirmationRound(ritualId);
|
|
||||||
}
|
|
||||||
emit TranscriptPosted(ritualId, msg.sender, transcriptDigest);
|
|
||||||
}
|
|
||||||
|
|
||||||
function postConfirmation(uint32 ritualId, uint256 nodeIndex, uint256[] calldata confirmedNodesIndexes) external {
|
|
||||||
Ritual storage ritual = rituals[ritualId];
|
|
||||||
require(ritual.status == RitualStatus.WAITING_FOR_CONFIRMATIONS, "Not waiting for confirmations");
|
|
||||||
require(
|
|
||||||
ritual.performance[nodeIndex].node == msg.sender &&
|
|
||||||
ritual.performance[nodeIndex].transcript != bytes32(0),
|
|
||||||
"Node not part of ritual"
|
|
||||||
);
|
|
||||||
|
|
||||||
require(confirmedNodesIndexes.length <= DKG_SIZE, "Invalid number of confirmations");
|
|
||||||
if ((uint32(block.timestamp) - ritual.initTimestamp) > TIMEOUT) {
|
|
||||||
ritual.status = RitualStatus.FAILED;
|
|
||||||
emit RitualEnded(ritualId);
|
|
||||||
revert("Ritual timed out");
|
|
||||||
}
|
|
||||||
|
|
||||||
address[] memory confirmedNodes = new address[](confirmedNodesIndexes.length);
|
|
||||||
|
|
||||||
// First, node adds itself to its list of confirmers
|
|
||||||
uint96 caller = uint96(2 ** nodeIndex);
|
|
||||||
ritual.performance[nodeIndex].confirmedBy |= caller;
|
|
||||||
for(uint256 i=0; i < confirmedNodesIndexes.length; i++){
|
|
||||||
uint256 confirmedNodeIndex = confirmedNodesIndexes[i];
|
|
||||||
require(confirmedNodeIndex < DKG_SIZE, "Invalid node index");
|
|
||||||
// We add caller to the list of confirmations of each confirmed node
|
|
||||||
ritual.performance[confirmedNodeIndex].confirmedBy |= caller;
|
|
||||||
confirmedNodes[i] = ritual.performance[confirmedNodeIndex].node;
|
|
||||||
}
|
|
||||||
ritual.totalConfirmations++;
|
|
||||||
if (ritual.totalConfirmations == DKG_SIZE){
|
|
||||||
ritual.status = RitualStatus.COMPLETED;
|
|
||||||
emit RitualEnded(ritualId);
|
|
||||||
}
|
|
||||||
emit ConfirmationPosted(ritualId, msg.sender, confirmedNodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,189 +0,0 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
pragma solidity ^0.8.0;
|
|
||||||
|
|
||||||
import "./proxy/Upgradeable.sol";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title CoordinatorV3
|
|
||||||
* @notice Coordination layer for DKG-TDec
|
|
||||||
*/
|
|
||||||
contract CoordinatorV3 is Upgradeable {
|
|
||||||
|
|
||||||
// Ritual
|
|
||||||
event StartRitual(uint32 indexed ritualId, address[] nodes, address initiator);
|
|
||||||
event StartTranscriptRound(uint32 indexed ritualId);
|
|
||||||
event StartAggregationRound(uint32 indexed ritualId);
|
|
||||||
event EndRitual(uint32 indexed ritualId, RitualStatus status, address initiator);
|
|
||||||
|
|
||||||
// Node
|
|
||||||
event TranscriptPosted(uint32 indexed ritualId, address indexed node, bytes32 transcriptDigest);
|
|
||||||
event AggregationPosted(uint32 indexed ritualId, address indexed node, bytes32 aggregatedTranscriptDigest);
|
|
||||||
|
|
||||||
// Admin
|
|
||||||
event TimeoutChanged(uint32 oldTimeout, uint32 newTimeout);
|
|
||||||
event MaxDkgSizeChanged(uint32 oldSize, uint32 newSize);
|
|
||||||
|
|
||||||
enum RitualStatus {
|
|
||||||
AWAITING_TRANSCRIPTS,
|
|
||||||
AWAITING_AGGREGATIONS,
|
|
||||||
AWAITING_FINALIZATION,
|
|
||||||
TIMED_OUT,
|
|
||||||
INVALID,
|
|
||||||
FINALIZED
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Rite {
|
|
||||||
address node;
|
|
||||||
bool aggregated;
|
|
||||||
bytes transcript;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Ritual {
|
|
||||||
uint32 id;
|
|
||||||
address initiator;
|
|
||||||
uint32 dkgSize;
|
|
||||||
uint32 threshold;
|
|
||||||
bytes32 publicMaterial;
|
|
||||||
uint32 initTimestamp;
|
|
||||||
uint32 totalTranscripts;
|
|
||||||
uint32 totalAggregations;
|
|
||||||
RitualStatus status;
|
|
||||||
Rite[] rite;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ritual[] public rituals;
|
|
||||||
|
|
||||||
uint32 public timeout;
|
|
||||||
uint32 public maxDkgSize;
|
|
||||||
|
|
||||||
constructor(uint32 _timeout, uint32 _maxDkgSize) {
|
|
||||||
timeout = _timeout;
|
|
||||||
maxDkgSize = _maxDkgSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _checkActiveRitual(Ritual storage _ritual) internal {
|
|
||||||
uint32 delta = uint32(block.timestamp) - _ritual.initTimestamp;
|
|
||||||
if (delta > timeout) {
|
|
||||||
_ritual.status = RitualStatus.TIMED_OUT;
|
|
||||||
emit EndRitual(_ritual.id, _ritual.status); // penalty hook, missing nodes can be known at this stage
|
|
||||||
revert("Ritual timed out");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkActiveRitual(uint32 ritualId) external {
|
|
||||||
Ritual storage ritual = rituals[ritualId];
|
|
||||||
_checkActiveRitual(ritual);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTimeout(uint32 newTimeout) external onlyOwner {
|
|
||||||
uint32 oldTimeout = timeout;
|
|
||||||
timeout = newTimeout;
|
|
||||||
emit TimeoutChanged(oldTimeout, newTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setMaxDkgSize(uint32 newSize) external onlyOwner {
|
|
||||||
uint32 oldSize = maxDkgSize;
|
|
||||||
maxDkgSize = newSize;
|
|
||||||
emit MaxDkgSizeChanged(oldSize, newSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
function numberOfRituals() external view returns(uint256) {
|
|
||||||
return rituals.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRites(uint32 ritualId) external view returns(Rite[] memory) {
|
|
||||||
Rite[] memory rites = new Rite[](rituals[ritualId].rite.length);
|
|
||||||
for(uint32 i=0; i < rituals[ritualId].rite.length; i++){
|
|
||||||
rites[i] = rituals[ritualId].rite[i];
|
|
||||||
}
|
|
||||||
return rites;
|
|
||||||
}
|
|
||||||
|
|
||||||
function initiateRitual(address[] calldata nodes) external returns (uint32) {
|
|
||||||
require(nodes.length <= maxDkgSize, "Invalid number of nodes");
|
|
||||||
|
|
||||||
uint32 id = uint32(rituals.length);
|
|
||||||
Ritual storage ritual = rituals.push();
|
|
||||||
ritual.id = id;
|
|
||||||
ritual.initiator = msg.sender;
|
|
||||||
ritual.threshold = threshold;
|
|
||||||
ritual.dkgSize = uint32(nodes.length);
|
|
||||||
ritual.initTimestamp = uint32(block.timestamp);
|
|
||||||
ritual.status = RitualStatus.AWAITING_TRANSCRIPTS;
|
|
||||||
|
|
||||||
address previousNode = nodes[0];
|
|
||||||
ritual.rite[0].node = previousNode;
|
|
||||||
address currentNode;
|
|
||||||
for(uint256 i=1; i < nodes.length; i++){
|
|
||||||
currentNode = nodes[i];
|
|
||||||
ritual.rite[i].node = currentNode;
|
|
||||||
previousNode = currentNode;
|
|
||||||
// TODO: Check nodes are eligible (staking, etc)
|
|
||||||
}
|
|
||||||
|
|
||||||
emit StartRitual(id, nodes, msg.sender);
|
|
||||||
return ritual.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
function postTranscript(uint32 ritualId, uint256 nodeIndex, bytes calldata transcript) external {
|
|
||||||
Ritual storage ritual = rituals[ritualId];
|
|
||||||
require(ritual.rite[nodeIndex].node == msg.sender, "Node not part of ritual");
|
|
||||||
require(ritual.status == RitualStatus.AWAITING_TRANSCRIPTS, "Not waiting for transcripts");
|
|
||||||
require(ritual.rite[nodeIndex].transcript.length == 0, "Node already posted transcript");
|
|
||||||
require(ritual.rite[nodeIndex].aggregated == false, "Node already posted aggregation");
|
|
||||||
_checkActiveRitual(ritual);
|
|
||||||
|
|
||||||
// Nodes commit to their transcript
|
|
||||||
bytes32 transcriptDigest = keccak256(transcript);
|
|
||||||
ritual.rite[nodeIndex].transcript = transcript;
|
|
||||||
emit TranscriptPosted(ritualId, msg.sender, transcriptDigest);
|
|
||||||
ritual.totalTranscripts++;
|
|
||||||
|
|
||||||
// end round
|
|
||||||
if (ritual.totalTranscripts == ritual.dkgSize){
|
|
||||||
ritual.status = RitualStatus.AWAITING_AGGREGATIONS;
|
|
||||||
emit StartAggregationRound(ritualId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function postAggregation(uint32 ritualId, uint256 nodeIndex, bytes calldata aggregatedTranscripts) external {
|
|
||||||
Ritual storage ritual = rituals[ritualId];
|
|
||||||
require(ritual.status == RitualStatus.AWAITING_AGGREGATIONS, "Not waiting for confirmations");
|
|
||||||
require(ritual.rite[nodeIndex].node == msg.sender, "Node not part of ritual");
|
|
||||||
_checkActiveRitual(ritual);
|
|
||||||
|
|
||||||
// nodes commit to their aggregation result
|
|
||||||
bytes32 aggregatedTranscriptDigest = keccak256(aggregatedTranscripts);
|
|
||||||
ritual.rite[nodeIndex].transcript = aggregatedTranscriptDigest;
|
|
||||||
ritual.rite[nodeIndex].aggregated = true;
|
|
||||||
emit AggregationPosted(ritualId, msg.sender, aggregatedTranscripts);
|
|
||||||
ritual.totalAggregations++;
|
|
||||||
|
|
||||||
// end round
|
|
||||||
if (ritual.totalAggregations == ritual.dkgSize){
|
|
||||||
ritual.status = RitualStatus.AWAITING_FINALIZATION;
|
|
||||||
emit EndRitual(ritualId, ritual.status, ritual.initiator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function finalizeRitual(uint32 ritualId) public {
|
|
||||||
Ritual storage ritual = rituals[ritualId];
|
|
||||||
require(ritual.status == RitualStatus.AWAITING_FINALIZATION, 'ritual cannot be finalized');
|
|
||||||
|
|
||||||
bytes32 firstRiteDigest = keccak256(ritual.rite[0].transcript);
|
|
||||||
for(uint32 i=1; i < ritual.rite.length; i++){
|
|
||||||
bytes32 currentRiteDigest = keccak256(ritual.rite[i].transcript);
|
|
||||||
if (firstRiteDigest != currentRiteDigest) {
|
|
||||||
ritual.status = RitualStatus.INVALID;
|
|
||||||
emit EndRitual(ritualId, ritual.status, ritual.initiator);
|
|
||||||
revert('aggregated transcripts do not match');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ritual.publicMaterial = firstRiteDigest;
|
|
||||||
ritual.status = RitualStatus.FINALIZED;
|
|
||||||
emit EndRitual(ritualId, ritual.status, ritual.initiator);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue