Skip to content
This repository has been archived by the owner on Mar 12, 2024. It is now read-only.

Quorum consensus #204

Closed
wants to merge 1 commit into from
Closed

Quorum consensus #204

wants to merge 1 commit into from

Conversation

guidanoli
Copy link
Contributor

@guidanoli guidanoli self-assigned this Jul 25, 2023
@guidanoli
Copy link
Contributor Author

guidanoli commented Jul 25, 2023

Here, I'm using three really handy OpenZeppelin contracts:

/// @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 {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that we have a limited set of validators, but I'm still not comfortable pushing transfers like this in a for loop.

Maybe we can use the payment splitter from Open Zeppelin.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, I will take a look into that. Thanks!


// If this claim already has half of the quorum's approval,
// then we can submit it to the history contract.
if (numOfVotesInFavour > quorumSize / 2) {
Copy link

@pedroargento pedroargento Jul 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be a parameter instead of a majority.

Also, should not be x/2 + 1? If there are three validators and we want the majority it results in a 1 of 3.

What happens when the quorum was reached and another validator publish the claim? Is there a risk of trying to add multiple times to history?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if quorumSize is 3 you need 2, division is integer, right? And it's >, not >=

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is true. I misread as >=

Copy link

@pedroargento pedroargento Jul 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, if we change to == we solve the problem of including multiple times to the history. It would only add to History when the second validator (in this 2 of 3 example) post and ignore future validators.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current implementation of History prevents its owner from claiming the same data twice. :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I see what you mean. Maybe we should change this to numOfVotesInFavour == 1 + quorumSize / 2?

@tuler
Copy link
Member

tuler commented Jul 25, 2023

I think the tricky part is that there is no coordination between the multiple nodes. They will submit claims at the exact same time. What would happen in that case? Imagine the "same" transaction, coming from multiple nodes, in the same block. Would it work?

@guidanoli
Copy link
Contributor Author

Rebased and applied @pedroargento suggestion to use PaymentSplitter.

@guidanoli
Copy link
Contributor Author

Applied @pedroargento's suggestion of using == instead of > for checking majority, and avoid submitting the same claim to the history twice.

@pedroargento
Copy link

Doesn't it make sense to make the number of necessary votes a parameter? Maybe one DApp wants to have a super majority or wants to accept only 2 signatures even with a lot of possible validators.

@guidanoli
Copy link
Contributor Author

Doesn't it make sense to make the number of necessary votes a parameter? Maybe one DApp wants to have a super majority or wants to accept only 2 signatures even with a lot of possible validators.

If we see a demand for such fine-grained configuration, sure.
For now, a simple majority scheme should suffice for an MVP.

@guidanoli
Copy link
Contributor Author

I think the tricky part is that there is no coordination between the multiple nodes. They will submit claims at the exact same time. What would happen in that case? Imagine the "same" transaction, coming from multiple nodes, in the same block. Would it work?

Great point. What will happen now that I've changed > to ==, is that it will only submit a claim once. Of course, it needs testing, but it should work (in my head).

@ZzzzHui
Copy link
Contributor

ZzzzHui commented Aug 10, 2023

By using PaymentSplitter, the set of validators and shares are fixed at the constructor and cannot be changed later on. Do we wanna assume the quorum to be fixed?

@guidanoli
Copy link
Contributor Author

By using PaymentSplitter, the set of validators and shares are fixed at the constructor and cannot be changed later on. Do we wanna assume the quorum to be fixed?

Yes, the PaymentSplitter contract assumes the set of shares is constant.

@ZzzzHui
Copy link
Contributor

ZzzzHui commented Aug 17, 2023

I think we can optimize the codes a bit:

  1. Currently, we call AccessControlEnumerable.grantRole in the constructor to add members into the quorum. AccessControlEnumerable is a combination of AccessControl and EnumerableSet. Since we don't have a dynamic quorum, we don't actually need to maintain a EnumerableSet. With the current codes, the only time we call this EnumerableSet is to check the size of the quorum, which can simply be done with a uint256. As for access control, we can simplify to a mapping(address => bool) members to indicate whether it's a member or not. The complete set of members is already shown in the constructor parameter, and we could emit an event if needed.
  2. The 2nd place that we use EnumerableSet is to record the set of members that voted yeas. Similarly, we don't actually need to maintain the set, we only need to record who has already voted yea. So instead of using this struct from EnumerableSet
struct Set {
        bytes32[] _values;
        mapping(bytes32 => uint256) _indexes;
    }

, we can simplify it to

struct Yeas {
        uint256 count;
        mapping(address => bool) voted;
}

Furthermore, if we want to optimize further. We can use a uint256 and split it into 2 parts. | who_has_voted(248 bit) | count(8 bit) | This way we can record up to 248 members, including who has voted and the current count of how many has voted. If there could be more than 248 members, then we could make it an array(mapping) of uint256s. P.S., in part 1, we mentioned using mapping(address => bool) members to indicate whether it's a member. Now it becomes mapping(address => uint256) members to indicate a member's bit position (>0) in the uint256 struct, with 0 indicating not a member.
The drawback is the code becomes more complex for sure. Let me know what you think and we can work on this together :)

Copy link

@xdaniortega xdaniortega left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good code work!

Here are my thoughts:
We trying to define here is a multi-signature consensus model so on the definition of the members of the signature validator is a bit inaccurate as members (or just a shared/distributed key) may not be validating the the claim and just signing. We could rename it to MEMBER_ROLE or SIGNER_ROLE.

I also remember that someone mentioned that Quorum can bring confusion to what this consensus model is really doing, and maybe the discussion can be retaken here. We can consider simplifying the name of this model to multisig or similar.

@guidanoli
Copy link
Contributor Author

@xdaniortega Thanks for the feedback!
And let us bring the discussion to the original issue, so that everyone can see and participate. :-)

@guidanoli
Copy link
Contributor Author

This PR has been migrated to cartesi/rollups-contracts#49.

@guidanoli guidanoli closed this Aug 28, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Design a contract that implements a Quorum consensus model
5 participants