From 29a98911074d79d1f2531b6d7ce5da7fe29514f3 Mon Sep 17 00:00:00 2001 From: David Meister Date: Wed, 19 Aug 2026 08:11:42 +0000 Subject: [PATCH] Port five concrete-boundary tests from rain.extrospection PR 50 Upstream rain.extrospection PR 50 (merged at 7192796) added five tests pinning the concrete Extrospect's delegation routing, after this repo's test copies were taken. rain.extrospection PR 131 deletes them upstream, so this repo is their only future home. Ported byte-exactly from upstream commit 719279616ccdd24d61668c0843ded7cb56105cc5, with imports adapted to the rain-extrospection-0.1.6/ prefix: - testCheckNotEOFBytecodeMetamorphicButNotEOF - testIsERC1167ProxyConcreteImplementationAddress - testIsERC1167ProxyEquivalenceEOF - testScanEVMOpcodesPresentInBytecodeUnreachableCreateCounted - testScanEVMOpcodesReachableInBytecodeUnreachableCreateNotCounted SOLIDITY_CBOR_RUNTIME_FIXTURE_TRIMMED is deliberately not ported: its only upstream consumers are test/src/lib/* tests that do not exist here, and no ported or existing concrete test references it. Co-Authored-By: Claude Fable 5 --- .../Extrospect.checkNotEOFBytecode.t.sol | 10 +++++++ .../concrete/Extrospect.isERC1167Proxy.t.sol | 27 +++++++++++++++++++ ...pect.scanEVMOpcodesPresentInBytecode.t.sol | 10 +++++++ ...ct.scanEVMOpcodesReachableInBytecode.t.sol | 9 +++++++ 4 files changed, 56 insertions(+) diff --git a/test/src/concrete/Extrospect.checkNotEOFBytecode.t.sol b/test/src/concrete/Extrospect.checkNotEOFBytecode.t.sol index 23911fe..1f8fae7 100644 --- a/test/src/concrete/Extrospect.checkNotEOFBytecode.t.sol +++ b/test/src/concrete/Extrospect.checkNotEOFBytecode.t.sol @@ -16,6 +16,16 @@ contract ExtrospectCheckNotEOFBytecodeTest is ExtrospectEquivalence { LibExtrospectBytecode.checkNotEOFBytecode(clean); } + /// Bytecode whose only notable content is a reachable CREATE (a metamorphic + /// risk op) is NOT EOF, so this must return without reverting. Pins that + /// `checkNotEOFBytecode` delegates to the EOF check and not to the + /// metamorphic check, which would revert `Metamorphic(1 << 0xF0)` here. + function testCheckNotEOFBytecodeMetamorphicButNotEOF() external view { + bytes memory createOnly = hex"F0"; + extrospect.checkNotEOFBytecode(createOnly); + LibExtrospectBytecode.checkNotEOFBytecode(createOnly); + } + function testCheckNotEOFBytecodeEquivalenceRevert() external { bytes memory eof = hex"EF00010203"; vm.expectRevert(LibExtrospectBytecode.EOFBytecodeNotSupported.selector); diff --git a/test/src/concrete/Extrospect.isERC1167Proxy.t.sol b/test/src/concrete/Extrospect.isERC1167Proxy.t.sol index 8db067d..97a342e 100644 --- a/test/src/concrete/Extrospect.isERC1167Proxy.t.sol +++ b/test/src/concrete/Extrospect.isERC1167Proxy.t.sol @@ -19,4 +19,31 @@ contract ExtrospectIsERC1167ProxyTest is ExtrospectEquivalence { assertEq(extIsProxy, libIsProxy); assertEq(extImpl, libImpl); } + + /// A well-formed ERC1167 proxy: the concrete must return the implementation + /// address embedded in the bytecode, not just the boolean. A delegation + /// that dropped the second return component would yield address(0) here. + function testIsERC1167ProxyConcreteImplementationAddress() external view { + address expectedImplementation = address(0x00112233445566778899AABbCCdDeeFf00112233); + bytes memory proxy = + abi.encodePacked(hex"363d3d373d3d3d363d73", expectedImplementation, hex"5af43d82803e903d91602b57fd5bf3"); + assertEq(proxy.length, 45); + (bool extIsProxy, address extImpl) = extrospect.isERC1167Proxy(proxy); + assertTrue(extIsProxy); + assertEq(extImpl, expectedImplementation); + } + + /// EOF bytecode of exactly the ERC1167 proxy length returns + /// `(false, address(0))` through the external entry point rather than + /// reverting. + function testIsERC1167ProxyEquivalenceEOF() external view { + bytes memory bytecode = + hex"EF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + (bool extIsProxy, address extImpl) = extrospect.isERC1167Proxy(bytecode); + (bool libIsProxy, address libImpl) = LibExtrospectERC1167Proxy.isERC1167Proxy(bytecode); + assertFalse(extIsProxy); + assertEq(extImpl, address(0)); + assertEq(extIsProxy, libIsProxy); + assertEq(extImpl, libImpl); + } } diff --git a/test/src/concrete/Extrospect.scanEVMOpcodesPresentInBytecode.t.sol b/test/src/concrete/Extrospect.scanEVMOpcodesPresentInBytecode.t.sol index 13848af..5cb3c58 100644 --- a/test/src/concrete/Extrospect.scanEVMOpcodesPresentInBytecode.t.sol +++ b/test/src/concrete/Extrospect.scanEVMOpcodesPresentInBytecode.t.sol @@ -36,4 +36,14 @@ contract ExtrospectScanEVMOpcodesPresentInBytecodeTest is ExtrospectEquivalence function testScanEVMOpcodesPresentInBytecodeEquivalenceEOF() external { assertScanEquivalence(hex"EF0000"); } + + /// `hex"00F0"` is STOP followed by CREATE, so CREATE is present in the + /// bytecode but unreachable (no JUMPDEST resumes execution after the halt). + /// The concrete must report the PRESENT bitmap — STOP and CREATE — which a + /// delegation to the reachable scan would not contain. + function testScanEVMOpcodesPresentInBytecodeUnreachableCreateCounted() external view { + //forge-lint: disable-next-line(incorrect-shift) + uint256 expected = uint256(1) | (uint256(1) << uint256(0xF0)); + assertEq(extrospect.scanEVMOpcodesPresentInBytecode(hex"00F0"), expected); + } } diff --git a/test/src/concrete/Extrospect.scanEVMOpcodesReachableInBytecode.t.sol b/test/src/concrete/Extrospect.scanEVMOpcodesReachableInBytecode.t.sol index f0d0dc9..c4aceda 100644 --- a/test/src/concrete/Extrospect.scanEVMOpcodesReachableInBytecode.t.sol +++ b/test/src/concrete/Extrospect.scanEVMOpcodesReachableInBytecode.t.sol @@ -37,4 +37,13 @@ contract ExtrospectScanEVMOpcodesReachableInBytecodeTest is ExtrospectEquivalenc function testScanEVMOpcodesReachableInBytecodeEquivalenceEOF() external { assertScanEquivalence(hex"EF0000"); } + + /// `hex"00F0"` is STOP followed by CREATE, so only STOP is reachable. The + /// concrete must report the REACHABLE bitmap, which a delegation to the + /// present scan would pollute with the CREATE bit. + function testScanEVMOpcodesReachableInBytecodeUnreachableCreateNotCounted() external view { + //forge-lint: disable-next-line(incorrect-shift) + uint256 expected = uint256(1); + assertEq(extrospect.scanEVMOpcodesReachableInBytecode(hex"00F0"), expected); + } }