This repository has been archived by the owner on Mar 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
133 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
// Copyright Cartesi Pte. Ltd. | ||
|
||
// SPDX-License-Identifier: Apache-2.0 | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
// this file except in compliance with the License. You may obtain a copy of the | ||
// License at http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
// Unless required by applicable law or agreed to in writing, software distributed | ||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
// specific language governing permissions and limitations under the License. | ||
|
||
pragma solidity ^0.8.8; | ||
|
||
import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; | ||
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; | ||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
|
||
import {AbstractConsensus} from "../AbstractConsensus.sol"; | ||
import {IConsensus} from "../IConsensus.sol"; | ||
import {IHistory} from "../../history/IHistory.sol"; | ||
|
||
/// @title Quorum consensus | ||
/// @notice A consensus model controlled by a small set of addresses, the validators. | ||
/// @dev This contract inherits from `AbstractConsensus` and OpenZeppelin's `AccessControlEnumerable` contract. | ||
/// For more information on `AccessControlEnumerable`, please consult OpenZeppelin's official documentation. | ||
contract Quorum is AbstractConsensus, AccessControlEnumerable { | ||
using EnumerableSet for EnumerableSet.AddressSet; | ||
|
||
/// @notice The validator role. | ||
/// @dev Only validators can submit claims. | ||
bytes32 public constant VALIDATOR_ROLE = keccak256("VALIDATOR_ROLE"); | ||
|
||
/// @notice The history contract. | ||
/// @dev See the `getHistory` function. | ||
IHistory internal immutable history; | ||
|
||
/// @notice For each claim, the set of validators that agree | ||
/// that it should be submitted to the history contract. | ||
mapping(bytes => EnumerableSet.AddressSet) internal yeas; | ||
|
||
/// @notice An ERC-20 token transfer failed (`transfer` returned `false`). | ||
error ERC20TokenTransferFailed(); | ||
|
||
/// @notice Construct a Quorum consensus | ||
/// @param _validators the list of validators | ||
constructor(IHistory _history, address[] memory _validators) { | ||
// Iterate through the array of validators, | ||
// and grant to each the validator role. | ||
for (uint256 i; i < _validators.length; ++i) { | ||
grantRole(VALIDATOR_ROLE, _validators[i]); | ||
} | ||
|
||
// Set history. | ||
history = _history; | ||
} | ||
|
||
/// @notice Get the history contract. | ||
/// @return The history contract | ||
function getHistory() external view returns (IHistory) { | ||
return history; | ||
} | ||
|
||
/// @notice Get a claim from the current history. | ||
/// The encoding of `_proofContext` might vary depending on the | ||
/// implementation of the current history contract. | ||
/// @inheritdoc IConsensus | ||
function getClaim( | ||
address _dapp, | ||
bytes calldata _proofContext | ||
) external view override returns (bytes32, uint256, uint256) { | ||
return history.getClaim(_dapp, _proofContext); | ||
} | ||
|
||
/// @notice Submits a claim for voting. | ||
/// If this is the claim that reaches the majority, then | ||
/// the claim is submitted to the history contract. | ||
/// The encoding of `_claimData` might vary depending on the | ||
/// implementation of the current history contract. | ||
/// @param _claimData Data for submitting a claim | ||
/// @dev Can only be called by a validator, | ||
/// and the `Quorum` contract must have ownership over | ||
/// its current history contract. | ||
function submitClaim( | ||
bytes calldata _claimData | ||
) external onlyRole(VALIDATOR_ROLE) { | ||
// Get the set of validators in favour of the claim | ||
EnumerableSet.AddressSet storage claimYeas = yeas[_claimData]; | ||
|
||
// Add the message sender to such set. | ||
claimYeas.add(msg.sender); | ||
|
||
// Get number of validators in favour of the claim. | ||
uint256 numOfVotesInFavour = claimYeas.length(); | ||
|
||
// Get the number of validators in the quorum. | ||
uint256 quorumSize = getRoleMemberCount(VALIDATOR_ROLE); | ||
|
||
// If this claim already has half of the quorum's approval, | ||
// then we can submit it to the history contract. | ||
if (numOfVotesInFavour > quorumSize / 2) { | ||
history.submitClaim(_claimData); | ||
} | ||
} | ||
|
||
/// @notice Equally share all tokens from some ERC-20 contract | ||
/// amongst all validators in the quorum. | ||
/// @param _token The token contract | ||
function shareERC20Tokens(IERC20 _token) external { | ||
// Get the total amount of ERC-20 tokens held by the quorum. | ||
uint256 balance = _token.balanceOf(address(this)); | ||
|
||
// Get the number of validators in the quorum. | ||
uint256 quorumSize = getRoleMemberCount(VALIDATOR_ROLE); | ||
|
||
// Calculate the share of ERC-20 tokens for each validator. | ||
uint256 tokensPerValidator = balance / quorumSize; | ||
|
||
// Iterate through the validator set. | ||
for (uint256 i; i < quorumSize; ++i) { | ||
// Get the i-th validator. | ||
address validator = getRoleMember(VALIDATOR_ROLE, i); | ||
|
||
// Transfer the share of ERC-20 tokens to the i-th validator. | ||
bool success = _token.transfer(validator, tokensPerValidator); | ||
|
||
// If the transfer fails, revert. | ||
if (!success) { | ||
revert ERC20TokenTransferFailed(); | ||
} | ||
} | ||
} | ||
} |