github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/src/Safe/SafeSigners.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity ^0.8.0;
     3  
     4  library SafeSigners {
     5      /// @notice Splits signature bytes into `uint8 v, bytes32 r, bytes32 s`.
     6      ///         Copied directly from
     7      /// https://github.com/safe-global/safe-contracts/blob/e870f514ad34cd9654c72174d6d4a839e3c6639f/contracts/common/SignatureDecoder.sol
     8      /// @dev Make sure to perform a bounds check for @param pos, to avoid out of bounds access on @param signatures
     9      ///      The signature format is a compact form of {bytes32 r}{bytes32 s}{uint8 v}
    10      ///      Compact means uint8 is not padded to 32 bytes.
    11      /// @param pos Which signature to read.
    12      ///            A prior bounds check of this parameter should be performed, to avoid out of bounds access.
    13      /// @param signatures Concatenated {r, s, v} signatures.
    14      /// @return v Recovery ID or Safe signature type.
    15      /// @return r Output value r of the signature.
    16      /// @return s Output value s of the signature.
    17      function signatureSplit(
    18          bytes memory signatures,
    19          uint256 pos
    20      )
    21          internal
    22          pure
    23          returns (uint8 v, bytes32 r, bytes32 s)
    24      {
    25          assembly {
    26              let signaturePos := mul(0x41, pos)
    27              r := mload(add(signatures, add(signaturePos, 0x20)))
    28              s := mload(add(signatures, add(signaturePos, 0x40)))
    29              /**
    30               * Here we are loading the last 32 bytes, including 31 bytes
    31               * of 's'. There is no 'mload8' to do this.
    32               * 'byte' is not working due to the Solidity parser, so lets
    33               * use the second best option, 'and'
    34               */
    35              v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff)
    36          }
    37      }
    38  
    39      /// @notice Extract the signers from a set of signatures.
    40      ///         This method is based closely on the code in the Safe.checkNSignatures() method.
    41      ///         https://github.com/safe-global/safe-contracts/blob/e870f514ad34cd9654c72174d6d4a839e3c6639f/contracts/Safe.sol#L274
    42      ///         It has been modified by removing all signature _validation_ code. We trust the Safe to properly validate
    43      ///         the signatures.
    44      ///         This method therefore simply extracts the addresses from the signatures.
    45      function getNSigners(
    46          bytes32 dataHash,
    47          bytes memory signatures,
    48          uint256 requiredSignatures
    49      )
    50          internal
    51          pure
    52          returns (address[] memory _owners)
    53      {
    54          _owners = new address[](requiredSignatures);
    55  
    56          address currentOwner;
    57          uint8 v;
    58          bytes32 r;
    59          bytes32 s;
    60          uint256 i;
    61          for (i = 0; i < requiredSignatures; i++) {
    62              (v, r, s) = signatureSplit(signatures, i);
    63              if (v == 0) {
    64                  // If v is 0 then it is a contract signature
    65                  // When handling contract signatures the address of the contract is encoded into r
    66                  currentOwner = address(uint160(uint256(r)));
    67              } else if (v == 1) {
    68                  // If v is 1 then it is an approved hash
    69                  // When handling approved hashes the address of the approver is encoded into r
    70                  currentOwner = address(uint160(uint256(r)));
    71              } else if (v > 30) {
    72                  // If v > 30 then default va (27,28) has been adjusted for eth_sign flow
    73                  // To support eth_sign and similar we adjust v and hash the messageHash with the Ethereum message prefix
    74                  // before applying ecrecover
    75                  currentOwner =
    76                      ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);
    77              } else {
    78                  // Default is the ecrecover flow with the provided data hash
    79                  // Use ecrecover with the messageHash for EOA signatures
    80                  currentOwner = ecrecover(dataHash, v, r, s);
    81              }
    82              _owners[i] = currentOwner;
    83          }
    84      }
    85  }