1
1
// SPDX-License-Identifier: MIT
2
2
3
- pragma solidity ^ 0.8.24 ;
3
+ pragma solidity ^ 0.8.28 ;
4
4
5
5
import {DisputeKitClassicBase, KlerosCore} from "./DisputeKitClassicBase.sol " ;
6
6
@@ -14,6 +14,19 @@ import {DisputeKitClassicBase, KlerosCore} from "./DisputeKitClassicBase.sol";
14
14
contract DisputeKitShutter is DisputeKitClassicBase {
15
15
string public constant override version = "0.12.0 " ;
16
16
17
+ // ************************************* //
18
+ // * Storage * //
19
+ // ************************************* //
20
+
21
+ mapping (uint256 localDisputeID = > mapping (uint256 localRoundID = > mapping (uint256 voteID = > bytes32 recoveryCommitment )))
22
+ public recoveryCommitments;
23
+
24
+ // ************************************* //
25
+ // * Transient Storage * //
26
+ // ************************************* //
27
+
28
+ bool transient callerIsJuror;
29
+
17
30
// ************************************* //
18
31
// * Events * //
19
32
// ************************************* //
@@ -22,12 +35,14 @@ contract DisputeKitShutter is DisputeKitClassicBase {
22
35
/// @param _coreDisputeID The identifier of the dispute in the Arbitrator contract.
23
36
/// @param _juror The address of the juror casting the vote commitment.
24
37
/// @param _commit The commitment hash.
38
+ /// @param _recoveryCommit The commitment hash without the justification.
25
39
/// @param _identity The Shutter identity used for encryption.
26
40
/// @param _encryptedVote The Shutter encrypted vote.
27
41
event CommitCastShutter (
28
42
uint256 indexed _coreDisputeID ,
29
43
address indexed _juror ,
30
44
bytes32 indexed _commit ,
45
+ bytes32 _recoveryCommit ,
31
46
bytes32 _identity ,
32
47
bytes _encryptedVote
33
48
);
@@ -74,17 +89,29 @@ contract DisputeKitShutter is DisputeKitClassicBase {
74
89
/// @param _coreDisputeID The ID of the dispute in Kleros Core.
75
90
/// @param _voteIDs The IDs of the votes.
76
91
/// @param _commit The commitment hash including the justification.
92
+ /// @param _recoveryCommit The commitment hash without the justification.
77
93
/// @param _identity The Shutter identity used for encryption.
78
94
/// @param _encryptedVote The Shutter encrypted vote.
79
95
function castCommitShutter (
80
96
uint256 _coreDisputeID ,
81
97
uint256 [] calldata _voteIDs ,
82
98
bytes32 _commit ,
99
+ bytes32 _recoveryCommit ,
83
100
bytes32 _identity ,
84
101
bytes calldata _encryptedVote
85
102
) external notJumped (_coreDisputeID) {
103
+ if (_recoveryCommit == bytes32 (0 )) revert EmptyRecoveryCommit ();
104
+
105
+ uint256 localDisputeID = coreDisputeIDToLocal[_coreDisputeID];
106
+ Dispute storage dispute = disputes[localDisputeID];
107
+ uint256 localRoundID = dispute.rounds.length - 1 ;
108
+ for (uint256 i = 0 ; i < _voteIDs.length ; i++ ) {
109
+ recoveryCommitments[localDisputeID][localRoundID][_voteIDs[i]] = _recoveryCommit;
110
+ }
111
+
112
+ // `_castCommit()` ensures that the caller owns the vote
86
113
_castCommit (_coreDisputeID, _voteIDs, _commit);
87
- emit CommitCastShutter (_coreDisputeID, msg .sender , _commit, _identity, _encryptedVote);
114
+ emit CommitCastShutter (_coreDisputeID, msg .sender , _commit, _recoveryCommit, _identity, _encryptedVote);
88
115
}
89
116
90
117
function castVoteShutter (
@@ -97,8 +124,12 @@ contract DisputeKitShutter is DisputeKitClassicBase {
97
124
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
98
125
address juror = dispute.rounds[dispute.rounds.length - 1 ].votes[_voteIDs[0 ]].account;
99
126
100
- // _castVote() ensures that all the _voteIDs do belong to `juror`
127
+ callerIsJuror = juror == msg .sender ;
128
+
129
+ // `_castVote()` ensures that all the `_voteIDs` do belong to `juror`
101
130
_castVote (_coreDisputeID, _voteIDs, _choice, _salt, _justification, juror);
131
+
132
+ callerIsJuror = false ;
102
133
}
103
134
104
135
// ************************************* //
@@ -116,8 +147,37 @@ contract DisputeKitShutter is DisputeKitClassicBase {
116
147
uint256 _choice ,
117
148
uint256 _salt ,
118
149
string memory _justification
119
- ) public pure override returns (bytes32 ) {
120
- bytes32 justificationHash = keccak256 (bytes (_justification));
121
- return keccak256 (abi.encode (_choice, _salt, justificationHash));
150
+ ) public view override returns (bytes32 ) {
151
+ if (callerIsJuror) {
152
+ // Caller is the juror, hash without `_justification` to facilitate recovery.
153
+ return keccak256 (abi.encodePacked (_choice, _salt));
154
+ } else {
155
+ // Caller is not the juror, hash with `_justification`.
156
+ bytes32 justificationHash = keccak256 (bytes (_justification));
157
+ return keccak256 (abi.encode (_choice, _salt, justificationHash));
158
+ }
159
+ }
160
+
161
+ /// @dev Returns the expected vote hash for a given vote.
162
+ /// @param _localDisputeID The ID of the dispute in the Dispute Kit.
163
+ /// @param _localRoundID The ID of the round in the Dispute Kit.
164
+ /// @param _voteID The ID of the vote.
165
+ /// @return The expected vote hash.
166
+ function getExpectedVoteHash (
167
+ uint256 _localDisputeID ,
168
+ uint256 _localRoundID ,
169
+ uint256 _voteID
170
+ ) internal view override returns (bytes32 ) {
171
+ if (callerIsJuror) {
172
+ return recoveryCommitments[_localDisputeID][_localRoundID][_voteID];
173
+ } else {
174
+ return disputes[_localDisputeID].rounds[_localRoundID].votes[_voteID].commit;
175
+ }
122
176
}
177
+
178
+ // ************************************* //
179
+ // * Errors * //
180
+ // ************************************* //
181
+
182
+ error EmptyRecoveryCommit ();
123
183
}
0 commit comments