@ethereum-sourcify/compilers
Version:
Wrapper around multiple compilers to download the right version and invoke the compilation with a common interface.
228 lines • 18.2 kB
JavaScript
// TODO: Handle nodejs only dependencies
import path from 'path';
import fs from 'fs';
import { spawnSync } from 'child_process';
import semver from 'semver';
import { Worker } from 'worker_threads';
import { logDebug, logError, logInfo, logWarn } from '../logger';
import { asyncExec, CompilerError, fetchWithBackoff } from './common';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const solc = require('solc');
const HOST_SOLC_REPO = 'https://binaries.soliditylang.org/';
export function findSolcPlatform() {
if (process.platform === 'darwin' && process.arch === 'x64') {
return 'macosx-amd64';
}
if (process.platform === 'linux' && process.arch === 'x64') {
return 'linux-amd64';
}
if (process.platform === 'win32' && process.arch === 'x64') {
return 'windows-amd64';
}
return false;
}
/**
* Searches for a solc: first for a local executable version, then from HOST_SOLC_REPO
* and then using the getSolcJs function.
* Once the compiler is retrieved, it is used, and the stringified solc output is returned.
*
* @param version the version of solc to be used for compilation
* @param input a JSON object of the standard-json format compatible with solc
* @param log the logger
* @returns stringified solc output
*/
export async function useSolidityCompiler(solcRepoPath, solJsonRepoPath, version, solcJsonInput, forceEmscripten = false) {
// For nightly builds, Solidity version is saved as 0.8.17-ci.2022.8.9+commit.6b60524c instead of 0.8.17-nightly.2022.8.9+commit.6b60524c.
// Not possible to retrieve compilers with "-ci.".
if (version.includes('-ci.'))
version = version.replace('-ci.', '-nightly.');
const inputStringified = JSON.stringify(solcJsonInput);
let compiled;
const solcPlatform = findSolcPlatform();
let solcPath;
if (solcPlatform && !forceEmscripten) {
// Catch, if this fails we'll fall back to solc-js e.g. very early solc 0.1.4
try {
solcPath = await getSolcExecutable(solcRepoPath, solcPlatform, version);
}
catch (error) {
logError('Error getting solc executable', {
error,
solcPlatform,
version,
solcRepoPath,
solJsonRepoPath,
});
}
}
let startCompilation;
if (solcPath && !forceEmscripten) {
logInfo('Compiling with solc binary', { version, solcPath });
startCompilation = Date.now();
try {
compiled = await asyncExec(`${solcPath} --standard-json`, inputStringified, 250 * 1024 * 1024);
}
catch (error) {
if (error?.code === 'ENOBUFS') {
throw new Error('Compilation output size too large');
}
throw error;
}
}
else {
logInfo('Compiling with solc-js', { version });
const solJson = await getSolcJs(solJsonRepoPath, version);
startCompilation = Date.now();
if (solJson) {
const coercedVersion = semver.coerce(new semver.SemVer(version))?.version ?? '';
// Run Worker for solc versions < 0.4.0 for clean compiler context. See https://github.com/argotorg/sourcify/issues/1099
if (semver.lt(coercedVersion, '0.4.0')) {
compiled = await new Promise((resolve, reject) => {
const worker = importWorker(path.resolve(__dirname, './compilerWorker.ts'), {
workerData: { solJsonRepoPath, version, inputStringified },
});
worker.once('message', (result) => {
resolve(result);
});
worker.once('error', (error) => {
reject(error);
});
});
}
else {
compiled = solJson.compile(inputStringified);
}
}
}
const endCompilation = Date.now();
logInfo('Local compiler - Compilation done', {
compiler: 'solidity',
timeInMs: endCompilation - startCompilation,
});
if (!compiled) {
throw new Error('Compilation failed. No output from the compiler.');
}
const compiledJSON = JSON.parse(compiled);
const errorMessages = compiledJSON?.errors?.filter((e) => e.severity === 'error');
if (errorMessages && errorMessages.length > 0) {
logError('Compiler error', {
errorMessages,
});
throw new CompilerError('Compiler error', errorMessages);
}
return compiledJSON;
}
export async function getSolcExecutable(solcRepoPath, platform, version) {
const fileName = `solc-${platform}-v${version}`;
const solcPath = path.join(solcRepoPath, fileName);
if (fs.existsSync(solcPath) && validateSolcPath(solcPath)) {
logDebug('Found existing solc', { version, platform, solcPath });
return solcPath;
}
await fetchAndSaveSolc(platform, solcPath, version, fileName);
if (!validateSolcPath(solcPath)) {
throw new Error(`Solc not found. Maybe an incorrect version was provided. ${solcPath} - ${version} - ${platform}`);
}
return solcPath;
}
function validateSolcPath(solcPath) {
// TODO: Handle nodejs only dependencies
const spawned = spawnSync(solcPath, ['--version']);
if (spawned.status === 0) {
return true;
}
const error = spawned?.error?.message ||
spawned.stderr.toString() ||
'Error running solc, are you on the right platoform? (e.g. x64 vs arm)';
logWarn(error);
return false;
}
/**
* Fetches a solc binary and saves it to the given path.
*
* If platform is "bin", it will download the solc-js binary.
*/
async function fetchAndSaveSolc(platform, solcPath, version, fileName) {
const encodedURIFilename = encodeURIComponent(fileName);
const githubSolcURI = `${HOST_SOLC_REPO}${platform}/${encodedURIFilename}`;
logInfo('Fetching solc', { version, platform, githubSolcURI, solcPath });
let res = await fetchWithBackoff(githubSolcURI);
let status = res.status;
let buffer;
// handle case in which the response is a link to another version
if (status === 200) {
buffer = await res.arrayBuffer();
const responseText = Buffer.from(buffer).toString();
if (/^([\w-]+)-v(\d+\.\d+\.\d+)\+commit\.([a-fA-F0-9]+).*$/.test(responseText)) {
const githubSolcURI = `${HOST_SOLC_REPO}${platform}/${responseText}`;
res = await fetchWithBackoff(githubSolcURI);
status = res.status;
buffer = await res.arrayBuffer();
}
}
if (status === 200 && buffer) {
fs.mkdirSync(path.dirname(solcPath), { recursive: true });
try {
fs.unlinkSync(solcPath);
}
catch (_e) {
undefined;
}
fs.writeFileSync(solcPath, new DataView(buffer), { mode: 0o755 });
logInfo('Saved solc', { version, platform, githubSolcURI, solcPath });
}
else {
logError('Failed fetching solc', {
version,
platform,
githubSolcURI,
solcPath,
});
throw new Error(`Failed fetching solc ${version} for platform ${platform}. Please check if the version is valid.`);
}
}
/**
* Fetches the requested version of the Solidity compiler (soljson).
* First attempts to search locally; if that fails, falls back to downloading it.
*
* @param version the solc version to retrieve: the expected format is
*
* "[v]<major>.<minor>.<patch>+commit.<hash>"
*
* e.g.: "0.6.6+commit.6c089d02"
*
* defaults to "latest"
*
* @param log a logger to track the course of events
*
* @returns the requested solc instance
*/
export async function getSolcJs(solJsonRepoPath, version) {
// /^\d+\.\d+\.\d+\+commit\.[a-f0-9]{8}$/
version = version.trim();
if (version !== 'latest' && !version.startsWith('v')) {
version = 'v' + version;
}
const fileName = `soljson-${version}.js`;
const solJsonPath = path.resolve(solJsonRepoPath, fileName);
if (!fs.existsSync(solJsonPath)) {
logDebug('Solc-js not found locally, downloading', {
version,
solJsonPath,
});
await fetchAndSaveSolc('bin', solJsonPath, version, fileName);
}
const solcjsImports = await import(solJsonPath);
return solc.setupMethods(solcjsImports);
}
// https://stackoverflow.com/questions/71795469/ts-node-using-worker-thread-cause-cannot-use-import-statement-outside-a-module
function importWorker(path, options) {
const resolvedPath = require.resolve(path);
return new Worker(resolvedPath, {
...options,
execArgv: /\.ts$/.test(resolvedPath)
? ['--require', 'ts-node/register']
: undefined,
});
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic29saWRpdHlDb21waWxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvc29saWRpdHlDb21waWxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx3Q0FBd0M7QUFDeEMsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQ3hCLE9BQU8sRUFBRSxNQUFNLElBQUksQ0FBQztBQUNwQixPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzFDLE9BQU8sTUFBTSxNQUFNLFFBQVEsQ0FBQztBQUU1QixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDeEMsT0FBTyxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNqRSxPQUFPLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQU10RSw4REFBOEQ7QUFDOUQsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0FBRTdCLE1BQU0sY0FBYyxHQUFHLG9DQUFvQyxDQUFDO0FBRTVELE1BQU0sVUFBVSxnQkFBZ0I7SUFDOUIsSUFBSSxPQUFPLENBQUMsUUFBUSxLQUFLLFFBQVEsSUFBSSxPQUFPLENBQUMsSUFBSSxLQUFLLEtBQUssRUFBRSxDQUFDO1FBQzVELE9BQU8sY0FBYyxDQUFDO0lBQ3hCLENBQUM7SUFDRCxJQUFJLE9BQU8sQ0FBQyxRQUFRLEtBQUssT0FBTyxJQUFJLE9BQU8sQ0FBQyxJQUFJLEtBQUssS0FBSyxFQUFFLENBQUM7UUFDM0QsT0FBTyxhQUFhLENBQUM7SUFDdkIsQ0FBQztJQUNELElBQUksT0FBTyxDQUFDLFFBQVEsS0FBSyxPQUFPLElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxLQUFLLEVBQUUsQ0FBQztRQUMzRCxPQUFPLGVBQWUsQ0FBQztJQUN6QixDQUFDO0lBQ0QsT0FB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