|
1 | | -# 🧱 TaskBoard |
| 1 | +# TaskBoard (A decentralized platform for CTF-style challenges using Ethereum, IPFS, and zkSNARKs.) |
2 | 2 |
|
3 | | -Design and build a **decentralized task board and badge collector** for developers and learners. Tasks are challenges with predefined correct solutions, and successful solvers earn **on-chain badges** (NFTs or SBTs) that they can collect as verifiable proof of their skills. |
| 3 | +See a small [blog](https://lasmichal.com/projects/taskboard/taskboard.html) about this dApp. |
| 4 | + |
| 5 | +TaskBoard is a dApp for creating and solving CTF-like tasks using smart contracts, IPFS, and zero-knowledge proofs (ZKP). |
| 6 | +Users solve challenges by proving knowledge of a hidden secret without revealing it. Successful solvers are rewarded with task-specific tokens. |
4 | 7 |
|
5 | 8 | To ensure trustless and scalable operation: |
6 | 9 |
|
7 | 10 | * Solutions are verified automatically (no manual approval). |
8 | 11 | * Tasks and descriptions are stored off-chain via IPFS. |
9 | 12 | * Proof validation is handled using ZK circuits. |
10 | 13 | * Submissions remain **private** to avoid copying. |
| 14 | +* Rewards issued as non-transferable ERC-1155 tokens. |
11 | 15 |
|
12 | | ---- |
13 | | - |
14 | | -## Core Features |
| 16 | +## Tech stack |
15 | 17 |
|
16 | | -### 1. **Task Creation** |
| 18 | +* Frontend – React |
| 19 | +* Backend – [Node.js](https://nodejs.org/en) ([Fastify](https://fastify.dev/)) + [Postgres](https://www.postgresql.org/) |
| 20 | +* Smart contracts – Solidity (ERC-1155, AccessControl) |
| 21 | +* Storage – IPFS (local - [Kubo IPFS](https://docs.ipfs.tech/install/command-line/) / [Pinata](https://pinata.cloud/)) |
| 22 | +* Proof system – snarkjs + Groth16 |
17 | 23 |
|
18 | | -* Any user can create a task |
19 | | -* Task includes: |
| 24 | +## How it works |
20 | 25 |
|
21 | | - * Title, description, difficulty |
22 | | - * Required tags (e.g., Solidity, ZK, Rust) |
23 | | - * Solution validation mechanism (hash or ZK circuit hash) |
24 | | - * Reward badge config (SBT metadata) |
25 | | - * Deadline (optional) |
26 | | -* Metadata is uploaded to IPFS |
27 | | -* The contract stores minimal task data + IPFS CID |
| 26 | +1. Task creation – Creator commits a secret (Poseidon hash + salt) stored on IPFS. |
| 27 | +2. Solving – Solver submits a ZKP proving they know the secret. |
| 28 | +3. Verification – Smart contract checks the proof and mints a reward token. |
28 | 29 |
|
29 | 30 | --- |
30 | 31 |
|
31 | | -### 2. **Task Solving / Proof Submission** |
32 | | - |
33 | | -* Any dev can attempt the task |
34 | | -* They submit a **proof** of solution |
35 | | -* The contract verifies it: |
| 32 | +## Set up |
36 | 33 |
|
37 | | - * If correct → mints badge (NFT/SBT) to solver |
38 | | - * If wrong → rejected, optional retry delay |
39 | | -* Optional: support encrypted solution uploads for future reference |
| 34 | +1. Compile the circuit and generate a trusted setup with `make circuit` (testing only). |
| 35 | +2. Export smart contract ABI with `make export-task-manager-abi`. |
| 36 | +3. Start the Postgres DB and use `make delete-db` and `make init-db`. |
| 37 | + - Postgres has to have user `app` and created database called `task_board_db`. |
| 38 | +4. Start Anvil and deploy smart contracts with `make deploy-local` and add creator `add-creator-local`. |
| 39 | + - The creator is Anvil's account (1) with address _0x70997970C51812dc3A010C7d01b50e0d17dc79C8_, add it to your wallet (e.g., MetaMask). |
| 40 | +5. Start Kubo IPFS `ipfs daemon`. |
| 41 | +6. Start the backend from `/backend` directory with `npm run dev`. |
| 42 | +7. Start the frontend from `/frontend` directory with `npm run dev`. |
40 | 43 |
|
41 | | ---- |
42 | | - |
43 | | -### 3. **Badge Minting** |
| 44 | +## Environment files |
44 | 45 |
|
45 | | -* Each task has a custom badge |
46 | | -* Badges are: |
| 46 | +### Root `.env` (for smart contracts) |
47 | 47 |
|
48 | | - * Non-transferable (SBT) |
49 | | - * Include metadata about task |
50 | | - * Linked to IPFS data |
51 | | -* Optional: user profile page with badge portfolio (off-chain frontend) |
52 | | - |
53 | | ---- |
| 48 | +```bash |
| 49 | +### ENV for local Anvil node |
| 50 | +PRIVATE_KEY_LOCAL=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 |
| 51 | +PRIVATE_KEY_CREATOR=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d |
| 52 | +PRIVATE_KEY_SOLVER=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a |
54 | 53 |
|
55 | | -## Example Task Flow |
| 54 | +CREATOR_ADDR=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 |
56 | 55 |
|
57 | | -1. Alice creates a task: |
| 56 | +TASK_MANAGER_ADDR=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 |
| 57 | +TASK_ACCESS_ADDR=0x5FbDB2315678afecb367f032d93F642f64180aa3 |
58 | 58 |
|
59 | | - * **"Write a smart contract that verifies Merkle Proofs"** |
60 | | - * IPFS CID with metadata: `bafy...abc` |
61 | | - * Creator writes a ZK circuit |
| 59 | +TASK_ID_TO_DEACTIVATE=1 |
62 | 60 |
|
63 | | -2. Task posted on-chain with IPFS CID and hash |
| 61 | +### ENV for Sepolia deployment |
64 | 62 |
|
65 | | -3. Bob solves it: |
66 | | - |
67 | | - * Generates a ZK proof using the circuit |
68 | | - * Submits the proof to the smart contract |
69 | | - * If valid → badge minted |
70 | | - |
71 | | ---- |
72 | | - |
73 | | -## Proof Validation Logic Options |
74 | | - |
75 | | -Choose from these validation types (extendable): |
76 | | - |
77 | | ---- |
78 | | - |
79 | | -### Zero-Knowledge Proofs (Advanced)** |
80 | | - |
81 | | -* Creator writes a ZK circuit using **Circom** |
82 | | -* Contract stores `circuitHash` |
83 | | -* Solver: |
84 | | - |
85 | | - * Builds the solution off-chain |
86 | | - * Generates a ZK proof using the circuit |
87 | | - * Submits the proof to the smart contract |
88 | | -* Contract verifies proof → badge minted |
89 | | - |
90 | | -**Good for**: |
91 | | - |
92 | | -* Math challenges, logic puzzles, data constraints, Merkle inclusion |
93 | | - |
94 | | -**Requires**: |
| 63 | +PRIVATE_KEY=0xYOUR_PRIVATE_KEY # Wallet private key |
| 64 | +ETHERSCAN_API_KEY=YOUR_ETHERSCAN_KEY # Etherscan API key (for verification) |
| 65 | +SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/<YOUR_ALCHEMY_KEY> # RPC provider (Alchemy) |
| 66 | +``` |
95 | 67 |
|
96 | | -* ZK circuit storage (IPFS or registry) |
| 68 | +### Backend `.env` |
| 69 | + |
| 70 | +```bash |
| 71 | +SERVER_PORT=3000 |
| 72 | +IPFS_STORAGE_MODE='local' # 'dev', 'local', or 'prod' |
| 73 | +IPFS_LOCAL_API_URL='http://127.0.0.1:5001' # if IPFS_STORAGE_MODE='local', then this is the API URL of the local IPFS node |
| 74 | +IPFS_LOCAL_GATEWAY_URL='http://127.0.0.1:8080' # if IPFS_STORAGE_MODE='local', then this is the gateway URL of the local IPFS node |
| 75 | +PINATA_API_KEY='<Pinata API key>' # if IPFS_STORAGE_MODE='prod', then this is the Pinata API key |
| 76 | +PINATA_JWT_TOKEN='<Pinata JWT token>' # if IPFS_STORAGE_MODE='prod', then this is the Pinata JWT token |
| 77 | +PINATA_GATEWAY_URL='blush-peculiar-quail-924.mypinata.cloud' # if IPFS_STORAGE_MODE='prod', then this is the Pinata gateway URL |
| 78 | +PINATA_TASKS_GROUP_ID='<Pinata tasks group ID>' # if IPFS_STORAGE_MODE='prod', then this is the Pinata tasks group ID |
| 79 | +PINATA_IMAGES_GROUP_ID='<Pinata images group ID>' # if IPFS_STORAGE_MODE='prod', then this is the Pinata images group ID |
| 80 | + |
| 81 | +LISTENER_MODE= 'local' # 'local' or 'prod' |
| 82 | +INFURA_API_KEY='<Infura API Key>' # if LISTENER_MODE='prod', then this is the Infura API Key |
| 83 | +CONTRACT_ADDRESS=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 # TaskManager contract deployed address |
| 84 | +START_BLOCK=0 # Needed for fetching history of events. This has to be number of block inside which the TaskManager contract was deployed |
| 85 | +FINALITY_BLOCKS=5 # Number of blocks until events will be recorded into the database |
| 86 | + |
| 87 | +DATABASE_USER='app' |
| 88 | +DATABASE_PWD='app' |
| 89 | +DATABASE_HOST='localhost' |
| 90 | +DATABASE_NAME='task_board_db' |
| 91 | +DATABASE_PORT=5432 |
| 92 | +``` |
97 | 93 |
|
98 | 94 | --- |
99 | 95 |
|
100 | | -## Task Metadata (IPFS) |
101 | | - |
102 | | -Example task metadata JSON: |
| 96 | +## Token metadata and Task Data |
103 | 97 |
|
104 | 98 | ```json |
105 | 99 | { |
106 | | - "title": "Merkle Tree Challenge", |
107 | | - "description": "Build a Solidity contract that generates a Merkle root and verifies leaf inclusion using proof. Submit the root hash as the answer.", |
108 | | - "difficulty": "Medium", |
109 | | - "tags": ["Solidity", "Merkle", "Security"], |
110 | | - "validationType": "hash", |
111 | | - "solutionHash": "0xdeadbeef123...", |
112 | | - "testSuiteCID": "bafy...xyz", |
113 | | - "createdBy": "0x1234abcd...", |
114 | | - "createdAt": 1729849200 |
| 100 | + "name": "Stego Sleuth", |
| 101 | + "description": "Proof of your ability to uncover secrets hidden in plain sight.", |
| 102 | + "image": "ipfs://<cid>", |
| 103 | + "task": { |
| 104 | + "title": "Steganography in the Image", |
| 105 | + "description": "A PNG file hides a message in its least significant bits. Extract it to find the secret number.", |
| 106 | + "difficulty": "Easy", |
| 107 | + "tags": ["stego", "forensics", "binary"], |
| 108 | + "createdBy": "0x1234abcd...", |
| 109 | + "createdAt": 1729849200, |
| 110 | + "requirements": ["steganography tools", "hex editor"], |
| 111 | + "resources": ["ipfs://<cid>"] |
| 112 | + } |
115 | 113 | } |
116 | | -``` |
117 | | - |
118 | | -You can upload this with: |
119 | | - |
120 | | -* [Web3.Storage](https://web3.storage) |
121 | | -* [NFT.Storage](https://nft.storage) |
122 | | -* [Pinata](https://pinata.cloud) |
123 | | - |
124 | | -Use returned CID in your smart contract. |
125 | | - |
| 114 | +``` |
0 commit comments