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 }