3. 区块哈希作为随机源:使用blockhash()的原理,安全风险分析,为什么不能单独依赖区块哈希
好,咱们来聊聊链上随机数生成里最“朴素”的一种做法——直接用区块哈希。
说实话,我刚入行那会儿也觉得这招挺妙的。区块哈希嘛,每个块都有,谁都能验证,看起来又随机又透明。但后来在真实项目里踩过坑,才明白这里面水有多深。
3.1 blockhash() 的原理:它到底怎么工作的?
简单说,Solidity 里有个全局函数叫 blockhash(uint blockNumber)。你传一个区块号进去,它返回那个区块的哈希值。
但有个关键限制:你只能拿到最近 256 个区块的哈希。再早的,就返回 0 了。
举个例子:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleRandom {
function getRandomNumber() public view returns (uint256) {
// 直接用当前区块的哈希
return uint256(blockhash(block.number - 1));
}
}
这段代码看着挺简单吧?嗯,问题就出在这个“简单”上。
3.2 安全风险分析:为什么不能单独依赖区块哈希?
我直接说结论:单独用区块哈希做随机源,在大多数场景下都不安全。原因有三点,咱们一个一个拆开看。
3.2.1 矿工可以操纵区块哈希
这是最核心的问题。矿工在挖矿时,可以尝试不同的 nonce 值,直到找到一个对自己有利的哈希。
你想想看,矿工手里有巨大的算力。他完全可以试几百万次,直到哈希值满足他的需求。比如你的合约里写着“如果哈希值大于某个数,我就赢”,那矿工就能一直试,直到试出那个数。
我在一个 DeFi 项目里就遇到过这种事。项目方用区块哈希决定谁能获得空投,结果被矿工盯上了。矿工连续挖了好几个块,挑了一个对自己最有利的哈希提交。最后项目方赔了不少钱。
3.2.2 区块哈希可以被预测
你可能觉得:“矿工操纵哈希,那我不选当前块,选未来块不就行了?”
嗯,想法很好,但现实很骨感。如果你在交易里指定了“用第 N 个块的哈希”,那矿工在打包你的交易时,其实已经知道第 N 个块的大致范围了。
说白了,只要你的交易被广播出去,矿工就能提前计算各种可能性。他可以选择在哪个块打包你的交易,从而间接影响结果。
3.2.3 重入攻击与回滚问题
这个坑我印象特别深。有一次审计一个项目,发现他们用区块哈希做随机数,然后根据结果给用户发奖励。
问题出在哪?如果矿工挖出一个块,发现哈希值对自己不利,他可以选择不把这个块广播出去,或者干脆回滚到上一个块。这在某些公链上是可以做到的。
更可怕的是,如果你的合约逻辑允许重入,攻击者可以在一次交易里反复调用随机函数,直到拿到满意的结果。
3.3 什么时候可以勉强用?
说实话,我一般不推荐用区块哈希。但如果你非要用,至少得满足这几个条件:
- 低价值场景:比如抽奖的奖品价值很低,矿工没动力攻击
- 结合其他熵源:比如把区块哈希和用户地址、nonce 混在一起
- 使用未来区块:比如指定 100 个块之后的哈希,但这也只能降低风险,不能消除
举个例子,一个相对“好一点”的做法:
// 不推荐,但比直接用要好一点
function getBetterRandom(address user, uint256 nonce) public view returns (uint256) {
// 结合未来区块哈希和用户输入
uint256 futureBlock = block.number + 100;
return uint256(
keccak256(
abi.encodePacked(
blockhash(futureBlock), // 注意:这里可能返回0
user,
nonce
)
)
);
}
但你看,这里又有个新问题:blockhash(futureBlock) 在 futureBlock 还没产生时返回的是 0。所以你得等 100 个块之后才能调用这个函数。这又引入了延迟问题。
3.4 避坑指南
我总结了几条实战经验,你记一下:
- 不要单独依赖区块哈希——这是底线
- 不要用 block.number 作为随机种子——它太容易被预测了
- 不要相信“未来区块”的承诺——矿工可以重组链
- 如果必须用,至少混入用户私密输入——比如签名、nonce
3.5 小结
区块哈希作为随机源,原理简单,实现也简单。但它的安全缺陷是结构性的,不是靠打补丁能解决的。
我记得有一次在技术群里,有人问:“我就用区块哈希抽个奖,能有多大问题?”结果第二天就有人贴出了被攻击的交易记录。嗯,从那以后,我再也不敢小看这个问题了。
下一章,咱们聊聊 VRF 是怎么解决这些问题的。说白了,VRF 就是给随机数生成加了一把“密码学锁”,让矿工想作弊都无从下手。