Skip to content

Commit ffc9348

Browse files
committed
feat: strong claim acceptance for quorum
1 parent 62a4e5c commit ffc9348

File tree

4 files changed

+277
-19
lines changed

4 files changed

+277
-19
lines changed

.changeset/petite-numbers-read.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cartesi/rollups": major
3+
---
4+
5+
Avoid conflicting claims in Quorum

src/consensus/quorum/IQuorum.sol

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,27 @@ interface IQuorum is IConsensus {
2727
/// @dev Invalid IDs map to address zero.
2828
function validatorById(uint256 id) external view returns (address);
2929

30+
/// @notice Get the number of validators in favor of any claim in a given epoch.
31+
/// @param appContract The application contract address
32+
/// @param lastProcessedBlockNumber The number of the last processed block
33+
/// @return Number of validators in favor of any claim in the epoch
34+
function numOfValidatorsInFavorOfAnyClaimInEpoch(
35+
address appContract,
36+
uint256 lastProcessedBlockNumber
37+
) external view returns (uint256);
38+
39+
/// @notice Check whether a validator is in favor of any claim in a given epoch.
40+
/// @param appContract The application contract address
41+
/// @param lastProcessedBlockNumber The number of the last processed block
42+
/// @param id The ID of the validator
43+
/// @return Whether validator is in favor of any claim in the epoch
44+
/// @dev Assumes the provided ID is valid.
45+
function isValidatorInFavorOfAnyClaimInEpoch(
46+
address appContract,
47+
uint256 lastProcessedBlockNumber,
48+
uint256 id
49+
) external view returns (bool);
50+
3051
/// @notice Get the number of validators in favor of a claim.
3152
/// @param appContract The application contract address
3253
/// @param lastProcessedBlockNumber The number of the last processed block

src/consensus/quorum/Quorum.sol

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ contract Quorum is IQuorum, AbstractConsensus {
3636
BitMaps.BitMap inFavorById;
3737
}
3838

39+
/// @notice Votes indexed by application contract address,
40+
/// and last processed block number.
41+
/// @dev See the `numOfValidatorsInFavorOfAnyClaimInEpoch`
42+
/// and `isValidatorInFavorOfAnyClaimInEpoch` functions.
43+
mapping(address => mapping(uint256 => Votes)) private _allVotes;
44+
3945
/// @notice Votes indexed by application contract address,
4046
/// last processed block number and outputs Merkle root.
4147
/// @dev See the `numOfValidatorsInFavorOf` and `isValidatorInFavorOf` functions.
@@ -77,7 +83,23 @@ contract Quorum is IQuorum, AbstractConsensus {
7783
Votes storage votes =
7884
_getVotes(appContract, lastProcessedBlockNumber, outputsMerkleRoot);
7985

86+
Votes storage allVotes = _getAllVotes(appContract, lastProcessedBlockNumber);
87+
88+
// Skip storage changes if validator already voted
89+
// for the same exact claim before
8090
if (!votes.inFavorById.get(id)) {
91+
// Revert if validator has submitted another claim for the same epoch
92+
require(
93+
!allVotes.inFavorById.get(id),
94+
NotFirstClaim(appContract, lastProcessedBlockNumber)
95+
);
96+
97+
// Register vote (for any claim in the epoch)
98+
allVotes.inFavorById.set(id);
99+
++allVotes.inFavorCount;
100+
101+
// Register vote (for the specific claim)
102+
// and accept the claim if a majority has been reached
81103
votes.inFavorById.set(id);
82104
if (++votes.inFavorCount == 1 + _numOfValidators / 2) {
83105
_acceptClaim(appContract, lastProcessedBlockNumber, outputsMerkleRoot);
@@ -97,6 +119,21 @@ contract Quorum is IQuorum, AbstractConsensus {
97119
return _validatorById[id];
98120
}
99121

122+
function numOfValidatorsInFavorOfAnyClaimInEpoch(
123+
address appContract,
124+
uint256 lastProcessedBlockNumber
125+
) external view override returns (uint256) {
126+
return _getAllVotes(appContract, lastProcessedBlockNumber).inFavorCount;
127+
}
128+
129+
function isValidatorInFavorOfAnyClaimInEpoch(
130+
address appContract,
131+
uint256 lastProcessedBlockNumber,
132+
uint256 id
133+
) external view override returns (bool) {
134+
return _getAllVotes(appContract, lastProcessedBlockNumber).inFavorById.get(id);
135+
}
136+
100137
function numOfValidatorsInFavorOf(
101138
address appContract,
102139
uint256 lastProcessedBlockNumber,
@@ -117,6 +154,18 @@ contract Quorum is IQuorum, AbstractConsensus {
117154
.get(id);
118155
}
119156

157+
/// @notice Get a `Votes` structure from storage from a given epoch.
158+
/// @param appContract The application contract address
159+
/// @param lastProcessedBlockNumber The number of the last processed block
160+
/// @return The `Votes` structure related to all claims in a given epoch
161+
function _getAllVotes(address appContract, uint256 lastProcessedBlockNumber)
162+
internal
163+
view
164+
returns (Votes storage)
165+
{
166+
return _allVotes[appContract][lastProcessedBlockNumber];
167+
}
168+
120169
/// @notice Get a `Votes` structure from storage from a given claim.
121170
/// @param appContract The application contract address
122171
/// @param lastProcessedBlockNumber The number of the last processed block

0 commit comments

Comments
 (0)