Skip to content

Commit 30804a8

Browse files
committed
feat: support for multiple voteIDs at once, fixed salt handling by bot
1 parent 020dbab commit 30804a8

File tree

2 files changed

+74
-27
lines changed

2 files changed

+74
-27
lines changed

contracts/scripts/shutterAutoVote.ts

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createPublicClient, createWalletClient, http, Hex, getContract } from "viem";
2-
import { privateKeyToAccount } from "viem/accounts";
2+
import { mnemonicToAccount } from "viem/accounts";
33
import { hardhat } from "viem/chains";
44
import { encrypt, decrypt, DECRYPTION_DELAY } from "./shutter";
55
import { abi as DisputeKitShutterPoCAbi } from "../deployments/localhost/DisputeKitShutterPoC.json";
@@ -9,35 +9,35 @@ import crypto from "crypto";
99
const SEPARATOR = "␟"; // U+241F
1010

1111
// Store encrypted votes for later decryption
12-
interface EncryptedVote {
12+
type EncryptedVote = {
1313
encryptedCommitment: string;
1414
identity: Hex;
1515
timestamp: number;
16-
voteId: bigint;
16+
voteIDs: bigint[];
1717
salt: Hex;
18-
}
18+
};
1919

2020
const encryptedVotes: EncryptedVote[] = [];
2121

22-
const PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" as const;
22+
const disputeKitAddress = "0x5FbDB2315678afecb367f032d93F642f64180aa3" as const;
2323

24-
const CONTRACT_ADDRESS = "0x5FbDB2315678afecb367f032d93F642f64180aa3" as const;
24+
const MNEMONIC = "test test test test test test test test test test test junk";
25+
const account = mnemonicToAccount(MNEMONIC);
2526

2627
const transport = http();
2728
const publicClient = createPublicClient({
2829
chain: hardhat,
2930
transport,
3031
});
3132

32-
const account = privateKeyToAccount(PRIVATE_KEY);
3333
const walletClient = createWalletClient({
3434
account,
3535
chain: hardhat,
3636
transport,
3737
});
3838

3939
const disputeKit = getContract({
40-
address: CONTRACT_ADDRESS,
40+
address: disputeKitAddress,
4141
abi: DisputeKitShutterPoCAbi,
4242
client: { public: publicClient, wallet: walletClient },
4343
});
@@ -49,6 +49,41 @@ function generateSalt(): Hex {
4949
return ("0x" + crypto.randomBytes(32).toString("hex")) as Hex;
5050
}
5151

52+
/**
53+
* Encodes vote parameters into a message string with separators
54+
*/
55+
function encode({
56+
coreDisputeID,
57+
voteIDs,
58+
choice,
59+
justification,
60+
salt,
61+
}: {
62+
coreDisputeID: bigint;
63+
voteIDs: bigint[];
64+
choice: bigint;
65+
justification: string;
66+
salt: Hex;
67+
}): string {
68+
return `${coreDisputeID}${SEPARATOR}${voteIDs.join(",")}${SEPARATOR}${choice}${SEPARATOR}${justification}${SEPARATOR}${salt}`;
69+
}
70+
71+
/**
72+
* Decodes a message string into its component parts
73+
* @param message The message to decode
74+
* @returns Object containing the decoded components
75+
*/
76+
function decode(message: string) {
77+
const [coreDisputeID, voteIDsStr, choice, justification, salt] = message.split(SEPARATOR);
78+
return {
79+
coreDisputeID,
80+
voteIDs: voteIDsStr.split(",").map((id) => BigInt(id)),
81+
choice,
82+
justification,
83+
salt,
84+
};
85+
}
86+
5287
/**
5388
* Cast a commit on-chain
5489
*/
@@ -64,15 +99,23 @@ async function castCommit({
6499
justification: string;
65100
}) {
66101
try {
67-
// Create message with U+241F separator
68-
const message = `${coreDisputeID}${SEPARATOR}${voteIDs[0]}${SEPARATOR}${choice}${SEPARATOR}${justification}`;
102+
// Generate salt first
103+
const salt = generateSalt();
104+
105+
// Encode the vote parameters into a message
106+
const message = encode({
107+
coreDisputeID,
108+
voteIDs,
109+
choice,
110+
justification,
111+
salt,
112+
});
69113

70114
// Encrypt the message using shutter.ts
71115
const { encryptedCommitment, identity } = await encrypt(message);
72116

73-
// Generate salt and compute hash
74-
const salt = generateSalt();
75-
const commitHash = await disputeKit.read.hashVote([coreDisputeID, voteIDs[0], choice, justification, salt]);
117+
// Compute hash using all vote IDs
118+
const commitHash = await disputeKit.read.hashVote([coreDisputeID, voteIDs, choice, justification, salt]);
76119

77120
// Cast the commit on-chain
78121
const txHash = await disputeKit.write.castCommit([coreDisputeID, voteIDs, commitHash, identity as Hex]);
@@ -89,7 +132,7 @@ async function castCommit({
89132
encryptedCommitment,
90133
identity: identity as Hex,
91134
timestamp: Math.floor(Date.now() / 1000),
92-
voteId: voteIDs[0],
135+
voteIDs,
93136
salt,
94137
});
95138

@@ -116,16 +159,16 @@ export async function autoVote() {
116159
// Attempt to decrypt the vote
117160
const decryptedMessage = await decrypt(vote.encryptedCommitment, vote.identity);
118161

119-
// Parse the decrypted message
120-
const [coreDisputeID, , choice, justification] = decryptedMessage.split(SEPARATOR);
162+
// Decode the decrypted message
163+
const { coreDisputeID, voteIDs, choice, justification, salt } = decode(decryptedMessage);
121164

122165
// Cast the vote on-chain
123166
const txHash = await disputeKit.write.castVote([
124167
BigInt(coreDisputeID),
125-
[vote.voteId],
168+
voteIDs,
126169
BigInt(choice),
127170
justification,
128-
vote.salt,
171+
salt,
129172
]);
130173

131174
// Wait for transaction to be mined
@@ -139,7 +182,7 @@ export async function autoVote() {
139182
const index = encryptedVotes.indexOf(vote);
140183
if (index > -1) encryptedVotes.splice(index, 1);
141184
} catch (error) {
142-
console.error(`Error processing vote ${vote.voteId}:`, error);
185+
console.error(`Error processing vote ${vote.voteIDs.join(",")}:`, error);
143186
}
144187
}
145188

@@ -158,9 +201,9 @@ async function main() {
158201
try {
159202
// Cast an encrypted commit
160203
await castCommit({
161-
coreDisputeID: BigInt(0),
162-
voteIDs: [BigInt(0)],
163-
choice: BigInt(2),
204+
coreDisputeID: 0n,
205+
voteIDs: [0n, 1n, 2n],
206+
choice: 2n,
164207
justification: "This is my vote justification",
165208
});
166209

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,26 +37,29 @@ contract DisputeKitShutterPoC {
3737
constructor() {
3838
address juror = msg.sender;
3939
votes.push(Vote({account: juror, commitHash: bytes32(0), choice: 0, voted: false}));
40+
votes.push(Vote({account: juror, commitHash: bytes32(0), choice: 0, voted: false}));
41+
votes.push(Vote({account: juror, commitHash: bytes32(0), choice: 0, voted: false}));
4042
}
4143

4244
/**
4345
* @dev Computes the hash of a vote using ABI encoding
4446
* @param _coreDisputeID The ID of the core dispute
45-
* @param _voteID The ID of the vote
47+
* @param _voteIDs Array of vote IDs
4648
* @param _choice The choice being voted for
4749
* @param _justification The justification for the vote
4850
* @param _salt A random salt for commitment
4951
* @return bytes32 The hash of the encoded vote parameters
5052
*/
5153
function hashVote(
5254
uint256 _coreDisputeID,
53-
uint256 _voteID,
55+
uint256[] calldata _voteIDs,
5456
uint256 _choice,
5557
string memory _justification,
5658
bytes32 _salt
5759
) public pure returns (bytes32) {
5860
bytes32 justificationHash = keccak256(bytes(_justification));
59-
return keccak256(abi.encode(_coreDisputeID, _voteID, _choice, justificationHash, _salt));
61+
bytes32 voteIDsHash = keccak256(abi.encodePacked(_voteIDs));
62+
return keccak256(abi.encode(_coreDisputeID, voteIDsHash, _choice, justificationHash, _salt));
6063
}
6164

6265
function castCommit(
@@ -86,9 +89,10 @@ contract DisputeKitShutterPoC {
8689

8790
// TODO: what happens if hiddenVotes are not enabled?
8891

92+
// Verify the commitment hash for all votes at once
93+
bytes32 computedHash = hashVote(_coreDisputeID, _voteIDs, _choice, _justification, _salt);
94+
8995
for (uint256 i = 0; i < _voteIDs.length; i++) {
90-
// Verify the commitment hash
91-
bytes32 computedHash = hashVote(_coreDisputeID, _voteIDs[i], _choice, _justification, _salt);
9296
require(votes[_voteIDs[i]].commitHash == computedHash, "The commitment hash does not match.");
9397
require(!votes[_voteIDs[i]].voted, "Vote already cast.");
9498
votes[_voteIDs[i]].choice = _choice;

0 commit comments

Comments
 (0)