@truffle/compile-solidity
Version:
Compiler helper and artifact manager for Solidity files
283 lines • 12.9 kB
JavaScript
"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