github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/test/Safe/SafeSigners.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 { Safe } from "safe-contracts/Safe.sol";
     6  import { SafeSigners } from "src/Safe/SafeSigners.sol";
     7  import "test/safe-tools/SafeTestTools.sol";
     8  
     9  import { SignatureDecoder } from "safe-contracts/common/SignatureDecoder.sol";
    10  
    11  contract SafeSigners_Test is Test, SafeTestTools {
    12      bytes4 internal constant EIP1271_MAGIC_VALUE = 0x20c13b0b;
    13  
    14      enum SigTypes {
    15          Eoa,
    16          EthSign,
    17          ApprovedHash,
    18          Contract
    19      }
    20  
    21      /// @dev Maps every key to one of the 4 signatures types.
    22      ///      This is used in the tests below as a pseudorandom mechanism for determining which
    23      ///      signature type to use for each key.
    24      /// @param _key The key to map to a signature type.
    25      function sigType(uint256 _key) internal pure returns (SigTypes sigType_) {
    26          uint256 t = _key % 4;
    27          sigType_ = SigTypes(t);
    28      }
    29  
    30      /// @dev Test that for a given set of signatures:
    31      ///      1. safe.checkNSignatures() succeeds
    32      ///      2. the getSigners() method returns the expected signers
    33      ///      3. the expected signers are all owners of the safe.
    34      ///      Demonstrating these three properties is sufficient to prove that the getSigners() method
    35      ///      returns the same signatures as those recovered by safe.checkNSignatures().
    36      function testDiff_getSignaturesVsCheckSignatures_succeeds(bytes memory _data, uint256 _numSigs) external {
    37          bytes32 digest = keccak256(_data);
    38  
    39          // Limit the number of signatures to 25
    40          uint256 numSigs = bound(_numSigs, 1, 25);
    41  
    42          (, uint256[] memory keys) = SafeTestLib.makeAddrsAndKeys("getSigsTest", numSigs);
    43          for (uint256 i; i < keys.length; i++) {
    44              if (sigType(keys[i]) == SigTypes.Contract) {
    45                  keys[i] =
    46                      SafeTestLib.encodeSmartContractWalletAsPK(SafeTestLib.decodeSmartContractWalletAsAddress(keys[i]));
    47              }
    48          }
    49  
    50          // Create a new safeInstance with M=N, so that it requires a signature from each key.
    51          SafeInstance memory safeInstance = SafeTestTools._setupSafe(keys, numSigs, 0);
    52  
    53          // Next we will generate signatures by iterating over the keys, and choosing the signature type
    54          // based on the key.
    55          uint8 v;
    56          bytes32 r;
    57          bytes32 s;
    58          uint256 contractSigs;
    59          bytes memory signatures;
    60          uint256[] memory pks = safeInstance.ownerPKs;
    61          for (uint256 i; i < pks.length; i++) {
    62              if (sigType(pks[i]) == SigTypes.Eoa) {
    63                  (v, r, s) = vm.sign(pks[i], digest);
    64                  signatures = bytes.concat(signatures, abi.encodePacked(r, s, v));
    65              } else if (sigType(pks[i]) == SigTypes.EthSign) {
    66                  (v, r, s) = vm.sign(pks[i], keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", digest)));
    67                  v += 4;
    68                  signatures = bytes.concat(signatures, abi.encodePacked(r, s, v));
    69              } else if (sigType(pks[i]) == SigTypes.ApprovedHash) {
    70                  vm.prank(SafeTestLib.getAddr(pks[i]));
    71                  safeInstance.safe.approveHash(digest);
    72                  v = 1;
    73                  // s is not checked on approved hash signatures, so we can leave it as zero.
    74                  r = bytes32(uint256(uint160(SafeTestLib.getAddr(pks[i]))));
    75                  signatures = bytes.concat(signatures, abi.encodePacked(r, s, v));
    76              } else if (sigType(pks[i]) == SigTypes.Contract) {
    77                  contractSigs++;
    78                  address addr = SafeTestLib.decodeSmartContractWalletAsAddress(pks[i]);
    79                  r = bytes32(uint256(uint160(addr)));
    80                  vm.mockCall(
    81                      addr, abi.encodeWithSignature("isValidSignature(bytes,bytes)"), abi.encode(EIP1271_MAGIC_VALUE)
    82                  );
    83                  v = 0;
    84                  // s needs to point to data that comes after the signatures
    85                  s = bytes32(numSigs * 65);
    86                  signatures = bytes.concat(signatures, abi.encodePacked(r, s, v));
    87              }
    88          }
    89  
    90          // For each contract sig, add 64 bytes to the signature data. This is necessary to satisfy
    91          // the validation checks that the Safe contract performs on the value of s on contract
    92          // signatures. The Safe contract checks that s correctly points to additional data appended
    93          // after the signatures, and that the length of the data is within bounds.
    94          for (uint256 i; i < contractSigs; i++) {
    95              signatures = bytes.concat(signatures, abi.encode(32, 1));
    96          }
    97  
    98          // Signature checking on the Safe should succeed.
    99          safeInstance.safe.checkNSignatures(digest, _data, signatures, numSigs);
   100  
   101          // Recover the signatures using the _getNSigners() method.
   102          address[] memory gotSigners =
   103              SafeSigners.getNSigners({ dataHash: digest, signatures: signatures, requiredSignatures: numSigs });
   104  
   105          // Compare the list of recovered signers to the expected signers.
   106          assertEq(gotSigners.length, numSigs);
   107          assertEq(gotSigners.length, safeInstance.owners.length);
   108          for (uint256 i; i < numSigs; i++) {
   109              assertEq(safeInstance.owners[i], gotSigners[i]);
   110          }
   111      }
   112  }