Skip to content

localhostwastaken/sshhh

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

18 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

ChainWhistle

Decentralised whistleblower protection on Ethereum Sepolia.
Blockchain = trust layer, not database. Claims live on IPFS. Stakes keep every actor honest.


The Problem

Whistleblowers carry extraordinary risk. They face:

  • Retaliation β€” loss of livelihood, prosecution, or worse.
  • No anonymity guarantees β€” centralised platforms can be subpoenaed, hacked, or shut down.
  • Zero financial incentive β€” leaking information of massive public value earns the source nothing.
  • Unverifiable claims β€” without a neutral validation layer, sensational but false stories flood the market.

Existing solutions (SecureDrop, Signal tips) solve the delivery problem but leave the economic and trust problems entirely unsolved.


The Solution

ChainWhistle turns the whistleblower pipeline into a permissionless, incentive-aligned protocol:

Participant What they do How they are held accountable
Whistleblower Submits an IPFS-pinned claim. No registration. Pseudonymous wallet = pseudonymous identity. Pays gas. Payout only on verified + published path.
Validator Orgs Stake ETH to join. Vote on claim authenticity via commit-reveal. Stake slashed 5% if their verdict is contradicted by the oracle cycle.
News Outlets Stake ETH. Bid for the right to break a verified story. Stake seized + permanent blacklist if they win the bid but never publish.
Oracle Nodes Stake ETH. 5 randomly selected per claim to verify actual publication. Minority voters slashed 10% (anti-collusion).

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         FRONTEND (React + ethers.js)            β”‚
β”‚ Whistleblower   β”‚  Validator Org  β”‚  News Outlet  β”‚  Oracle Nodeβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
           β”‚               β”‚                β”‚              β”‚
           β–Ό               β–Ό                β–Ό              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              ChainWhistle.sol (Ethereum Sepolia)                 β”‚
β”‚  submitClaim()  β”‚  commitVote() / revealVote()  β”‚  placeBid()   β”‚
β”‚  finalizeValidation()  β”‚  finalizeOracle()  β”‚  withdraw()       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚  ClaimSubmitted event
                               β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               LLM / TEE Stub Server (Node.js + Express)         β”‚
β”‚  β€’ Listens for ClaimSubmitted events                            β”‚
β”‚  β€’ Fetches active validator orgs + metadata                     β”‚
β”‚  β€’ Scores orgs 0-100 via OpenRouter (Claude Haiku)              β”‚
β”‚    β€” tag/expertise match, conflict-of-interest exclusion        β”‚
β”‚  β€’ Calls assignValidators(claimId, topN) on-chain               β”‚
β”‚  β€’ Proxies IPFS uploads to Pinata (keeps JWT off the client)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚  IPFS via Pinata β”‚
                    β”‚  Claim content   β”‚
                    β”‚  Proof files     β”‚
                    β”‚  Description CID β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Tech Stack

Layer Technology
Smart contracts Solidity 0.8.28, Hardhat, Hardhat Toolbox (Viem)
Testnet Ethereum Sepolia
Frontend React 19, Vite 8, TypeScript, Tailwind CSS v4
Blockchain client ethers.js v6
Off-chain server Node.js (ESM), Express, multer
LLM scoring OpenRouter API (default: anthropic/claude-haiku-4-5)
Decentralised storage IPFS via Pinata
Wallet MetaMask (required for all roles)

Claim Lifecycle

SUBMITTED ──(LLM assigns orgs)──► ASSIGNED
                                      β”‚
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
               β‰₯ 2/3 VERIFIED                           < 2/3 VERIFIED
                    β”‚                                         β”‚
                    β–Ό                                         β–Ό
               VERIFIED                                  FREE PUBLIC
           (bid window opens)                      (whistleblower gets nothing)
                    β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       Winning bid escrowed    No bids / timeout
          β”‚
          β–Ό
     ORACLE PENDING
  (5 random oracles selected)
          β”‚
     β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  Oracle majority: Published        Oracle majority: Not Published
          β”‚                                    β”‚
          β–Ό                                    β–Ό
      PUBLISHED                      Outlet BLACKLISTED
   (escrow distributed)              failedCycles++
   SETTLED                           If failedCycles < 3 β†’ reopen bids
                                     If failedCycles β‰₯ 3 β†’ FREE PUBLIC

Economic Design

Staking Requirements

Role Minimum Stake
Validator Org 1 ETH
News Outlet 2 ETH
Oracle Node 0.5 ETH

Escrow Distribution (happy path)

When the oracle majority confirms publication:

Recipient Share
Whistleblower 60%
VERIFIED-voting Validator Orgs 30% (split equally)
Published-voting Oracle Nodes 10% (split equally)

Slashing (unhappy path)

Event Who gets slashed Amount
Oracle minority (voted wrong side) Oracle node 10% of stake
Outlet fails to publish β†’ oracle cycle fails VERIFIED-voting orgs 5% of stake
Outlet fails to publish News outlet Full stake seized, permanent blacklist

All slashed funds are redistributed to the platform treasury or majority voters.


Validator Org Onboarding Flow

Validator orgs are not permissionless β€” they represent accountable, staked institutions:

  1. Org calls applyForValidator(name, description, website, tags) with β‰₯ 1 ETH stake.
  2. The LLM server indexes the application; the admin frontend shows it alongside OpenRouter-generated relevance scores.
  3. Owner calls approveValidator(org) or rejectValidator(org).
  4. Rejected applicants receive a full stake refund via the pull-withdrawal pattern.

The same two-step flow applies to News Outlets and Oracle Nodes (stake-only, no metadata required).


Local Setup

Prerequisites

  • Node.js β‰₯ 20, npm β‰₯ 10
  • MetaMask browser extension
  • Sepolia ETH (faucet: sepoliafaucet.com)

1. Clone and install

git clone https://github.com/your-org/chainwhistle.git
cd chainwhistle

# Smart contracts
cd chain && npm install

# Frontend
cd ../client && npm install

# LLM server
cd ../server && npm install

2. Configure environment variables

chain/.env

SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY
SEPOLIA_PRIVATE_KEY=0xYOUR_DEPLOYER_PRIVATE_KEY
LLM_SERVER_ADDRESS=0xYOUR_LLM_SERVER_WALLET

server/.env

RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY
LLM_SERVER_PRIVATE_KEY=0xYOUR_LLM_SERVER_PRIVATE_KEY
CONTRACT_ADDRESS=0xDEPLOYED_CONTRACT_ADDRESS
OPENROUTER_API_KEY=sk-or-...
PINATA_JWT=eyJ...

client/.env.local

VITE_CONTRACT_ADDRESS=0xDEPLOYED_CONTRACT_ADDRESS
VITE_SERVER_URL=http://localhost:3001

3. Compile and deploy contracts

cd chain
npx hardhat compile
npx hardhat run scripts/deploy.ts --network sepolia

Deployment info is written to deployment.json at the repo root.

4. Start the LLM server

cd server
node index.js

The server listens for ClaimSubmitted events on-chain and automatically calls assignValidators. It also scans recent blocks on startup to recover any missed events (useful after restarts).

5. Start the frontend

cd client
npm run dev

Open http://localhost:5173 and connect MetaMask to Sepolia.

Useful commands

# Compile contracts
npx hardhat compile

# Run tests (uses Hardhat's local network + viem)
npx hardhat test

# Lint Solidity
npx solhint 'contracts/**/*.sol'

# Frontend build
npm run build

Demo Walkthrough

Connect your wallet

  1. Open the app in a MetaMask-enabled browser.
  2. Click Connect Wallet β€” MetaMask prompts you to switch to Sepolia (chain ID 11155111).
  3. Your wallet role is detected automatically from on-chain state (whistleblower, org, outlet, oracle, or admin).

Submit a claim (Whistleblower)

  1. Click Submit Claim in the navigation.
  2. Complete the 2-step onboarding form:
    • Step 1 β€” Select a category tag (e.g., finance, health, environment). Upload up to 5 proof files. They are uploaded to IPFS via the Pinata proxy server; only the CIDs are stored on-chain.
    • Step 2 β€” Write a description. It is also uploaded to IPFS as a separate CID (descriptionCID).
  3. Confirm the MetaMask transaction. submitClaim(ipfsHash, tag, proofCIDs, descriptionCID) is called, emitting ClaimSubmitted.
  4. The LLM server picks up the event, scores active validator orgs against the tag using OpenRouter, and calls assignValidators on-chain within seconds.
  5. Your claim now shows as Assigned with a 2-day commit window.

Whitelist a verifier (Admin / Owner)

  1. Switch to the Admin panel (only visible to the deployer address).
  2. The Pending Applications tab lists orgs that have called applyForValidator with their stake.
  3. The LLM score panel (via GET /score/:tag) shows each org's relevance score and reasoning.
  4. Click Approve to call approveValidator(org) on-chain, or Reject to refund their stake.

Project Structure

chainwhistle/
β”œβ”€β”€ chain/                    # Hardhat project
β”‚   β”œβ”€β”€ contracts/
β”‚   β”‚   └── ChainWhistle.sol  # Core protocol contract
β”‚   β”œβ”€β”€ scripts/
β”‚   β”‚   └── deploy.ts         # Deployment script (writes deployment.json)
β”‚   └── test/
β”‚       └── ChainWhistle.ts   # Full lifecycle test suite (viem)
β”œβ”€β”€ client/                   # React + Vite frontend
β”‚   └── src/
β”‚       └── App.tsx           # Single-page app (all views)
β”œβ”€β”€ server/                   # LLM TEE stub + IPFS proxy
β”‚   └── index.js              # Express server + event listener
└── deployment.json           # Written by deploy.ts β€” consumed by client + server

Security Notes

  • Pseudonymous by design. Whistleblower identity is their Ethereum wallet address. There is no registration, email, or IP linkage at the protocol level.
  • No on-chain claim content. Only IPFS CIDs are stored on-chain. Claim text and proof files live on IPFS.
  • Checks-Effects-Interactions. All state mutations follow CEI ordering. External ETH transfers use a pull-withdrawal pattern (pendingWithdrawals) to eliminate reentrancy vectors.
  • Overflow-safe. Solidity ^0.8.x built-in overflow checks throughout. All basis-point arithmetic validated to sum to BPS_DENOM (10,000).

What's Next

  • Randomness improvement: Replace the current pseudo-random oracle selection (block hash seeded) with Chainlink VRF for verifiable fairness.
  • Claim confidentiality: Asymmetric encryption of claim content to the winning outlet's public key, so content stays private until the outlet is confirmed and keys can be shared.
  • Governance: On-chain DAO for protocol parameter changes (stake minimums, time windows, slash percentages) rather than owner-controlled constants.
  • Cross-chain: Deploy on an L2 (Optimism, Base) to reduce gas costs for whistleblowers who may be submitting from low-resource environments.

Team

Built at HackX Β· April 2026


Links

About

ChainWhistle is a decentralized Web3 platform that uses on-chain economic staking to guarantee whistleblower anonymity, financially reward verified leaks, and enforce strict accountability for validators and news outlets.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors