From 1da9510325f040bb12f56735f5fa78e82093de3b Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 7 Feb 2024 21:56:36 -0800 Subject: [PATCH 01/14] First draft --- .circleci/config.yml | 6 ++- lib/api.js | 5 +- lib/collector.js | 31 ++++++++++- lib/injector.js | 53 ++++++++++++++++--- lib/instrumenter.js | 2 +- package.json | 7 ++- plugins/hardhat.plugin.js | 7 ++- plugins/resources/nomiclabs.utils.js | 18 +++++++ plugins/resources/plugin.utils.js | 4 +- scripts/ci.sh | 8 ++- scripts/integration.sh | 3 ++ scripts/unit.sh | 3 ++ test/integration/standard.js | 2 +- .../contract-subfolders/hardhat.config.js | 8 ++- .../contracts/ContractA1.sol | 2 +- .../contracts/ContractB1.sol | 2 +- .../contracts/ContractC1.sol | 2 +- .../hardhat-compile-config/hardhat.config.js | 18 +++++-- .../hardhat-gas-reporter/hardhat.config.js | 8 ++- .../projects/hardhat-mine/hardhat.config.js | 8 ++- .../projects/hardhat-reset/hardhat.config.js | 8 ++- .../projects/import-paths/hardhat.config.js | 8 ++- .../projects/libraries/hardhat.config.js | 8 ++- .../sources/projects/matrix/hardhat.config.js | 8 ++- .../modifiers/contracts/ModifiersA.sol | 2 +- .../modifiers/contracts/ModifiersB.sol | 2 +- .../modifiers/contracts/ModifiersC.sol | 2 +- .../projects/modifiers/hardhat.config.js | 8 ++- .../multiple-suites/hardhat.config.js | 8 ++- .../projects/no-sources/hardhat.config.js | 8 ++- .../projects/skipping/hardhat.config.js | 8 ++- .../sources/projects/solc-8/hardhat.config.js | 8 ++- .../projects/task-hooks/hardhat.config.js | 8 ++- .../ternary-and-logical-or/hardhat.config.js | 8 ++- .../ternary-and-logical-or/test/test_or.js | 2 +- .../projects/test-files/hardhat.config.js | 8 ++- .../projects/tests-folder/hardhat.config.js | 8 ++- test/units/statements.js | 7 ++- test/util/integration.js | 8 ++- test/util/util.js | 11 +++- 40 files changed, 282 insertions(+), 53 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b4ced1a6..9ff9c9c8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -32,9 +32,13 @@ jobs: command: | yarn - run: - name: Run tests + name: Tests ( optimizer.enabled=false ) command: | npm run test:ci + - run: + name: Tests ( viaIR=true ) + command: | + npm run test:ci:viaIR - run: name: Upload coverage command: | diff --git a/lib/api.js b/lib/api.js index 296fcb48..7a10c70d 100644 --- a/lib/api.js +++ b/lib/api.js @@ -54,6 +54,7 @@ class API { this.istanbulFolder = config.istanbulFolder || false; this.istanbulReporter = config.istanbulReporter || ['html', 'lcov', 'text', 'json']; + this.viaIR = config.viaIR; this.solcOptimizerDetails = config.solcOptimizerDetails; this.setLoggingLevel(config.silent); @@ -92,6 +93,8 @@ class API { target.canonicalPath ); + //console.log('instrumented.contract --> ' + instrumented.contract); + this.coverage.addContract(instrumented, target.canonicalPath); outputs.push({ @@ -176,7 +179,7 @@ class API { // Hardhat async attachToHardhatVM(provider){ const self = this; - this.collector = new DataCollector(this.instrumenter.instrumentationData); + this.collector = new DataCollector(this.instrumenter.instrumentationData, this.viaIR); if ('init' in provider) { // Newer versions of Hardhat initialize the provider lazily, so we need to diff --git a/lib/collector.js b/lib/collector.js index d43ee7af..dffdd1bc 100644 --- a/lib/collector.js +++ b/lib/collector.js @@ -3,12 +3,30 @@ * coverage map constructed by the Instrumenter. */ class DataCollector { - constructor(instrumentationData={}){ + constructor(instrumentationData={}, viaIR){ this.instrumentationData = instrumentationData; this.validOpcodes = { "PUSH1": true, + "DUP1": viaIR, + "DUP2": viaIR, + "DUP3": viaIR, + "DUP4": viaIR, + "DUP5": viaIR, + "DUP6": viaIR, + "DUP7": viaIR, + "DUP8": viaIR, + "DUP9": viaIR, + "DUP10": viaIR, + "DUP11": viaIR, + "DUP12": viaIR, + "DUP13": viaIR, + "DUP14": viaIR, + "DUP15": viaIR, + "DUP16": viaIR, } + + this.lastHash = null; } /** @@ -22,6 +40,8 @@ class DataCollector { const idx = info.stack.length - 1; let hash = '0x' + info.stack[idx].toString(16); this._registerHash(hash) + + // console.log('hash: ' + info.opcode.name + " " + hash) } } catch (err) { /*Ignore*/ }; } @@ -45,8 +65,15 @@ class DataCollector { _registerHash(hash){ hash = this._normalizeHash(hash); + //console.log('normalized: ' + hash); + if(this.instrumentationData[hash]){ - this.instrumentationData[hash].hits++; + + // abi.encode (used to circumvent viaIR) puts the hash on the stack twice + if (this.lastHash !== hash) { + this.lastHash = hash; + this.instrumentationData[hash].hits++ + } } } diff --git a/lib/injector.js b/lib/injector.js index e1bd0b06..3072167e 100644 --- a/lib/injector.js +++ b/lib/injector.js @@ -1,7 +1,8 @@ const web3Utils = require("web3-utils"); class Injector { - constructor(){ + constructor(viaIR){ + this.viaIR = viaIR; this.hashCounter = 0; this.modifierCounter = 0; this.modifiers = {}; @@ -23,7 +24,9 @@ class Injector { case 'modifier': return ` ${this._getModifierIdentifier(id)} `; default: - return `${this._getDefaultMethodIdentifier(id)}(${hash}); /* ${type} */ \n`; + return (this.viaIR) + ? `${this._getAbiEncodeStatementHash(hash)} /* ${type} */ \n` + : `${this._getDefaultMethodIdentifier(id)}(${hash}); /* ${type} */ \n`; } } @@ -51,6 +54,17 @@ class Injector { return `c_mod${web3Utils.keccak256(id).slice(2,10)}` } + // Way to get hash on the stack with viaIR (which seems to ignore abi.encode (v0.8.24)) + // If you visiting solidity-coverage & understand what's happening here, please do not mention + // this obscure and irrelevant optimization opportunity in the issues at ethereum/solidity + _getAbiEncodeStatementHash(hash){ + return `abi.encode(${hash}); ` + } + + _getAbiEncodeStatementVar(hash){ + return `abi.encode(c__${hash}); ` + } + _getInjectionComponents(contract, injectionPoint, id, type){ const { start, end } = this._split(contract, injectionPoint); const hash = this._getHash(id) @@ -73,7 +87,10 @@ class Injector { _getDefaultMethodDefinition(id){ const hash = web3Utils.keccak256(id).slice(2,10); const method = this._getDefaultMethodIdentifier(id); - return `\nfunction ${method}(bytes8 c__${hash}) internal pure {}\n`; + + return (this.viaIR) + ? `` + : `\nfunction ${method}(bytes8 c__${hash}) internal pure {}\n`; } /** @@ -85,7 +102,11 @@ class Injector { _getFileScopedHashMethodDefinition(id, contract){ const hash = web3Utils.keccak256(id).slice(2,10); const method = this._getDefaultMethodIdentifier(id); - return `\nfunction ${method}(bytes8 c__${hash}) pure {}\n`; + const abi = this._getAbiEncodeStatementVar(hash); + + return (this.viaIR) + ? `\nfunction ${method}(bytes8 c__${hash}) pure { ${abi} }\n` + : `\nfunction ${method}(bytes8 c__${hash}) pure {}\n`; } /** @@ -97,7 +118,11 @@ class Injector { _getTrueMethodDefinition(id){ const hash = web3Utils.keccak256(id).slice(2,10); const method = this._getTrueMethodIdentifier(id); - return `function ${method}(bytes8 c__${hash}) internal pure returns (bool){ return true; }\n`; + const abi = this._getAbiEncodeStatementVar(hash); + + return (this.viaIR) + ? `function ${method}(bytes8 c__${hash}) internal pure returns (bool){ ${abi} return true; }\n` + : `function ${method}(bytes8 c__${hash}) internal pure returns (bool){ return true; }\n`; } /** @@ -110,7 +135,11 @@ class Injector { _getFileScopeTrueMethodDefinition(id){ const hash = web3Utils.keccak256(id).slice(2,10); const method = this._getTrueMethodIdentifier(id); - return `function ${method}(bytes8 c__${hash}) pure returns (bool){ return true; }\n`; + const abi = this._getAbiEncodeStatementVar(hash); + + return (this.viaIR) + ? `function ${method}(bytes8 c__${hash}) pure returns (bool){ ${abi} return true; }\n` + : `function ${method}(bytes8 c__${hash}) pure returns (bool){ return true; }\n`; } /** @@ -122,7 +151,11 @@ class Injector { _getFalseMethodDefinition(id){ const hash = web3Utils.keccak256(id).slice(2,10); const method = this._getFalseMethodIdentifier(id); - return `function ${method}(bytes8 c__${hash}) internal pure returns (bool){ return false; }\n`; + const abi = this._getAbiEncodeStatementVar(hash); + + return (this.viaIR) + ? `function ${method}(bytes8 c__${hash}) internal pure returns (bool){ ${abi} return false; }\n` + : `function ${method}(bytes8 c__${hash}) internal pure returns (bool){ return false; }\n`; } /** @@ -135,7 +168,11 @@ class Injector { _getFileScopedFalseMethodDefinition(id){ const hash = web3Utils.keccak256(id).slice(2,10); const method = this._getFalseMethodIdentifier(id); - return `function ${method}(bytes8 c__${hash}) pure returns (bool){ return false; }\n`; + const abi = this._getAbiEncodeStatementVar(hash); + + return (this.viaIR) + ? `function ${method}(bytes8 c__${hash}) pure returns (bool){ ${abi} return false; }\n` + : `function ${method}(bytes8 c__${hash}) pure returns (bool){ return false; }\n`; } _getModifierDefinitions(contractId, instrumentation){ diff --git a/lib/instrumenter.js b/lib/instrumenter.js index 68bcfe09..4ae909a1 100644 --- a/lib/instrumenter.js +++ b/lib/instrumenter.js @@ -14,7 +14,7 @@ class Instrumenter { constructor(config={}){ this.instrumentationData = {}; - this.injector = new Injector(); + this.injector = new Injector(config.viaIR); this.modifierWhitelist = config.modifierWhitelist || []; this.enabled = { statements: (config.measureStatementCoverage === false) ? false : true, diff --git a/package.json b/package.json index 299e1649..de1bd343 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,4 @@ -{ + { "name": "solidity-coverage", "version": "0.8.6", "description": "Code coverage for Solidity testing", @@ -12,7 +12,10 @@ "scripts": { "test:unit": "./scripts/unit.sh", "test:integration": "./scripts/integration.sh", - "test:ci": "./scripts/ci.sh" + "test:ci": "./scripts/ci.sh", + "test:uint:viaIR": "VIA_IR=true ./scripts/unit.sh", + "test:integration:viaIR": "VIA_IR=true ./scripts/integration.sh", + "test:ci:viaIR": "VIA_IR=true ./scripts/ci.sh" }, "homepage": "https://github.com/sc-forks/solidity-coverage", "repository": { diff --git a/plugins/hardhat.plugin.js b/plugins/hardhat.plugin.js index e11a58af..2ad88755 100644 --- a/plugins/hardhat.plugin.js +++ b/plugins/hardhat.plugin.js @@ -52,10 +52,13 @@ subtask(TASK_COMPILE_SOLIDITY_GET_COMPILATION_JOB_FOR_FILE).setAction(async (_, if (settings.optimizer === undefined) { settings.optimizer = {}; } + // Unset useLiteralContent due to solc metadata size restriction settings.metadata.useLiteralContent = false; - // Override optimizer settings for all compilers - settings.optimizer.enabled = false; + + // Beginning with v0.8.7, we let the optimizer run if viaIR is true and + // instrument using `verbatim` YUL keyword. Otherwise turn the optimizer off. + if (!settings.viaIR) settings.optimizer.enabled = false; // This is fixes a stack too deep bug in ABIEncoderV2 // Experimental because not sure this works as expected across versions.... diff --git a/plugins/resources/nomiclabs.utils.js b/plugins/resources/nomiclabs.utils.js index 2966d080..10092598 100644 --- a/plugins/resources/nomiclabs.utils.js +++ b/plugins/resources/nomiclabs.utils.js @@ -36,6 +36,13 @@ function normalizeConfig(config, args={}){ ? sources = path.join(config.paths.sources, args.sources) : sources = config.paths.sources; + //const { inspect } = require('util'); + //console.log('config --> ' + inspect(config.solidity.compilers)); + + if (config.solidity && config.solidity.compilers.length) { + config.viaIR = isUsingViaIR(config.solidity); + } + config.workingDir = config.paths.root; config.contractsDir = sources; config.testDir = config.paths.tests; @@ -55,6 +62,17 @@ function normalizeConfig(config, args={}){ return config; } +function isUsingViaIR(solidity) { + let viaIR = false; + for (compiler of solidity.compilers) { + if (compiler.settings && compiler.settings.viaIR) { + viaIR = true; + } + } + // Also handle overrides... + return viaIR; +} + async function setupHardhatNetwork(env, api, ui){ const hardhatPackage = require('hardhat/package.json'); const { createProvider } = require("hardhat/internal/core/providers/construction"); diff --git a/plugins/resources/plugin.utils.js b/plugins/resources/plugin.utils.js index c722a08f..f2e9e891 100644 --- a/plugins/resources/plugin.utils.js +++ b/plugins/resources/plugin.utils.js @@ -227,7 +227,9 @@ function loadSolcoverJS(config={}){ coverageConfig = {}; } - // Truffle writes to coverage config + // viaIR is eval'd in `nomiclab.utils.normalizeConfig` + coverageConfig.viaIR = config.viaIR; + coverageConfig.log = log; coverageConfig.cwd = config.workingDir; coverageConfig.originalContractsDir = config.contractsDir; diff --git a/scripts/ci.sh b/scripts/ci.sh index 84e8d307..849d047e 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -1,6 +1,12 @@ #!/usr/bin/env bash -SILENT=true node --max-old-space-size=4096 \ +# Toggles optimizer on/off +VIAR_IR=$VIA_IR + +# Minimize integration test output +SILENT=true + +node --max-old-space-size=4096 \ ./node_modules/.bin/nyc \ --reporter=lcov \ --exclude '**/sc_temp/**' \ diff --git a/scripts/integration.sh b/scripts/integration.sh index c9d080a7..ee2112dd 100755 --- a/scripts/integration.sh +++ b/scripts/integration.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash +# Toggles optimizer on/off +VIA_IR=$VIA_IR + node --max-old-space-size=4096 \ ./node_modules/.bin/nyc \ --exclude '**/sc_temp/**' \ diff --git a/scripts/unit.sh b/scripts/unit.sh index e727105c..044a5667 100755 --- a/scripts/unit.sh +++ b/scripts/unit.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash +# Toggles optimizer on/off +VIAR_IR=$VIA_IR + node --max-old-space-size=4096 \ ./node_modules/.bin/nyc \ --exclude '**/sc_temp/**' \ diff --git a/test/integration/standard.js b/test/integration/standard.js index fd4275fa..cc86c874 100644 --- a/test/integration/standard.js +++ b/test/integration/standard.js @@ -364,7 +364,7 @@ describe('Hardhat Plugin: standard use cases', function() { verify.lineCoverage(expected); }) - it('logicalOR & ternary conditionals', async function(){ + it.only('logicalOR & ternary conditionals', async function(){ mock.installFullProject('ternary-and-logical-or'); mock.hardhatSetupEnv(this); diff --git a/test/sources/projects/contract-subfolders/hardhat.config.js b/test/sources/projects/contract-subfolders/hardhat.config.js index 4e58d0c9..a9740668 100644 --- a/test/sources/projects/contract-subfolders/hardhat.config.js +++ b/test/sources/projects/contract-subfolders/hardhat.config.js @@ -8,7 +8,13 @@ module.exports={ } }, solidity: { - version: "0.8.17" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, paths: { sources: './contracts/A' diff --git a/test/sources/projects/hardhat-compile-config/contracts/ContractA1.sol b/test/sources/projects/hardhat-compile-config/contracts/ContractA1.sol index b6a0b09d..ad19d2c1 100644 --- a/test/sources/projects/hardhat-compile-config/contracts/ContractA1.sol +++ b/test/sources/projects/hardhat-compile-config/contracts/ContractA1.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.5.5; +pragma solidity >=0.8.0 <0.9.0; contract ContractA { diff --git a/test/sources/projects/hardhat-compile-config/contracts/ContractB1.sol b/test/sources/projects/hardhat-compile-config/contracts/ContractB1.sol index daa42f7d..6d5f53d4 100644 --- a/test/sources/projects/hardhat-compile-config/contracts/ContractB1.sol +++ b/test/sources/projects/hardhat-compile-config/contracts/ContractB1.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.5.0; +pragma solidity >=0.8.0 <0.9.0; contract ContractB { diff --git a/test/sources/projects/hardhat-compile-config/contracts/ContractC1.sol b/test/sources/projects/hardhat-compile-config/contracts/ContractC1.sol index 17ddf977..8c24195f 100644 --- a/test/sources/projects/hardhat-compile-config/contracts/ContractC1.sol +++ b/test/sources/projects/hardhat-compile-config/contracts/ContractC1.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.6.0; +pragma solidity >=0.8.0 <0.9.0; contract ContractC { diff --git a/test/sources/projects/hardhat-compile-config/hardhat.config.js b/test/sources/projects/hardhat-compile-config/hardhat.config.js index 443e085b..2eee95d9 100644 --- a/test/sources/projects/hardhat-compile-config/hardhat.config.js +++ b/test/sources/projects/hardhat-compile-config/hardhat.config.js @@ -5,14 +5,20 @@ module.exports={ solidity: { compilers: [ { - version: "0.5.5" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, { - version: "0.5.7" + version: "0.8.19" }, // Make sure optimizer gets disabled { - version: "0.6.7", + version: "0.8.12", settings: { optimizer: { enabled: true, @@ -23,11 +29,13 @@ module.exports={ ], overrides: { "contracts/ContractA.sol": { - version: "0.5.5", + version: "0.8.24", settings: { optimizer: { enabled: true, - runs: 200 + runs: 200, + viaIR: process.env.VIA_IR === "true", + evmVersion: 'paris' } } } diff --git a/test/sources/projects/hardhat-gas-reporter/hardhat.config.js b/test/sources/projects/hardhat-gas-reporter/hardhat.config.js index e42a38ab..87417f57 100644 --- a/test/sources/projects/hardhat-gas-reporter/hardhat.config.js +++ b/test/sources/projects/hardhat-gas-reporter/hardhat.config.js @@ -4,7 +4,13 @@ require(__dirname + "/../plugins/nomiclabs.plugin"); module.exports = { solidity: { - version: "0.8.17" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, logger: process.env.SILENT ? { log: () => {} } : console, }; diff --git a/test/sources/projects/hardhat-mine/hardhat.config.js b/test/sources/projects/hardhat-mine/hardhat.config.js index 803b0994..aac3f870 100644 --- a/test/sources/projects/hardhat-mine/hardhat.config.js +++ b/test/sources/projects/hardhat-mine/hardhat.config.js @@ -4,7 +4,13 @@ require(__dirname + "/../plugins/nomiclabs.plugin"); module.exports = { solidity: { - version: "0.8.17" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, logger: process.env.SILENT ? { log: () => {} } : console, }; diff --git a/test/sources/projects/hardhat-reset/hardhat.config.js b/test/sources/projects/hardhat-reset/hardhat.config.js index 7954d6cb..00428efc 100644 --- a/test/sources/projects/hardhat-reset/hardhat.config.js +++ b/test/sources/projects/hardhat-reset/hardhat.config.js @@ -10,7 +10,13 @@ if (!process.env.ALCHEMY_TOKEN){ module.exports = { solidity: { - version: "0.8.17" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, networks: { hardhat: { diff --git a/test/sources/projects/import-paths/hardhat.config.js b/test/sources/projects/import-paths/hardhat.config.js index c417cf81..cfc8f8ee 100644 --- a/test/sources/projects/import-paths/hardhat.config.js +++ b/test/sources/projects/import-paths/hardhat.config.js @@ -3,7 +3,13 @@ require(__dirname + "/../plugins/nomiclabs.plugin"); module.exports = { solidity: { - version: "0.8.17" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, logger: process.env.SILENT ? { log: () => {} } : console, }; diff --git a/test/sources/projects/libraries/hardhat.config.js b/test/sources/projects/libraries/hardhat.config.js index c417cf81..cfc8f8ee 100644 --- a/test/sources/projects/libraries/hardhat.config.js +++ b/test/sources/projects/libraries/hardhat.config.js @@ -3,7 +3,13 @@ require(__dirname + "/../plugins/nomiclabs.plugin"); module.exports = { solidity: { - version: "0.8.17" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, logger: process.env.SILENT ? { log: () => {} } : console, }; diff --git a/test/sources/projects/matrix/hardhat.config.js b/test/sources/projects/matrix/hardhat.config.js index 7c8bb986..983e864b 100644 --- a/test/sources/projects/matrix/hardhat.config.js +++ b/test/sources/projects/matrix/hardhat.config.js @@ -3,7 +3,13 @@ require(__dirname + "/../plugins/nomiclabs.plugin"); module.exports={ solidity: { - version: "0.8.17" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, logger: process.env.SILENT ? { log: () => {} } : console, }; diff --git a/test/sources/projects/modifiers/contracts/ModifiersA.sol b/test/sources/projects/modifiers/contracts/ModifiersA.sol index 5e00fb9c..9c2497ee 100644 --- a/test/sources/projects/modifiers/contracts/ModifiersA.sol +++ b/test/sources/projects/modifiers/contracts/ModifiersA.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.6.0; +pragma solidity >=0.8.0 <0.9.0; import "./ModifiersB.sol"; diff --git a/test/sources/projects/modifiers/contracts/ModifiersB.sol b/test/sources/projects/modifiers/contracts/ModifiersB.sol index 8c607383..3d8b3d93 100644 --- a/test/sources/projects/modifiers/contracts/ModifiersB.sol +++ b/test/sources/projects/modifiers/contracts/ModifiersB.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.6.0; +pragma solidity >=0.8.0 <0.9.0; contract ModifiersB { diff --git a/test/sources/projects/modifiers/contracts/ModifiersC.sol b/test/sources/projects/modifiers/contracts/ModifiersC.sol index c5302b1a..3d4d171f 100644 --- a/test/sources/projects/modifiers/contracts/ModifiersC.sol +++ b/test/sources/projects/modifiers/contracts/ModifiersC.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.6.0; +pragma solidity >=0.8.0 <0.9.0; import "./ModifiersB.sol"; diff --git a/test/sources/projects/modifiers/hardhat.config.js b/test/sources/projects/modifiers/hardhat.config.js index 448b09d2..cfc8f8ee 100644 --- a/test/sources/projects/modifiers/hardhat.config.js +++ b/test/sources/projects/modifiers/hardhat.config.js @@ -3,7 +3,13 @@ require(__dirname + "/../plugins/nomiclabs.plugin"); module.exports = { solidity: { - version: "0.6.7" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, logger: process.env.SILENT ? { log: () => {} } : console, }; diff --git a/test/sources/projects/multiple-suites/hardhat.config.js b/test/sources/projects/multiple-suites/hardhat.config.js index 9b8f17b5..0a601f37 100644 --- a/test/sources/projects/multiple-suites/hardhat.config.js +++ b/test/sources/projects/multiple-suites/hardhat.config.js @@ -8,7 +8,13 @@ module.exports={ } }, solidity: { - version: "0.8.17" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, logger: process.env.SILENT ? { log: () => {} } : console, }; diff --git a/test/sources/projects/no-sources/hardhat.config.js b/test/sources/projects/no-sources/hardhat.config.js index c417cf81..cfc8f8ee 100644 --- a/test/sources/projects/no-sources/hardhat.config.js +++ b/test/sources/projects/no-sources/hardhat.config.js @@ -3,7 +3,13 @@ require(__dirname + "/../plugins/nomiclabs.plugin"); module.exports = { solidity: { - version: "0.8.17" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, logger: process.env.SILENT ? { log: () => {} } : console, }; diff --git a/test/sources/projects/skipping/hardhat.config.js b/test/sources/projects/skipping/hardhat.config.js index c417cf81..cfc8f8ee 100644 --- a/test/sources/projects/skipping/hardhat.config.js +++ b/test/sources/projects/skipping/hardhat.config.js @@ -3,7 +3,13 @@ require(__dirname + "/../plugins/nomiclabs.plugin"); module.exports = { solidity: { - version: "0.8.17" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, logger: process.env.SILENT ? { log: () => {} } : console, }; diff --git a/test/sources/projects/solc-8/hardhat.config.js b/test/sources/projects/solc-8/hardhat.config.js index 049df459..fd242498 100644 --- a/test/sources/projects/solc-8/hardhat.config.js +++ b/test/sources/projects/solc-8/hardhat.config.js @@ -3,7 +3,13 @@ require(__dirname + "/../plugins/nomiclabs.plugin"); module.exports = { solidity: { - version: "0.8.21" + version: "0.8.21", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, logger: process.env.SILENT ? { log: () => {} } : console, }; diff --git a/test/sources/projects/task-hooks/hardhat.config.js b/test/sources/projects/task-hooks/hardhat.config.js index c417cf81..cfc8f8ee 100644 --- a/test/sources/projects/task-hooks/hardhat.config.js +++ b/test/sources/projects/task-hooks/hardhat.config.js @@ -3,7 +3,13 @@ require(__dirname + "/../plugins/nomiclabs.plugin"); module.exports = { solidity: { - version: "0.8.17" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, logger: process.env.SILENT ? { log: () => {} } : console, }; diff --git a/test/sources/projects/ternary-and-logical-or/hardhat.config.js b/test/sources/projects/ternary-and-logical-or/hardhat.config.js index c417cf81..cfc8f8ee 100644 --- a/test/sources/projects/ternary-and-logical-or/hardhat.config.js +++ b/test/sources/projects/ternary-and-logical-or/hardhat.config.js @@ -3,7 +3,13 @@ require(__dirname + "/../plugins/nomiclabs.plugin"); module.exports = { solidity: { - version: "0.8.17" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, logger: process.env.SILENT ? { log: () => {} } : console, }; diff --git a/test/sources/projects/ternary-and-logical-or/test/test_or.js b/test/sources/projects/ternary-and-logical-or/test/test_or.js index de2491c2..3cb16cb7 100644 --- a/test/sources/projects/ternary-and-logical-or/test/test_or.js +++ b/test/sources/projects/ternary-and-logical-or/test/test_or.js @@ -5,7 +5,7 @@ contract("contract_or", function(accounts) { before(async () => instance = await Contract_OR.new()) - it('_if', async function(){ + it.only('_if', async function(){ await instance._if(0); await instance._if(7); }); diff --git a/test/sources/projects/test-files/hardhat.config.js b/test/sources/projects/test-files/hardhat.config.js index c417cf81..5be70475 100644 --- a/test/sources/projects/test-files/hardhat.config.js +++ b/test/sources/projects/test-files/hardhat.config.js @@ -3,7 +3,13 @@ require(__dirname + "/../plugins/nomiclabs.plugin"); module.exports = { solidity: { - version: "0.8.17" + version: "0.8.24", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, logger: process.env.SILENT ? { log: () => {} } : console, }; diff --git a/test/sources/projects/tests-folder/hardhat.config.js b/test/sources/projects/tests-folder/hardhat.config.js index c417cf81..cfc8f8ee 100644 --- a/test/sources/projects/tests-folder/hardhat.config.js +++ b/test/sources/projects/tests-folder/hardhat.config.js @@ -3,7 +3,13 @@ require(__dirname + "/../plugins/nomiclabs.plugin"); module.exports = { solidity: { - version: "0.8.17" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } }, logger: process.env.SILENT ? { log: () => {} } : console, }; diff --git a/test/units/statements.js b/test/units/statements.js index ade10822..678b22d0 100644 --- a/test/units/statements.js +++ b/test/units/statements.js @@ -51,8 +51,11 @@ describe('generic statements', () => { util.report(info.solcOutput.errors); }); - // FAILING WITH 0.8.x ... unskip when viaIR work is done - it.skip('should instrument without triggering stack-too-deep', () => { + it('should instrument without triggering stack-too-deep', () => { + // Only compiles if compiler.settings.viaIR = true + // Tests run with optimizer turned on/off to validate both instrumentation techniques + if (!process.env.VIA_IR) return; + const info = util.instrumentAndCompile('statements/stack-too-deep'); util.report(info.solcOutput.errors); }); diff --git a/test/util/integration.js b/test/util/integration.js index be672b73..027802f8 100644 --- a/test/util/integration.js +++ b/test/util/integration.js @@ -114,7 +114,13 @@ function getDefaultHardhatConfig() { const config = getDefaultNomicLabsConfig() config.defaultNetwork = HARDHAT_NETWORK_NAME; config.solidity = { - version: "0.8.17" + version: "0.8.17", + settings: { + optimizer: { + enabled: true + }, + viaIR: process.env.VIA_IR === "true" + } } return config; } diff --git a/test/util/util.js b/test/util/util.js index 5bb5c2d6..6f461e40 100644 --- a/test/util/util.js +++ b/test/util/util.js @@ -61,7 +61,8 @@ function codeToCompilerInput(code) { sources: { 'test.sol': { content: code } }, settings: { outputSelection: {'*': { '*': [ '*' ] }}, - evmVersion: "paris" + evmVersion: "paris", + viaIR: process.env.VIA_IR === "true" } }); } @@ -89,11 +90,14 @@ function getDiffABIs(sourceName, testFile="test.sol", original="Old", current="N // ============================ // Instrumentation Correctness // ============================ -function instrumentAndCompile(sourceName, api={}) { +function instrumentAndCompile(sourceName, api={ config: {} }) { + api.config.viaIR = process.env.VIA_IR === "true"; const contract = getCode(`${sourceName}.sol`) const instrumenter = new Instrumenter(api.config); const instrumented = instrumenter.instrument(contract, filePath); + //console.log('instrumented: --> ' + instrumented.contract); + return { contract: contract, instrumented: instrumented, @@ -117,6 +121,9 @@ function report(output=[]) { async function bootstrapCoverage(file, api, provider){ const info = instrumentAndCompile(file, api); + const {inspect} = require('util'); + //console.log('info --> ' + inspect(info.solcOutput)); + // Need to define a gasLimit for contract calls because otherwise ethers will estimateGas // and cause duplicate hits for everything info.gas = { gasLimit: 2_000_000 } From f8cf8177158d11d1d9cbf41fade26ae599f7009a Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 8 Feb 2024 10:30:46 -0800 Subject: [PATCH 02/14] Fix collector for DUP hashes --- lib/collector.js | 30 +++++++++++++++---- test/integration/standard.js | 2 +- .../ternary-and-logical-or/test/test_or.js | 2 +- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/lib/collector.js b/lib/collector.js index dffdd1bc..32d9f688 100644 --- a/lib/collector.js +++ b/lib/collector.js @@ -27,6 +27,7 @@ class DataCollector { } this.lastHash = null; + this.viaIR = viaIR; } /** @@ -39,9 +40,9 @@ class DataCollector { if (this.validOpcodes[info.opcode.name] && info.stack.length > 0){ const idx = info.stack.length - 1; let hash = '0x' + info.stack[idx].toString(16); + this._registerHash(hash) - // console.log('hash: ' + info.opcode.name + " " + hash) } } catch (err) { /*Ignore*/ }; } @@ -65,15 +66,28 @@ class DataCollector { _registerHash(hash){ hash = this._normalizeHash(hash); - //console.log('normalized: ' + hash); - if(this.instrumentationData[hash]){ - // abi.encode (used to circumvent viaIR) puts the hash on the stack twice if (this.lastHash !== hash) { this.lastHash = hash; this.instrumentationData[hash].hits++ } + return; + } + + // Detect and recover from viaIR mangled hashes by left-padding single `0` + // for some cases + if(this.viaIR && hash.length === 18) { + hash = hash.slice(2); + hash = '0' + hash; + hash = hash.slice(0,16); + hash = '0x' + hash; + if(this.instrumentationData[hash]){ + if (this.lastHash !== hash) { + this.lastHash = hash; + this.instrumentationData[hash].hits++ + } + } } } @@ -83,10 +97,14 @@ class DataCollector { * but prevents left-padding shorter irrelevant hashes * * @param {String} hash data hash from evm stack. - * @return {String} 0x prefixed hash of length 66. + * @return {String} 0x prefixed hash of length 18. */ _normalizeHash(hash){ - if (hash.length < 18 && hash.length > 11){ + // viaIR sometimes zero right-pads the hashes out to 32bytes + // but it doesn't preserve leading zeroes when it does this + if (this.viaIR && hash.length >= 18) { + hash = hash.slice(0,18); + } else if (hash.length < 18 && hash.length > 11){ hash = hash.slice(2); while(hash.length < 16) hash = '0' + hash; hash = '0x' + hash diff --git a/test/integration/standard.js b/test/integration/standard.js index cc86c874..fd4275fa 100644 --- a/test/integration/standard.js +++ b/test/integration/standard.js @@ -364,7 +364,7 @@ describe('Hardhat Plugin: standard use cases', function() { verify.lineCoverage(expected); }) - it.only('logicalOR & ternary conditionals', async function(){ + it('logicalOR & ternary conditionals', async function(){ mock.installFullProject('ternary-and-logical-or'); mock.hardhatSetupEnv(this); diff --git a/test/sources/projects/ternary-and-logical-or/test/test_or.js b/test/sources/projects/ternary-and-logical-or/test/test_or.js index 3cb16cb7..de2491c2 100644 --- a/test/sources/projects/ternary-and-logical-or/test/test_or.js +++ b/test/sources/projects/ternary-and-logical-or/test/test_or.js @@ -5,7 +5,7 @@ contract("contract_or", function(accounts) { before(async () => instance = await Contract_OR.new()) - it.only('_if', async function(){ + it('_if', async function(){ await instance._if(0); await instance._if(7); }); From e66365bc613b3b66335a6685925112da5e7480f1 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 8 Feb 2024 11:42:47 -0800 Subject: [PATCH 03/14] Fix nvm in ci? --- .circleci/config.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9ff9c9c8..b685aaa4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,13 +10,15 @@ step_install_nvm: &step_install_nvm name: "Install nvm for machine" command: | set +e - export NVM_DIR="/opt/circleci/.nvm" + wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.1/install.sh | bash + export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - nvm install v14.19.0 - nvm alias default v14.19.0 - echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV - echo "[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"" >> $BASH_ENV + [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" + nvm install v14 + nvm alias default 14.19.0 + echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV + echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV jobs: unit-test: docker: From b4b4d2f95fa6035061e8a88a213e8fd59b4060d0 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 8 Feb 2024 11:45:15 -0800 Subject: [PATCH 04/14] Try to fix nvm in CI? (2) --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b685aaa4..37b341c8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,7 +15,6 @@ step_install_nvm: &step_install_nvm [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" nvm install v14 - nvm alias default 14.19.0 echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV From 472bc501d6ec99332c8bf8a4f8749d22e23f435a Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 8 Feb 2024 11:46:59 -0800 Subject: [PATCH 05/14] Try to fix nvm in CI? (3) --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 37b341c8..b685aaa4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,6 +15,7 @@ step_install_nvm: &step_install_nvm [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" nvm install v14 + nvm alias default 14.19.0 echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV From 2f59e3a2c4bcbd6c926b956b354f911105586f7e Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 8 Feb 2024 11:48:06 -0800 Subject: [PATCH 06/14] Try to fix nvm in CI? (4) --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b685aaa4..7459bf29 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,7 +15,7 @@ step_install_nvm: &step_install_nvm [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" nvm install v14 - nvm alias default 14.19.0 + nvm alias default 14.21.3 echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV From 69a86d46d9b3d3b405b811d175ec16ab99c7f172 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 8 Feb 2024 11:52:27 -0800 Subject: [PATCH 07/14] Try to fix nvm in CI? (5) --- .circleci/config.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7459bf29..5929562b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,15 +10,12 @@ step_install_nvm: &step_install_nvm name: "Install nvm for machine" command: | set +e - wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.1/install.sh | bash - export NVM_DIR="$HOME/.nvm" + export NVM_DIR="/opt/circleci/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" - nvm install v14 - nvm alias default 14.21.3 - - echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV - echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV + nvm install v14.19.0 + nvm alias default v14.19.0 + echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV + echo "[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"" >> $BASH_ENV jobs: unit-test: docker: @@ -57,10 +54,10 @@ jobs: command: | ./scripts/zeppelin.sh e2e-nomiclabs: - machine: true + docker: + - image: cimg/node:18.19.0 steps: - checkout - - <<: *step_install_nvm - run: name: Hardhat E2E command: | From 1f4594c75424011e9fe19749b9baf0a19979feb1 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 8 Feb 2024 11:53:54 -0800 Subject: [PATCH 08/14] Try to fix nvm in CI? (6) --- scripts/nomiclabs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nomiclabs.sh b/scripts/nomiclabs.sh index 6569854c..d08cc339 100755 --- a/scripts/nomiclabs.sh +++ b/scripts/nomiclabs.sh @@ -22,7 +22,7 @@ function verifyMatrixExists { # Get rid of any caches sudo rm -rf node_modules -echo "NVM CURRENT >>>>>" && nvm current +# echo "NVM CURRENT >>>>>" && nvm current # Use PR env variables (for forks) or fallback on local if PR not available SED_REGEX="s/git@github.com:/https:\/\/github.com\//" From 968d7c69efcad6d9396d25eddf95247c0817188c Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 8 Feb 2024 11:57:17 -0800 Subject: [PATCH 09/14] Try to fix nvm in CI? (7) --- scripts/nomiclabs.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/nomiclabs.sh b/scripts/nomiclabs.sh index d08cc339..be7f8071 100755 --- a/scripts/nomiclabs.sh +++ b/scripts/nomiclabs.sh @@ -69,15 +69,15 @@ cd .. npm install -g yarn git clone https://github.com/cgewecke/template-ethereum-contracts.git cd template-ethereum-contracts -yarn -yarn add $PR_PATH --dev +sudo yarn +sudo yarn add $PR_PATH --dev cat package.json # Here we want to make sure that HH cache triggers a # complete recompile after coverage runs by verifying # that gas consumption is same in both runs. -yarn run gas -yarn run coverage -yarn run gas +sudo yarn run gas +sudo yarn run coverage +sudo yarn run gas verifyCoverageExists From 2f29de17aa599816997738d41f1e466782fb1c87 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 8 Feb 2024 12:05:11 -0800 Subject: [PATCH 10/14] Revert shell script changes --- .circleci/config.yml | 4 ++-- scripts/nomiclabs.sh | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5929562b..153342c2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -54,10 +54,10 @@ jobs: command: | ./scripts/zeppelin.sh e2e-nomiclabs: - docker: - - image: cimg/node:18.19.0 + machine: true steps: - checkout + - <<: *step_install_nvm - run: name: Hardhat E2E command: | diff --git a/scripts/nomiclabs.sh b/scripts/nomiclabs.sh index be7f8071..6569854c 100755 --- a/scripts/nomiclabs.sh +++ b/scripts/nomiclabs.sh @@ -22,7 +22,7 @@ function verifyMatrixExists { # Get rid of any caches sudo rm -rf node_modules -# echo "NVM CURRENT >>>>>" && nvm current +echo "NVM CURRENT >>>>>" && nvm current # Use PR env variables (for forks) or fallback on local if PR not available SED_REGEX="s/git@github.com:/https:\/\/github.com\//" @@ -69,15 +69,15 @@ cd .. npm install -g yarn git clone https://github.com/cgewecke/template-ethereum-contracts.git cd template-ethereum-contracts -sudo yarn -sudo yarn add $PR_PATH --dev +yarn +yarn add $PR_PATH --dev cat package.json # Here we want to make sure that HH cache triggers a # complete recompile after coverage runs by verifying # that gas consumption is same in both runs. -sudo yarn run gas -sudo yarn run coverage -sudo yarn run gas +yarn run gas +yarn run coverage +yarn run gas verifyCoverageExists From 090268dfd2ebdaae2372d434dbcbdcd7b5f19c74 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 8 Feb 2024 12:25:58 -0800 Subject: [PATCH 11/14] Try to fix nvm in CI? (8) --- scripts/zeppelin.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/zeppelin.sh b/scripts/zeppelin.sh index 73353612..016e617c 100755 --- a/scripts/zeppelin.sh +++ b/scripts/zeppelin.sh @@ -8,6 +8,7 @@ set -o errexit # Get rid of any caches sudo rm -rf node_modules echo "NVM CURRENT >>>>>" && nvm current +nvm use 14 # Use PR env variables (for forks) or fallback on local if PR not available SED_REGEX="s/git@github.com:/https:\/\/github.com\//" From 6a4df6eff1bcdd7c285d9e472e6b284ab47ad924 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 8 Feb 2024 12:29:43 -0800 Subject: [PATCH 12/14] Try to fix nvm in CI? (9) --- .circleci/config.yml | 3 +-- scripts/zeppelin.sh | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 153342c2..fdb57df5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,8 +12,7 @@ step_install_nvm: &step_install_nvm set +e export NVM_DIR="/opt/circleci/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - nvm install v14.19.0 - nvm alias default v14.19.0 + nvm install v18 echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV echo "[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"" >> $BASH_ENV jobs: diff --git a/scripts/zeppelin.sh b/scripts/zeppelin.sh index 016e617c..96418b71 100755 --- a/scripts/zeppelin.sh +++ b/scripts/zeppelin.sh @@ -8,7 +8,7 @@ set -o errexit # Get rid of any caches sudo rm -rf node_modules echo "NVM CURRENT >>>>>" && nvm current -nvm use 14 +nvm use 18 # Use PR env variables (for forks) or fallback on local if PR not available SED_REGEX="s/git@github.com:/https:\/\/github.com\//" From 4dbfbc7a6457f7b194b38073a0b20bedab69be33 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 8 Feb 2024 16:08:04 -0800 Subject: [PATCH 13/14] Detect viaIR in solidityConfig overrides --- plugins/resources/nomiclabs.utils.js | 14 ++++++--- test/integration/standard.js | 25 ++++++++++++++++ .../projects/overrides-viaIR/.solcover.js | 4 +++ .../contracts/ContractOverA2.sol | 17 +++++++++++ .../contracts/ContractOverB2.sol | 17 +++++++++++ .../contracts/ContractOverC2.sol | 17 +++++++++++ .../overrides-viaIR/hardhat.config.js | 29 +++++++++++++++++++ .../overrides-viaIR/test/contract_over_a2.js | 11 +++++++ .../overrides-viaIR/test/contract_over_b2.js | 15 ++++++++++ .../overrides-viaIR/test/contract_over_c2.js | 20 +++++++++++++ 10 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 test/sources/projects/overrides-viaIR/.solcover.js create mode 100644 test/sources/projects/overrides-viaIR/contracts/ContractOverA2.sol create mode 100644 test/sources/projects/overrides-viaIR/contracts/ContractOverB2.sol create mode 100644 test/sources/projects/overrides-viaIR/contracts/ContractOverC2.sol create mode 100644 test/sources/projects/overrides-viaIR/hardhat.config.js create mode 100644 test/sources/projects/overrides-viaIR/test/contract_over_a2.js create mode 100644 test/sources/projects/overrides-viaIR/test/contract_over_b2.js create mode 100644 test/sources/projects/overrides-viaIR/test/contract_over_c2.js diff --git a/plugins/resources/nomiclabs.utils.js b/plugins/resources/nomiclabs.utils.js index 10092598..ad25e9e7 100644 --- a/plugins/resources/nomiclabs.utils.js +++ b/plugins/resources/nomiclabs.utils.js @@ -63,14 +63,20 @@ function normalizeConfig(config, args={}){ } function isUsingViaIR(solidity) { - let viaIR = false; + for (compiler of solidity.compilers) { if (compiler.settings && compiler.settings.viaIR) { - viaIR = true; + return true; + } + } + if (solidity.overrides) { + for (key of Object.keys(solidity.overrides)){ + if (solidity.overrides[key].settings && solidity.overrides[key].settings.viaIR) { + return true; + } } } - // Also handle overrides... - return viaIR; + return false; } async function setupHardhatNetwork(env, api, ui){ diff --git a/test/integration/standard.js b/test/integration/standard.js index fd4275fa..d53ff483 100644 --- a/test/integration/standard.js +++ b/test/integration/standard.js @@ -348,6 +348,31 @@ describe('Hardhat Plugin: standard use cases', function() { verify.lineCoverage(expected); }) + it('detects viaIR when specified in config overrides only', async function(){ + mock.installFullProject('overrides-viaIR'); + mock.hardhatSetupEnv(this); + + await this.env.run("coverage"); + + const expected = [ + { + file: mock.pathToContract(hardhatConfig, 'ContractOverA2.sol'), + pct: 33.33 + }, + { + file: mock.pathToContract(hardhatConfig, 'ContractOverB2.sol'), + pct: 100, + }, + { + file: mock.pathToContract(hardhatConfig, 'ContractOverC2.sol'), + pct: 100, + }, + + ]; + + verify.lineCoverage(expected); + }) + it('locates .coverage_contracts correctly when dir is subfolder', async function(){ mock.installFullProject('contract-subfolders'); mock.hardhatSetupEnv(this); diff --git a/test/sources/projects/overrides-viaIR/.solcover.js b/test/sources/projects/overrides-viaIR/.solcover.js new file mode 100644 index 00000000..71b990cc --- /dev/null +++ b/test/sources/projects/overrides-viaIR/.solcover.js @@ -0,0 +1,4 @@ +module.exports = { + "silent": false, + "istanbulReporter": [ "json-summary", "text"] +} diff --git a/test/sources/projects/overrides-viaIR/contracts/ContractOverA2.sol b/test/sources/projects/overrides-viaIR/contracts/ContractOverA2.sol new file mode 100644 index 00000000..ad19d2c1 --- /dev/null +++ b/test/sources/projects/overrides-viaIR/contracts/ContractOverA2.sol @@ -0,0 +1,17 @@ +pragma solidity >=0.8.0 <0.9.0; + + +contract ContractA { + uint x; + constructor() public { + } + + function sendFn() public { + x = 5; + } + + function callFn() public pure returns (uint){ + uint y = 5; + return y; + } +} diff --git a/test/sources/projects/overrides-viaIR/contracts/ContractOverB2.sol b/test/sources/projects/overrides-viaIR/contracts/ContractOverB2.sol new file mode 100644 index 00000000..6d5f53d4 --- /dev/null +++ b/test/sources/projects/overrides-viaIR/contracts/ContractOverB2.sol @@ -0,0 +1,17 @@ +pragma solidity >=0.8.0 <0.9.0; + + +contract ContractB { + uint x; + constructor() public { + } + + function sendFn() public { + x = 5; + } + + function callFn() public pure returns (uint){ + uint y = 5; + return y; + } +} diff --git a/test/sources/projects/overrides-viaIR/contracts/ContractOverC2.sol b/test/sources/projects/overrides-viaIR/contracts/ContractOverC2.sol new file mode 100644 index 00000000..8c24195f --- /dev/null +++ b/test/sources/projects/overrides-viaIR/contracts/ContractOverC2.sol @@ -0,0 +1,17 @@ +pragma solidity >=0.8.0 <0.9.0; + + +contract ContractC { + uint x; + constructor() public { + } + + function sendFn() public { + x = 5; + } + + function callFn() public pure returns (uint){ + uint y = 5; + return y; + } +} diff --git a/test/sources/projects/overrides-viaIR/hardhat.config.js b/test/sources/projects/overrides-viaIR/hardhat.config.js new file mode 100644 index 00000000..b9c83a15 --- /dev/null +++ b/test/sources/projects/overrides-viaIR/hardhat.config.js @@ -0,0 +1,29 @@ +require("@nomiclabs/hardhat-truffle5"); +require(__dirname + "/../plugins/nomiclabs.plugin"); + +module.exports={ + solidity: { + compilers: [ + { + version: "0.8.17", + }, + { + version: "0.8.19" + }, + ], + overrides: { + "contracts/ContractA.sol": { + version: "0.8.24", + settings: { + optimizer: { + enabled: true, + runs: 200, + viaIR: process.env.VIA_IR === "true", + evmVersion: 'paris' + } + } + } + } + }, + logger: process.env.SILENT ? { log: () => {} } : console, +}; diff --git a/test/sources/projects/overrides-viaIR/test/contract_over_a2.js b/test/sources/projects/overrides-viaIR/test/contract_over_a2.js new file mode 100644 index 00000000..4182d2b5 --- /dev/null +++ b/test/sources/projects/overrides-viaIR/test/contract_over_a2.js @@ -0,0 +1,11 @@ +const ContractA = artifacts.require("ContractA"); + +contract("contracta", function(accounts) { + let instance; + + before(async () => instance = await ContractA.new()) + + it('sends', async function(){ + await instance.sendFn(); + }); +}); diff --git a/test/sources/projects/overrides-viaIR/test/contract_over_b2.js b/test/sources/projects/overrides-viaIR/test/contract_over_b2.js new file mode 100644 index 00000000..42d8cb84 --- /dev/null +++ b/test/sources/projects/overrides-viaIR/test/contract_over_b2.js @@ -0,0 +1,15 @@ +const ContractB = artifacts.require("ContractB"); + +contract("contractB", function(accounts) { + let instance; + + before(async () => instance = await ContractB.new()) + + it('sends', async function(){ + await instance.sendFn(); + }); + + it('calls', async function(){ + await instance.callFn(); + }) +}); diff --git a/test/sources/projects/overrides-viaIR/test/contract_over_c2.js b/test/sources/projects/overrides-viaIR/test/contract_over_c2.js new file mode 100644 index 00000000..9b3d950d --- /dev/null +++ b/test/sources/projects/overrides-viaIR/test/contract_over_c2.js @@ -0,0 +1,20 @@ +const ContractC = artifacts.require("ContractC"); + +contract("contractc", function(accounts) { + let instance; + + before(async () => instance = await ContractC.new()) + + it('sends', async function(){ + await instance.sendFn(); + }); + + it('calls', async function(){ + await instance.callFn(); + }) + + it('sends', async function(){ + await instance.sendFn(); + }); + +}); From 62408ea9176fe1490ca7f4fdef3d6d1f3ec7db64 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 8 Feb 2024 16:24:03 -0800 Subject: [PATCH 14/14] Clean up logs / comments --- lib/api.js | 2 -- lib/collector.js | 7 ++----- lib/injector.js | 7 +++---- package.json | 2 +- plugins/hardhat.plugin.js | 8 ++++---- plugins/resources/nomiclabs.utils.js | 3 --- test/util/util.js | 5 ----- 7 files changed, 10 insertions(+), 24 deletions(-) diff --git a/lib/api.js b/lib/api.js index 7a10c70d..8632dcc2 100644 --- a/lib/api.js +++ b/lib/api.js @@ -93,8 +93,6 @@ class API { target.canonicalPath ); - //console.log('instrumented.contract --> ' + instrumented.contract); - this.coverage.addContract(instrumented, target.canonicalPath); outputs.push({ diff --git a/lib/collector.js b/lib/collector.js index 32d9f688..062de100 100644 --- a/lib/collector.js +++ b/lib/collector.js @@ -40,9 +40,7 @@ class DataCollector { if (this.validOpcodes[info.opcode.name] && info.stack.length > 0){ const idx = info.stack.length - 1; let hash = '0x' + info.stack[idx].toString(16); - this._registerHash(hash) - } } catch (err) { /*Ignore*/ }; } @@ -67,7 +65,7 @@ class DataCollector { hash = this._normalizeHash(hash); if(this.instrumentationData[hash]){ - // abi.encode (used to circumvent viaIR) puts the hash on the stack twice + // abi.encode (used to circumvent viaIR) sometimes puts the hash on the stack twice if (this.lastHash !== hash) { this.lastHash = hash; this.instrumentationData[hash].hits++ @@ -76,7 +74,6 @@ class DataCollector { } // Detect and recover from viaIR mangled hashes by left-padding single `0` - // for some cases if(this.viaIR && hash.length === 18) { hash = hash.slice(2); hash = '0' + hash; @@ -100,7 +97,7 @@ class DataCollector { * @return {String} 0x prefixed hash of length 18. */ _normalizeHash(hash){ - // viaIR sometimes zero right-pads the hashes out to 32bytes + // viaIR sometimes right-pads the hashes out to 32 bytes // but it doesn't preserve leading zeroes when it does this if (this.viaIR && hash.length >= 18) { hash = hash.slice(0,18); diff --git a/lib/injector.js b/lib/injector.js index 3072167e..693a28fd 100644 --- a/lib/injector.js +++ b/lib/injector.js @@ -54,10 +54,9 @@ class Injector { return `c_mod${web3Utils.keccak256(id).slice(2,10)}` } - // Way to get hash on the stack with viaIR (which seems to ignore abi.encode (v0.8.24)) - // If you visiting solidity-coverage & understand what's happening here, please do not mention - // this obscure and irrelevant optimization opportunity in the issues at ethereum/solidity - _getAbiEncodeStatementHash(hash){ + // Way to get hash on the stack with viaIR (which seems to ignore abi.encode builtin) + // Tested with v0.8.17, v0.8.24 + _getAbiEncodeStatementHash(hash){ return `abi.encode(${hash}); ` } diff --git a/package.json b/package.json index de1bd343..e93460a2 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,4 @@ - { +{ "name": "solidity-coverage", "version": "0.8.6", "description": "Code coverage for Solidity testing", diff --git a/plugins/hardhat.plugin.js b/plugins/hardhat.plugin.js index 2ad88755..ab8805f5 100644 --- a/plugins/hardhat.plugin.js +++ b/plugins/hardhat.plugin.js @@ -52,16 +52,16 @@ subtask(TASK_COMPILE_SOLIDITY_GET_COMPILATION_JOB_FOR_FILE).setAction(async (_, if (settings.optimizer === undefined) { settings.optimizer = {}; } - // Unset useLiteralContent due to solc metadata size restriction settings.metadata.useLiteralContent = false; // Beginning with v0.8.7, we let the optimizer run if viaIR is true and - // instrument using `verbatim` YUL keyword. Otherwise turn the optimizer off. + // instrument using `abi.encode(bytes8 covHash)`. Otherwise turn the optimizer off. if (!settings.viaIR) settings.optimizer.enabled = false; - // This is fixes a stack too deep bug in ABIEncoderV2 - // Experimental because not sure this works as expected across versions.... + // This sometimes fixed a stack-too-deep bug in ABIEncoderV2 for coverage plugin versions up to 0.8.6 + // Although issue should be fixed in 0.8.7, am leaving this option in because it may still be necessary + // to configure optimizer details in some cases. if (configureYulOptimizer) { if (optimizerDetails === undefined) { settings.optimizer.details = { diff --git a/plugins/resources/nomiclabs.utils.js b/plugins/resources/nomiclabs.utils.js index ad25e9e7..110199bb 100644 --- a/plugins/resources/nomiclabs.utils.js +++ b/plugins/resources/nomiclabs.utils.js @@ -36,9 +36,6 @@ function normalizeConfig(config, args={}){ ? sources = path.join(config.paths.sources, args.sources) : sources = config.paths.sources; - //const { inspect } = require('util'); - //console.log('config --> ' + inspect(config.solidity.compilers)); - if (config.solidity && config.solidity.compilers.length) { config.viaIR = isUsingViaIR(config.solidity); } diff --git a/test/util/util.js b/test/util/util.js index 6f461e40..05f44e16 100644 --- a/test/util/util.js +++ b/test/util/util.js @@ -96,8 +96,6 @@ function instrumentAndCompile(sourceName, api={ config: {} }) { const instrumenter = new Instrumenter(api.config); const instrumented = instrumenter.instrument(contract, filePath); - //console.log('instrumented: --> ' + instrumented.contract); - return { contract: contract, instrumented: instrumented, @@ -121,9 +119,6 @@ function report(output=[]) { async function bootstrapCoverage(file, api, provider){ const info = instrumentAndCompile(file, api); - const {inspect} = require('util'); - //console.log('info --> ' + inspect(info.solcOutput)); - // Need to define a gasLimit for contract calls because otherwise ethers will estimateGas // and cause duplicate hits for everything info.gas = { gasLimit: 2_000_000 }