solidity icon indicating copy to clipboard operation
solidity copied to clipboard

Different bytecode produced via IR for some of OpenZeppelin's contracts when extra contracts are included in the input

Open cameel opened this issue 2 months ago • 2 comments

Description

This looks like yet another bug where different AST IDs lead to differences in the generated bytecode. I found it when experimenting with parallel compilation of OpenZeppelin.

When each source file is compiled in isolation by providing only that single file as input (rather than providing all of them and only selecting one via outputSelection), three contracts produce different bytecode than when all contracts are compiled together:

  • test/metatx/ERC2771Forwarder.t.sol:ERC2771ForwarderTest
  • test/token/ERC20/extensions/ERC4626.t.sol:ERC4626StdTest
  • test/utils/structs/Checkpoints.t.sol:CheckpointsTrace224Test

Environment

  • Compiler version: 0.8.26
  • Target EVM version (as per compiler settings): cancun

Steps to Reproduce

git clone --depth=1 https://github.com/OpenZeppelin/openzeppelin-contracts --branch v5.0.2
cd openzeppelin-contracts/
forge install

cat <<EOF > input.json
{
    "language": "Solidity",
    "sources": {
        "contracts/mocks/token/ERC4626Mock.sol":     {"urls": ["contracts/mocks/token/ERC4626Mock.sol"]},
        "test/token/ERC20/extensions/ERC4626.t.sol": {"urls": ["test/token/ERC20/extensions/ERC4626.t.sol"]}
    },
    "settings": {
        "remappings": [
            "@openzeppelin/contracts/=contracts/",
            "ds-test/=lib/forge-std/lib/ds-test/src/",
            "erc4626-tests/=lib/erc4626-tests/",
            "forge-std/=lib/forge-std/src/"
        ],
        "metadata": {"appendCBOR": false},
        "outputSelection": {"*": {"*": ["evm.bytecode"]}},
        "viaIR": true
    }
}
EOF

cat input.json \
    | solc --standard-json > output-select2.json
cat input.json \
    | jq 'del(.sources."contracts/mocks/token/ERC4626Mock.sol")' \
    | solc --standard-json > output-select1.json

diff --color --unified \
    <(cat output-select2.json \
        | jq '.contracts."test/token/ERC20/extensions/ERC4626.t.sol"."ERC4626StdTest".evm.bytecode.object' \
        | fold --width 160 \
    ) \
    <(cat output-select1.json \
        | jq '.contracts."test/token/ERC20/extensions/ERC4626.t.sol"."ERC4626StdTest".evm.bytecode.object' \
        | fold --width 160 \
    )

Note: This is a minimized example that's enough to reproduce one of the bytecode differences. With the full input there are more of them and they may or may not have the same cause. To make sure, when the bug is fixed, verify with the full original input: openzeppelin-5.0.2-full-input.json. It should produce the same bytecode for the three contracts when compiled as is and when you include only the source files containing those contracts.

cameel avatar May 23 '24 11:05 cameel