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

Verification flow refactoring #1845

Draft
wants to merge 9 commits into
base: staging
Choose a base branch
from
2 changes: 1 addition & 1 deletion packages/lib-sourcify/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"check": "run-s check:*",
"check:eslint": "eslint . --ext .ts",
"check:prettier": "prettier \"./**/*.ts\" --check",
"test": "c8 --reporter=none mocha -r ts-node/register test/**/*.spec.ts --no-timeout --exit",
"test": "c8 --reporter=none mocha -r ts-node/register test/**/*.spec.ts test/*.spec.ts --no-timeout --exit",
"check-cli": "run-s test diff-integration-tests check-integration-tests",
"check-integration-tests": "run-s check-integration-test:*",
"diff-integration-tests": "mkdir -p diff && rm -rf diff/test && cp -r test diff/test && rm -rf diff/test/test-*/.git && cd diff && git init --quiet && git add -A && git commit --quiet --no-verify --allow-empty -m 'WIP' && echo '\\n\\nCommitted most recent integration test output in the \"diff\" directory. Review the changes with \"cd diff && git diff HEAD\" or your preferred git diff viewer.'",
Expand Down
148 changes: 148 additions & 0 deletions packages/lib-sourcify/src/Compilation/AbstractCompilation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { AuxdataStyle } from '@ethereum-sourcify/bytecode-utils';
import {
CompilationTarget,
CompiledContractCborAuxdata,
Metadata,
LinkReferences,
} from './CompilationTypes';
import {
ImmutableReferences,
ISolidityCompiler,
SolidityJsonInput,
SolidityOutput,
SolidityOutputContract,
} from './SolidityTypes';
import {
IVyperCompiler,
VyperJsonInput,
VyperOutput,
VyperOutputContract,
} from './VyperTypes';
import { logInfo, logSilly, logWarn } from '../logger';

export abstract class AbstractCompilation {
/**
* Constructor parameters
*/
abstract compiler: ISolidityCompiler | IVyperCompiler;
abstract compilerVersion: string;
abstract compilationTarget: CompilationTarget;
abstract jsonInput: SolidityJsonInput | VyperJsonInput;

protected _metadata?: Metadata;
compilerOutput?: SolidityOutput | VyperOutput;

abstract auxdataStyle: AuxdataStyle;

/** Marks the positions of the CborAuxdata parts in the bytecode */
protected _creationBytecodeCborAuxdata?: CompiledContractCborAuxdata;
protected _runtimeBytecodeCborAuxdata?: CompiledContractCborAuxdata;

/**
* Recompiles the contract with the specified compiler settings
* @param forceEmscripten Whether to force using the WebAssembly binary for compilation (only for Solidity)
*/
abstract compile(forceEmscripten?: boolean): Promise<void>;
abstract generateCborAuxdataPositions(
forceEmscripten?: boolean,
): Promise<boolean>;

public async compileAndReturnCompilationTarget(
forceEmscripten = false,
): Promise<SolidityOutputContract | VyperOutputContract> {
const version = this.compilerVersion;

const compilationStartTime = Date.now();
logInfo('Compiling contract', {
version,
contract: this.compilationTarget.name,
path: this.compilationTarget.path,
forceEmscripten,
});
logSilly('Compilation input', { solcJsonInput: this.jsonInput });
this.compilerOutput = await this.compiler.compile(
version,
this.jsonInput as any,
forceEmscripten,
);
if (this.compilerOutput === undefined) {
const error = new Error('Compiler error');
logWarn('Compiler error', {
errorMessages: ['compilerOutput is undefined'],
});
throw error;
}

// We call getCompilationTarget() before logging because it can throw an error
const compilationTarget = this.compilationTargetContract;

const compilationEndTime = Date.now();
const compilationDuration = compilationEndTime - compilationStartTime;
logSilly('Compilation output', { compilerOutput: this.compilerOutput });
logInfo('Compiled contract', {
version,
contract: this.compilationTarget.name,
path: this.compilationTarget.path,
forceEmscripten,
compilationDuration: `${compilationDuration}ms`,
});

return compilationTarget;
}

get compilationTargetContract():
| SolidityOutputContract
| VyperOutputContract {
if (!this.compilerOutput) {
logWarn('Compiler output is undefined');
throw new Error('Compiler output is undefined');
}
if (
!this.compilerOutput.contracts ||
!this.compilerOutput.contracts[this.compilationTarget.path] ||
!this.compilerOutput.contracts[this.compilationTarget.path][
this.compilationTarget.name
]
) {
const error = new Error('Contract not found in compiler output');
logWarn('Contract not found in compiler output');
throw error;
}
return this.compilerOutput.contracts[this.compilationTarget.path][
this.compilationTarget.name
];
}

get creationBytecode() {
return `0x${this.compilationTargetContract.evm.bytecode.object}`;
}

get runtimeBytecode() {
return `0x${this.compilationTargetContract.evm.deployedBytecode.object}`;
}

get metadata() {
if (!this._metadata) {
throw new Error('Metadata is not set');
}
return this._metadata;
}

abstract get immutableReferences(): ImmutableReferences;
abstract get runtimeLinkReferences(): LinkReferences;
abstract get creationLinkReferences(): LinkReferences;

get creationBytecodeCborAuxdata(): CompiledContractCborAuxdata {
if (!this._creationBytecodeCborAuxdata) {
throw new Error('Creation bytecode cbor auxdata is not set');
}
return this._creationBytecodeCborAuxdata;
}

get runtimeBytecodeCborAuxdata(): CompiledContractCborAuxdata {
if (!this._runtimeBytecodeCborAuxdata) {
throw new Error('Runtime bytecode cbor auxdata is not set');
}
return this._runtimeBytecodeCborAuxdata;
}
}
125 changes: 125 additions & 0 deletions packages/lib-sourcify/src/Compilation/CompilationTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { Abi } from 'abitype';
import { SoliditySettings } from './SolidityTypes';

export interface LinkReferences {
[filePath: string]: {
[libraryName: string]: [
{
length: number;
start: number;
},
];
};
}

export interface MetadataSource {
keccak256: string;
content?: string;
urls?: string[];
license?: string;
}

export interface MetadataSourceMap {
[index: string]: MetadataSource;
}

export interface Devdoc {
author?: string;
details?: string;
errors?: {
[index: string]: {
details?: string;
};
};
events?: {
[index: string]: {
details?: string;
params?: any;
};
};
kind: 'dev';
methods: {
[index: string]: {
details?: string;
params?: any;
returns?: any;
};
};
stateVariables?: any;
title?: string;
version?: number;
}

export interface Userdoc {
errors?: {
[index: string]: {
notice?: string;
}[];
};
events?: {
[index: string]: {
notice?: string;
};
};
kind: 'user';
methods: {
[index: string]: {
notice: string;
};
};
version?: number;
}

export interface MetadataOutput {
abi: Abi;
devdoc: Devdoc;
userdoc: Userdoc;
}

// Metadata JSON's "settings" does have extra "compilationTarget" and its "libraries" field is in a different format
// ( libraries["MyContract.sol:Mycontract"]:"0xab..cd" vs libraries["MyContract.sol"]["MyContract"]:"0xab..cd")
export interface MetadataCompilerSettings
extends Omit<SoliditySettings, 'libraries' | 'outputSelection'> {
compilationTarget: {
[sourceName: string]: string;
};
libraries?: {
[index: string]: string;
};
}

// Metadata type that reflects the metadata object from
// https://docs.soliditylang.org/en/latest/metadata.html
export interface Metadata {
compiler: {
keccak256?: string;
version: string;
};
language: string;
output: MetadataOutput;
settings: MetadataCompilerSettings;
sources: MetadataSourceMap;
version: number;
}

export interface CompiledContractCborAuxdata {
[index: string]: {
offset: number;
value: string;
};
}

export interface StringMap {
[key: string]: string;
}

export interface AuxdataDiff {
real: string;
diffStart: number;
diff: string;
}

export interface CompilationTarget {
name: string;
path: string;
}
Loading