-
Notifications
You must be signed in to change notification settings - Fork 49
Shutterized Dispute Kit #1965
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jaybuidl
wants to merge
64
commits into
dev
Choose a base branch
from
feat/shutter-dispute-kit
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Shutterized Dispute Kit #1965
Changes from 12 commits
Commits
Show all changes
64 commits
Select commit
Hold shift + click to select a range
2bffa61
chore: shutter script experiment
jaybuidl 3570b46
feat(shutter): added decryption logic
jaybuidl ddae29a
fix: better error handling
jaybuidl 04e3f5c
feat: naive shutterized dispute kit and auto-voting bot
jaybuidl be6e1ce
feat: commitment hashing and verification onchain
jaybuidl 020dbab
chore: cleanup
jaybuidl 30804a8
feat: support for multiple voteIDs at once, fixed salt handling by bot
jaybuidl 1a9b72d
chore: cleanup before integration into a fully fletched dispute kit
jaybuidl b580556
feat: fully fletched DisputeKitShutter
jaybuidl 8819241
chore: removed redundant node-fetch
jaybuidl cd016b3
chore: cleanup
jaybuidl 8e46e05
feat: fully fletched DisputeKitShutter
jaybuidl 98ec3ba
chore: deployment of DisputeKitShutter in devnet
jaybuidl 74d1506
fix: missing parameter, upgraded DisputeKitShutter
jaybuidl b40abc2
chore: upgraded DisputeKitClassic
jaybuidl cb7997f
fix: external call to castCommit() changes msg.sender, fixed by extra…
jaybuidl ee2ceb6
chore: enable shutter DK on the devnet general court
jaybuidl 317aed6
feat: support for shutter disputekit in devnet
kemuru 05dcdb0
fix: create new classic dispute entity correctly with correct round i…
kemuru 44ea55e
fix: correct round check
kemuru a995e1e
chore: subgraph update scripts now relies on a template and removes d…
jaybuidl ca14ada
Merge branch 'dev' into feat/shutter-dispute-kit
jaybuidl f3f235c
Merge branch 'feat/shutter-dispute-kit' into feat(subgraph)/support-f…
kemuru 4b85135
Merge branch 'dev' into feat/shutter-dispute-kit
jaybuidl edc4a7d
Merge branch 'dev' into feat/shutter-dispute-kit
jaybuidl 78b2951
chore: fix to support deployment to Alchemy
jaybuidl 6b32fc3
chore: lock file
jaybuidl 74625e1
Merge pull request #1966 from kleros/feat(subgraph)/support-for-multi…
jaybuidl 0dad7c4
Merge branch 'dev' into feat/shutter-dispute-kit
kemuru 5ac64f5
feat: shutter support in dispute commiting
kemuru 6cd4369
Merge branch 'dev' into feat/shutter-dispute-kit
kemuru 724a949
Merge branch 'feat/shutter-dispute-kit' into feat(web)/shutter-fronte…
kemuru 8a5b2f4
feat: shutter appeal support
kemuru 14994db
fix: bug fix in subgraph
kemuru 142273c
Merge pull request #1995 from kleros/fix(subgraph)/localrounds-fix
kemuru 6b240a6
Merge branch 'feat/shutter-dispute-kit' into feat(web)/shutter-fronte…
kemuru 5989928
chore: subgraphs version bump
jaybuidl df98319
Merge branch 'dev' into feat/shutter-dispute-kit
kemuru 05711fe
Merge branch 'feat/shutter-dispute-kit' into feat(web)/shutter-fronte…
kemuru 729777d
feat: postinstall script, use correct commit function
kemuru 8bf3772
fix: vote hashing during commitment must follow DisputeKitShutter.has…
jaybuidl 8c74d4a
feat: decryption delay is the remaining of the commit period
kemuru b1d3fa9
chore: shutter event tweak, extra dispute kit views
jaybuidl 47049eb
Merge branch 'feat/shutter-dispute-kit' into feat(web)/shutter-fronte…
jaybuidl bae8db4
chore: remove postinstall script, tell vite where to find it
kemuru aa01874
feat: keeper bot for shutter auto-reveal (wip)
jaybuidl e983fdd
Merge branch 'feat/shutter-dispute-kit' into feat(web)/shutter-fronte…
kemuru 2e5d2a7
feat: support for revealing shutter commit from the frontend
kemuru bf7a3c0
chore: remove console logs, few code smells
kemuru bafbd14
Merge branch 'dev' into feat/shutter-dispute-kit
jaybuidl c8bf188
Merge branch 'feat/shutter-dispute-kit' into feat(web)/shutter-fronte…
kemuru e52d59b
Merge branch 'dev' into feat/shutter-dispute-kit
jaybuidl 99f9852
feat: shutter auto-reveal logic integrated into the main bot, improve…
jaybuidl 04605db
chore: extra 5 min decryptiondelay
kemuru b3817bd
fix: trycatch in case voting fails
kemuru f816ca3
Merge branch 'feat/shutter-dispute-kit' into feat(web)/shutter-fronte…
kemuru fe7eb85
Merge branch 'dev' into feat/shutter-dispute-kit
kemuru 4e2dea9
Merge branch 'feat/shutter-dispute-kit' into feat(web)/shutter-fronte…
kemuru 469039a
Merge pull request #1994 from kleros/feat(web)/shutter-frontend-rende…
alcercu 7bbe6df
chore: upgrade-all support for Shutter DK
jaybuidl 1fd3526
chore: shutter env variables
kemuru c3f6611
feat: keeper bot passes the period to Voting before auto-revealing
jaybuidl 959b765
chore: keeper bot logging
jaybuidl d43e68e
Merge branch 'chore/shutter-env-variables' into feat/shutter-dispute-kit
jaybuidl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,267 @@ | ||
import { encryptData, decrypt as shutterDecrypt } from "@shutter-network/shutter-sdk"; | ||
import { Hex, stringToHex, hexToString } from "viem"; | ||
import crypto from "crypto"; | ||
import "isomorphic-fetch"; | ||
|
||
// Time in seconds to wait before the message can be decrypted | ||
export const DECRYPTION_DELAY = 5; | ||
|
||
interface ShutterApiMessageData { | ||
eon: number; | ||
identity: string; | ||
identity_prefix: string; | ||
eon_key: string; | ||
tx_hash: string; | ||
} | ||
|
||
interface ShutterApiResponse { | ||
message: ShutterApiMessageData; | ||
error?: string; | ||
} | ||
|
||
interface ShutterDecryptionKeyData { | ||
decryption_key: string; | ||
identity: string; | ||
decryption_timestamp: number; | ||
} | ||
|
||
/** | ||
* Fetches encryption data from the Shutter API | ||
* @param decryptionTimestamp Unix timestamp when decryption should be possible | ||
* @returns Promise with the eon key and identity | ||
*/ | ||
async function fetchShutterData(decryptionTimestamp: number): Promise<ShutterApiMessageData> { | ||
try { | ||
console.log(`Sending request to Shutter API with decryption timestamp: ${decryptionTimestamp}`); | ||
|
||
// Generate a random identity prefix | ||
const identityPrefix = generateRandomBytes32(); | ||
console.log(`Generated identity prefix: ${identityPrefix}`); | ||
|
||
const response = await fetch("https://shutter-api.shutter.network/api/register_identity", { | ||
method: "POST", | ||
headers: { | ||
accept: "application/json", | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify({ | ||
decryptionTimestamp, | ||
identityPrefix, | ||
}), | ||
}); | ||
|
||
// Log the response status | ||
console.log(`API response status: ${response.status}`); | ||
|
||
// Get the response text | ||
const responseText = await response.text(); | ||
|
||
if (!response.ok) { | ||
throw new Error(`API request failed with status ${response.status}: ${responseText}`); | ||
} | ||
|
||
// Parse the JSON response | ||
let jsonResponse: ShutterApiResponse; | ||
try { | ||
jsonResponse = JSON.parse(responseText); | ||
} catch (error) { | ||
throw new Error(`Failed to parse API response as JSON: ${responseText}`); | ||
} | ||
|
||
// Check if we have the message data | ||
if (!jsonResponse.message) { | ||
throw new Error(`API response missing message data: ${JSON.stringify(jsonResponse)}`); | ||
} | ||
|
||
return jsonResponse.message; | ||
} catch (error) { | ||
console.error("Error fetching data from Shutter API:", error); | ||
throw error; | ||
} | ||
} | ||
|
||
/** | ||
* Fetches the decryption key from the Shutter API | ||
* @param identity The identity used for encryption | ||
* @returns Promise with the decryption key data | ||
*/ | ||
async function fetchDecryptionKey(identity: string): Promise<ShutterDecryptionKeyData> { | ||
console.log(`Fetching decryption key for identity: ${identity}`); | ||
|
||
const response = await fetch(`https://shutter-api.shutter.network/api/get_decryption_key?identity=${identity}`, { | ||
method: "GET", | ||
headers: { | ||
accept: "application/json", | ||
}, | ||
}); | ||
|
||
// Get the response text | ||
const responseText = await response.text(); | ||
|
||
// Try to parse the error response even if the request failed | ||
let jsonResponse; | ||
try { | ||
jsonResponse = JSON.parse(responseText); | ||
} catch (error) { | ||
throw new Error(`Failed to parse API response as JSON: ${responseText}`); | ||
} | ||
|
||
// Handle the "too early" error case specifically | ||
if (!response.ok) { | ||
if (jsonResponse?.description?.includes("timestamp not reached yet")) { | ||
throw new Error( | ||
`Cannot decrypt yet: The decryption timestamp has not been reached.\n` + | ||
`Please wait at least ${DECRYPTION_DELAY} seconds after encryption before attempting to decrypt.\n` + | ||
`Error details: ${jsonResponse.description}` | ||
); | ||
} | ||
throw new Error(`API request failed with status ${response.status}: ${responseText}`); | ||
} | ||
|
||
// Check if we have the message data | ||
if (!jsonResponse.message) { | ||
throw new Error(`API response missing message data: ${JSON.stringify(jsonResponse)}`); | ||
} | ||
|
||
return jsonResponse.message; | ||
} | ||
|
||
/** | ||
* Ensures a string is a valid hex string with 0x prefix | ||
* @param hexString The hex string to validate | ||
* @returns The validated hex string with 0x prefix | ||
*/ | ||
function ensureHexString(hexString: string | undefined): `0x${string}` { | ||
if (!hexString) { | ||
throw new Error("Hex string is undefined or null"); | ||
} | ||
|
||
// Add 0x prefix if it doesn't exist | ||
const prefixedHex = hexString.startsWith("0x") ? hexString : `0x${hexString}`; | ||
return prefixedHex as `0x${string}`; | ||
} | ||
|
||
/** | ||
* Generates a random 32 bytes | ||
* @returns Random 32 bytes as a hex string with 0x prefix | ||
*/ | ||
function generateRandomBytes32(): `0x${string}` { | ||
return ("0x" + | ||
crypto | ||
.getRandomValues(new Uint8Array(32)) | ||
.reduce((acc, byte) => acc + byte.toString(16).padStart(2, "0"), "")) as Hex; | ||
} | ||
|
||
/** | ||
* Encrypts a message using the Shutter API | ||
* @param message The message to encrypt | ||
* @returns Promise with the encrypted commitment and identity | ||
*/ | ||
export async function encrypt(message: string): Promise<{ encryptedCommitment: string; identity: string }> { | ||
// Set decryption timestamp | ||
const decryptionTimestamp = Math.floor(Date.now() / 1000) + DECRYPTION_DELAY; | ||
|
||
// Fetch encryption data from Shutter API | ||
console.log(`Fetching encryption data for decryption at timestamp ${decryptionTimestamp}...`); | ||
const shutterData = await fetchShutterData(decryptionTimestamp); | ||
|
||
// Extract the eon key and identity from the response and ensure they have the correct format | ||
const eonKeyHex = ensureHexString(shutterData.eon_key); | ||
const identityHex = ensureHexString(shutterData.identity); | ||
|
||
// Message to encrypt | ||
const msgHex = stringToHex(message); | ||
|
||
// Generate a random sigma | ||
const sigmaHex = generateRandomBytes32(); | ||
|
||
console.log("Eon Key:", eonKeyHex); | ||
console.log("Identity:", identityHex); | ||
console.log("Sigma:", sigmaHex); | ||
|
||
// Encrypt the message | ||
const encryptedCommitment = await encryptData(msgHex, identityHex, eonKeyHex, sigmaHex); | ||
|
||
return { encryptedCommitment, identity: identityHex }; | ||
} | ||
|
||
/** | ||
* Decrypts a message using the Shutter API | ||
* @param encryptedMessage The encrypted message to decrypt | ||
* @param identity The identity used for encryption | ||
* @returns Promise with the decrypted message | ||
*/ | ||
export async function decrypt(encryptedMessage: string, identity: string): Promise<string> { | ||
// Fetch the decryption key | ||
const decryptionKeyData = await fetchDecryptionKey(identity); | ||
console.log("Decryption key:", decryptionKeyData.decryption_key); | ||
|
||
// Ensure the decryption key is properly formatted | ||
const decryptionKey = ensureHexString(decryptionKeyData.decryption_key); | ||
|
||
// Decrypt the message | ||
const decryptedHexMessage = await shutterDecrypt(encryptedMessage, decryptionKey); | ||
|
||
// Convert the decrypted hex message back to a string | ||
return hexToString(decryptedHexMessage as `0x${string}`); | ||
} | ||
|
||
async function main() { | ||
try { | ||
const command = process.argv[2]?.toLowerCase(); | ||
|
||
if (!command) { | ||
console.error(` | ||
Usage: yarn ts-node shutter.ts <command> [arguments] | ||
|
||
Commands: | ||
encrypt <message> Encrypt a message | ||
decrypt <encrypted message> <identity> Decrypt a message (requires the identity used during encryption) | ||
|
||
Examples: | ||
yarn ts-node shutter.ts encrypt "my secret message" | ||
yarn ts-node shutter.ts decrypt "encrypted-data" "0x1234..."`); | ||
process.exit(1); | ||
} | ||
|
||
switch (command) { | ||
case "encrypt": { | ||
const message = process.argv[3]; | ||
if (!message) { | ||
console.error("Error: Missing message to encrypt"); | ||
console.error("Usage: yarn ts-node shutter.ts encrypt <message>"); | ||
process.exit(1); | ||
} | ||
const { encryptedCommitment, identity } = await encrypt(message); | ||
console.log("\nEncrypted Commitment:", encryptedCommitment); | ||
console.log("Identity:", identity); | ||
break; | ||
} | ||
case "decrypt": { | ||
const [encryptedMessage, identity] = [process.argv[3], process.argv[4]]; | ||
if (!encryptedMessage || !identity) { | ||
console.error("Error: Missing required arguments for decrypt"); | ||
console.error("Usage: yarn ts-node shutter.ts decrypt <encrypted-message> <identity>"); | ||
console.error("Note: The identity is the one returned during encryption"); | ||
process.exit(1); | ||
} | ||
const decryptedMessage = await decrypt(encryptedMessage, identity); | ||
console.log("\nDecrypted Message:", decryptedMessage); | ||
break; | ||
} | ||
default: { | ||
console.error(`Error: Unknown command '${command}'`); | ||
console.error("Valid commands are: encrypt, decrypt"); | ||
process.exit(1); | ||
} | ||
} | ||
} catch (error) { | ||
console.error("\nError:", error); | ||
process.exit(1); | ||
} | ||
} | ||
|
||
// Execute if run directly | ||
if (require.main === module) { | ||
main(); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.