Skip to content

Commit

Permalink
fix: call fallback verification with full source codes when BE throws… (
Browse files Browse the repository at this point in the history
#968)

* fix: call fallback verification with full soruce codes when BE throws a compilation error

* fix: update regex to support new lines

* fix: update just source codes not whole input

---------

Co-authored-by: Marko Arambasic <[email protected]>
  • Loading branch information
kiriyaga-txfusion and kiriyaga authored Apr 3, 2024
1 parent b32243a commit bfe86f2
Show file tree
Hide file tree
Showing 3 changed files with 323 additions and 19 deletions.
15 changes: 15 additions & 0 deletions packages/hardhat-zksync-verify/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,18 @@ This means that unrelated contracts may be displayed on the zksync block explore

export const COMPILERS_CONFLICT_ZKVM_SOLC = (version: string) =>
`Solidity compiler versions in your Hardhat config file are in conflict for version ${version}. Please specify version of compiler only with zkVm support(eraVersion) or without it`;

export const COMPILATION_ERRORS = [
{
error: 'CompilationError',
pattern: /^Backend verification error: Compilation error.*$/s,
},
{
error: 'MissingSource',
pattern: /^Backend verification error: There is no .* source file$/,
},
{
error: 'MissingContract',
pattern: /^Backend verification error: Contract with .* name is missing in sources$/,
},
];
8 changes: 6 additions & 2 deletions packages/hardhat-zksync-verify/src/task-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
ENCODED_ARAGUMENTS_NOT_FOUND_ERROR,
CONSTRUCTOR_MODULE_IMPORTING_ERROR,
BUILD_INFO_NOT_FOUND_ERROR,
COMPILATION_ERRORS,
} from './constants';

import { encodeArguments, extractModule, normalizeCompilerVersions, retrieveContractBytecode } from './utils';
Expand Down Expand Up @@ -231,12 +232,15 @@ export async function verifyContract(
} catch (error: any) {
// The first verirication attempt with 'minimal' source code was unnsuccessful.
// Now try with the full source code from the compilation context.
if (error.message !== NO_MATCHING_CONTRACT) {
if (
error.message !== NO_MATCHING_CONTRACT &&
COMPILATION_ERRORS.filter((compilationError) => compilationError.pattern.test(error.message)).length === 0
) {
throw error;
}
console.info(chalk.red(UNSUCCESSFUL_CONTEXT_COMPILATION_MESSAGE));

request.sourceCode = contractInformation.compilerInput;
request.sourceCode.sources = contractInformation.compilerInput.sources;

const fallbackResponse = await verifyContractRequest(request, hre.network.verifyURL);
const fallbackVerificationId = parseInt(fallbackResponse.message, 10);
Expand Down
319 changes: 302 additions & 17 deletions packages/hardhat-zksync-verify/test/tests/task-actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,34 @@ import * as metadata from '../../src/solc/metadata';
import * as service from '../../src/zksync-block-explorer/service';
import * as plugin from '../../src/plugin';
import * as bytecode from '../../src/solc/bytecode';
import { ZkSyncVerifyPluginError } from '../../src/errors';

describe('verifyContract', async function () {
beforeEach(() => {
sinon.stub(utils, 'retrieveContractBytecode').resolves('0x1234567890');
sinon.stub(metadata, 'inferSolcVersion').resolves('0.8.0');
sinon.stub(service, 'getSupportedCompilerVersions').resolves(['0.8.0']);
sinon.stub(plugin, 'getSolidityStandardJsonInput').resolves({
language: 'Solidity',
sources: {
'contracts/Contract.sol': {
content: 'contract Contract {}',
},
},
});
});

const args = {
address: '0x1234567890123456789012345678901234567890',
constructorArguments: [],
contract: 'contract',
libraries: 'libraries',
noCompile: false,
};

afterEach(() => {
sinon.restore();
});
it('should call runSuper if zksync is false', async function () {
const runSuperStub = sinon.stub().resolves(0);
const hre = {
Expand Down Expand Up @@ -86,17 +112,6 @@ describe('verifyContract', async function () {
});

it('should call run with the correct arguments if zksync is true and address is valid', async function () {
sinon.stub(utils, 'retrieveContractBytecode').resolves('0x1234567890');
sinon.stub(metadata, 'inferSolcVersion').resolves('0.8.0');
sinon.stub(service, 'getSupportedCompilerVersions').resolves(['0.8.0']);
sinon.stub(plugin, 'getSolidityStandardJsonInput').resolves({
language: 'Solidity',
sources: {
'contracts/Contract.sol': {
content: 'contract Contract {}',
},
},
});
sinon.stub(service, 'verifyContractRequest').resolves({
status: 200,
message: '1',
Expand Down Expand Up @@ -167,12 +182,281 @@ describe('verifyContract', async function () {
}),
};

const args = {
address: '0x1234567890123456789012345678901234567890',
constructorArguments: [],
contract: 'contract',
libraries: 'libraries',
noCompile: false,
await verifyContract(args, hre as any, runSuperStub as any);
expect(runSuperStub.calledOnce).to.equal(false);
expect(hre.run.firstCall.args[0]).to.equal(TASK_VERIFY_GET_COMPILER_VERSIONS);
expect(hre.run.secondCall.args[0]).to.equal(TASK_COMPILE);
expect(hre.run.thirdCall.args[0]).to.equal(TASK_VERIFY_GET_CONTRACT_INFORMATION);
expect(hre.run.getCall(3).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH);
expect(hre.run.getCall(4).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS);
});

it('should call fallback request sent if there is compilation error', async function () {
const errorMessage =
'Backend verification error: Compilation errorDeclarationError: Undeclared identifier.--> contracts-preprocessed/libraries/EfficientCall.sol:134:32:|134 | address callAddr = RAW_FAR_CALL_BY_REF_CALL_ADDRESS;|';

sinon
.stub(service, 'verifyContractRequest')
.onFirstCall()
.resolves({ status: 503, message: '1', isOk: () => false })
.onSecondCall()
.resolves({
status: 200,
message: '2',
isOk: () => true,
});

const runSuperStub = sinon.stub().resolves(0);
const hre = {
network: {
config: {
url: 'http://localhost:3000',
},
zksync: true,
verifyURL: 'http://localhost:3000/verify',
},
run: sinon
.stub()
.onThirdCall()
.resolves({
contractName: 'Contract',
sourceName: 'contracts/Contract.sol',
compilerInput: {
language: 'Solidity',
sources: {
'contracts/Contract.sol': {
content: 'contract Contract {}',
},
},
settings: {
optimizer: {
enabled: true,
},
outputSelection: {
'*': {
'*': ['evm'],
},
},
},
},
contractOutput: {
abi: [],
metadata: {
zk_version: '0.1.0',
solc_metadata: '0x1234567890',
optimizer_settings: '0x1234567890',
},
evm: {
bytecode: {
linkReferences: {},
object: '0x1234567890',
opcodes: '0x1234567890',
sourceMap: '0x1234567890',
},
deployedBytecode: {
linkReferences: {},
object: '0x1234567890',
opcodes: '0x1234567890',
sourceMap: '0x1234567890',
},
methodIdentifiers: {},
},
},
solcVersion: '0.8.0',
})
.onCall(3)
.resolves({
getResolvedFiles: sinon.stub().resolves(),
})
.onCall(4)
.throwsException(new ZkSyncVerifyPluginError(errorMessage))
.onCall(5)
.resolves(true),
};

await verifyContract(args, hre as any, runSuperStub as any);
expect(runSuperStub.calledOnce).to.equal(false);
expect(hre.run.firstCall.args[0]).to.equal(TASK_VERIFY_GET_COMPILER_VERSIONS);
expect(hre.run.secondCall.args[0]).to.equal(TASK_COMPILE);
expect(hre.run.thirdCall.args[0]).to.equal(TASK_VERIFY_GET_CONTRACT_INFORMATION);
expect(hre.run.getCall(3).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH);
expect(hre.run.getCall(4).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS);
expect(hre.run.getCall(5).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS);
});

it('should call fallback request sent if there is missing source file', async function () {
const errorMessage = 'Backend verification error: There is no contracts/Contract.sol source file';

sinon
.stub(service, 'verifyContractRequest')
.onFirstCall()
.resolves({ status: 503, message: '1', isOk: () => false })
.onSecondCall()
.resolves({
status: 200,
message: '2',
isOk: () => true,
});

const runSuperStub = sinon.stub().resolves(0);
const hre = {
network: {
config: {
url: 'http://localhost:3000',
},
zksync: true,
verifyURL: 'http://localhost:3000/verify',
},
run: sinon
.stub()
.onThirdCall()
.resolves({
contractName: 'Contract',
sourceName: 'contracts/Contract.sol',
compilerInput: {
language: 'Solidity',
sources: {
'contracts/Contract.sol': {
content: 'contract Contract {}',
},
},
settings: {
optimizer: {
enabled: true,
},
outputSelection: {
'*': {
'*': ['evm'],
},
},
},
},
contractOutput: {
abi: [],
metadata: {
zk_version: '0.1.0',
solc_metadata: '0x1234567890',
optimizer_settings: '0x1234567890',
},
evm: {
bytecode: {
linkReferences: {},
object: '0x1234567890',
opcodes: '0x1234567890',
sourceMap: '0x1234567890',
},
deployedBytecode: {
linkReferences: {},
object: '0x1234567890',
opcodes: '0x1234567890',
sourceMap: '0x1234567890',
},
methodIdentifiers: {},
},
},
solcVersion: '0.8.0',
})
.onCall(3)
.resolves({
getResolvedFiles: sinon.stub().resolves(),
})
.onCall(4)
.throwsException(new ZkSyncVerifyPluginError(errorMessage))
.onCall(5)
.resolves(true),
};

await verifyContract(args, hre as any, runSuperStub as any);
expect(runSuperStub.calledOnce).to.equal(false);
expect(hre.run.firstCall.args[0]).to.equal(TASK_VERIFY_GET_COMPILER_VERSIONS);
expect(hre.run.secondCall.args[0]).to.equal(TASK_COMPILE);
expect(hre.run.thirdCall.args[0]).to.equal(TASK_VERIFY_GET_CONTRACT_INFORMATION);
expect(hre.run.getCall(3).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH);
expect(hre.run.getCall(4).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS);
expect(hre.run.getCall(5).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS);
});

it('should call fallback request sent if there is missing contract file', async function () {
const errorMessage =
'Backend verification error: Contract with contracts/Contract.sol name is missing in sources';

sinon
.stub(service, 'verifyContractRequest')
.onFirstCall()
.resolves({ status: 503, message: '1', isOk: () => false })
.onSecondCall()
.resolves({
status: 200,
message: '2',
isOk: () => true,
});

const runSuperStub = sinon.stub().resolves(0);
const hre = {
network: {
config: {
url: 'http://localhost:3000',
},
zksync: true,
verifyURL: 'http://localhost:3000/verify',
},
run: sinon
.stub()
.onThirdCall()
.resolves({
contractName: 'Contract',
sourceName: 'contracts/Contract.sol',
compilerInput: {
language: 'Solidity',
sources: {
'contracts/Contract.sol': {
content: 'contract Contract {}',
},
},
settings: {
optimizer: {
enabled: true,
},
outputSelection: {
'*': {
'*': ['evm'],
},
},
},
},
contractOutput: {
abi: [],
metadata: {
zk_version: '0.1.0',
solc_metadata: '0x1234567890',
optimizer_settings: '0x1234567890',
},
evm: {
bytecode: {
linkReferences: {},
object: '0x1234567890',
opcodes: '0x1234567890',
sourceMap: '0x1234567890',
},
deployedBytecode: {
linkReferences: {},
object: '0x1234567890',
opcodes: '0x1234567890',
sourceMap: '0x1234567890',
},
methodIdentifiers: {},
},
},
solcVersion: '0.8.0',
})
.onCall(3)
.resolves({
getResolvedFiles: sinon.stub().resolves(),
})
.onCall(4)
.throwsException(new ZkSyncVerifyPluginError(errorMessage))
.onCall(5)
.resolves(true),
};

await verifyContract(args, hre as any, runSuperStub as any);
Expand All @@ -182,6 +466,7 @@ describe('verifyContract', async function () {
expect(hre.run.thirdCall.args[0]).to.equal(TASK_VERIFY_GET_CONTRACT_INFORMATION);
expect(hre.run.getCall(3).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH);
expect(hre.run.getCall(4).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS);
expect(hre.run.getCall(5).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS);
});
});
describe('getCompilerVersions', async function () {
Expand Down

0 comments on commit bfe86f2

Please sign in to comment.