Skip to content

Implement export-hashes and compare-hashes subcommands for report processor#250

Open
elle-j wants to merge 24 commits intomainfrom
lj/compare-hashes
Open

Implement export-hashes and compare-hashes subcommands for report processor#250
elle-j wants to merge 24 commits intomainfrom
lj/compare-hashes

Conversation

@elle-j
Copy link
Contributor

@elle-j elle-j commented Mar 10, 2026

Summary

Extends the report processor with support for extracting bytecode hashes from the reports generated by retester, and running hash comparisons between multiple reports.

The primary purpose is to be able to verify reproducible resolc builds by verifying that identical bytecode was generated by each build across the provided optimization modes.

Notes

Exporting hashes

The source paths from the reports are platform-specific, therefore export-hashes will generate normalized relative paths in the output file provided.

Example run command:

report-processor export-hashes \
    --report-path ./workdir/report.json \
    --output-path ./hashes/hashes-macos.json \
    --remove-prefix /path/to/resolc-compiler-tests/fixtures \
    --platform-label macos

Example of extracted hashes output:

{
  "platform": "macos",
  "hashes": {
    "Y M0 S+": {
      "solidity/simple/block/pyramid.sol": {
        "Test": "0x3126bf12aaf6024767da0dba42f40a7bd602c3899a705904e7e8d0606f505000"
      },
      "solidity/simple/call_chain/address_size1.sol": {
        "Test": "0xc552314bd6c7a6293f0e7f45dc1b339670ec7a4c53f11445c23db163a44f1083",
        "TestA": "0x411ce1cc977316c69a03acb0217b78d4c6ab74b1257283e721187cc27129320e"
      },
    },
    "Y M3 S+": {
      "solidity/simple/block/pyramid.sol": {
        "Test": "0x3126bf12aaf6024767da0dba42f40a7bd602c3899a705904e7e8d0606f505000"
      },
      "solidity/simple/call_chain/address_size1.sol": {
        "Test": "0xc552314bd6c7a6293f0e7f45dc1b339670ec7a4c53f11445c23db163a44f1083",
        "TestA": "0x411ce1cc977316c69a03acb0217b78d4c6ab74b1257283e721187cc27129320e"
      },
    },
    "Y Mz S+": {
      "solidity/simple/block/pyramid.sol": {
        "Test": "0x3126bf12aaf6024767da0dba42f40a7bd602c3899a705904e7e8d0606f505000"
      },
      "solidity/simple/call_chain/address_size1.sol": {
        "Test": "0xc552314bd6c7a6293f0e7f45dc1b339670ec7a4c53f11445c23db163a44f1083",
        "TestA": "0x411ce1cc977316c69a03acb0217b78d4c6ab74b1257283e721187cc27129320e"
      },
    }
  }
}

Comparing hashes

  • For each mode, the union of all source paths and contracts is traversed.
  • For each contract, all platforms' hashes are compared.
  • Any mismatch (either due to a different hash or missing hash) in any platform is reported.
  • The comparison result is written to the --output-path and a human readable summary is printed to stdout.
  • The above approach is used, rather than e.g. a diff-based approach, in order to generate more structured results across multi-platform comparisons.

Example run command:

If --mode is specified, only the provided modes will be compared, otherwise the union of all modes found in the files will be compared. (The mode format shares the same parsing logic as used for the retester.)

report-processor compare-hashes \
    --hash-path ./hashes/hashes-macos.json \
    --hash-path ./hashes/hashes-linux.json \
    --hash-path ./hashes/hashes-windows.json \
    --mode "Y M0 S+" \
    --mode "Y M3 S+" \
    --mode "Y Mz S+" \
    --output-path ./hash-comparison.json

Example comparison result and summary:

Expand to see an example comparison result
{
  "platforms": [
    "linux",
    "macos",
    "windows"
  ],
  "hash_counts": {
    "Y M0 S+": {
      "linux": 2978,
      "macos": 2978,
      "windows": 2978
    },
    "Y M3 S+": {
      "linux": 2978,
      "macos": 2978,
      "windows": 2968
    },
    "Y Mz S+": {
      "linux": 2978,
      "macos": 2978,
      "windows": 2978
    }
  },
  "mismatches": {
    "Y M0 S+": [],
    "Y M3 S+": [
      {
        "path": "fixtures/solidity/complex/create/create_in_library/main.sol",
        "contract_name": "Main",
        "hashes": {
          "linux": "0x7d9b1f1ce522121e54ca39a6bf0ffc1bdea31677e44be556faa2156212b1c6cf",
          "macos": "0xd5f5a8014f5326d9f027e35c9bf02635f6aefa4e6f5f499b6cf8c4219a6e06a5",
          "windows": "0x1d9c35bd92bacfa5b936e5a5c106b8f425720fe24f4852cf96fb94fb96cc1d92"
        }
      },
      {
        "path": "fixtures/solidity/complex/defi/UniswapV4/TickMath_loop.sol",
        "contract_name": "TickMathLoop",
        "hashes": {
          "linux": "0xe065e2b56c9247ec351dba8b0a3cf46dc445ec3235e595bf92e3b8dc8bf97366",
          "macos": "0xe065e2b56c9247ec351dba8b0a3cf46dc445ec3235e595bf92e3b8dc8bf97366",
          "windows": "0x137eb27e3d94e51185391fbc40753b9dce63a3b3bbfd18336ed8f6292f421b87"
        }
      },
      {
        "path": "fixtures/solidity/complex/indirect_recursion_fact/first.sol",
        "contract_name": "First",
        "hashes": {
          "linux": "0x9a3f8f95825d47314f2db875bc82e4a7a0922facf03896206d7073e1f06a457f",
          "macos": "0x9a3f8f95825d47314f2db875bc82e4a7a0922facf03896206d7073e1f06a457f",
          "windows": null
        }
      },
      // ...
    ],
    "Y Mz S+": []
  }
}
Expand to see an example stdout summary
===========================================
COMPARISONS
===========================================

-------------------------------------------
Mode: Y M0 S+
-------------------------------------------

Hash counts:
    - linux: 2978
    - macos: 2978
    - windows: 2978

Mismatches: ✅ 0

-------------------------------------------
Mode: Y M3 S+
-------------------------------------------

Hash counts:
    - linux: 2978
    - macos: 2978
    - windows: 2968

Mismatches: ❌ 64 (including 10 missing hashes)

    - path: fixtures/solidity/complex/create/create_in_library/main.sol
      contract: Main
      linux: 0x7d9b1f1ce522121e54ca39a6bf0ffc1bdea31677e44be556faa2156212b1c6cf
      macos: 0xd5f5a8014f5326d9f027e35c9bf02635f6aefa4e6f5f499b6cf8c4219a6e06a5
      windows: 0x1d9c35bd92bacfa5b936e5a5c106b8f425720fe24f4852cf96fb94fb96cc1d92

    - path: fixtures/solidity/complex/defi/UniswapV4/TickMath_loop.sol
      contract: TickMathLoop
      linux: 0xe065e2b56c9247ec351dba8b0a3cf46dc445ec3235e595bf92e3b8dc8bf97366
      macos: 0xe065e2b56c9247ec351dba8b0a3cf46dc445ec3235e595bf92e3b8dc8bf97366
      windows: 0x137eb27e3d94e51185391fbc40753b9dce63a3b3bbfd18336ed8f6292f421b87

    - path: fixtures/solidity/complex/indirect_recursion_fact/first.sol
      contract: First
      linux: 0x9a3f8f95825d47314f2db875bc82e4a7a0922facf03896206d7073e1f06a457f
      macos: 0x9a3f8f95825d47314f2db875bc82e4a7a0922facf03896206d7073e1f06a457f
      windows: MISSING

    ... and 61 more

-------------------------------------------
Mode: Y Mz S+
-------------------------------------------

Hash counts:
    - linux: 2978
    - macos: 2978
    - windows: 2978

Mismatches: ✅ 0

===========================================
SUMMARY
===========================================

* Platforms compared: 
    - linux
    - macos
    - windows

* Modes compared: 
    - Y M0 S+
    - Y M3 S+
    - Y Mz S+

Total mismatches for all modes: 64 (including 10 missing hashes)

❌ FAILURE: Mismatches found among the compared platforms!

===========================================

@elle-j elle-j requested review from 0xOmarA and xermicus March 10, 2026 14:27
@elle-j elle-j marked this pull request as ready for review March 10, 2026 14:27
@elle-j elle-j requested a review from kvpanch March 12, 2026 14:28
@0xOmarA
Copy link
Contributor

0xOmarA commented Mar 16, 2026

Question, why do we need one of the files to be used as the reference platform? Could we not: get all of the files, extract all of the metadata file paths from them into a set and extract all of their modes into a set and then iterate through this super set comparing all of the items in the process?

}

/// Compares all platforms' hashes at the given `mode`.
fn compare(all_hashes: &[&HashData], mode: &str) -> Vec<Mismatch> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Question, why do we need one of the files to be used as the reference platform? Could we not: get all of the files, extract all of the metadata file paths from them into a set and extract all of their modes into a set and then iterate through this super set comparing all of the items in the process?

@0xOmarA , to answer your earlier comment ☝️ I'll create a thread here for easier communication.

All modes, file paths, and contracts were already extracted into sets. Selecting a reference platform is not needed per say, it's just one comparison approach. I've updated it now tho to show all platforms' hashes compared to each other rather than each one against the reference. The human parsing of the output becomes easier to understand, so thanks for this input :)

elle-j added 2 commits March 18, 2026 15:50
comparability.

Source paths in the standard JSON compiler input become part of fully
qualified library names embedded in bytecode. Different absolute paths
per platform produced different bytecode for the same contract,
preventing cross-platform hash comparison. On Windows, canonicalize()
also produced \\?\ prefixed paths that broke solc import resolution for
contracts with circular or multi-file imports, causing compilation
failures.

This normalizes source and library paths to relative paths (from the
metadata directory) before passing them to solc/resolc, and
re-absolutizes the compiler output source paths before canonicalizing.
/// Returns an error if `base_path` is `Some` but is not a base directory of `path`.
///
/// Purpose of normalization:
/// 1. **Cross-platform bytecode comparability**: Source paths passed via solc's standard JSON
Copy link
Member

Choose a reason for hiding this comment

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

Why is that? If the symbols are propagated into the PVM blob (why?), then we need to fix this in the compiler instead. But I don't see why and I can't reproduce this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants