Attack合约调用GuessTheRandomNumberChallenge的guess()函数持续回退求助
问题排查:调用GuessTheRandomNumberChallenge.guess()触发回退的原因
以下是几种可能导致交易回退的核心原因及对应解决方案:
1. 攻击合约未实现ETH接收函数
当guess()调用成功时,目标合约会向攻击合约转账2 ETH。如果攻击合约没有定义receive()或fallback()函数,ETH转账会失败,进而触发整个交易回滚。这是最常见的触发回退的原因。
修复方式:在攻击合约中添加ETH接收函数:
// 必须添加,用于接收目标合约转来的奖励ETH receive() external payable {}
2. 猜测的数值x计算错误
虽然错误的猜测不会直接触发交易回退,但如果测试代码中验证了isComplete()状态,会误以为是交易回退。目标合约的答案计算逻辑是在构造函数中执行的:
answer = uint8(uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp))));
需要确保攻击合约(或测试代码)中使用与构造函数完全一致的参数计算x:
- 如果在测试中,部署目标合约后立刻计算答案,需保证
block.number和block.timestamp与构造函数执行时一致(可在同一个setUp或测试函数中连续执行) - 如果攻击合约在目标合约部署后的区块调用,需确保能正确获取目标合约构造时的
blockhash(block.number -1)(区块哈希在256个块内可访问)
示例测试代码中正确计算答案的方式:
function testAttack() public { // 部署目标合约后立刻计算答案,此时block.number和timestamp与构造函数一致 uint8 correctAnswer = uint8(uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)))); vm.startPrank(attackerAddr); // 传入正确的答案调用攻击函数 attacker.attack{value: 1 ether}(correctAnswer); assertTrue(challenge.isComplete()); vm.stopPrank(); }
3. 交易ETH金额不匹配
目标合约的guess()函数要求调用时必须传入恰好1 ETH,如果攻击合约调用victim.guess{value: 1 ether}(x)时自身余额不足,或者测试中给攻击合约的ETH不够,会触发回退。
验证方式:
- 确保测试代码中用
vm.deal()给攻击地址分配足够的ETH(至少1 ETH) - 确保攻击合约的
attack()函数被调用时传入了{value: 1 ether}
4. Solidity版本或ABI兼容性问题
如果目标合约和攻击合约使用的Solidity版本差异过大(比如目标用0.6.0,攻击用0.8.0),可能存在函数签名或编码差异导致调用失败。
修复方式:
- 确保两个合约使用相同或兼容的Solidity版本(比如统一用^0.8.19)
- 检查合约导入路径是否正确,避免ABI生成错误
内容的提问来源于stack exchange,提问作者nebula
相关产品推荐
相关产品推荐

