Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check compiled contracts are within EIP-3860 limits #4885

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/checks.yml
Expand Up @@ -39,6 +39,8 @@ jobs:
uses: ./.github/actions/setup
- name: Run tests and generate gas report
run: npm run test
- name: Check EIP-3860 limits
run: npm run test:artifacts-size
- name: Check linearisation of the inheritance graph
run: npm run test:inheritance
- name: Check proceduraly generated contracts are up-to-date
Expand Down Expand Up @@ -66,6 +68,10 @@ jobs:
run: bash scripts/upgradeable/transpile.sh
- name: Run tests
run: npm run test
env:
UNLIMITED: true
- name: Check EIP-3860 limits
run: npm run test:artifacts-size
- name: Check linearisation of the inheritance graph
run: npm run test:inheritance
- name: Check storage layout
Expand Down
45 changes: 24 additions & 21 deletions hardhat.config.js
@@ -1,12 +1,13 @@
/// ENVVAR
// - COMPILE_VERSION: compiler version (default: 0.8.20)
// - SRC: contracts folder to compile (default: contracts)
// - COMPILE_MODE: production modes enables optimizations (default: development)
// - IR: enable IR compilation (default: false)
// - COVERAGE: enable coverage report
// - ENABLE_GAS_REPORT: enable gas report
// - COINMARKETCAP: coinmarkercat api key for USD value in gas report
// - CI: output gas report to file instead of stdout
// - COMPILER: compiler version (default: 0.8.20)
// - SRC: contracts folder to compile (default: contracts)
// - RUNS: number of optimization runs (default: 200)
// - IR: enable IR compilation (default: false)
// - UNLIMITED: allow deployment of contracts larger than 24k (default: false)
// - COVERAGE: enable coverage report (default: false)
// - GAS: enable gas report (default: false)
// - COINMARKETCAP: coinmarketcap api key for USD value in gas report
// - CI: output gas report to file instead of stdout

const fs = require('fs');
const path = require('path');
Expand All @@ -25,17 +26,21 @@ const { argv } = require('yargs/yargs')()
type: 'string',
default: 'contracts',
},
mode: {
alias: 'compileMode',
type: 'string',
choices: ['production', 'development'],
default: 'development',
runs: {
alias: 'optimizationRuns',
type: 'number',
default: 200,
},
ir: {
alias: 'enableIR',
type: 'boolean',
default: false,
},
unlimited: {
alias: 'allowUnlimitedContractSize',
type: 'boolean',
default: false,
},
// Extra modules
coverage: {
type: 'boolean',
Expand Down Expand Up @@ -64,9 +69,6 @@ for (const f of fs.readdirSync(path.join(__dirname, 'hardhat'))) {
require(path.join(__dirname, 'hardhat', f));
}

const withOptimizations = argv.gas || argv.coverage || argv.compileMode === 'production';
const allowUnlimitedContractSize = argv.gas || argv.coverage || argv.compileMode === 'development';

/**
* @type import('hardhat/config').HardhatUserConfig
*/
Expand All @@ -75,10 +77,11 @@ module.exports = {
version: argv.compiler,
settings: {
optimizer: {
enabled: withOptimizations,
runs: 200,
enabled: true,
runs: argv.runs,
details: { yul: true },
},
viaIR: withOptimizations && argv.ir,
viaIR: argv.ir,
outputSelection: { '*': { '*': ['storageLayout'] } },
},
},
Expand All @@ -88,14 +91,14 @@ module.exports = {
'initcode-size': 'off',
},
'*': {
'code-size': withOptimizations,
'code-size': true,
'unused-param': !argv.coverage, // coverage causes unused-param warnings
default: 'error',
},
},
networks: {
hardhat: {
allowUnlimitedContractSize,
allowUnlimitedContractSize: argv.gas || argv.coverage || argv.unlimited,
initialBaseFeePerGas: argv.coverage ? 0 : undefined,
},
},
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -25,6 +25,7 @@
"generate": "scripts/generate/run.js",
"version": "scripts/release/version.sh",
"test": "hardhat test",
"test:artifacts-size": "scripts/checks/artifacts-size.js artifacts/build-info/*",
"test:inheritance": "scripts/checks/inheritance-ordering.js artifacts/build-info/*",
"test:generation": "scripts/checks/generation.sh",
"gas-report": "env ENABLE_GAS_REPORT=true npm run test",
Expand Down
41 changes: 41 additions & 0 deletions scripts/checks/artifacts-size.js
@@ -0,0 +1,41 @@
#!/usr/bin/env node

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

// Constants from EIP-3860
const MAX_CODE_SIZE = 24576;

// Get all compiled contracts from compilation artifacts
const contracts = Object.fromEntries(
artifacts
.map(artifact => require(path.resolve(__dirname, '../..', artifact)))
.flatMap(buildInfo =>
Object.entries(buildInfo.output.contracts).flatMap(([file, contracts]) =>
Object.entries(contracts).map(([name, details]) => [
`${file}:${name}`,
Object.assign(details, { extra: { file, name, compilerSettings: buildInfo.input.settings } }),
]),
),
),
);

for (const [id, details] of Object.entries(contracts)) {
// Skip "exposed" contracts
if (details.extra.name.startsWith('$')) {
continue;
}

// Skip (with warning) contracts that were not compiled with optimizations
if (!details.extra.compilerSettings.optimizer.enabled) {
console.warn(`[Warning] ${id} was compiled without optimizations. Skipped.`);
continue;
}

// Check deployed bytecode length is within limits
const { length: deployedBytecodeLength } = Buffer.from(details.evm.deployedBytecode.object, 'hex');
if (deployedBytecodeLength > MAX_CODE_SIZE) {
console.log('[deployedBytecode over limit]', id, deployedBytecodeLength);
process.exitCode = 1;
}
}
2 changes: 2 additions & 0 deletions scripts/checks/compare-layout.js 100644 → 100755
@@ -1,3 +1,5 @@
#!/usr/bin/env node

const fs = require('fs');
const { getStorageUpgradeReport } = require('@openzeppelin/upgrades-core/dist/storage');

Expand Down
2 changes: 2 additions & 0 deletions scripts/checks/extract-layout.js 100644 → 100755
@@ -1,3 +1,5 @@
#!/usr/bin/env node

const fs = require('fs');
const { findAll, astDereferencer, srcDecoder } = require('solidity-ast/utils');
const { extractStorageLayout } = require('@openzeppelin/upgrades-core/dist/storage/extract');
Expand Down