UNPKG

@ethereum-sourcify/compilers

Version:

Wrapper around multiple compilers to download the right version and invoke the compilation with a common interface.

219 lines 17.7 kB
// 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, 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) { solcPath = await getSolcExecutable(solcRepoPath, solcPlatform, version); } 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/ethereum/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) { const error = new Error('Compiler error:\n ' + JSON.stringify(errorMessages)); logError(error.message); throw error; } 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; } const success = await fetchAndSaveSolc(platform, solcPath, version, fileName); if (success && !validateSolcPath(solcPath)) { logError(`Cannot validate solc ${version}.`); return null; } return success ? solcPath : null; } 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 }); return true; } else { logWarn('Failed fetching solc', { version, platform, githubSolcURI, solcPath, }); } return false; } /** * 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, }); if (!(await fetchAndSaveSolc('bin', solJsonPath, version, fileName))) { return false; } } 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic29saWRpdHlDb21waWxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvc29saWRpdHlDb21waWxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx3Q0FBd0M7QUFDeEMsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQ3hCLE9BQU8sRUFBRSxNQUFNLElBQUksQ0FBQztBQUNwQixPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzFDLE9BQU8sTUFBTSxNQUFNLFFBQVEsQ0FBQztBQUM1QixPQUFPLEVBQUUsTUFBTSxFQUFpQixNQUFNLGdCQUFnQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDakUsT0FBTyxFQUFFLFNBQVMsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQU12RCw4REFBOEQ7QUFDOUQsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0FBRTdCLE1BQU0sY0FBYyxHQUFHLG9DQUFvQyxDQUFDO0FBRTVELE1BQU0sVUFBVSxnQkFBZ0I7SUFDOUIsSUFBSSxPQUFPLENBQUMsUUFBUSxLQUFLLFFBQVEsSUFBSSxPQUFPLENBQUMsSUFBSSxLQUFLLEtBQUssRUFBRSxDQUFDO1FBQzVELE9BQU8sY0FBYyxDQUFDO0lBQ3hCLENBQUM7SUFDRCxJQUFJLE9BQU8sQ0FBQyxRQUFRLEtBQUssT0FBTyxJQUFJLE9BQU8sQ0FBQyxJQUFJLEtBQUssS0FBSyxFQUFFLENBQUM7UUFDM0QsT0FBTyxhQUFhLENBQUM7SUFDdkIsQ0FBQztJQUNELElBQUksT0FBTyxDQUFDLFFBQVEsS0FBSyxPQUFPLElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxLQUFLLEVBQUUsQ0FBQztRQUMzRCxPQUFPLGVBQWUsQ0FBQztJQUN6QixDQUFDO0lBQ0QsT0FBTyxLQUFLLENB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