github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/test/invariants/Hashing.t.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  import { Test } from "forge-std/Test.sol";
     5  import { StdInvariant } from "forge-std/StdInvariant.sol";
     6  import { Encoding } from "src/libraries/Encoding.sol";
     7  import { Hashing } from "src/libraries/Hashing.sol";
     8  import { InvariantTest } from "test/invariants/InvariantTest.sol";
     9  
    10  contract Hash_CrossDomainHasher {
    11      bool public failedCrossDomainHashHighVersion;
    12      bool public failedCrossDomainHashV0;
    13      bool public failedCrossDomainHashV1;
    14  
    15      /// @notice Takes the necessary parameters to perform a cross domain hash with a randomly
    16      ///         generated version. Only schema versions 0 and 1 are supported and all others
    17      ///         should revert.
    18      function hashCrossDomainMessageHighVersion(
    19          uint16 _version,
    20          uint240 _nonce,
    21          address _sender,
    22          address _target,
    23          uint256 _value,
    24          uint256 _gasLimit,
    25          bytes memory _data
    26      )
    27          external
    28      {
    29          // generate the versioned nonce
    30          uint256 encodedNonce = Encoding.encodeVersionedNonce(_nonce, _version);
    31  
    32          // hash the cross domain message. we don't need to store the result since the function
    33          // validates and should revert if an invalid version (>1) is encoded
    34          Hashing.hashCrossDomainMessage(encodedNonce, _sender, _target, _value, _gasLimit, _data);
    35  
    36          // check that execution never makes it this far for an invalid version
    37          if (_version > 1) {
    38              failedCrossDomainHashHighVersion = true;
    39          }
    40      }
    41  
    42      /// @notice Takes the necessary parameters to perform a cross domain hash using the v0 schema
    43      ///         and compares the output of a call to the unversioned function to the v0 function
    44      ///         directly.
    45      function hashCrossDomainMessageV0(
    46          uint240 _nonce,
    47          address _sender,
    48          address _target,
    49          uint256 _value,
    50          uint256 _gasLimit,
    51          bytes memory _data
    52      )
    53          external
    54      {
    55          // generate the versioned nonce with the version set to 0
    56          uint256 encodedNonce = Encoding.encodeVersionedNonce(_nonce, 0);
    57  
    58          // hash the cross domain message using the unversioned and versioned functions for
    59          // comparison
    60          bytes32 sampleHash1 = Hashing.hashCrossDomainMessage(encodedNonce, _sender, _target, _value, _gasLimit, _data);
    61          bytes32 sampleHash2 = Hashing.hashCrossDomainMessageV0(_target, _sender, _data, encodedNonce);
    62  
    63          // check that the output of both functions matches
    64          if (sampleHash1 != sampleHash2) {
    65              failedCrossDomainHashV0 = true;
    66          }
    67      }
    68  
    69      /// @notice Takes the necessary parameters to perform a cross domain hash using the v1 schema
    70      ///         and compares the output of a call to the unversioned function to the v1 function
    71      ///         directly.
    72      function hashCrossDomainMessageV1(
    73          uint240 _nonce,
    74          address _sender,
    75          address _target,
    76          uint256 _value,
    77          uint256 _gasLimit,
    78          bytes memory _data
    79      )
    80          external
    81      {
    82          // generate the versioned nonce with the version set to 1
    83          uint256 encodedNonce = Encoding.encodeVersionedNonce(_nonce, 1);
    84  
    85          // hash the cross domain message using the unversioned and versioned functions for
    86          // comparison
    87          bytes32 sampleHash1 = Hashing.hashCrossDomainMessage(encodedNonce, _sender, _target, _value, _gasLimit, _data);
    88          bytes32 sampleHash2 = Hashing.hashCrossDomainMessageV1(encodedNonce, _sender, _target, _value, _gasLimit, _data);
    89  
    90          // check that the output of both functions matches
    91          if (sampleHash1 != sampleHash2) {
    92              failedCrossDomainHashV1 = true;
    93          }
    94      }
    95  }
    96  
    97  contract Hashing_Invariant is StdInvariant, InvariantTest {
    98      Hash_CrossDomainHasher internal actor;
    99  
   100      function setUp() public override {
   101          super.setUp();
   102          // Create a hasher actor.
   103          actor = new Hash_CrossDomainHasher();
   104  
   105          targetContract(address(actor));
   106  
   107          bytes4[] memory selectors = new bytes4[](3);
   108          selectors[0] = actor.hashCrossDomainMessageHighVersion.selector;
   109          selectors[1] = actor.hashCrossDomainMessageV0.selector;
   110          selectors[2] = actor.hashCrossDomainMessageV1.selector;
   111          FuzzSelector memory selector = FuzzSelector({ addr: address(actor), selectors: selectors });
   112          targetSelector(selector);
   113      }
   114  
   115      /// @custom:invariant `hashCrossDomainMessage` reverts if `version` is > `1`.
   116      ///
   117      ///                   The `hashCrossDomainMessage` function should always revert if
   118      ///                   the `version` passed is > `1`.
   119      function invariant_hash_xdomain_msg_high_version() external {
   120          // ASSERTION: The round trip aliasing done in testRoundTrip(...) should never fail.
   121          assertFalse(actor.failedCrossDomainHashHighVersion());
   122      }
   123  
   124      /// @custom:invariant `version` = `0`: `hashCrossDomainMessage` and `hashCrossDomainMessageV0`
   125      ///                   are equivalent.
   126      ///
   127      ///                   If the version passed is 0, `hashCrossDomainMessage` and
   128      ///                   `hashCrossDomainMessageV0` should be equivalent.
   129      function invariant_hash_xdomain_msg_0() external {
   130          // ASSERTION: A call to hashCrossDomainMessage and hashCrossDomainMessageV0
   131          // should always match when the version passed is 0
   132          assertFalse(actor.failedCrossDomainHashV0());
   133      }
   134  
   135      /// @custom:invariant `version` = `1`: `hashCrossDomainMessage` and `hashCrossDomainMessageV1`
   136      ///                   are equivalent.
   137      ///
   138      ///                   If the version passed is 1, `hashCrossDomainMessage` and
   139      ///                   `hashCrossDomainMessageV1` should be equivalent.
   140      function invariant_hash_xdomain_msg_1() external {
   141          // ASSERTION: A call to hashCrossDomainMessage and hashCrossDomainMessageV1
   142          // should always match when the version passed is 1
   143          assertFalse(actor.failedCrossDomainHashV1());
   144      }
   145  }