Skip to content

Commit

Permalink
Check storage layout consistency in PRs (#3967)
Browse files Browse the repository at this point in the history
Co-authored-by: Francisco <frangio.1@gmail.com>
  • Loading branch information
Amxx and frangio authored Jan 31, 2023
1 parent 501a78e commit a70ee4e
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 0 deletions.
55 changes: 55 additions & 0 deletions .github/actions/storage-layout/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Compare storage layouts
inputs:
token:
description: github token
required: true
buildinfo:
description: compilation artifacts
required: false
default: artifacts/build-info/*.json
layout:
description: extracted storage layout
required: false
default: HEAD.layout.json
out_layout:
description: storage layout to upload
required: false
default: ${{ github.ref_name }}.layout.json
ref_layout:
description: storage layout for the reference branch
required: false
default: ${{ github.base_ref }}.layout.json

runs:
using: composite
steps:
- name: Extract layout
run: |
node scripts/checks/extract-layout.js ${{ inputs.buildinfo }} > ${{ inputs.layout }}
shell: bash
- name: Download reference
if: github.event_name == 'pull_request'
run: |
RUN_ID=`gh run list --repo ${{ github.repository }} --branch ${{ github.base_ref }} --workflow ${{ github.workflow }} --limit 100 --json 'conclusion,databaseId,event' --jq 'map(select(.conclusion=="success" and .event!="pull_request"))[0].databaseId'`
gh run download ${RUN_ID} --repo ${{ github.repository }} -n layout
env:
GITHUB_TOKEN: ${{ inputs.token }}
shell: bash
continue-on-error: true
id: reference
- name: Compare layouts
if: steps.reference.outcome == 'success' && github.event_name == 'pull_request'
run: |
node scripts/checks/compare-layout.js --head ${{ inputs.layout }} --ref ${{ inputs.ref_layout }} >> $GITHUB_STEP_SUMMARY
shell: bash
- name: Rename artifacts for upload
if: github.event_name != 'pull_request'
run: |
mv ${{ inputs.layout }} ${{ inputs.out_layout }}
shell: bash
- name: Save artifacts
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v3
with:
name: layout
path: ${{ inputs.out_layout }}
4 changes: 4 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ jobs:
uses: ./.github/actions/gas-compare
with:
token: ${{ github.token }}
- name: Check storage layout
uses: ./.github/actions/storage-layout
with:
token: ${{ github.token }}

foundry-tests:
if: github.repository != 'OpenZeppelin/openzeppelin-contracts-upgradeable'
Expand Down
144 changes: 144 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"@nomiclabs/hardhat-web3": "^2.0.0",
"@openzeppelin/docs-utils": "^0.1.3",
"@openzeppelin/test-helpers": "^0.5.13",
"@openzeppelin/upgrades-core": "^1.20.6",
"chai": "^4.2.0",
"eslint": "^8.30.0",
"eslint-config-prettier": "^8.5.0",
Expand Down
20 changes: 20 additions & 0 deletions scripts/checks/compare-layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const fs = require('fs');
const { getStorageUpgradeReport } = require('@openzeppelin/upgrades-core/dist/storage');

const { ref, head } = require('yargs').argv;

const oldLayout = JSON.parse(fs.readFileSync(ref));
const newLayout = JSON.parse(fs.readFileSync(head));

for (const name in oldLayout) {
if (name in newLayout) {
const report = getStorageUpgradeReport(oldLayout[name], newLayout[name], {});
if (!report.ok) {
console.log(`ERROR: Storage incompatibility in ${name}`);
console.log(report.explain());
process.exitCode = 1;
}
} else {
console.log(`WARNING: ${name} is missing from the current branch`);
}
}
40 changes: 40 additions & 0 deletions scripts/checks/extract-layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const fs = require('fs');
const { findAll } = require('solidity-ast/utils');
const { astDereferencer } = require('@openzeppelin/upgrades-core/dist/ast-dereferencer');
const { solcInputOutputDecoder } = require('@openzeppelin/upgrades-core/dist/src-decoder');
const { extractStorageLayout } = require('@openzeppelin/upgrades-core/dist/storage/extract');

const { _ } = require('yargs').argv;

const skipPath = ['contracts/mocks/', 'contracts-exposed/'];
const skipKind = ['interface', 'library'];

function extractLayouts(path) {
const layout = {};
const { input, output } = JSON.parse(fs.readFileSync(path));

const decoder = solcInputOutputDecoder(input, output);
const deref = astDereferencer(output);

for (const src in output.contracts) {
if (skipPath.some(prefix => src.startsWith(prefix))) {
continue;
}

for (const contractDef of findAll('ContractDefinition', output.sources[src].ast)) {
if (skipKind.includes(contractDef.contractKind)) {
continue;
}

layout[contractDef.name] = extractStorageLayout(
contractDef,
decoder,
deref,
output.contracts[src][contractDef.name].storageLayout,
);
}
}
return layout;
}

console.log(JSON.stringify(Object.assign(..._.map(extractLayouts))));

0 comments on commit a70ee4e

Please sign in to comment.