UNPKG

@truffle/compile-solidity

Version:
283 lines 12.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.NoUrlError = exports.VersionRange = void 0; const debug_1 = __importDefault(require("debug")); const debug = (0, debug_1.default)("compile:compilerSupplier"); const require_from_string_1 = __importDefault(require("require-from-string")); // must polyfill AbortController to use axios >=0.20.0, <=0.27.2 on node <= v14.x require("../../polyfill"); const axios_1 = __importDefault(require("axios")); const semver_1 = __importDefault(require("semver")); const wrapper_1 = __importDefault(require("solc/wrapper")); const Cache_1 = require("../Cache"); const observeListeners_1 = require("../observeListeners"); const errors_1 = require("../errors"); class VersionRange { constructor(options) { const defaultConfig = { compilerRoots: [ // this order of url root preference was recommended by Cameel from the // Solidity team here -- https://github.com/trufflesuite/truffle/pull/5008 "https://binaries.soliditylang.org/emscripten-wasm32/", "https://binaries.soliditylang.org/emscripten-asmjs/", "https://solc-bin.ethereum.org/bin/", "https://ethereum.github.io/solc-bin/bin/", "https://relay.trufflesuite.com/solc/emscripten-wasm32/", "https://relay.trufflesuite.com/solc/emscripten-asmjs/" ] }; this.config = Object.assign({}, defaultConfig, options); this.cache = new Cache_1.Cache(); } load(versionRange) { return __awaiter(this, void 0, void 0, function* () { const rangeIsSingleVersion = semver_1.default.valid(versionRange); if (rangeIsSingleVersion && (yield this.versionIsCached(versionRange))) { return yield this.getCachedSolcByVersionRange(versionRange); } try { return yield this.getSolcFromCacheOrUrl(versionRange); } catch (error) { if (error.message.includes("Failed to complete request")) { return yield this.getSatisfyingVersionFromCache(versionRange); } throw error; } }); } list(index = 0) { return __awaiter(this, void 0, void 0, function* () { if (index >= this.config.compilerRoots.length) { throw new Error(`Failed to fetch the list of Solidity compilers from the following ` + `sources: ${this.config.compilerRoots}. Make sure you are connected ` + `to the internet.`); } let data; try { const attemptNumber = index + 1; data = yield this.getSolcVersionsForSource(this.config.compilerRoots[index], attemptNumber); } catch (error) { if (error.message.includes("Failed to fetch compiler list at")) { return yield this.list(index + 1); } throw error; } const { latestRelease } = data; const prereleases = data.builds .filter(build => build["prerelease"]) .map(build => build["longVersion"]); // ensure releases are listed in descending order const releases = semver_1.default.rsort(Object.keys(data.releases)); return { prereleases, releases, latestRelease }; }); } compilerFromString(code) { const listeners = (0, observeListeners_1.observeListeners)(); try { const soljson = (0, require_from_string_1.default)(code); return (0, wrapper_1.default)(soljson); } finally { listeners.cleanup(); } } findNewestValidVersion(version, allVersions) { return semver_1.default.maxSatisfying(Object.keys(allVersions.releases || {}), version); } getCachedSolcByFileName(fileName) { return __awaiter(this, void 0, void 0, function* () { const listeners = (0, observeListeners_1.observeListeners)(); try { const soljson = yield this.cache.loadFile(fileName); debug("soljson %o", soljson); return this.compilerFromString(soljson); } finally { listeners.cleanup(); } }); } // Range can also be a single version specification like "0.5.0" getCachedSolcByVersionRange(version) { return __awaiter(this, void 0, void 0, function* () { const cachedCompilerFileNames = yield this.cache.list(); const validVersions = cachedCompilerFileNames.filter(fileName => { const match = fileName.match(/v\d+\.\d+\.\d+.*/); if (match) return semver_1.default.satisfies(match[0], version); }); const multipleValidVersions = validVersions.length > 1; const compilerFileName = multipleValidVersions ? this.getMostRecentVersionOfCompiler(validVersions) : validVersions[0]; return yield this.getCachedSolcByFileName(compilerFileName); }); } getCachedSolcFileName(commit) { return __awaiter(this, void 0, void 0, function* () { const cachedCompilerFileNames = yield this.cache.list(); return cachedCompilerFileNames.find(fileName => { return fileName.includes(commit); }); }); } getMostRecentVersionOfCompiler(versions) { return versions.reduce((mostRecentVersionFileName, fileName) => { const match = fileName.match(/v\d+\.\d+\.\d+.*/); const mostRecentVersionMatch = mostRecentVersionFileName.match(/v\d+\.\d+\.\d+.*/); return semver_1.default.gtr(match[0], mostRecentVersionMatch[0]) ? fileName : mostRecentVersionFileName; }, "-v0.0.0+commit"); } getSatisfyingVersionFromCache(versionRange) { return __awaiter(this, void 0, void 0, function* () { if (yield this.versionIsCached(versionRange)) { return yield this.getCachedSolcByVersionRange(versionRange); } throw new errors_1.NoVersionError(versionRange); }); } getAndCacheSolcByUrl(fileName, index) { return __awaiter(this, void 0, void 0, function* () { const { events, compilerRoots } = this.config; const url = `${compilerRoots[index].replace(/\/+$/, "")}/${fileName}`; events.emit("downloadCompiler:start", { attemptNumber: index + 1 }); let response; try { response = yield axios_1.default.get(url, { maxRedirects: 50 }); } catch (error) { events.emit("downloadCompiler:fail"); throw error; } events.emit("downloadCompiler:succeed"); try { yield this.cache.add(response.data, fileName); } catch (error) { if (error.message.includes("EACCES: permission denied")) { const warningMessage = "There was an error attempting to save the compiler to disk. " + "The current user likely does not have sufficient permissions to " + "write to disk in Truffle's compiler cache directory. See the error" + `printed below for more information about this directory.\n${error}`; console.warn(warningMessage); } } return this.compilerFromString(response.data); }); } getSolcFromCacheOrUrl(versionConstraint, index = 0) { return __awaiter(this, void 0, void 0, function* () { // go through all sources (compilerRoots) trying to locate a // suitable version of the Solidity compiler const { compilerRoots, events } = this.config; if (!compilerRoots || compilerRoots.length === 0) { events.emit("fetchSolcList:fail"); throw new NoUrlError(); } if (index >= compilerRoots.length) { throw new errors_1.CompilerFetchingError(compilerRoots); } let allVersionsForSource, versionToUse; try { const attemptNumber = index + 1; allVersionsForSource = yield this.getSolcVersionsForSource(compilerRoots[index], attemptNumber); const isVersionRange = !semver_1.default.valid(versionConstraint); versionToUse = isVersionRange ? this.findNewestValidVersion(versionConstraint, allVersionsForSource) : versionConstraint; if (versionToUse === null) { throw new Error("No valid version found for source."); } const fileName = this.getSolcVersionFileName(versionToUse, allVersionsForSource); if (!fileName) throw new errors_1.NoVersionError(versionToUse); if (yield this.cache.has(fileName)) { return yield this.getCachedSolcByFileName(fileName); } return yield this.getAndCacheSolcByUrl(fileName, index); } catch (error) { const attemptNumber = index + 1; return yield this.getSolcFromCacheOrUrl(versionConstraint, attemptNumber); } }); } getSolcVersionsForSource(urlRoot, attemptNumber) { return __awaiter(this, void 0, void 0, function* () { const { events } = this.config; events.emit("fetchSolcList:start", { attemptNumber }); // trim trailing slashes from compilerRoot const url = `${urlRoot.replace(/\/+$/, "")}/list.json`; try { const response = yield axios_1.default.get(url, { maxRedirects: 50 }); events.emit("fetchSolcList:succeed"); return response.data; } catch (error) { events.emit("fetchSolcList:fail"); throw new Error(`Failed to fetch compiler list at ${url}`); } }); } getSolcVersionFileName(version, allVersions) { if (allVersions.releases[version]) return allVersions.releases[version]; const isPrerelease = version.includes("nightly") || version.includes("commit"); if (isPrerelease) { for (let build of allVersions.builds) { const exists = build["prerelease"] === version || build["build"] === version || build["longVersion"] === version; if (exists) return build["path"]; } } const versionToUse = this.findNewestValidVersion(version, allVersions); if (versionToUse) return allVersions.releases[versionToUse]; return null; } versionIsCached(version) { return __awaiter(this, void 0, void 0, function* () { const cachedCompilerFileNames = yield this.cache.list(); const cachedVersions = cachedCompilerFileNames .map(fileName => { const match = fileName.match(/v\d+\.\d+\.\d+.*/); if (match) return match[0]; }) .filter((version) => !!version); return cachedVersions.find(cachedVersion => semver_1.default.satisfies(cachedVersion, version)); }); } } exports.VersionRange = VersionRange; class NoUrlError extends Error { constructor() { super("compiler root URL missing"); } } exports.NoUrlError = NoUrlError; //# sourceMappingURL=VersionRange.js.map