GenchevDan/CodeBounty

GitHub: GenchevDan/CodeBounty

基于智能合约的开源微额赏金平台,通过链上 USDC 托管和赢家通吃机制激励陌生人为小型 bug 修复提交补丁。

Stars: 0 | Forks: 0

# [BUG] GitHub issue 是一个没有资金支持的愿望 — `status: wontfix-for-free` **组件:** 开源维护 · 没人愿意免费做的五分钟修复 **严重程度:** 单个问题较低,总体无限大 **仓库:** `GenchevDan/codebounty` · **在线演示:** https://codebounty-arc.vercel.app/ ### 复现步骤 1. 提交一个 issue:`off-by-one in cursor pagination, last page repeats the final item`。 2. 等待。 3. ... ### 预期结果 一个陌生人花十五分钟,提交一个补丁,并得到你很乐意支付的那一美元。 ### 实际情况 这个 issue 悬而未决了九个月。在结构上,它就是一个愿望。它背后没有资金支持,因此项目外部的任何人都没有理由去碰它,而项目*内部*的人正忙于不去做他们自己版本的那同样的五分钟修复。瓶颈从来都不是技术能力——而是因为“我愿意为此付一块钱”这件事,你实际上无法以一种具有约束力的方式*对整个互联网说出来*。 ### 根本原因 意愿不能作为抵押。一个存在于评论 thread 中的承诺奖励可能会蒸发、引发争议,或者在代码提交后被直接无视。除非在任何工作开始之前就把这一美元*锁定*——并且*原子地*支付给选定的唯一获胜者——否则激励无法兑现,也就不会有理性的人出现。 ### 修复方案 先让这一美元变成现实。`postBounty(...)` 会在发布的那一刻将 USDC 托管在链上;修复者可以在花一分钟时间之前就看到资金已经承诺;`acceptFix(...)` 会将全部赏金交给作者选定的那一份提交,这在同一个 transaction 中完成,中间没有任何作者可以退出的步骤。 合约就是结算层。判断权仍然属于人类——链无法为补丁打分,它也不假装能做到。 ### 赏金流程 ``` postBounty(taskUri, taskHash, lang, window) payable, [$0.50 .. $2.00] → Status.Open, escrow locked the task body rides inline as compact JSON in taskUri; taskHash = keccak256(it) so it can't be edited later submitFix(id, fixUri, fixHash, note) anyone but the author, one per address, free* → emits FixSubmitted fixUri = gist/commit/PR pointer · note ≤ 280 chars · fixHash = keccak256(fixUri + " " + note) *free = no protocol fee; you still pay Arc's USDC gas, which is the whole anti-spam story acceptFix(id, fixIndex) AUTHOR ONLY → Status.Paid, 100% to that fixer, atomically — or, after the deadline — refundExpired(id) AUTHOR ONLY → Status.Refunded, 100% back to the author ``` 给阅读代码的人的提示: - **赢家通吃是核心。** 一个被接受的修复拿走全部 `b.amount`;其他所有人一无所获。这就是让 1 美元赏金值得一个陌生人花一晚上的时间,而不是将其分割成毫无价值的粉尘的原因。 - **`acceptFix` 是唯一的人类资金决策**,并且它仅限于 `msg.sender == b.author`。没有所有者,没有 agent,没有第三方可以动用哪怕一 wei 的托管资金。仅有两条退出路径:*接受时给赢家* 和 *退款给作者*。 - **两条支付路径都遵循 checks-effects-interactions 模式:** 状态在 transfer 之前就会翻转为终态,因此重入调用只会触发 `NotOpen` 并 revert。 - **合约永远不是金库。** `receive()`/`fallback()` 会 revert (`NoLooseFunds`);它能持有的唯一代币就是仍然处于 `Open` 状态的赏金。没有任何东西会累积;也没有任何东西会被保留。 - **边界是常量,而不是配置项:** `MIN_BOUNTY = 5e17`, `MAX_BOUNTY = 2e18`, `MIN_WINDOW = 1 hours`, `MAX_WINDOW = 30 days`。由于不存在 admin,部署后没有任何管理员可以移动这个区间。 ### 为什么这只有在 Arc 上才说得通 追溯一个赏金生命周期的费用明细:**发布、提交、接受、领取支出**——三到四个链上事件,每一个都是强制性的,围绕着一笔最高两美元的奖金。在一个 gas 是你需要持有并盯盘的、波动的独立 token 的链上,那笔开销会把奖金吞噬殆尽:你将不得不花费第二种资产中的真实资金,仅仅为了送出第一种资产中的一美元。“多个修复者竞争,一人获胜,几美分的资金易手”的单位经济效益在这种摩擦下根本无法存活。 Arc 通过 `msg.value` 以原生 USDC 进行结算——没有 ERC-20,没有 `approve` 往返——因此托管资金、奖金以及这条路径上的每一笔 transaction 费用,都是以赏金所涉及的同一个美元来计价的。发布的金额、支付的金额、发布和接受的成本:一个记账单位,在接受时具有亚秒级的最终确定性。 这种合并——费用货币 == 奖金货币 == 记账单位——是让美分级、竞相修复的赏金得以存在的前提条件,也正是这个前提条件让编码 *agent* 能够赚取 90 美分的赏金,而不至于让元成本远远超过奖励本身。 ### Agent(什么是真实的,什么是演示) - `app/api/x402/submit/route.ts` — **一个真实的服务器路由**,使用纯正的 HTTP-402 线上传输协议格式(`402` challenge → `X-PAYMENT` → `X-PAYMENT-RESPONSE`)。由于提交不携带协议费用,诚实的模型是 **先付款后证明**:一个 agent 从它自己的 wallet 调用 `submitFix`(支付 Arc 的 USDC gas——作为反垃圾机制),然后出示 tx hash;该路由获取 receipt,确认 `to == CONTRACT_ADDRESS`,解析 `FixSubmitted` event,重新计算 `keccak256`,并使用 600 秒的新鲜度窗口对其进行重放边界限制。没有 facilitator,没有受信任的 relayer——它直接针对链本身进行验证。 - `agent/triage.mjs` — **一个真实的自主监控器**,只读,**零资金权限**。每 12 秒轮询一次开放的赏金,获取每个修复指针以检查其是否可访问,重新计算链上哈希以捕获在提交后被替换的补丁,标记重复的哈希(复制并重新提交),并按最早优先的原则对干净的候选项进行排名,以便为作者提供参考。它不能接受任何东西——只有 `b.author` 可以。 - `agent/submit-demo.mjs`, `agent/demo.mjs` — **演示。** 第一个从注资的修复者 wallet 端到端驱动 x402 循环;第二个播种一个完整的“发布→竞争→接受→支付”周期,以便你可以实时观看。 ### 已知限制(已公开提交,未作隐藏) - 链上保证的是**资金**,而不是**价值**。没有 oracle 来决定补丁是否正确;注资作者自己做决定,这与真实的代码审查运作方式一致。 - 截止日期限制的是修复者等待的时间,而不是他们能否拿到钱。作者可以执行 `refundExpired` 而不是接受。已提前声明,以免任何人感到惊讶。 - 剽窃是在链下对抗的:内容哈希 + 首次提交时间戳 + triage agent。合约不对原创性进行裁决。 ## 更新日志 ### [deployed] — Arc 测试网 - `CodeBounty.sol` (Solidity 0.8.35, MIT) 已在 `0xA773A6f6a0C2C7b627a2F19Ed5725cc2CD895E86` 上线。 - 托管区间 `[$0.50, $2.00]` 和时间窗口 `[1h, 30d]` 已作为不可变常量固化。 ### [已添加] - `postBounty` / `submitFix` / `acceptFix` / `refundExpired` — 完整的赢家通吃循环。 - 内联任务 payload:bug/TODO 正文以紧凑的 JSON 格式存储在 `taskUri` 中,由 `taskHash` 承诺。 - 单个修复的防篡改证据:`fixHash = keccak256(fixUri + " " + note)`,由 triage agent 和 x402 路由重新校验。 - `latest(n)`, `getBounty`, `getFixes`,加上实时统计(`openCount`, `paidVolume`, `paidCount`, `refundedCount`, `totalFixes`, `fixerCount`)——仅作展示用,绝不会限制资金操作。 - 带有链上自我验证的真实 x402 提交路由。 - 自主只读 triage agent。 ### [已保护] - `acceptFix` / `refundExpired` 采用 CEI 模式并带有终态保护;重入将触发 revert `NotOpen`。 - `receive`/`fallback` 触发 revert `NoLooseFunds` — 合约余额 == Σ 处于开放状态的托管资金,始终如此。 - 每个地址对每个赏金只能提交一次(`hasSubmitted`);作者不能修复自己的问题(`AuthorCannotFix`)。 ### [运行] ``` npm install npm run dev # http://localhost:3000 node agent/triage.mjs # autonomous read-only triage (polls every 12s) node agent/demo.mjs # seed a live post -> compete -> accept -> payout (needs a funded wallet) ``` ## 环境 ``` chain Arc testnet · eip155:5042002 settlement native USDC, 18 decimals, via msg.value (no ERC-20, no approvals) contract CodeBounty.sol · 0xA773A6f6a0C2C7b627a2F19Ed5725cc2CD895E86 explorer https://testnet.arcscan.app/address/0xA773A6f6a0C2C7b627a2F19Ed5725cc2CD895E86 front end Next.js · ethers v6 reported-by Daniel Genchev (@GenchevDan) ``` 如果你能重现最初的 bug——一个背后没有资金支持且变得陈旧的开放 issue——就在它上面托管一美元,并将修复任务分配给最先关闭它的人。这就是那个补丁。
标签:Web3, Web3应用, 众包, 加密货币, 区块链, 威胁情报, 开发者工具, 智能合约, 自动化攻击