UNPKG

@truffle/compile-solidity

Version:
218 lines 10.3 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 __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.NoStringError = exports.NoDockerError = exports.Docker = void 0; // 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 axios_retry_1 = __importDefault(require("axios-retry")); // @ts-ignore const fs_1 = __importDefault(require("fs")); const child_process_1 = require("child_process"); const semver_1 = __importDefault(require("semver")); const Cache_1 = require("../Cache"); const normalizeSolcVersion_1 = require("../normalizeSolcVersion"); const errors_1 = require("../errors"); const iter_tools_1 = require("iter-tools"); class Docker { constructor(options) { const defaultConfig = { dockerTagsUrl: "https://registry.hub.docker.com/v2/repositories/ethereum/solc/tags/" }; this.config = Object.assign({}, defaultConfig, options); this.cache = new Cache_1.Cache(); } load() { return __awaiter(this, void 0, void 0, function* () { // Set a sensible limit for maxBuffer // See https://github.com/nodejs/node/pull/23027 let maxBuffer = 1024 * 1024 * 100; if (this.config.spawn && this.config.spawn.maxBuffer) { maxBuffer = this.config.spawn.maxBuffer; } const versionString = yield this.validateAndGetSolcVersion(); const command = "docker run --platform=linux/amd64 --rm -i ethereum/solc:" + this.config.version + " --standard-json"; try { return { compile: options => String((0, child_process_1.execSync)(command, { input: options, maxBuffer })), version: () => versionString }; } catch (error) { if (error.message === "No matching version found") { throw new errors_1.NoVersionError(versionString); } throw error; } }); } /** * Fetch list of solc versions available as Docker images. * * This returns a promise for an object with three fields: * { latestRelease, releases, prereleases } * NOTE that `releases` and `prereleases` in this object are both * AsyncIterableIterators (thus, use only `for await (const ...)` to consume) */ list() { var _a; return __awaiter(this, void 0, void 0, function* () { const allTags = this.streamAllDockerTags(); // split stream of all tags into separate releases and prereleases streams const isRelease = name => !!semver_1.default.valid(name); const isPrerelease = name => name.match(/nightly/); const [allTagsA, allTagsB] = (0, iter_tools_1.asyncFork)(allTags); // construct prereleases stream const prereleases = (0, iter_tools_1.asyncFilter)(isPrerelease, allTagsB); // construct releases stream and immediately fork so as to allow consuming // the first value in the stream safely const [releases, forkedReleases] = (0, iter_tools_1.asyncFork)((0, iter_tools_1.asyncFilter)(isRelease, allTagsA)); // grab the latest release from the forked releases stream; // coerce semver to remove possible `-alpine` suffix used by this repo const latestRelease = (_a = semver_1.default.coerce(yield (0, iter_tools_1.asyncFirst)(forkedReleases))) === null || _a === void 0 ? void 0 : _a.version; return { prereleases, releases, latestRelease }; }); } /* * Private methods */ downloadDockerImage(image) { if (!semver_1.default.valid(image)) { const message = `The image version you have provided is not valid.\n` + `Please ensure that ${image} is a valid docker image name.`; throw new Error(message); } this.config.events.emit("compile:downloadDockerImage:start"); try { (0, child_process_1.execSync)(`docker pull ethereum/solc:${image}`); this.config.events.emit("compile:downloadDockerImage:succeed"); } catch (error) { this.config.events.emit("compile:downloadDockerImage:fail", { error }); } } validateAndGetSolcVersion() { return __awaiter(this, void 0, void 0, function* () { const image = this.config.version; const fileName = image + ".version"; // Skip validation if they've validated for this image before. if (yield this.cache.has(fileName)) { const cachePath = this.cache.resolve(fileName); return fs_1.default.readFileSync(cachePath, "utf-8"); } // Image specified if (!image) throw new NoStringError(image); // Docker exists locally try { (0, child_process_1.execSync)("docker -v"); } catch (error) { throw new NoDockerError(); } // Image exists locally try { (0, child_process_1.execSync)("docker inspect --type=image ethereum/solc:" + image); } catch (error) { console.log(`${image} does not exist locally.\n`); this.downloadDockerImage(image); } // Get version & cache. const version = (0, child_process_1.execSync)("docker run --platform=linux/amd64 ethereum/solc:" + image + " --version"); const normalized = (0, normalizeSolcVersion_1.normalizeSolcVersion)(version); yield this.cache.add(normalized, fileName); return normalized; }); } streamAllDockerTags() { // build http client to account for rate limit problems // use axiosRetry to instate exponential backoff when requests come back // with expected 429 const client = axios_1.default.create(); (0, axios_retry_1.default)(client, { retries: 5, retryDelay: axios_retry_1.default.exponentialDelay, shouldResetTimeout: true, retryCondition: error => { const tooManyRequests = !!(error && error.response && error.response.status === 429); return (axios_retry_1.default.isNetworkOrIdempotentRequestError(error) || tooManyRequests); } }); const { dockerTagsUrl } = this.config; let nextUrl = dockerTagsUrl; return (function () { return __asyncGenerator(this, arguments, function* () { do { try { const { data: { // page of results results, // next page url next } } = yield __await(client.get(nextUrl, { maxRedirects: 50 })); for (const { name } of results) { yield yield __await(name); } nextUrl = next; } catch (error) { throw new errors_1.FailedRequestError(dockerTagsUrl, error); } } while (nextUrl); }); })(); } } exports.Docker = Docker; class NoDockerError extends Error { constructor() { super("You are trying to run dockerized solc, but docker is not installed."); } } exports.NoDockerError = NoDockerError; class NoStringError extends Error { constructor(input) { const message = "`compilers.solc.version` option must be a string specifying:\n" + " - a path to a locally installed solcjs\n" + " - a solc version or range (ex: '0.4.22' or '^0.5.0')\n" + " - a docker image name (ex: 'stable')\n" + " - 'native' to use natively installed solc\n" + "Received: " + input + " instead."; super(message); } } exports.NoStringError = NoStringError; //# sourceMappingURL=Docker.js.map