// @flow /* eslint-disable consistent-return */ import fs from 'fs'; import path from 'path'; import cp from 'child_process'; import crypto from 'crypto'; import util from 'util'; import eres from 'eres'; import got from 'got'; import Queue from 'p-queue'; // eslint-disable-next-line import { app, mainWindow } from '../electron'; import getBinariesPath from './get-binaries-path'; import log from './logger'; const queue = new Queue({ concurrency: 2, autoStart: false }); const httpClient = got.extend({ baseUrl: 'https://z.cash/downloads/', retry: 3, useElectronNet: true, }); const FILES: Array<{ name: string, hash: string }> = [ { name: 'sprout-proving.key', hash: '8bc20a7f013b2b58970cddd2e7ea028975c88ae7ceb9259a5344a16bc2c0eef7', }, { name: 'sprout-verifying.key', hash: '4bd498dae0aacfd8e98dc306338d017d9c08dd0918ead18172bd0aec2fc5df82', }, { name: 'sapling-spend.params', hash: '8e48ffd23abb3a5fd9c5589204f32d9c31285a04b78096ba40a79b75677efc13', }, { name: 'sapling-output.params', hash: '2f0ebbcbb9bb0bcffe95a397e7eba89c29eb4dde6191c339db88570e3f3fb0e4', }, { name: 'sprout-groth16.params', hash: 'b685d700c60328498fbde589c8c7c484c722b788b265b72af448a5bf0ee55b50', }, ]; // eslint-disable-next-line max-len const checkSha256 = ( pathToFile: string, expectedHash: string, ) => new Promise((resolve, reject) => { fs.readFile(pathToFile, (err, file) => { if (err) return reject(new Error(err)); const sum = crypto.createHash('sha256'); sum.update(file); resolve(sum.digest('hex') === expectedHash); }); }); // eslint-disable-next-line max-len const downloadFile = ({ file, pathToSave, }): Promise<*> => new Promise((resolve, reject) => { if (!mainWindow.isDestroyed()) mainWindow.webContents.send('zcashd-params-download', `Downloading ${file.name}...`); log(`Downloading ${file.name}...`); httpClient .stream(file.name) .on('end', () => { checkSha256(pathToSave, file.hash) .then((isValid) => { if (isValid) { log(`SHA256 validation for file ${file.name} succeeded!`); resolve(file.name); } else { reject(new Error(`SHA256 validation failed for file: ${file.name}`)); } }) .catch(resolve); }) .on('error', err => reject(new Error(err))) .pipe(fs.createWriteStream(pathToSave)); }); let missingDownloadParam = false; // eslint-disable-next-line export default (): Promise<*> => new Promise((resolve, reject) => { const firstRunProcess = cp.spawn(path.join(getBinariesPath(), 'win', 'first-run.bat')); firstRunProcess.stdout.on('data', data => log(data.toString())); firstRunProcess.stderr.on('data', data => reject(data.toString())); firstRunProcess.on('exit', async (code, err) => { if (code !== 0 || err) return reject(new Error(err)); await Promise.all( FILES.map(async (file) => { const pathToSave = path.join(app.getPath('userData'), '..', 'ZcashParams', file.name); const [cannotAccess] = await eres( util.promisify(fs.access)(pathToSave, fs.constants.F_OK), ); if (cannotAccess) { missingDownloadParam = true; // eslint-disable-next-line max-len queue.add(() => downloadFile({ file, pathToSave }).then(() => log(`Download ${file.name} finished!`))); } else { const isValid = await checkSha256(pathToSave, file.hash); if (isValid) { log(`${file.name} already is in ${pathToSave}...`); } else { log(`File: ${file.name} failed in the SHASUM validation, downloading again...`); queue.add(() => { // eslint-disable-next-line max-len downloadFile({ file, pathToSave }).then(() => log(`Download ${file.name} finished!`)); }); } } }), ); if (!missingDownloadParam) return resolve(); /* Manual approach to check the end of the queue onIdle/onEmpty was not working */ const interval = setInterval(() => { if (queue.size === 0 && queue.pending === 0) { clearInterval(interval); resolve(); } }, 500); queue.start(); }); });