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