@sentio/truffle-fetch-and-compile
Version:
Used to obtain external verified sources and compile them
201 lines • 9.44 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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSortedFetcherConstructors = exports.fetchAndCompileForRecognizer = void 0;
const debug_1 = __importDefault(require("debug"));
const debug = (0, debug_1.default)("fetch-and-compile:fetch");
const semver_1 = __importDefault(require("semver"));
const source_fetcher_1 = __importDefault(require("@truffle/source-fetcher"));
const source_fetcher_2 = require("@truffle/source-fetcher");
const config_1 = __importDefault(require("@truffle/config"));
const { Compile } = require("@truffle/compile-solidity"); //sorry for untyped import!
const utils_1 = require("./utils");
function fetchAndCompileForRecognizer(recognizer, options, optimizerOverride) {
return __awaiter(this, void 0, void 0, function* () {
const normalizedOptions = (0, utils_1.normalizeFetchAndCompileOptions)(options);
const fetcherConstructors = getSortedFetcherConstructors((0, utils_1.normalizeFetcherNames)(normalizedOptions));
const fetchers = yield getFetchers(fetcherConstructors, normalizedOptions, recognizer);
//now: the main loop!
let address;
while ((address = recognizer.getAnUnrecognizedAddress()) !== undefined) {
yield tryFetchAndCompileAddress(address, fetchers, recognizer, normalizedOptions, optimizerOverride);
}
});
}
exports.fetchAndCompileForRecognizer = fetchAndCompileForRecognizer;
//sort/filter fetchers by user's order, if given; otherwise use default order
function getSortedFetcherConstructors(userFetcherNames) {
let sortedFetchers = [];
if (userFetcherNames) {
for (let name of userFetcherNames) {
let Fetcher = source_fetcher_1.default.find(Fetcher => Fetcher.fetcherName === name);
if (Fetcher) {
sortedFetchers.push(Fetcher);
}
else {
throw new Error(`Unknown external source service ${name}.`);
}
}
}
else {
sortedFetchers = source_fetcher_1.default;
}
return sortedFetchers;
}
exports.getSortedFetcherConstructors = getSortedFetcherConstructors;
function getFetchers(fetcherConstructors, options, recognizer) {
return __awaiter(this, void 0, void 0, function* () {
const networkId = options.network.networkId;
//make fetcher instances. we'll filter out ones that don't support this
//network (and note ones that yielded errors)
return (yield Promise.all(fetcherConstructors.map((Fetcher) => __awaiter(this, void 0, void 0, function* () {
try {
return yield Fetcher.forNetworkId(networkId, ((options.fetch || {}).fetcherOptions || {})[Fetcher.fetcherName]);
}
catch (error) {
if (!(error instanceof source_fetcher_2.InvalidNetworkError)) {
//if it's *not* just an invalid network, log the error.
recognizer.markBadFetcher(Fetcher.fetcherName);
}
//either way, filter this fetcher out
return null;
}
})))).filter((fetcher) => fetcher !== null);
});
}
function tryFetchAndCompileAddress(address, fetchers, recognizer, fetchAndCompileOptions, optimizerOverride) {
var _a, _b, _c;
return __awaiter(this, void 0, void 0, function* () {
let found = false;
let failureReason; //undefined if no failure
let failureError;
//(this includes if no source is found)
for (const fetcher of fetchers) {
//now comes all the hard parts!
//get our sources
let result;
try {
debug("getting sources for %s via %s", address, fetcher.fetcherName);
result = yield fetcher.fetchSourcesForAddress(address);
}
catch (error) {
debug("error in getting sources! %o", error);
failureReason = "fetch";
failureError = error;
continue;
}
if (result === null) {
debug("no sources found");
//null means they don't have that address
continue;
}
//if we do have it, extract sources & options
debug("got sources!");
const { sources, options } = result; //not same options as above, sorry for name confusion
if (options.language === "Vyper") {
//if it's not Solidity, bail out now
debug("found Vyper, bailing out!");
recognizer.markUnrecognizable(address, "language");
//break out of the fetcher loop, since *no* fetcher will work here
break;
}
//set up the config
let externalConfig = config_1.default.default().with({
compilers: {
solc: options
}
});
//if using docker, transform it (this does nothing if not using docker)
externalConfig = transformIfUsingDocker(externalConfig, fetchAndCompileOptions);
// TODO handle nightly version, see docker
if ((_a = fetchAndCompileOptions.compile) === null || _a === void 0 ? void 0 : _a.nativeCompilerMap) {
externalConfig.compilers.solc.nativeCompilerMap =
(_b = fetchAndCompileOptions.compile) === null || _b === void 0 ? void 0 : _b.nativeCompilerMap;
}
if (optimizerOverride) {
externalConfig.compilers.solc.settings.optimizer = optimizerOverride;
// it is deliberately dropped in source fetcher
// not sure why
if ((_c = options.specializations) === null || _c === void 0 ? void 0 : _c.libraries) {
externalConfig.compilers.solc.settings.libraries =
options.specializations.libraries;
}
}
//compile the sources
let compileResult;
try {
compileResult = yield Compile.sources({
options: externalConfig.with({ quiet: true }),
sources
});
}
catch (error) {
debug("compile error: %O", error);
failureReason = "compile";
failureError = error;
continue; //try again with a different fetcher, I guess?
}
//add it!
yield recognizer.addCompiledInfo({
compileResult,
sourceInfo: result,
fetchedVia: fetcher.fetcherName
}, address);
failureReason = undefined; //mark as *not* failed in case a previous fetcher failed
failureError = undefined;
//check: did this actually help?
debug("checking result");
if (!recognizer.isAddressUnrecognized(address)) {
debug("address %s successfully recognized via %s", address, fetcher.fetcherName);
found = true;
//break out of the fetcher loop -- we got what we want
break;
}
debug("address %s still unrecognized", address);
}
if (found === false) {
//if we couldn't find it, add it to the list of addresses to skip
recognizer.markUnrecognizable(address, failureReason, failureError);
}
});
}
function transformIfUsingDocker(externalConfig, fetchAndCompileOptions) {
const useDocker = Boolean((fetchAndCompileOptions.compile || {}).docker);
if (!useDocker) {
//if they're not using docker, no need to transform anything :)
return externalConfig;
}
const givenVersion = externalConfig.compilers.solc.version;
//if they are, we have to ask: are they using a nightly?
if (semver_1.default.prerelease(givenVersion)) {
//we're not going to attempt to make Docker work with nightlies.
//just keep Docker turned off.
return externalConfig;
}
//otherwise, turn on Docker, and reduce the version to its simple form.
const simpleVersion = semver_1.default.valid(givenVersion);
if (simpleVersion === null) {
//this should never happen
throw new Error("Fetched source has unparseable compiler version");
}
return externalConfig.merge({
compilers: {
solc: {
version: simpleVersion,
docker: true
}
}
});
}
//# sourceMappingURL=fetch.js.map