UNPKG

hardhat

Version:

Hardhat is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.

262 lines 12.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CompilerDownloader = exports.CompilerPlatform = void 0; const path_1 = __importDefault(require("path")); const fs_extra_1 = __importDefault(require("fs-extra")); const debug_1 = __importDefault(require("debug")); const os_1 = __importDefault(require("os")); const child_process_1 = require("child_process"); const util_1 = require("util"); const download_1 = require("../../util/download"); const errors_1 = require("../../core/errors"); const errors_list_1 = require("../../core/errors-list"); const multi_process_mutex_1 = require("../../util/multi-process-mutex"); const log = (0, debug_1.default)("hardhat:core:solidity:downloader"); const COMPILER_REPOSITORY_URL = "https://binaries.soliditylang.org"; var CompilerPlatform; (function (CompilerPlatform) { CompilerPlatform["LINUX"] = "linux-amd64"; CompilerPlatform["WINDOWS"] = "windows-amd64"; CompilerPlatform["MACOS"] = "macosx-amd64"; CompilerPlatform["WASM"] = "wasm"; })(CompilerPlatform = exports.CompilerPlatform || (exports.CompilerPlatform = {})); /** * Default implementation of ICompilerDownloader. * * Important things to note: * 1. If a compiler version is not found, this downloader may fail. * 1.1. It only re-downloads the list of compilers once every X time. * 1.1.1 If a user tries to download a new compiler before X amount of time * has passed since its release, they may need to clean the cache, as * indicated in the error messages. */ class CompilerDownloader { static getCompilerPlatform() { // TODO: This check is seriously wrong. It doesn't take into account // the architecture nor the toolchain. This should check the triplet of // system instead (see: https://wiki.osdev.org/Target_Triplet). // // The only reason this downloader works is that it validates if the // binaries actually run. switch (os_1.default.platform()) { case "win32": return CompilerPlatform.WINDOWS; case "linux": return CompilerPlatform.LINUX; case "darwin": return CompilerPlatform.MACOS; default: return CompilerPlatform.WASM; } } static getConcurrencySafeDownloader(platform, compilersDir) { const key = platform + compilersDir; if (!this._downloaderPerPlatform.has(key)) { this._downloaderPerPlatform.set(key, new CompilerDownloader(platform, compilersDir)); } return this._downloaderPerPlatform.get(key); } /** * Use CompilerDownloader.getConcurrencySafeDownloader instead */ constructor(_platform, _compilersDir, _compilerListCachePeriodMs = CompilerDownloader.defaultCompilerListCachePeriod, _downloadFunction = download_1.download) { this._platform = _platform; this._compilersDir = _compilersDir; this._compilerListCachePeriodMs = _compilerListCachePeriodMs; this._downloadFunction = _downloadFunction; this._mutex = new multi_process_mutex_1.MultiProcessMutex("compiler-download"); } async isCompilerDownloaded(version) { const build = await this._getCompilerBuild(version); if (build === undefined) { return false; } const downloadPath = this._getCompilerBinaryPathFromBuild(build); return fs_extra_1.default.pathExists(downloadPath); } async downloadCompiler(version, downloadStartedCb, downloadEndedCb) { // Since only one process at a time can acquire the mutex, we avoid the risk of downloading the same compiler multiple times. // This is because the mutex blocks access until a compiler has been fully downloaded, preventing any new process // from checking whether that version of the compiler exists. Without mutex it might incorrectly // return false, indicating that the compiler isn't present, even though it is currently being downloaded. await this._mutex.use(async () => { const isCompilerDownloaded = await this.isCompilerDownloaded(version); if (isCompilerDownloaded === true) { return; } await downloadStartedCb(isCompilerDownloaded); let build = await this._getCompilerBuild(version); if (build === undefined && (await this._shouldDownloadCompilerList())) { try { await this._downloadCompilerList(); } catch (e) { throw new errors_1.HardhatError(errors_list_1.ERRORS.SOLC.VERSION_LIST_DOWNLOAD_FAILED, {}, e); } build = await this._getCompilerBuild(version); } if (build === undefined) { throw new errors_1.HardhatError(errors_list_1.ERRORS.SOLC.INVALID_VERSION, { version }); } let downloadPath; try { downloadPath = await this._downloadCompiler(build); } catch (e) { throw new errors_1.HardhatError(errors_list_1.ERRORS.SOLC.DOWNLOAD_FAILED, { remoteVersion: build.longVersion, }, e); } const verified = await this._verifyCompilerDownload(build, downloadPath); if (!verified) { throw new errors_1.HardhatError(errors_list_1.ERRORS.SOLC.INVALID_DOWNLOAD, { remoteVersion: build.longVersion, }); } await this._postProcessCompilerDownload(build, downloadPath); await downloadEndedCb(isCompilerDownloaded); }); } async getCompiler(version) { const build = await this._getCompilerBuild(version); (0, errors_1.assertHardhatInvariant)(build !== undefined, "Trying to get a compiler before it was downloaded"); const compilerPath = this._getCompilerBinaryPathFromBuild(build); (0, errors_1.assertHardhatInvariant)(await fs_extra_1.default.pathExists(compilerPath), "Trying to get a compiler before it was downloaded"); if (await fs_extra_1.default.pathExists(this._getCompilerDoesntWorkFile(build))) { return undefined; } return { version, longVersion: build.longVersion, compilerPath, isSolcJs: this._platform === CompilerPlatform.WASM, }; } async _getCompilerBuild(version) { const listPath = this._getCompilerListPath(); if (!(await fs_extra_1.default.pathExists(listPath))) { return undefined; } const list = await this._readCompilerList(listPath); return list.builds.find((b) => b.version === version); } _getCompilerListPath() { return path_1.default.join(this._compilersDir, this._platform, "list.json"); } async _readCompilerList(listPath) { return fs_extra_1.default.readJSON(listPath); } _getCompilerDownloadPathFromBuild(build) { return path_1.default.join(this._compilersDir, this._platform, build.path); } _getCompilerBinaryPathFromBuild(build) { const downloadPath = this._getCompilerDownloadPathFromBuild(build); if (this._platform !== CompilerPlatform.WINDOWS || !downloadPath.endsWith(".zip")) { return downloadPath; } return path_1.default.join(this._compilersDir, build.version, "solc.exe"); } _getCompilerDoesntWorkFile(build) { return `${this._getCompilerBinaryPathFromBuild(build)}.does.not.work`; } async _shouldDownloadCompilerList() { const listPath = this._getCompilerListPath(); if (!(await fs_extra_1.default.pathExists(listPath))) { return true; } const stats = await fs_extra_1.default.stat(listPath); const age = new Date().valueOf() - stats.ctimeMs; return age > this._compilerListCachePeriodMs; } async _downloadCompilerList() { log(`Downloading compiler list for platform ${this._platform}`); const url = `${COMPILER_REPOSITORY_URL}/${this._platform}/list.json`; const downloadPath = this._getCompilerListPath(); await this._downloadFunction(url, downloadPath); } async _downloadCompiler(build) { log(`Downloading compiler ${build.longVersion}`); const url = `${COMPILER_REPOSITORY_URL}/${this._platform}/${build.path}`; const downloadPath = this._getCompilerDownloadPathFromBuild(build); await this._downloadFunction(url, downloadPath); return downloadPath; } async _verifyCompilerDownload(build, downloadPath) { const { bytesToHex } = require("@nomicfoundation/ethereumjs-util"); const { keccak256 } = await Promise.resolve().then(() => __importStar(require("../../util/keccak"))); const expectedKeccak256 = build.keccak256; const compiler = await fs_extra_1.default.readFile(downloadPath); const compilerKeccak256 = bytesToHex(keccak256(compiler)); if (expectedKeccak256 !== compilerKeccak256) { await fs_extra_1.default.unlink(downloadPath); return false; } return true; } async _postProcessCompilerDownload(build, downloadPath) { if (this._platform === CompilerPlatform.WASM) { return; } if (this._platform === CompilerPlatform.LINUX || this._platform === CompilerPlatform.MACOS) { fs_extra_1.default.chmodSync(downloadPath, 0o755); } else if (this._platform === CompilerPlatform.WINDOWS && downloadPath.endsWith(".zip")) { // some window builds are zipped, some are not const AdmZip = require("adm-zip"); const solcFolder = path_1.default.join(this._compilersDir, build.version); await fs_extra_1.default.ensureDir(solcFolder); const zip = new AdmZip(downloadPath); zip.extractAllTo(solcFolder); } log("Checking native solc binary"); const nativeSolcWorks = await this._checkNativeSolc(build); if (nativeSolcWorks) { return; } await fs_extra_1.default.createFile(this._getCompilerDoesntWorkFile(build)); } async _checkNativeSolc(build) { const solcPath = this._getCompilerBinaryPathFromBuild(build); const execFileP = (0, util_1.promisify)(child_process_1.execFile); try { await execFileP(solcPath, ["--version"]); return true; } catch { return false; } } } CompilerDownloader._downloaderPerPlatform = new Map(); CompilerDownloader.defaultCompilerListCachePeriod = 360000; exports.CompilerDownloader = CompilerDownloader; //# sourceMappingURL=downloader.js.map