@@ -36,6 +36,12 @@ contract Quorum is IQuorum, AbstractConsensus {
36
36
BitMaps.BitMap inFavorById;
37
37
}
38
38
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
+
39
45
/// @notice Votes indexed by application contract address,
40
46
/// last processed block number and outputs Merkle root.
41
47
/// @dev See the `numOfValidatorsInFavorOf` and `isValidatorInFavorOf` functions.
@@ -77,7 +83,23 @@ contract Quorum is IQuorum, AbstractConsensus {
77
83
Votes storage votes =
78
84
_getVotes (appContract, lastProcessedBlockNumber, outputsMerkleRoot);
79
85
86
+ Votes storage allVotes = _getAllVotes (appContract, lastProcessedBlockNumber);
87
+
88
+ // Skip storage changes if validator already voted
89
+ // for the same exact claim before
80
90
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
81
103
votes.inFavorById.set (id);
82
104
if (++ votes.inFavorCount == 1 + _numOfValidators / 2 ) {
83
105
_acceptClaim (appContract, lastProcessedBlockNumber, outputsMerkleRoot);
@@ -97,6 +119,21 @@ contract Quorum is IQuorum, AbstractConsensus {
97
119
return _validatorById[id];
98
120
}
99
121
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
+
100
137
function numOfValidatorsInFavorOf (
101
138
address appContract ,
102
139
uint256 lastProcessedBlockNumber ,
@@ -117,6 +154,18 @@ contract Quorum is IQuorum, AbstractConsensus {
117
154
.get (id);
118
155
}
119
156
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
+
120
169
/// @notice Get a `Votes` structure from storage from a given claim.
121
170
/// @param appContract The application contract address
122
171
/// @param lastProcessedBlockNumber The number of the last processed block
0 commit comments