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); + } }