UNPKG

@nomiclabs/buidler

Version:

Buidler is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.

177 lines (143 loc) 4.75 kB
import fsExtra from "fs-extra"; import path from "path"; import { BuidlerError } from "../../core/errors"; import { ERRORS } from "../../core/errors-list"; export interface CompilerBuild { path: string; version: string; build: string; longVersion: string; keccak256: string; urls: string[]; } export interface CompilersList { builds: CompilerBuild[]; releases: { [version: string]: string; }; latestRelease: string; } const COMPILER_FILES_DIR_URL = "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/"; const COMPILERS_LIST_URL = `${COMPILER_FILES_DIR_URL}list.json`; async function downloadFile( url: string, destinationFile: string ): Promise<void> { // This library indirectly validates the TLS certs, if it didn't this // would be MITM-able. const { default: download } = await import("download"); await download(url, path.dirname(destinationFile), { filename: path.basename(destinationFile), }); } export class CompilerDownloader { private readonly _compilersDir: string; private readonly _localSolcVersion: string; private readonly _download: ( url: string, destinationFile: string ) => Promise<void>; constructor( readonly compilersDir: string, readonly localSolcVersion: string, readonly download = downloadFile ) { this._compilersDir = compilersDir; this._localSolcVersion = localSolcVersion; this._download = download; } public async getDownloadedCompilerPath(version: string): Promise<string> { const compilerBuild = await this.getCompilerBuild(version); const downloadedFilePath = path.join( this._compilersDir, compilerBuild.path ); if (!(await this._fileExists(downloadedFilePath))) { await this.downloadCompiler(compilerBuild, downloadedFilePath); } await this.verifyCompiler(compilerBuild, downloadedFilePath); return downloadedFilePath; } public async getCompilerBuild(version: string): Promise<CompilerBuild> { const compilersListExisted = await this.compilersListExists(); let list = await this.getCompilersList(); let compilerBuildPath = list.releases[version]; // We may need to re-download the compilers list. if (compilerBuildPath === undefined && compilersListExisted) { await fsExtra.unlink(this.getCompilersListPath()); list = await this.getCompilersList(); compilerBuildPath = list.releases[version]; } const compilerBuild = list.builds.find((b) => b.path === compilerBuildPath); if (compilerBuild === undefined) { throw new BuidlerError(ERRORS.SOLC.INVALID_VERSION, { version }); } return compilerBuild; } public async getCompilersList(): Promise<CompilersList> { if (!(await this.compilersListExists())) { await this.downloadCompilersList(); } return fsExtra.readJson(this.getCompilersListPath()); } public getCompilersListPath() { return path.join(this._compilersDir, "list.json"); } public async compilersListExists() { return fsExtra.pathExists(this.getCompilersListPath()); } public async downloadCompilersList() { try { await this._download(COMPILERS_LIST_URL, this.getCompilersListPath()); } catch (error) { throw new BuidlerError( ERRORS.SOLC.VERSION_LIST_DOWNLOAD_FAILED, { localVersion: this._localSolcVersion, }, error ); } } public async downloadCompiler( compilerBuild: CompilerBuild, downloadedFilePath: string ) { console.debug(`Downloading compiler version ${compilerBuild.version}`); const compilerUrl = COMPILER_FILES_DIR_URL + compilerBuild.path; try { await this._download(compilerUrl, downloadedFilePath); } catch (error) { throw new BuidlerError( ERRORS.SOLC.DOWNLOAD_FAILED, { remoteVersion: compilerBuild.version, localVersion: this._localSolcVersion, }, error ); } } public async verifyCompiler( compilerBuild: CompilerBuild, downloadedFilePath: string ) { const ethereumjsUtil = await import("ethereumjs-util"); const expectedKeccak256 = compilerBuild.keccak256; const compiler = await fsExtra.readFile(downloadedFilePath); const compilerKeccak256 = ethereumjsUtil.bufferToHex( ethereumjsUtil.keccak(compiler) ); if (expectedKeccak256 !== compilerKeccak256) { await fsExtra.unlink(downloadedFilePath); throw new BuidlerError(ERRORS.SOLC.INVALID_DOWNLOAD, { remoteVersion: compilerBuild.version, localVersion: this._localSolcVersion, }); } } private async _fileExists(filePath: string) { return fsExtra.pathExists(filePath); } }