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 }