LEVEL 13 (GatekeeperOne):
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import '@openzeppelin/contracts/math/SafeMath.sol';
contract GatekeeperOne {
using SafeMath for uint256;
address public entrant;
modifier gateOne() {
require(msg.sender != tx.origin);
_;
}
modifier gateTwo() {
require(gasleft().mod(8191) == 0);
_;
}
modifier gateThree(bytes8 _gateKey) {
require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), "GatekeeperOne: invalid gateThree part one");
require(uint32(uint64(_gateKey)) != uint64(_gateKey), "GatekeeperOne: invalid gateThree part two");
require(uint32(uint64(_gateKey)) == uint16(tx.origin), "GatekeeperOne: invalid gateThree part three");
_;
}
function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) {
entrant = tx.origin;
return true;
}
}
通关要求
entrant=player
要点
1.tx.origin和msg.sender的区别,这个前面有讲
2.gas费用的设置
https://docs.soliditylang.org/en/v0.8.14/control-structures.html?highlight=gas%3A#external-function-calls
3.类型转换和位值
https://docs.soliditylang.org/en/v0.8.14/types.html#conversions-between-elementary-types
解题思路
gateOne:在自己的合约调用其他合约,则tx.origin和msg.sender就会不一样
gateTwo:要先了解下gasleft()和合约是如何消耗gas,如何指定调用的最大gas
可以通过console.log打印出对应合约大体的gasLeft()+ (8191的倍数)即可
这个gasLeft()线上和本地的不一定完全一样,可能是有没开启优化的关系,故可以加个循环gas+1不断尝试
gateThree:
这个要了解单位转换的截取和补齐,详见代码备注
contracts/13GatekeeperOneRun.sol
function run(address _runAddress) external {
//gas = 254+8191; (用console.log打印出gas剩余,本地测算是254,不过线上可能有些许差异可能是有带优化参数,所以可以调小254进行循环测试)
uint256 gas = 254+81910;
/**
*
* 1.require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), "GatekeeperOne: invalid gateThree part one");
* 2.require(uint32(uint64(_gateKey)) != uint64(_gateKey), "GatekeeperOne: invalid gateThree part two");
* 3.require(uint32(uint64(_gateKey)) == uint16(tx.origin), "GatekeeperOne: invalid gateThree part three");
* gateKey三个条件都满足才能通过
* 分析:
* 首先要把address转成对应的uint160,gateKey是byte8对应uint64
* 根据条件3:tx.origin的0-16跟gateKey的0-16是一样
* 根据条件1:16-32位要全是0 (不然截取后不会相等)
* 根据条件2:32-64位不全是0 (不然截取后会相等)
* 所以uint64(uint16(uint160(tx.origin)),把address转成160后截取成16再转成64位后,自动满足条件1和3,但不满足条件2,故再加上0x1_00_00_00_00即可(把33设为1)
*
*/
bytes8 gateKey = bytes8(uint64(uint16(uint160(tx.origin))+0x100000000));
while (true) {
(bool result,) = _runAddress.call{gas:gas}(abi.encodeWithSignature("enter(bytes8)", gateKey));
if (result) {
break;
}
gas++;
}
}
Top comments (0)