Skip to content

Commit

Permalink
perf: optimize quorum using bitmaps
Browse files Browse the repository at this point in the history
  • Loading branch information
ZzzzHui committed Sep 22, 2023
1 parent 33b4453 commit 5e4ca6a
Showing 1 changed file with 17 additions and 10 deletions.
27 changes: 17 additions & 10 deletions onchain/rollups/contracts/consensus/quorum/Quorum.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
pragma solidity ^0.8.8;

import {PaymentSplitter} from "@openzeppelin/contracts/finance/PaymentSplitter.sol";
import {BitMaps} from "@openzeppelin/contracts/utils/structs/BitMaps.sol";

import {AbstractConsensus} from "../AbstractConsensus.sol";
import {IConsensus} from "../IConsensus.sol";
Expand All @@ -12,31 +13,35 @@ import {IHistory} from "../../history/IHistory.sol";
/// @title Quorum consensus
/// @notice A consensus model controlled by a small set of addresses, the validators.
/// In this version, the validator set is immutable.
/// @dev This contract uses OpenZeppelin `PaymentSplitter`.
/// For more information on `PaymentSplitter`, please consult OpenZeppelin's official documentation.
/// @dev This contract uses OpenZeppelin `PaymentSplitter` and `BitMaps`.
/// For more information on those, please consult OpenZeppelin's official documentation.
contract Quorum is AbstractConsensus, PaymentSplitter {
using BitMaps for BitMaps.BitMap;

/// @notice The history contract.
/// @dev See the `getHistory` function.
IHistory internal immutable history;

// Quorum members
// Map an address to true if it's a validator
mapping(address => bool) public validators;
// Map an address to its index in the validator set.
// The first validator has index 1. Thus, index 0 means the address is not in the validator set.
mapping(address => uint256) public validatorIndex;
uint256 public immutable quorumSize;

// Quorum votes
struct Voted {
// how many has voted yea
uint256 count;
// Map an address to true if it has voted yea
mapping(address => bool) voted;
// use BitMap to record who has voted
BitMaps.BitMap votedBitMap;
}
// Map a claim to struct Voted
mapping(bytes => Voted) internal yeas;

/// @notice Raised if not a validator
error OnlyValidator();
modifier onlyValidator() {
if (!validators[msg.sender]) {
if (validatorIndex[msg.sender] == 0) {
revert OnlyValidator();
}
_;
Expand All @@ -46,14 +51,15 @@ contract Quorum is AbstractConsensus, PaymentSplitter {
/// @param _validators the list of validators
/// @param _shares the list of shares
/// @param _history the history contract
/// @dev PaymentSplitter checks for duplicates in _validators
constructor(
address[] memory _validators,
uint256[] memory _shares,
IHistory _history
) PaymentSplitter(_validators, _shares) {
// Add the array of validators into the quorum
for (uint256 i; i < _validators.length; ++i) {
validators[_validators[i]] = true;
validatorIndex[_validators[i]] = i + 1; // index starts from 1
}
quorumSize = _validators.length;
history = _history;
Expand All @@ -70,10 +76,11 @@ contract Quorum is AbstractConsensus, PaymentSplitter {
/// its current history contract.
function submitClaim(bytes calldata _claimData) external onlyValidator {
Voted storage claimYeas = yeas[_claimData];
uint256 index = validatorIndex[msg.sender];

// If the msg.sender hasn't submitted the same claim before
if (!claimYeas.voted[msg.sender]) {
claimYeas.voted[msg.sender] = true;
if (!claimYeas.votedBitMap.get(index)) {
claimYeas.votedBitMap.set(index);

// If this claim has now just over half of the quorum's votes,
// then we can submit it to the history contract.
Expand Down

0 comments on commit 5e4ca6a

Please sign in to comment.