@truffle/compile-solidity
Version:
Compiler helper and artifact manager for Solidity files
238 lines • 10.5 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 = debug_1.default("compile:compilerSupplier");
const require_from_string_1 = __importDefault(require("require-from-string"));
const original_require_1 = __importDefault(require("original-require"));
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: [
// NOTE this relay address exists so that we have a backup option in
// case more official distribution mechanisms fail.
//
// currently this URL just redirects (302 Found); we may alter this to
// host for real in the future.
"https://relay.trufflesuite.com/solc/bin/",
"https://solc-bin.ethereum.org/bin/",
"https://ethereum.github.io/solc-bin/bin/"
]
};
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 && this.versionIsCached(versionRange)) {
return this.getCachedSolcByVersionRange(versionRange);
}
try {
return yield this.getSolcFromCacheOrUrl(versionRange);
}
catch (error) {
if (error.message.includes("Failed to complete request")) {
return this.getSatisfyingVersionFromCache(versionRange);
}
throw error;
}
});
}
list() {
return __awaiter(this, void 0, void 0, function* () {
const data = yield this.getSolcVersions();
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 = observeListeners_1.observeListeners();
try {
const soljson = require_from_string_1.default(code);
return wrapper_1.default(soljson);
}
finally {
listeners.cleanup();
}
}
findNewestValidVersion(version, allVersions) {
return semver_1.default.maxSatisfying(Object.keys((allVersions === null || allVersions === void 0 ? void 0 : allVersions.releases) || {}), version);
}
getCachedSolcByFileName(fileName) {
const listeners = observeListeners_1.observeListeners();
try {
const filePath = this.cache.resolve(fileName);
const soljson = original_require_1.default(filePath);
debug("soljson %o", soljson);
return wrapper_1.default(soljson);
}
finally {
listeners.cleanup();
}
}
// Range can also be a single version specification like "0.5.0"
getCachedSolcByVersionRange(version) {
const cachedCompilerFileNames = 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 this.getCachedSolcByFileName(compilerFileName);
}
getCachedSolcFileName(commit) {
const cachedCompilerFileNames = 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) {
if (this.versionIsCached(versionRange)) {
return this.getCachedSolcByVersionRange(versionRange);
}
throw new errors_1.NoVersionError(versionRange);
}
getAndCacheSolcByUrl(fileName, index = 0) {
return __awaiter(this, void 0, void 0, function* () {
const url = `${this.config.compilerRoots[index].replace(/\/+$/, "")}/${fileName}`;
const { events } = this.config;
events.emit("downloadCompiler:start", {
attemptNumber: index + 1
});
try {
const response = yield axios_1.default.get(url, { maxRedirects: 50 });
events.emit("downloadCompiler:succeed");
this.cache.add(response.data, fileName);
return this.compilerFromString(response.data);
}
catch (error) {
events.emit("downloadCompiler:fail");
if (index >= this.config.compilerRoots.length - 1) {
throw new errors_1.NoRequestError("compiler URLs", error);
}
return this.getAndCacheSolcByUrl(fileName, index + 1);
}
});
}
getSolcFromCacheOrUrl(versionConstraint) {
return __awaiter(this, void 0, void 0, function* () {
let allVersions, versionToUse;
try {
allVersions = yield this.getSolcVersions();
}
catch (error) {
throw new errors_1.NoRequestError(versionConstraint, error);
}
const isVersionRange = !semver_1.default.valid(versionConstraint);
versionToUse = isVersionRange
? this.findNewestValidVersion(versionConstraint, allVersions)
: versionConstraint;
const fileName = this.getSolcVersionFileName(versionToUse, allVersions);
if (!fileName)
throw new errors_1.NoVersionError(versionToUse);
if (this.cache.has(fileName))
return this.getCachedSolcByFileName(fileName);
return this.getAndCacheSolcByUrl(fileName);
});
}
getSolcVersions(index = 0) {
const { events } = this.config;
events.emit("fetchSolcList:start", { attemptNumber: index + 1 });
if (!this.config.compilerRoots || this.config.compilerRoots.length < 1) {
events.emit("fetchSolcList:fail");
throw new NoUrlError();
}
const { compilerRoots } = this.config;
// trim trailing slashes from compilerRoot
const url = `${compilerRoots[index].replace(/\/+$/, "")}/list.json`;
return axios_1.default
.get(url, { maxRedirects: 50 })
.then(response => {
events.emit("fetchSolcList:succeed");
return response.data;
})
.catch(error => {
events.emit("fetchSolcList:fail");
if (index >= this.config.compilerRoots.length - 1) {
throw new errors_1.NoRequestError("version URLs", error);
}
return this.getSolcVersions(index + 1);
});
}
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) {
const cachedCompilerFileNames = 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