temporary inclusion of Coordinatorv3 from baacbcd529cace1451dbaab84c0acb4beebea384

pull/3091/head
Kieran Prasch 2023-02-10 12:47:06 -08:00
parent bc77489e94
commit cf8749be38
1 changed files with 117 additions and 108 deletions

View File

@ -2,174 +2,183 @@
pragma solidity ^0.8.0;
import "./proxy/Upgradeable.sol";
/**
* @title CoordinatorV1
* @title CoordinatorV3
* @notice Coordination layer for DKG-TDec
*/
contract CoordinatorV1 {
contract CoordinatorV3 is Upgradeable {
uint32 public constant DKG_SIZE = 8;
uint32 public TIMEOUT = 9600;
event StartRitual(uint32 indexed ritualId, address[] nodes);
// Ritual
event StartRitual(uint32 indexed ritualId, address[] nodes, address initiator);
event StartTranscriptRound(uint32 indexed ritualId);
event StartConfirmationRound(uint32 indexed ritualId);
event RitualEnded(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 ConfirmationPosted(uint32 indexed ritualId, address indexed node, address[] confirmedNodes);
event AggregationPosted(uint32 indexed ritualId, address indexed node, bytes32 aggregatedTranscriptDigest);
event TimeoutChanged(uint32 timeout);
event DkgSizeChanged(uint8 dkgSize);
// Admin
event TimeoutChanged(uint32 oldTimeout, uint32 newTimeout);
event MaxDkgSizeChanged(uint32 oldSize, uint32 newSize);
enum RitualStatus {
WAITING_FOR_CHECKINS,
WAITING_FOR_TRANSCRIPTS,
WAITING_FOR_CONFIRMATIONS,
COMPLETED,
FAILED
WAITING_FOR_AGGREGATIONS,
WAITING_FOR_FINALIZATION,
FAILED_TIMEOUT,
FAILED_INVALID_TRANSCRIPTS,
FINALIZED
}
// TODO: Find better name
struct Performance {
struct Rite {
address node;
uint32 checkinTimestamp;
uint96 confirmedBy;
bytes32 transcript;
bool aggregated;
bytes transcript;
}
struct Ritual {
uint32 id;
address initiator;
uint32 dkgSize;
uint32 initTimestamp;
uint32 totalCheckins;
uint32 totalTranscripts;
uint32 totalConfirmations;
uint32 totalAggregations;
uint32 threshold;
bytes32 publicMaterial;
RitualStatus status;
Performance[DKG_SIZE] performance;
Rite[] rite;
}
Ritual[] public rituals;
function numberOfRituals() external view returns(uint256){
uint32 public timeout;
uint32 public maxDkgSize;
constructor(uint32 _timeout) {
timeout = _timeout;
maxDkgSize = 64; // TODO Who knows? https://www.youtube.com/watch?v=hzqFmXZ8tOE&ab_channel=Protoje
}
function _checkActiveRitual(Ritual storage _ritual) internal {
uint32 delta = uint32(block.timestamp) - _ritual.initTimestamp;
if (delta > timeout) {
_ritual.status = RitualStatus.FAILED_TIMEOUT;
emit EndRitual(_ritual.id, _ritual.status); // penalty hook, missing nodes can be known at this stage
revert("Ritual timed out");
}
}
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 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];
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 performances;
return rites;
}
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");
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.WAITING_FOR_CHECKINS;
ritual.totalTranscripts = 0;
ritual.totalConfirmations = 0;
ritual.totalCheckins = 0;
ritual.status = RitualStatus.WAITING_FOR_TRANSCRIPTS;
address previousNode = nodes[0];
ritual.performance[0].node = previousNode;
ritual.rite[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;
ritual.rite[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);
}
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.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");
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.performance[nodeIndex].transcript = transcriptDigest;
ritual.totalTranscripts++;
if (ritual.totalTranscripts == DKG_SIZE){
ritual.status = RitualStatus.WAITING_FOR_CONFIRMATIONS;
emit StartConfirmationRound(ritualId);
}
ritual.rite[nodeIndex].transcript = transcript;
emit TranscriptPosted(ritualId, msg.sender, transcriptDigest);
ritual.totalTranscripts++;
// end round
if (ritual.totalTranscripts == ritual.dkgSize){
ritual.status = RitualStatus.WAITING_FOR_AGGREGATIONS;
emit StartAggregationRound(ritualId);
}
}
function postConfirmation(uint32 ritualId, uint256 nodeIndex, uint256[] calldata confirmedNodesIndexes) external {
function postAggregation(uint32 ritualId, uint256 nodeIndex, bytes calldata aggregatedTranscripts) 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(ritual.status == RitualStatus.WAITING_FOR_AGGREGATIONS, "Not waiting for confirmations");
require(ritual.rite[nodeIndex].node == msg.sender, "Node not part of ritual");
_checkActiveRitual(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");
// 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.WAITING_FOR_FINALIZATION;
emit EndRitual(ritualId, ritual.status, ritual.initiator);
}
}
function finalizeRitual(uint32 ritualId) public {
Ritual storage ritual = rituals[ritualId];
require(ritual.status == RitualStatus.WAITING_FOR_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.FAILED_INVALID_TRANSCRIPTS;
emit EndRitual(ritualId, ritual.status, ritual.initiator);
revert('aggregated transcripts do not match');
}
}
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);
ritual.publicMaterial = firstRiteDigest;
ritual.status = RitualStatus.FINALIZED;
emit EndRitual(ritualId, ritual.status, ritual.initiator);
}
}