Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
ExtrospectionBytecodeTest:testBytecode(address) (runs: 2048, μ: 308793, ~: 308847)
ExtrospectionBytecodeTest:testBytecodeHash(address) (runs: 2048, μ: 303472, ~: 303531)
ExtrospectionBytecodeTest:testScanEVMOpcodesPresentInAccount(address) (runs: 2048, μ: 303697, ~: 303224)
ExtrospectionBytecodeTest:testScanEVMOpcodesReachableInAccount(address) (runs: 2048, μ: 303952, ~: 303265)
ExtrospectionERC1167ProxyTest:testExtrospectionERC1167ProxyFailure(address,bytes) (runs: 2014, μ: 305214, ~: 305199)
ExtrospectionERC1167ProxyTest:testExtrospectionERC1167ProxySuccess(address,address) (runs: 2014, μ: 305264, ~: 305264)
ExtrospectionBytecodeTest:testBytecode(address) (runs: 2048, μ: 308822, ~: 308847)
ExtrospectionBytecodeTest:testBytecodeHash(address) (runs: 2048, μ: 303501, ~: 303531)
ExtrospectionBytecodeTest:testScanEVMOpcodesPresentInAccount(address) (runs: 2048, μ: 303726, ~: 303224)
ExtrospectionBytecodeTest:testScanEVMOpcodesReachableInAccount(address) (runs: 2048, μ: 303981, ~: 303265)
ExtrospectionERC1167ProxyTest:testExtrospectionERC1167ProxyFailure(address,bytes) (runs: 2048, μ: 305417, ~: 305332)
ExtrospectionERC1167ProxyTest:testExtrospectionERC1167ProxySuccess(address,address) (runs: 2048, μ: 305264, ~: 305264)
ExtrospectionInterpreterTest:testCallCodeooor() (gas: 353111)
ExtrospectionInterpreterTest:testCallooor() (gas: 353110)
ExtrospectionInterpreterTest:testCreate2ooor() (gas: 352015)
Expand All @@ -18,27 +18,28 @@ ExtrospectionInterpreterTest:testNoopooor() (gas: 335193)
ExtrospectionInterpreterTest:testSLoadooor() (gas: 353360)
ExtrospectionInterpreterTest:testSStoreooor() (gas: 349547)
ExtrospectionInterpreterTest:testSelfDestructooor() (gas: 348804)
LibExtrospectBytecodeTrimSolidityCBORMetadataTest:testTrimSolidityCBORMetadataBytecodeContrived(bytes) (runs: 2048, μ: 6406, ~: 6324)
LibExtrospectBytecodeTrimSolidityCBORMetadataTest:testTrimSolidityCBORMetadataBytecodeShort(bytes) (runs: 1468, μ: 3555, ~: 3555)
LibExtrospectBytecodeCheckCBORTrimmedBytecodeHashTest:testCheckCBORTrimmedBytecodeHashSuccess() (gas: 7562)
LibExtrospectBytecodeScanEVMOpcodesPresentInBytecodeTest:testScanEVMOpcodesPresentPush1() (gas: 715)
LibExtrospectBytecodeScanEVMOpcodesPresentInBytecodeTest:testScanEVMOpcodesPresentReference(bytes) (runs: 2048, μ: 34088, ~: 10497)
LibExtrospectBytecodeScanEVMOpcodesPresentInBytecodeTest:testScanEVMOpcodesPresentSimple() (gas: 879)
LibExtrospectBytecodeTrimSolidityCBORMetadataTest:testTrimSolidityCBORMetadataBytecodeContrived(bytes) (runs: 2048, μ: 6802, ~: 6603)
LibExtrospectBytecodeTrimSolidityCBORMetadataTest:testTrimSolidityCBORMetadataBytecodeShort(bytes) (runs: 2048, μ: 3555, ~: 3555)
LibExtrospectBytecodeTrimSolidityCBORMetadataTest:testTrimSolidityCBORMetdataBytecodeReal() (gas: 3963)
LibExtrospectERC1167ProxyTest:testIsERC1167ProxyGasFailLength() (gas: 365)
LibExtrospectERC1167ProxyTest:testIsERC1167ProxyGasFailPrefix() (gas: 684)
LibExtrospectERC1167ProxyTest:testIsERC1167ProxyGasFailSuffix() (gas: 663)
LibExtrospectERC1167ProxyTest:testIsERC1167ProxyGasSuccess() (gas: 662)
LibExtrospectERC1167ProxyTest:testIsERC1167ProxyLength(bytes) (runs: 2037, μ: 3901, ~: 3898)
LibExtrospectERC1167ProxyTest:testIsERC1167ProxyPrefixFail(bytes,address) (runs: 2048, μ: 4292, ~: 4282)
LibExtrospectERC1167ProxyTest:testIsERC1167ProxySlowFail(bytes) (runs: 2048, μ: 3883, ~: 3847)
LibExtrospectERC1167ProxyTest:testIsERC1167ProxyLength(bytes) (runs: 2048, μ: 3920, ~: 3898)
LibExtrospectERC1167ProxyTest:testIsERC1167ProxyPrefixFail(bytes,address) (runs: 2048, μ: 4381, ~: 4282)
LibExtrospectERC1167ProxyTest:testIsERC1167ProxySlowFail(bytes) (runs: 2048, μ: 3952, ~: 3847)
LibExtrospectERC1167ProxyTest:testIsERC1167ProxySlowSuccess(address) (runs: 2048, μ: 12367, ~: 12367)
LibExtrospectERC1167ProxyTest:testIsERC1167ProxySuccess(address) (runs: 2048, μ: 1332, ~: 1332)
LibExtrospectERC1167ProxyTest:testIsERC1167ProxySuffixFail(bytes,address) (runs: 2048, μ: 4311, ~: 4299)
LibExtrospectScanEVMOpcodesPresentInBytecodeTest:testScanEVMOpcodesPresentPush1() (gas: 715)
LibExtrospectScanEVMOpcodesPresentInBytecodeTest:testScanEVMOpcodesPresentReference(bytes) (runs: 2048, μ: 10334, ~: 12897)
LibExtrospectScanEVMOpcodesPresentInBytecodeTest:testScanEVMOpcodesPresentSimple() (gas: 879)
LibExtrospectERC1167ProxyTest:testIsERC1167ProxySuffixFail(bytes,address) (runs: 2048, μ: 4396, ~: 4299)
LibExtrospectScanEVMOpcodesReachableInBytecodeTest:testScanEVMOpcodesReachableInvalid() (gas: 1142)
LibExtrospectScanEVMOpcodesReachableInBytecodeTest:testScanEVMOpcodesReachableJumpdest() (gas: 1954)
LibExtrospectScanEVMOpcodesReachableInBytecodeTest:testScanEVMOpcodesReachablePush1() (gas: 789)
LibExtrospectScanEVMOpcodesReachableInBytecodeTest:testScanEVMOpcodesReachablePush4() (gas: 1024)
LibExtrospectScanEVMOpcodesReachableInBytecodeTest:testScanEVMOpcodesReachableReference(bytes) (runs: 2048, μ: 11882, ~: 12259)
LibExtrospectScanEVMOpcodesReachableInBytecodeTest:testScanEVMOpcodesReachableReference(bytes) (runs: 2048, μ: 41242, ~: 12259)
LibExtrospectScanEVMOpcodesReachableInBytecodeTest:testScanEVMOpcodesReachableReportedFalsePositive() (gas: 1713108)
LibExtrospectScanEVMOpcodesReachableInBytecodeTest:testScanEVMOpcodesReachableReportedFalsePositiveBytecode() (gas: 1664573)
LibExtrospectScanEVMOpcodesReachableInBytecodeTest:testScanEVMOpcodesReachableReturn() (gas: 1122)
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/rainix.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ jobs:
ETH_RPC_URL: ${{ secrets.CI_DEPLOY_RPC_URL }}
ETHERSCAN_API_KEY: ${{ secrets.EXPLORER_VERIFICATION_KEY }}
DEPLOY_VERIFIER: 'etherscan'
RPC_URL_ARBITRUM_FORK: ${{ secrets.CI_DEPLOY_ARBITRUM_RPC_URL }}
run: nix develop -c ${{ matrix.task }}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
out
cache
cache
.env
59 changes: 38 additions & 21 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions foundry.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"lib/forge-std": {
"rev": "b8f065fda83b8cd94a6b2fec8fcd911dc3b444fd"
"rev": "1801b0541f4fda118a10798fd3486bb7051c5dd6"
},
"lib/rain.solmem": {
"rev": "228b35c6725877e7fbcd2432b4c692357f16f510"
"rev": "26bce6197383f193e35326bab4d4424cf6eafde7"
}
}
2 changes: 1 addition & 1 deletion lib/forge-std
Submodule forge-std updated 57 files
+14 −31 .github/workflows/ci.yml
+1 −1 .github/workflows/sync.yml
+2 −2 CONTRIBUTING.md
+11 −9 README.md
+3 −12 foundry.toml
+2 −2 package.json
+2 −12 scripts/vm.py
+2 −2 src/Base.sol
+1 −1 src/Config.sol
+2 −2 src/LibVariable.sol
+2 −2 src/Script.sol
+28 −13 src/StdAssertions.sol
+8 −5 src/StdChains.sol
+9 −13 src/StdCheats.sol
+24 −4 src/StdConfig.sol
+2 −2 src/StdConstants.sol
+2 −2 src/StdError.sol
+2 −4 src/StdInvariant.sol
+4 −12 src/StdJson.sol
+2 −2 src/StdMath.sol
+11 −9 src/StdStorage.sol
+2 −2 src/StdStyle.sol
+4 −12 src/StdToml.sol
+5 −13 src/StdUtils.sol
+2 −4 src/Test.sol
+26 −41 src/Vm.sol
+10 −19 src/console.sol
+2 −2 src/console2.sol
+2 −2 src/interfaces/IERC1155.sol
+2 −2 src/interfaces/IERC165.sol
+2 −2 src/interfaces/IERC20.sol
+2 −2 src/interfaces/IERC4626.sol
+2 −2 src/interfaces/IERC6909.sol
+2 −2 src/interfaces/IERC721.sol
+4 −10 src/interfaces/IERC7540.sol
+2 −2 src/interfaces/IERC7575.sol
+3 −8 src/interfaces/IMulticall3.sol
+691 −1,380 src/safeconsole.sol
+2 −2 test/CommonBase.t.sol
+34 −5 test/Config.t.sol
+19 −1 test/LibVariable.t.sol
+2 −2 test/StdAssertions.t.sol
+24 −24 test/StdChains.t.sol
+19 −20 test/StdCheats.t.sol
+2 −2 test/StdConstants.t.sol
+3 −4 test/StdError.t.sol
+2 −2 test/StdJson.t.sol
+6 −6 test/StdMath.t.sol
+24 −27 test/StdStorage.t.sol
+2 −2 test/StdStyle.t.sol
+2 −2 test/StdToml.t.sol
+13 −13 test/StdUtils.t.sol
+3 −3 test/Vm.t.sol
+2 −4 test/compilation/CompilationScript.sol
+2 −4 test/compilation/CompilationScriptBase.sol
+2 −4 test/compilation/CompilationTest.sol
+2 −4 test/compilation/CompilationTestBase.sol
41 changes: 21 additions & 20 deletions src/interface/IExtrospectInterpreterV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,30 @@ import {
/// @dev https://eips.ethereum.org/EIPS/eip-214#specification
//forge-lint: disable-next-line(incorrect-shift)
uint256 constant NON_STATIC_OPS = (1 << uint256(EVM_OP_CREATE)) | (1 << uint256(EVM_OP_CREATE2))
//forge-lint: disable-next-line(incorrect-shift)
| (1 << uint256(EVM_OP_LOG0)) | (1 << uint256(EVM_OP_LOG1)) | (1 << uint256(EVM_OP_LOG2)) | (1 << uint256(EVM_OP_LOG3))
//forge-lint: disable-next-line(incorrect-shift)
| (1 << uint256(EVM_OP_LOG4)) | (1 << uint256(EVM_OP_SSTORE)) | (1 << uint256(EVM_OP_SELFDESTRUCT))
//forge-lint: disable-next-line(incorrect-shift)
| (1 << uint256(EVM_OP_CALL));
//forge-lint: disable-next-line(incorrect-shift)
| (1 << uint256(EVM_OP_LOG0)) | (1 << uint256(EVM_OP_LOG1)) | (1 << uint256(EVM_OP_LOG2))
| (1 << uint256(EVM_OP_LOG3))
//forge-lint: disable-next-line(incorrect-shift)
| (1 << uint256(EVM_OP_LOG4)) | (1 << uint256(EVM_OP_SSTORE)) | (1 << uint256(EVM_OP_SELFDESTRUCT))
//forge-lint: disable-next-line(incorrect-shift)
| (1 << uint256(EVM_OP_CALL));

/// @dev The interpreter ops allowlist is stricter than the static ops list.
uint256 constant INTERPRETER_DISALLOWED_OPS = NON_STATIC_OPS
// Interpreter cannot store so it has no reason to load from storage.
//forge-lint: disable-next-line(incorrect-shift)
| (1 << uint256(EVM_OP_SLOAD))
// Interpreter MUST NOT delegate call as we have no idea what could run and
// it could easily mutate the interpreter if allowed.
//forge-lint: disable-next-line(incorrect-shift)
| (1 << uint256(EVM_OP_DELEGATECALL))
// Interpreter MUST use static call only.
//forge-lint: disable-next-line(incorrect-shift)
| (1 << uint256(EVM_OP_CALLCODE))
// Interpreter MUST use static call only.
// Redundant with static list for clarity as static list allows 0 value calls.
//forge-lint: disable-next-line(incorrect-shift)
| (1 << uint256(EVM_OP_CALL));
// Interpreter cannot store so it has no reason to load from storage.
//forge-lint: disable-next-line(incorrect-shift)
| (1 << uint256(EVM_OP_SLOAD))
// Interpreter MUST NOT delegate call as we have no idea what could run and
// it could easily mutate the interpreter if allowed.
//forge-lint: disable-next-line(incorrect-shift)
| (1 << uint256(EVM_OP_DELEGATECALL))
// Interpreter MUST use static call only.
//forge-lint: disable-next-line(incorrect-shift)
| (1 << uint256(EVM_OP_CALLCODE))
// Interpreter MUST use static call only.
// Redundant with static list for clarity as static list allows 0 value calls.
//forge-lint: disable-next-line(incorrect-shift)
| (1 << uint256(EVM_OP_CALL));

/// @title IExtrospectInterpreterV1
/// @notice External functions for offchain processing to determine if an
Expand Down
4 changes: 2 additions & 2 deletions src/lib/EVMOpcodes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -171,5 +171,5 @@ uint8 constant EVM_OP_SELFDESTRUCT = 0xFF;

//forge-lint: disable-next-line(incorrect-shift)
uint256 constant HALTING_BITMAP = (1 << EVM_OP_STOP) | (1 << EVM_OP_RETURN) | (1 << EVM_OP_REVERT)
//forge-lint: disable-next-line(incorrect-shift)
| (1 << EVM_OP_INVALID) | (1 << EVM_OP_SELFDESTRUCT);
//forge-lint: disable-next-line(incorrect-shift)
| (1 << EVM_OP_INVALID) | (1 << EVM_OP_SELFDESTRUCT);
27 changes: 27 additions & 0 deletions src/lib/LibExtrospectBytecode.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ import {EVM_OP_JUMPDEST, HALTING_BITMAP} from "./EVMOpcodes.sol";
library LibExtrospectBytecode {
using LibBytes for bytes;

/// Thrown when bytecode metadata is not trimmed as expected.
error MetadataNotTrimmed();

/// Thrown when the bytecode hash does not match the expected value.
/// @param expected The expected bytecode hash.
/// @param actual The actual bytecode hash.
error BytecodeHashMismatch(bytes32 expected, bytes32 actual);

/// https://docs.soliditylang.org/en/latest/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode
///
/// The encoding is not super complex, but requires having a CBOR decoder to
Expand All @@ -37,6 +45,8 @@ library LibExtrospectBytecode {
/// parts still have constant length.
///
/// NOTE bytecode is mutated in place.
/// @param bytecode The bytecode to trim metadata from.
/// @return didTrim Whether metadata was detected and trimmed.
//forge-lint: disable-next-line(mixed-case-function)
function trimSolidityCBORMetadata(bytes memory bytecode) internal pure returns (bool didTrim) {
uint256 length = bytecode.length;
Expand All @@ -59,6 +69,23 @@ library LibExtrospectBytecode {
}
}

/// Checks that the bytecode of an account, after trimming Solidity CBOR
/// metadata, matches an expected hash. Reverts if the metadata was not
/// trimmed or if the hash does not match after trimming.
/// @param account The account whose bytecode to check.
/// @param expected The expected hash of the trimmed bytecode.
function checkCBORTrimmedBytecodeHash(address account, bytes32 expected) internal view {
bytes memory bytecode = account.code;
bool didTrim = LibExtrospectBytecode.trimSolidityCBORMetadata(bytecode);
if (!didTrim) {
revert MetadataNotTrimmed();
}
bytes32 actual = keccak256(bytecode);
if (expected != actual) {
revert BytecodeHashMismatch(expected, actual);
}
}

/// Scans for opcodes that are reachable during execution of a contract.
/// Adapted from https://github.com/MrLuit/selfdestruct-detect/blob/master/src/index.ts
/// @param bytecode The bytecode to scan.
Expand Down
6 changes: 4 additions & 2 deletions src/lib/LibExtrospectERC1167Proxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ library LibExtrospectERC1167Proxy {
uint256 implementationAddressOffset = ERC1167_IMPLEMENTATION_ADDRESS_OFFSET;
uint256 implementationAddressMask = type(uint160).max;
assembly ("memory-safe") {
implementationAddress :=
and(mload(add(bytecode, implementationAddressOffset)), implementationAddressMask)
implementationAddress := and(
mload(add(bytecode, implementationAddressOffset)),
implementationAddressMask
)
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions test/lib/LibExtrospectTestProd.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
pragma solidity ^0.8.25;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Minor: Pragma version inconsistency.

This file uses ^0.8.25 (caret) while other test files in this PR use =0.8.25 (exact version pinning). Consider aligning for consistency.

♻️ Suggested fix
-pragma solidity ^0.8.25;
+pragma solidity =0.8.25;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pragma solidity ^0.8.25;
pragma solidity =0.8.25;
🤖 Prompt for AI Agents
In `@test/lib/LibExtrospectTestProd.sol` at line 3, Update the Solidity pragma in
this file to match the project's exact version pinning: replace the caret-style
pragma declaration (^0.8.25) with the exact version (=0.8.25) in the top-level
pragma statement so it is consistent with other tests and avoids accidental
compiler version drift.


import {Vm} from "forge-std/StdCheats.sol";

library LibExtrospectTestProd {
uint256 constant PROD_TEST_BLOCK_NUMBER_ARBITRUM = 424463066;

function createSelectForkArbitrum(Vm vm) internal {
vm.createSelectFork(vm.envString("RPC_URL_ARBITRUM_FORK"), PROD_TEST_BLOCK_NUMBER_ARBITRUM);
}
Comment on lines +10 to +12
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider adding a descriptive error message if the environment variable is missing.

If RPC_URL_ARBITRUM_FORK is not set, vm.envString will revert with a generic error. This could confuse developers who run tests without the proper environment setup.

♻️ Suggested improvement
 function createSelectForkArbitrum(Vm vm) internal {
+    // Will revert if RPC_URL_ARBITRUM_FORK is not set
     vm.createSelectFork(vm.envString("RPC_URL_ARBITRUM_FORK"), PROD_TEST_BLOCK_NUMBER_ARBITRUM);
 }

Alternatively, you could use vm.envOr with a fallback or add a custom revert message, but a simple comment may suffice for test helper code.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function createSelectForkArbitrum(Vm vm) internal {
vm.createSelectFork(vm.envString("RPC_URL_ARBITRUM_FORK"), PROD_TEST_BLOCK_NUMBER_ARBITRUM);
}
function createSelectForkArbitrum(Vm vm) internal {
// Will revert if RPC_URL_ARBITRUM_FORK is not set
vm.createSelectFork(vm.envString("RPC_URL_ARBITRUM_FORK"), PROD_TEST_BLOCK_NUMBER_ARBITRUM);
}
🤖 Prompt for AI Agents
In `@test/lib/LibExtrospectTestProd.sol` around lines 10 - 12, The helper function
createSelectForkArbitrum currently calls vm.envString("RPC_URL_ARBITRUM_FORK")
which will revert with a generic error if the env var is missing; update
createSelectForkArbitrum to either use vm.envOr("RPC_URL_ARBITRUM_FORK",
"<placeholder_or_default>") or explicitly check the result and revert with a
clear message like "missing RPC_URL_ARBITRUM_FORK environment variable" so test
failures give a descriptive error; refer to the createSelectForkArbitrum
function and vm.envString/vm.envOr when making the change.

}
5 changes: 3 additions & 2 deletions test/src/concrete/Extrospection.ERC1167Proxy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ contract ExtrospectionERC1167ProxyTest is Test {
// Proxy can't be extrospection, otherwise we'll etch over it.
vm.assume(proxy != address(extrospection));
// Proxy can't be a precompile either.
vm.assume(uint160(proxy) > 10);
// Force incorrect proxy implementation into the proxy address.
vm.assume(uint160(proxy) > type(uint160).max / 2);
vm.assume(proxy.code.length == 0);

vm.etch(proxy, bytecode);

(bool result, address implementation) = extrospection.isERC1167Proxy(proxy);
Expand Down
Loading