This repository implements the required commit-reveal track for the Ritual Chain workshop bounty judge.
hardhat/contains the Solidity contract, tests, and deployment module.web/contains the Next.js UI for creating bounties, committing, revealing, judging, and finalizing winners.
Ritual Chain configuration used by this repo:
- Chain ID:
1979 - RPC:
https://rpc.ritualfoundation.org - Currency:
RITUAL - Explorer:
https://explorer.ritualfoundation.org
-
The bounty owner calls
createBounty(title, rubric, deadline)with the reward value attached. -
During the commit phase, participants do not submit plaintext answers. They compute:
keccak256(abi.encode(answer, salt, msg.sender, bountyId))
and call
submitCommitment(uint256 bountyId, bytes32 commitment). -
The contract stores only the
bytes32commitment before the deadline. It also enforces one commitment per wallet per bounty and a maximum of 10 commitments. -
After the deadline, participants call
revealAnswer(uint256 bountyId, string answer, bytes32 salt). -
A reveal is accepted only when the recomputed hash matches the stored commitment for
msg.senderandbountyId. Only accepted reveals are appended to thesubmissionsarray. -
The bounty owner calls
judgeAll(uint256 bountyId, bytes llmInput)after the deadline. The frontend batches all revealed answers into one Ritual LLM request, so the AI judges all eligible answers together. -
The owner calls
finalizeWinner(uint256 bountyId, uint256 winnerIndex)after judging. The contract checks that the index exists, marks the bounty finalized, and pays the reward.
The old submitAnswer entry point is left as a reverting compatibility stub:
callers must use commit-reveal.
The updated AIJudge contract implements:
submitCommitment(uint256 bountyId, bytes32 commitment)revealAnswer(uint256 bountyId, string calldata answer, bytes32 salt)judgeAll(uint256 bountyId, bytes calldata llmInput)finalizeWinner(uint256 bountyId, uint256 winnerIndex)
It also exposes getCommitment(bountyId, submitter) so the UI can show whether
the connected wallet has committed or revealed.
Automated tests live in hardhat/test/AIJudge.ts.
Run:
cd hardhat
pnpm install
pnpm exec hardhat test nodejsCovered reveal cases:
- A commitment emits
CommitmentSubmittedand does not increase the revealed submission count. - Revealing before the deadline reverts with
reveal not open. - Revealing with the wrong salt reverts with
commitment mismatch. - Revealing from a wallet that did not create the commitment reverts with
no commitment. - A valid reveal after the deadline emits
AnswerRevealed, stores the plaintext answer, and marks the commitment revealed. judgeAllreverts before the deadline and also reverts after the deadline when there are no revealed submissions.
Manual test cases to run from the UI:
- Commit with wallet A, switch to wallet B, confirm wallet B cannot reveal A's commitment.
- Commit with one answer, edit the answer before reveal, and confirm reveal reverts unless the original answer and salt are used.
- Reveal several submissions, run
judgeAll, then finalize a valid winner. - Try finalizing an out-of-range winner index and confirm the contract reverts.
Plaintext exists only in the participant's browser/local notes until reveal. The
chain stores bounty metadata, reward, deadlines, commitment hashes, reveal
status, and revealed answers. After the deadline, revealed plaintext answers are
public on-chain and are batched into one LLM input for judgeAll.
The frontend stores the answer and salt in the user's browser localStorage
after committing so the user can reveal later. This is a convenience, not a
security boundary; participants should still keep their salt safely.
Advanced Ritual-Native Hidden Submissions
A stronger Ritual-native version would keep encrypted answers off-chain until the TEE-backed judging step:
- On-chain: bounty metadata, deadline, reward escrow, commitment or encrypted blob references, reveal/judging state, AI review hash/result, and final winner.
- Off-chain: encrypted answers in user storage, IPFS, object storage, or another content-addressed store. The ciphertext reference is posted on-chain.
- Plaintext: never appears on-chain. It exists on the participant's machine before encryption and inside the Ritual TEE while the batch judge decrypts and evaluates submissions.
- LLM input: the judge step sends all encrypted references/private inputs in one batch to Ritual's TEE-backed execution path. The TEE decrypts, builds a single prompt containing every submission, calls the LLM once, and returns the ranking/result to the contract.
This keeps the fairness property of batch judging while avoiding one LLM call per answer and avoiding public plaintext reveals.
Public data in a bounty system should include the bounty title, rubric, reward, deadline, rules, commitment hashes, judging status, and final payout, because participants need to verify the process. Answers should stay hidden during the submission phase so competitors cannot copy, remix, or strategically improve on someone else's work before the deadline. Salts should remain private forever unless needed for reveal, because they are what make commitments resistant to guessing. AI should help with batch scoring, ranking, rubric consistency, and summarizing trade-offs across submissions. Humans should decide the final winner when judgment involves taste, ethics, project fit, or ambiguous requirements. The contract should enforce deadlines, eligibility, payment, and auditability because those are objective rules. The best bounty system uses AI as an advisory reviewer, humans as accountable decision makers, and smart contracts as the rule-enforcing settlement layer.