lucifer

大家好,我是 Lucifer。之前写 DePIN 的时候,我随口带过一句”通过 DAO,你可以用 token 投票决定网络升级”。当时一笔带过,但这句话背后其实站着一种完整的组织形态——它没有 CEO、没有董事会、没有财务总监,金库里的钱却比很多上市公司还多。这就是今天的主角:DAO,去中心化自治组织。

先剧透一个真实结局。2022 年 4 月,有人对这种”没有老板的公司”下手了:一分钟前他还是个穷光蛋,靠闪电贷借来约 10 亿美元的投票权,投完票、搬完金库、把票原样还回去,前后约 13 秒,净落袋近亿美元。他利用了几个代码漏洞?零。偷了谁的私钥?没有。他走的完全是正规流程:提案、投票、执行,一步一步,手续齐全。Beanstalk 协议金库里约 1.82 亿美元,就这么被”合法地”搬空了。

这篇文章咱们就把这件事讲透:DAO 到底怎么运转?为什么能被一秒钟搬空?防御方手里那几张牌又怎么打?照例,全程用 Python 模拟关键过程,建议你亲手跑一遍——治理这件事,光看懂没用,得亲眼看到攻击的净利润那个数字,你才会记住”公示期”三个字值多少钱。

DAO 是什么:你家小区业委会的链上版

DAO 全称 Decentralized Autonomous Organization,去中心化自治组织。名字听着玄乎,原型你天天见——你家小区的业委会。

先对比着看。大多数小区没有业委会,雇一家物业公司:干不干、怎么干、钱花哪,物业经理说了算,业主只有交钱的份。少数小区成立业委会:没有”领导”,大事全体业主投票,公共维修基金躺在专门账户里,谁想动一分钱都得先过业主大会。传统公司就是那个物业公司——董事会拍板、财务总监管钱、公章锁在柜子里;DAO 就是那个业委会——人人有票,钱箱公有,章程上链。

也有人管它叫”全员持股的小饭馆”:从洗碗工到大厨人人有股份,明天进什么菜、涨不涨价,按持股数投票定,没有一个能单独拍桌子的人。行,那就把这家”小饭馆”拆开看,一共三个词:

  • 去中心化:没有单一控制人。不是”老板娘管钱、老板签字”,而是规则写在智能合约里,部署上链后连开发者自己都得按合约办事;
  • 自治:运转不靠人盯。提案通过就自动执行,投票截止就自动结算,合约像一台不会打瞌睡的行政机器;
  • 组织:一群人 + 一个金库 + 一套决策流程。这三样恰好就是一家公司的全部要素。

和传统公司摆在一起,区别一目了然:

维度 传统公司 DAO
决策层 董事会、股东会 持治理代币的人,按持币量投票
财务 财务总监管 U 盾、盖章 金库(treasury)由合约保管,动钱必须走提案
规则执行 HR、法务、公章 智能合约自动执行,没人能”通融”
变更章程 修订公司章程、工商变更 发起新提案,投票通过后升级合约

这张表里最要紧的一行是”财务”。DAO 的金库就是小区的公共维修基金——一个由合约看管的公共钱箱:钱躺在链上,任何人的私钥都碰不到它,唯一的取款方式是”投票通过的提案”。物业经理挪用维修基金这种事,在 DAO 里从物理上就不成立——不是不敢,是合约里压根没有这个函数。

先别急着鼓掌,这里有个常见误解要拆掉:没有老板 ≠ 没有规则。DAO 的规则不是消失了,而是从”写在公司章程 PDF 里、靠法院背书”变成了”写在智能合约里、靠区块链背书”。老板是没了,但老板的位置上站着一串不可篡改的字节码。这也埋下了本文的伏笔:当规则是代码时,谁能在代码允许的范围内拿到投票权,谁就是临时的老板——哪怕他只当一秒钟。

一套 DAO 怎么运转?业委会早演过一遍

治理系统的零件不少,但每个零件你家小区都有对应物。假设要动用公共维修基金翻新外墙,放在 DAO 里,全流程是这样走的:

1. 治理代币(governance token):业主大会的票权。 这是 DAO 的”股权”。绝大多数 DAO 用最朴素的规则:1 token = 1 票。你持有 1000 枚,投出的票就是 1000 票的分量——相当于业委会按房产面积算票权,你家 140 平就是 140 票。代币可以买、可以赚、可以领空投。问题也出在这:既然能买,投票权就能临时买。这句话请先记住,全文都为它服务。

2. 提案(proposal):业主动议。 任何人想动 DAO 的资源,都得先发提案,比如”把金库里 50 万 USDC 转给开发激励合约”。提案不是随便发的:通常要求持有一定数量的治理代币(比如流通量的 1%)或者押一笔保证金——相当于业委会规定”联名满 1% 才能上会议程”,防止有人天天刷垃圾提案。

3. 投票期:投票窗口。 提案上链后开一个固定的投票窗口,常见 3 到 7 天。所有人用治理代币投票:赞成、反对、弃权。像在电梯间贴出投票箱,给你一周时间去投。

4. quorum 检查:到会率。 投票结束,合约先检查法定参与率:如果参与票数连流通总量的 20% 都不到,提案直接作废——哪怕 100% 赞成也没用。业委会也这么干:全小区 300 户只来了 30 户,表决结果不算数。这条防的是”少数人偷偷替全小区做主”。

5. Timelock(时间锁):公示期。 提案通过后,并不立即执行,而是进一个锁定期,常见 24 到 48 小时。就像小区贴出告示”三天后动工施工”,这期间谁有意见都能提——链上任何人都能看到”三天后将执行这笔转账”,社区若发现提案有诈,守护者 multisig(后面细讲)可以趁公示期把它撤销。

6. 自动执行:到期开工。 公示期满没人拦,合约自动把提案内容执行掉。没人签字,没人盖章,也不存在”经办人今天请假”。

这一套不用从零造轮子,OpenZeppelin 的 Governor 合约就是社区标准实现:提案、投票、quorum、和 TimelockController 配合的延迟执行,全是现成积木。咱们用 Python 把这条时间线跑一遍——一个只有 10,000 枚治理代币的迷你 DAO,看一笔正经支出要走多久:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# -*- coding: utf-8 -*-
# 模拟一次正常的 DAO 治理流程:提案 -> 投票 -> quorum 检查 -> Timelock -> 执行
# 简化模型,纯标准库

SUPPLY = 10_000 # 治理代币流通总量
PROPOSAL_THRESHOLD = 100 # 提案权门槛:持有 >= 100 枚(1% 流通量)
QUORUM = 0.20 # 法定参与率:>= 20% 流通量参与投票,提案才有效
PASS_RATE = 0.50 # 通过线:赞成票占全部投票 > 50%
TIMELOCK_HOURS = 48 # 提案通过后锁定 48 小时才可执行

# 金库(treasury):DAO 的钱包,由合约保管,谁都摸不到私钥
treasury_usd = 1_000_000.0 # 金库存放 100 万 USDC

# 股东名册(地址 -> 持币量)
holders = {
"Alice(提案人)": 250,
"Bob": 1_800,
"Carol": 1_200,
"Dave": 300,
"Eve": 900,
"社区基金": 5_550,
}

proposal_title = "将金库 50% 资金用于季度开发激励"

def stamp(t):
return f"T+{t}h"

print("=" * 62)
print("DAO 正常治理流程模拟")
print(f"流通代币 {SUPPLY:,} 枚 | quorum >= {QUORUM:.0%} | timelock {TIMELOCK_HOURS}h")
print("=" * 62)

t = 0
print(f"\n[{stamp(t)}] 提案阶段")
alice = holders["Alice(提案人)"]
if alice >= PROPOSAL_THRESHOLD:
print(f" Alice 持有 {alice} 枚 >= 提案门槛 {PROPOSAL_THRESHOLD} 枚,提案合法上链")
print(f" 提案内容:{proposal_title}(转出 500,000 USDC)")
else:
print(" Alice 持币不足,提案被合约直接拒绝")
raise SystemExit

t += 1
print(f"\n[{stamp(t)}] 投票期开始(为期 7 天 = 168 小时)")
tally = {"赞成": 0, "反对": 0}
votes = [
("Alice(提案人)", "赞成", 250),
("Bob", "赞成", 1_800),
("Carol", "赞成", 1_200),
("Dave", "反对", 300),
("Eve", "赞成", 900),
]
for who, choice, amount in votes:
tally[choice] += amount
print(f" {who} 投{choice} {amount:,} 票")

t += 168
total_votes = tally["赞成"] + tally["反对"]
print(f"\n[{stamp(t)}] 投票期结束,合约自动检查")
print(f" 参与票数 {total_votes:,} / {SUPPLY:,} = {total_votes/SUPPLY:.1%}(quorum 要求 >= 20%)")
yes_share = tally["赞成"] / total_votes
print(f" 赞成 {tally['赞成']:,} vs 反对 {tally['反对']:,},赞成率 {yes_share:.1%}")

if total_votes / SUPPLY >= QUORUM and yes_share > PASS_RATE:
print(" => quorum 达标且赞成过半,提案通过,进入 Timelock")
else:
print(" => 未通过,流程终止")
raise SystemExit

t += TIMELOCK_HOURS
print(f"\n[{stamp(t)}] Timelock 到期(通过后锁定 {TIMELOCK_HOURS}h,期间社区无异议)")
payout = treasury_usd * 0.5
treasury_usd -= payout
print(f" 合约自动执行:转出 {payout:,.0f} USDC 到开发激励合约")
print(f" 金库余额:{treasury_usd:,.0f} USDC")
print(f"\n整个流程耗时 {t} 小时(约 {t/24:.0f} 天),其中 {TIMELOCK_HOURS} 小时是给社区留的反悔窗口")

代码说明:

先说直觉,这段代码就是业委会流程的翻译:门槛(能不能上议程)→ 投票 → 两道检查(到会率 + 赞成率)→ 公示期 → 自动划款。对着代码看几个关键参数:

  • QUORUM = 0.20:到会率 20%。到不了会,赞成票再多也白搭;
  • PASS_RATE = 0.50:赞成票要过半,防止 30% 的人替 100% 的人做主;
  • TIMELOCK_HOURS = 48:通过后还要公示 48 小时,这是留给全小区的”反悔窗口”;
  • 金库存 100 万 USDC、提案只转一半,模拟的是最普通的日常支出。

真实运行输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
==============================================================
DAO 正常治理流程模拟
流通代币 10,000 枚 | quorum >= 20% | timelock 48h
==============================================================

[T+0h] 提案阶段
Alice 持有 250 枚 >= 提案门槛 100 枚,提案合法上链
提案内容:将金库 50% 资金用于季度开发激励(转出 500,000 USDC)

[T+1h] 投票期开始(为期 7 天 = 168 小时)
Alice(提案人) 投赞成 250 票
Bob 投赞成 1,800 票
Carol 投赞成 1,200 票
Dave 投反对 300 票
Eve 投赞成 900 票

[T+169h] 投票期结束,合约自动检查
参与票数 4,450 / 10,000 = 44.5%(quorum 要求 >= 20%)
赞成 4,150 vs 反对 300,赞成率 93.3%
=> quorum 达标且赞成过半,提案通过,进入 Timelock

[T+217h] Timelock 到期(通过后锁定 48h,期间社区无异议)
合约自动执行:转出 500,000 USDC 到开发激励合约
金库余额:500,000 USDC

整个流程耗时 217 小时(约 9 天),其中 48 小时是给社区留的反悔窗口

看到那个 T+217h 了吗?一笔 50 万美元的支出,从提案到执行花了 9 天。换成物业公司,经理一个电话当天就办了。慢,是 DAO 故意为之:这 9 天里每一步都写在链上,每一分钱的去向人人可查,谁也赖不了账——用效率换透明,这笔买卖 DAO 们认为划算。

正常治理流程时间线:从提案到执行 217 小时,其中 48 小时是留给社区反悔的公示期

治理权是价值,还是画饼?

写到这里必须泼半盆冷水,呼应一下之前 代币经济学 那篇讲过的 UNI 案例:Uniswap 协议本身赚得盆满钵满,但 UNI 代币长期只有治理权、不分协议收入——好比你在那家全员持股的小饭馆里握着一摞股份,饭馆天天爆满,老板却说”股份只能用来投票选菜单,分红没有这回事”。持有者看着协议日入千万,自己分不到一分钱。

这就是治理权的尴尬之处——它是价值,但只在”治理权真的能决定钱的去向”时才是。如果一个 DAO 的提案机制形同虚设、金库实际由核心团队 multisig 把持,那你手里的治理代币就是一张能参加班会但不能决定任何事的出席证。反过来,治理权强大到”能直接动金库”,它就非常值钱——值钱到本文接下来的主人公,愿意为一个区块的投票权付出上千万成本。

闪电贷治理攻击:一秒钟搬空 1.82 亿

主角登场前,先补 30 秒闪电贷的课(DeFi 那篇 只提了一句):闪电贷是 DeFi 的邪道发明——你在同一笔交易内借出巨额资金、用完立即归还;还不上,整笔交易回滚,就像什么都没发生过。不需要抵押,因为”还不上就等于没借”。像跟银行约好”钱借你一秒,出这个门必须原样奉还,否则刚才发生的一切作废”。它平时用来套利和清算,用歪了,就是瞬间白嫖巨额资金的传送门。

现在把闪电贷和”1 token = 1 票”拼在一起,一个恐怖的化学反应出现了:既然投票权按持币量即时计算,而闪电贷能瞬间借到任意多的币,那攻击者就能在单一交易内瞬间成为最大股东。你琢磨一下这个画面:一个人推着空购物车进超市,在门口租到一辆堆满商品的购物车,推进去刷完码结账,把空车还回门口,拎着货走人——超市全程没发现任何异常。攻击四步走:

  1. 闪电贷借入巨额治理代币——零抵押,一分钟前还是穷光蛋,此刻已是 DAO 的”控股方”;
  2. 把借来的票全部投给恶意提案——提案内容通常朴实无华:”将金库全部资金转给攻击者地址”;
  3. 投票通过,并在同一区块内执行——只要协议没有 Timelock,提案通过后可以当块调用执行;
  4. 归还闪电贷——代币原样还回去(投票记录已上链,不影响结果),净利润 = 偷到的钱 − 闪电贷手续费 − 贿选成本 − gas。

整个过程发生在同一个区块里,前后十几秒。社区还在睡觉,金库已经清零。

Beanstalk 复盘:一个穷光蛋的 13 秒

把时钟拨回 2022 年 4 月 17 日。主角是一个匿名地址,口袋里几乎没有资产,目标是以太坊上的稳定币协议 Beanstalk——金库里躺着约 1.82 亿美元。来看他这 13 秒的完整剧本:

  • 踩点:Beanstalk 的治理规则里有个”紧急提案”通道——只要拿到 2/3 超多数赞成票,提案可以跳过正常投票期、当块提交当块执行。设计初衷是应对紧急情况(比如协议遭攻击时紧急刹车),实际效果是亲手拆掉了自己的安全带:没有等待期,也没有 Timelock——等于小区章程写着”只要有 2/3 业主同意,可以不公示直接动工”;
  • 筹票:攻击者通过 Aave 等协议闪电贷借入约 10 亿美元资产,换成 BEAN 代币——一瞬间,他手里握有约 2/3 以上的投票权。光有借来的票还不够稳,他先捐了两笔小额慈善款刷”好感票”(其中一笔是 25 万枚 Beans 打给乌克兰救济地址):社区里不少老实人一看”这是善人啊”,跟着投了赞成票。这可能是史上回报率最高的一笔贿选——用 25 万枚代币的捐款,撬动 1.82 亿美元的金库;
  • 收割:恶意提案当块提交、当块通过、当块执行,金库约 1.82 亿美元转入攻击者地址,闪电贷原路还清,前后约 13 秒;
  • 结账:扣除闪电贷手续费、慈善捐款等全部成本,净落袋约 7600 万到 8000 万美元。一分钟前的穷光蛋,一分钟后的亿万富翁。

它为什么能得手?两个条件缺一不可:第一,通过条件只看票数——借来的票和攒了十年的票一样重,持币人身份完全不校验,相当于业委会不查房本,谁举着票就算谁的;第二,通过后当块即可执行——没有公示期,社区从投票通过到钱被转走,反应时间是零。一句话总结:Beanstalk 把”谁能投票”这个最重要的问题,外包给了市场上最好买的东西。

反面教材:Build Finance DAO

有人会说:那我把投票门槛调高点、把流程堵严实点,总能防住吧?2022 年 2 月的 Build Finance DAO 表示:不太行。一个叫 suho.eth 的用户悄悄攒够多数投票权,然后让一个”允许单方面增发代币”的提案合法通过——注意,不是钻空子,是从提案、投票到执行每一步都合规,合约层面无懈可击。他借此控制了代币合约和金库,卷走约 47 万美元。社区连用规则拦截的机会都没有:你要拦,拿什么拦?规则就是他刚合法改完的那份。

但后续发展反而给所有人上了一课:社区宣布抛弃被夺走的合约和 BUILD 代币,整个组织 fork 重建——名字接着叫,业务接着做,只是换了个新合约地址重新开张。攻击者最后攥着的,是一个金库已空、没人认账的空壳代币。像抢银行抢到手才发现,里面全是代金券,而且全城商户刚集体宣布只认新版代金券。

这两个案例放一起看,结论有点哲学:代码挡不住合法的投票,但社区可以搬家。DAO 的最后一道防线不在链上,而在共识里。当然,靠”跑路重建”防守的成本极高——正经的防御设计,还是要在合约层面把门焊死。这就到了本文最实用的部分。

防御设计:把攻击面一条条焊死

对着上面那张攻击四步清单,防御方手里其实有好几张牌,咱们逐条打:

1. Timelock——最有效的一招,本质就是”公示期”。 闪电贷借来的票有个物理属性:只能撑一个区块。借贷必须在同一笔交易里完成归还,攻击者从借票、投票到执行,必须压缩在十几秒内。现在把业委会那条规矩搬上来:提案通过了?行,先贴 24 小时告示再动工。等到执行那一刻,借来的票早就还回去了——提案要么已在公示期内被社区发现并撤销,要么攻击者只能去真实市场花一天以上时间囤票,成本天量且全程暴露。绝大多数治理攻击在加入 Timelock 后归零。

Timelock 为什么能废掉闪电贷:借来的票保质期只有一个区块,公示期却要撑 24 小时

2. quorum 门槛。 最低参与率能挡住低热度协议被少数人偷家。但注意它的局限:闪电贷借来的票也会计入参与率,攻击者甚至顺手把 quorum 都凑够了。quorum 是必要非充分条件。

3. 提案押金与身份门槛。 提案要押保证金(通过才退还、失败即罚没),或者要求提案人持币超过一定时间,抬高”提案即弃子”的攻击成本。

4. veToken 锁仓加权。 这是 代币经济学 里 veCRV 的思路:投票权重不看持币量,而看”持币量 × 锁仓时长”——锁 4 年票权满格,不锁几乎零票权。像业主大会只认”持有房本满 4 年”的老业主,刚过户的新房本说话分量约等于零。闪电贷借来的票是刚到手的,锁仓时长为零,权重趋近于零,攻击从数学上就不成立。Curve 用这套机制稳了这么多年不是没道理的。

5. 多签 + DAO 混合。 把金库拆两层:日常大额资金放在守护者 multisig 手里(MPC 那篇 讲的多签思路——多人共持密钥,缺一不可),小额和小步快跑的决策交给 DAO 投票。代价是去中心化程度打折,但这正是去中心化的现实:安全性和纯度经常是一对 trade-off。

纸上谈兵到此为止,咱们把 Timelock 的效果跑个对照实验。先说直觉:同一个攻击者、同一套攻击四步,投票环节完全一致,唯一变量是协议有没有 Timelock——就看这一个变量,能把结局翻得多彻底:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# -*- coding: utf-8 -*-
# 闪电贷治理攻击对照实验:同一个攻击者、同一套攻击四步,
# 唯一区别:协议有没有 Timelock。简化模型,纯标准库。

FLASH_FEE = 0.0009 # 闪电贷手续费 0.09%(Aave 历史费率)
GAS = 200.0 # 攻击交易的 gas,折算成美元
BRIBE = 250_000.0 # 贿选成本:一笔慈善捐款换"好感票"(呼应 Beanstalk 的 25 万枚 Beans 捐款)

SUPPLY = 100_000_000 # 治理代币流通量,价格按 1 美元计
TREASURY = 182_000_000 # 金库 1.82 亿美元(Beanstalk 同款规模)
QUORUM = 0.40 # 参与 40% 流通量才算数
PASS_RATE = 2 / 3 # 需 2/3 超多数赞成(紧急提案口径)

attacker_balance = 0 # 攻击者原有持仓:0 枚
borrowed = int(SUPPLY * 0.60) # 闪电贷借入 60% 流通代币

print("=" * 62)
print("闪电贷治理攻击对照实验")
print("=" * 62)
print(f"金库 ${TREASURY:,} | 流通 {SUPPLY:,} 枚 | 攻击者持仓 {attacker_balance} 枚")
print(f"\n[第 1 步] 同一笔交易内闪电贷借入 {borrowed:,} 枚(价值 ${borrowed:,})")
print(f"[第 2 步] 捐出 ${BRIBE:,.0f} 做慈善换好感票,再把 {borrowed:,} 枚全部赞成投出")
honest_yes, honest_no = 1_500_000, 4_500_000 # 老实人:一部分被捐款打动,其余反对
print(f" 老实人只投了 {honest_yes:,} 赞成 / {honest_no:,} 反对")

def run_attack(label, timelock_h):
print("\n" + "-" * 62)
print(f"场景{label}:" + ("有 24h Timelock" if timelock_h else "无 Timelock(当块可执行)"))
print("-" * 62)
yes = borrowed + honest_yes
no = honest_no
total = yes + no
print(f"[第 3 步] 恶意提案(把金库全部转给攻击者)表决:")
print(f" 赞成 {yes:,} vs 反对 {no:,} | 参与率 {total/SUPPLY:.1%}(quorum 40%)| 赞成率 {yes/total:.1%}(通过线 66.7%)")
passed = total / SUPPLY >= QUORUM and yes / total > PASS_RATE
if not passed:
print(" 提案未通过,攻击失败")
stolen = 0.0
elif timelock_h:
print(f" 提案通过,但先进 Timelock 锁 {timelock_h} 小时,不可执行")
print(f" 锁定第 {timelock_h} 小时内:社区监控发现恶意提案,守护者紧急撤销")
print("[第 4 步] 执行被取消,金库分文未动")
stolen = 0.0
else:
print(" 没有 Timelock:提案通过后同一区块内自动执行")
print(f"[第 4 步] 金库 ${TREASURY:,} 被搬空 -> 攻击者")
stolen = float(TREASURY)
fee = borrowed * FLASH_FEE
cost = fee + BRIBE + GAS
net = stolen - cost
print(f"\n 成本:闪电贷手续费 ${fee:,.0f} + 贿选 ${BRIBE:,.0f} + gas ${GAS:,.0f} = ${cost:,.0f}")
print(f" 净利润 = ${stolen:,.0f} - ${cost:,.0f} = ${net:,.0f}")
return net

p1 = run_attack("一(Beanstalk 同款设定)", None)
p2 = run_attack("二(仅加一条 Timelock)", 24)

print("\n" + "=" * 62)
print(f"对照结论:无 Timelock 净利润 ${p1:,.0f};有 24h Timelock 净利润 ${p2:,.0f}")
print("闪电贷借来的票只能撑一个区块,Timelock 一锁,攻击者倒贴手续费和贿款")

真实运行输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
==============================================================
闪电贷治理攻击对照实验
==============================================================
金库 $182,000,000 | 流通 100,000,000 枚 | 攻击者持仓 0 枚

[第 1 步] 同一笔交易内闪电贷借入 60,000,000 枚(价值 $60,000,000)
[第 2 步] 捐出 $250,000 做慈善换好感票,再把 60,000,000 枚全部赞成投出
老实人只投了 1,500,000 赞成 / 4,500,000 反对

--------------------------------------------------------------
场景一(Beanstalk 同款设定):无 Timelock(当块可执行)
--------------------------------------------------------------
[第 3 步] 恶意提案(把金库全部转给攻击者)表决:
赞成 61,500,000 vs 反对 4,500,000 | 参与率 66.0%(quorum 40%)| 赞成率 93.2%(通过线 66.7%)
没有 Timelock:提案通过后同一区块内自动执行
[第 4 步] 金库 $182,000,000 被搬空 -> 攻击者

成本:闪电贷手续费 $54,000 + 贿选 $250,000 + gas $200 = $304,200
净利润 = $182,000,000 - $304,200 = $181,695,800

--------------------------------------------------------------
场景二(仅加一条 Timelock):有 24h Timelock
--------------------------------------------------------------
[第 3 步] 恶意提案(把金库全部转给攻击者)表决:
赞成 61,500,000 vs 反对 4,500,000 | 参与率 66.0%(quorum 40%)| 赞成率 93.2%(通过线 66.7%)
提案通过,但先进 Timelock 锁 24 小时,不可执行
锁定第 24 小时内:社区监控发现恶意提案,守护者紧急撤销
[第 4 步] 执行被取消,金库分文未动

成本:闪电贷手续费 $54,000 + 贿选 $250,000 + gas $200 = $304,200
净利润 = $0 - $304,200 = $-304,200

==============================================================
对照结论:无 Timelock 净利润 $181,695,800;有 24h Timelock 净利润 $-304,200
闪电贷借来的票只能撑一个区块,Timelock 一锁,攻击者倒贴手续费和贿款

注意这两个场景里,投票环节一模一样:参与率 66.0%、赞成率 93.2%,quorum 和超多数条款全被借来的票填满了——所以光靠”提高 quorum”根本防不住。唯一的变量是 Timelock,它把攻击净利润从 +$181,695,800 干到了 −$304,200:金库分文未动,攻击者倒贴 54,000 美元闪电贷手续费、250,000 美元贿款和 200 美元 gas,一个区块的”老板梦”做完,还欠了一屁股债。顺便说一句,攻击者的钱也不是绝对安全的——万一他真偷着了,DeFi 保险 那篇风险表里的”治理攻击 Beanstalk $182M”正是保险协议要覆盖的头号场景。

同一套攻击流程两种结局:没有公示期净赚 $181,695,800,有了 24 小时公示期倒贴 $304,200

总结

咱们把今天的内容串一遍:

  • DAO = 链上的业委会:金库是合约看管的公共钱箱,规则由合约执行,决策由持币人投票。没有老板,但规则以代码形式活着;
  • 运转五件套:提案(有门槛)→ 投票期 → quorum 检查 → Timelock 公示期 → 自动执行。OpenZeppelin Governor 是标准实现,别自己造轮子;
  • 治理权的价值取决于它能不能真的动钱:能,它就很值钱(UNI 的反面是它只有治理权却分不到钱,画饼);
  • 闪电贷治理攻击是”1 token = 1 票”的致命软肋:借票、投票、当块执行、还贷,四步一气呵成,穷光蛋一分钟变亿万富翁。Beanstalk 用 1.82 亿美元证明了这个模型;Build Finance 则证明合法投票连合约都拦不住,最后一道防线是社区 fork 搬家的能力;
  • 防御优先级:Timelock 公示期是性价比之王(借来的票只能活一个区块,撑不过一夜),veToken 锁仓加权从数学上废掉闪电贷票权,quorum / 提案押金抬高成本,multisig 混合架构兜底。

记住那个对照实验的数字:+$181,695,800 vs −$304,200,一行 Timelock 代码的差距。下回再有人跟你吹”链上治理完美无缺”,你可以把这个数字拍他脸上——去中心化不是免费的,安全也不是。咱们下篇再见。

相关阅读


 评论


博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议

本站使用 Material X 作为主题 。
载入天数...载入时分秒...