Skip to content

Commit 87e5759

Browse files
fix(ShutterDK): replace recovery with justification commit
1 parent 6b7e365 commit 87e5759

File tree

4 files changed

+168
-186
lines changed

4 files changed

+168
-186
lines changed

contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,8 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
341341
if (round.votes[_voteIDs[i]].account != _juror) revert JurorHasToOwnTheVote();
342342
if (hiddenVotes && _getExpectedVoteHash(localDisputeID, localRoundID, _voteIDs[i]) != actualVoteHash)
343343
revert HashDoesNotMatchHiddenVoteCommitment();
344+
if (!_checkJustification(localDisputeID, localRoundID, _voteIDs[i], _justification, _salt))
345+
revert WrongJustification();
344346
if (round.votes[_voteIDs[i]].voted) revert VoteAlreadyCast();
345347
round.votes[_voteIDs[i]].choice = _choice;
346348
round.votes[_voteIDs[i]].voted = true;
@@ -724,6 +726,18 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
724726
return disputes[_localDisputeID].rounds[_localRoundID].votes[_voteID].commit;
725727
}
726728

729+
/// @notice Returns true if submitted justification matches. Only used by specific Dispute Kits (eg Shutter).
730+
/// @return Whether justification matches or not.
731+
function _checkJustification(
732+
uint256 /*_localDisputeID*/,
733+
uint256 /*_localRoundID*/,
734+
uint256 /*_voteID*/,
735+
string memory /*_justification*/,
736+
uint256 /*_salt*/
737+
) internal view virtual returns (bool) {
738+
return true;
739+
}
740+
727741
/// @notice Checks that the chosen address satisfies certain conditions for being drawn.
728742
///
729743
/// @dev No need to check the minStake requirement here because of the implicit staking in parent courts.
@@ -771,4 +785,5 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
771785
error AppealFeeIsAlreadyPaid();
772786
error DisputeNotResolved();
773787
error CoreIsPaused();
788+
error WrongJustification();
774789
}

contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol

Lines changed: 31 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase {
3434
// * Storage * //
3535
// ************************************* //
3636

37-
mapping(uint256 localDisputeID => mapping(uint256 localRoundID => mapping(uint256 voteID => bytes32 recoveryCommitment)))
38-
public recoveryCommitments;
37+
mapping(uint256 localDisputeID => mapping(uint256 localRoundID => mapping(uint256 voteID => bytes32 justificationCommitment)))
38+
public justificationCommitments;
3939

4040
// ************************************* //
4141
// * Transient Storage * //
@@ -50,15 +50,15 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase {
5050
/// @dev Emitted when a vote is cast.
5151
/// @param _coreDisputeID The identifier of the dispute in the Arbitrator contract.
5252
/// @param _juror The address of the juror casting the vote commitment.
53-
/// @param _commit The commitment hash.
54-
/// @param _recoveryCommit The commitment hash without the justification.
53+
/// @param _choiceCommit The commitment hash without the justification.
54+
/// @param _justificationCommit The commitment hash for the justification.
5555
/// @param _identity The Shutter identity used for encryption.
5656
/// @param _encryptedVote The Shutter encrypted vote.
5757
event CommitCastShutter(
5858
uint256 indexed _coreDisputeID,
5959
address indexed _juror,
60-
bytes32 indexed _commit,
61-
bytes32 _recoveryCommit,
60+
bytes32 indexed _choiceCommit,
61+
bytes32 _justificationCommit,
6262
bytes32 _identity,
6363
bytes _encryptedVote
6464
);
@@ -107,30 +107,37 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase {
107107
///
108108
/// @param _coreDisputeID The ID of the dispute in Kleros Core.
109109
/// @param _voteIDs The IDs of the votes.
110-
/// @param _commit The commitment hash including the justification.
111-
/// @param _recoveryCommit The commitment hash without the justification.
110+
/// @param _choiceCommit The commitment hash without the justification.
111+
/// @param _justificationCommit The commitment hash for justification.
112112
/// @param _identity The Shutter identity used for encryption.
113113
/// @param _encryptedVote The Shutter encrypted vote.
114114
function castCommitShutter(
115115
uint256 _coreDisputeID,
116116
uint256[] calldata _voteIDs,
117-
bytes32 _commit,
118-
bytes32 _recoveryCommit,
117+
bytes32 _choiceCommit,
118+
bytes32 _justificationCommit,
119119
bytes32 _identity,
120120
bytes calldata _encryptedVote
121121
) external {
122-
if (_recoveryCommit == bytes32(0)) revert EmptyRecoveryCommit();
122+
if (_justificationCommit == bytes32(0)) revert EmptyJustificationCommit();
123123

124124
uint256 localDisputeID = coreDisputeIDToLocal[_coreDisputeID];
125125
Dispute storage dispute = disputes[localDisputeID];
126126
uint256 localRoundID = dispute.rounds.length - 1;
127127
for (uint256 i = 0; i < _voteIDs.length; i++) {
128-
recoveryCommitments[localDisputeID][localRoundID][_voteIDs[i]] = _recoveryCommit;
128+
justificationCommitments[localDisputeID][localRoundID][_voteIDs[i]] = _justificationCommit;
129129
}
130130

131131
// `_castCommit()` ensures that the caller owns the vote and that dispute is active
132-
_castCommit(_coreDisputeID, _voteIDs, _commit);
133-
emit CommitCastShutter(_coreDisputeID, msg.sender, _commit, _recoveryCommit, _identity, _encryptedVote);
132+
_castCommit(_coreDisputeID, _voteIDs, _choiceCommit);
133+
emit CommitCastShutter(
134+
_coreDisputeID,
135+
msg.sender,
136+
_choiceCommit,
137+
_justificationCommit,
138+
_identity,
139+
_encryptedVote
140+
);
134141
}
135142

136143
/// @notice Version of the `castVote` function designed specifically for Shutter.
@@ -158,44 +165,23 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase {
158165
callerIsJuror = false;
159166
}
160167

161-
// ************************************* //
162-
// * Public Views * //
163-
// ************************************* //
164-
165-
/// @notice Computes the hash of a vote using ABI encoding
166-
/// @param _choice The choice being voted for
167-
/// @param _salt A random salt for commitment
168-
/// @param _justification The justification for the vote
169-
/// @return bytes32 The hash of the encoded vote parameters
170-
function hashVote(
171-
uint256 _choice,
172-
uint256 _salt,
173-
string memory _justification
174-
) public view override returns (bytes32) {
175-
if (callerIsJuror) {
176-
// Caller is the juror, hash without `_justification` to facilitate recovery.
177-
return keccak256(abi.encodePacked(_choice, _salt));
178-
} else {
179-
// Caller is not the juror, hash with `_justification`.
180-
bytes32 justificationHash = keccak256(bytes(_justification));
181-
return keccak256(abi.encode(_choice, _salt, justificationHash));
182-
}
183-
}
184-
185168
// ************************************* //
186169
// * Internal * //
187170
// ************************************* //
188171

189172
/// @inheritdoc DisputeKitClassicBase
190-
function _getExpectedVoteHash(
173+
function _checkJustification(
191174
uint256 _localDisputeID,
192175
uint256 _localRoundID,
193-
uint256 _voteID
194-
) internal view override returns (bytes32) {
195-
if (callerIsJuror) {
196-
return recoveryCommitments[_localDisputeID][_localRoundID][_voteID];
176+
uint256 _voteID,
177+
string memory _justification,
178+
uint256 _salt
179+
) internal view override returns (bool) {
180+
if (!callerIsJuror) {
181+
bytes32 justificationCommit = justificationCommitments[_localDisputeID][_localRoundID][_voteID];
182+
return justificationCommit == keccak256(abi.encode(_salt, keccak256(bytes(_justification))));
197183
} else {
198-
return disputes[_localDisputeID].rounds[_localRoundID].votes[_voteID].commit;
184+
return true;
199185
}
200186
}
201187

@@ -254,5 +240,5 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase {
254240
// * Errors * //
255241
// ************************************* //
256242

257-
error EmptyRecoveryCommit();
243+
error EmptyJustificationCommit();
258244
}

contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol

Lines changed: 32 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ contract DisputeKitShutter is DisputeKitClassicBase {
1818
// * Storage * //
1919
// ************************************* //
2020

21-
mapping(uint256 localDisputeID => mapping(uint256 localRoundID => mapping(uint256 voteID => bytes32 recoveryCommitment)))
22-
public recoveryCommitments;
21+
mapping(uint256 localDisputeID => mapping(uint256 localRoundID => mapping(uint256 voteID => bytes32 justificationCommitment)))
22+
public justificationCommitments;
2323

2424
// ************************************* //
2525
// * Transient Storage * //
@@ -34,15 +34,15 @@ contract DisputeKitShutter is DisputeKitClassicBase {
3434
/// @notice Emitted when a vote is cast.
3535
/// @param _coreDisputeID The identifier of the dispute in the Arbitrator contract.
3636
/// @param _juror The address of the juror casting the vote commitment.
37-
/// @param _commit The commitment hash.
38-
/// @param _recoveryCommit The commitment hash without the justification.
37+
/// @param _choiceCommit The commitment hash without the justification.
38+
/// @param _justificationCommit The commitment hash for the justification.
3939
/// @param _identity The Shutter identity used for encryption.
4040
/// @param _encryptedVote The Shutter encrypted vote.
4141
event CommitCastShutter(
4242
uint256 indexed _coreDisputeID,
4343
address indexed _juror,
44-
bytes32 indexed _commit,
45-
bytes32 _recoveryCommit,
44+
bytes32 indexed _choiceCommit,
45+
bytes32 _justificationCommit,
4646
bytes32 _identity,
4747
bytes _encryptedVote
4848
);
@@ -91,30 +91,37 @@ contract DisputeKitShutter is DisputeKitClassicBase {
9191
///
9292
/// @param _coreDisputeID The ID of the dispute in Kleros Core.
9393
/// @param _voteIDs The IDs of the votes.
94-
/// @param _commit The commitment hash including the justification.
95-
/// @param _recoveryCommit The commitment hash without the justification.
94+
/// @param _choiceCommit The commitment hash without the justification.
95+
/// @param _justificationCommit The commitment hash for justification.
9696
/// @param _identity The Shutter identity used for encryption.
9797
/// @param _encryptedVote The Shutter encrypted vote.
9898
function castCommitShutter(
9999
uint256 _coreDisputeID,
100100
uint256[] calldata _voteIDs,
101-
bytes32 _commit,
102-
bytes32 _recoveryCommit,
101+
bytes32 _choiceCommit,
102+
bytes32 _justificationCommit,
103103
bytes32 _identity,
104104
bytes calldata _encryptedVote
105105
) external {
106-
if (_recoveryCommit == bytes32(0)) revert EmptyRecoveryCommit();
106+
if (_justificationCommit == bytes32(0)) revert EmptyJustificationCommit();
107107

108108
uint256 localDisputeID = coreDisputeIDToLocal[_coreDisputeID];
109109
Dispute storage dispute = disputes[localDisputeID];
110110
uint256 localRoundID = dispute.rounds.length - 1;
111111
for (uint256 i = 0; i < _voteIDs.length; i++) {
112-
recoveryCommitments[localDisputeID][localRoundID][_voteIDs[i]] = _recoveryCommit;
112+
justificationCommitments[localDisputeID][localRoundID][_voteIDs[i]] = _justificationCommit;
113113
}
114114

115115
// `_castCommit()` ensures that the caller owns the vote and that dispute is active
116-
_castCommit(_coreDisputeID, _voteIDs, _commit);
117-
emit CommitCastShutter(_coreDisputeID, msg.sender, _commit, _recoveryCommit, _identity, _encryptedVote);
116+
_castCommit(_coreDisputeID, _voteIDs, _choiceCommit);
117+
emit CommitCastShutter(
118+
_coreDisputeID,
119+
msg.sender,
120+
_choiceCommit,
121+
_justificationCommit,
122+
_identity,
123+
_encryptedVote
124+
);
118125
}
119126

120127
/// @notice Version of `castVote` function designed specifically for Shutter.
@@ -142,54 +149,29 @@ contract DisputeKitShutter is DisputeKitClassicBase {
142149
callerIsJuror = false;
143150
}
144151

145-
// ************************************* //
146-
// * Public Views * //
147-
// ************************************* //
148-
149-
/// @notice Computes the hash of a vote using ABI encoding
150-
/// @param _choice The choice being voted for
151-
/// @param _salt A random salt for commitment
152-
/// @param _justification The justification for the vote
153-
/// @return bytes32 The hash of the encoded vote parameters
154-
function hashVote(
155-
uint256 _choice,
156-
uint256 _salt,
157-
string memory _justification
158-
) public view override returns (bytes32) {
159-
if (callerIsJuror) {
160-
// Caller is the juror, hash without `_justification` to facilitate recovery.
161-
return keccak256(abi.encodePacked(_choice, _salt));
162-
} else {
163-
// Caller is not the juror, hash with `_justification`.
164-
bytes32 justificationHash = keccak256(bytes(_justification));
165-
return keccak256(abi.encode(_choice, _salt, justificationHash));
166-
}
167-
}
168-
169152
// ************************************* //
170153
// * Internal * //
171154
// ************************************* //
172155

173-
/// @notice Returns the expected vote hash for a given vote.
174-
/// @param _localDisputeID The ID of the dispute in the Dispute Kit.
175-
/// @param _localRoundID The ID of the round in the Dispute Kit.
176-
/// @param _voteID The ID of the vote.
177-
/// @return The expected vote hash.
178-
function _getExpectedVoteHash(
156+
/// @inheritdoc DisputeKitClassicBase
157+
function _checkJustification(
179158
uint256 _localDisputeID,
180159
uint256 _localRoundID,
181-
uint256 _voteID
182-
) internal view override returns (bytes32) {
183-
if (callerIsJuror) {
184-
return recoveryCommitments[_localDisputeID][_localRoundID][_voteID];
160+
uint256 _voteID,
161+
string memory _justification,
162+
uint256 _salt
163+
) internal view override returns (bool) {
164+
if (!callerIsJuror) {
165+
bytes32 justificationCommit = justificationCommitments[_localDisputeID][_localRoundID][_voteID];
166+
return justificationCommit == keccak256(abi.encode(_salt, keccak256(bytes(_justification))));
185167
} else {
186-
return disputes[_localDisputeID].rounds[_localRoundID].votes[_voteID].commit;
168+
return true;
187169
}
188170
}
189171

190172
// ************************************* //
191173
// * Errors * //
192174
// ************************************* //
193175

194-
error EmptyRecoveryCommit();
176+
error EmptyJustificationCommit();
195177
}

0 commit comments

Comments
 (0)