From 1965ce2c4ed093eee2a637adcd5b8ba3cb983ca2 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 5 Feb 2018 10:47:29 -0800 Subject: [PATCH 1/5] Use data directory based on network name, e.g. '~/.cosmos/gaia-2' --- app/src/main/index.js | 7 ++----- app/src/network.js | 16 ++++++++++++++++ app/src/root.js | 8 +++++++- 3 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 app/src/network.js diff --git a/app/src/main/index.js b/app/src/main/index.js index a3d7593be9..42c407926f 100644 --- a/app/src/main/index.js +++ b/app/src/main/index.js @@ -26,6 +26,8 @@ let nodeIP let connecting = false const root = require('../root.js') +const networkPath = require('../network.js').path + const baseserverHome = join(root, 'baseserver') const WIN = /^win/.test(process.platform) const DEV = process.env.NODE_ENV === 'development' @@ -41,11 +43,6 @@ const RELAY_PORT = DEV ? config.relay_port : config.relay_port_prod const LCD_PORT = DEV ? config.lcd_port : config.lcd_port_prod const NODE = process.env.COSMOS_NODE -// this network gets used if none is specified via the -// COSMOS_NETWORK env var -let DEFAULT_NETWORK = join(__dirname, '../networks/gaia-2') -let networkPath = process.env.COSMOS_NETWORK || DEFAULT_NETWORK - let SERVER_BINARY = 'gaia' + (WIN ? '.exe' : '') function log (...args) { diff --git a/app/src/network.js b/app/src/network.js new file mode 100644 index 0000000000..0a743d5752 --- /dev/null +++ b/app/src/network.js @@ -0,0 +1,16 @@ +let { join } = require('path') +let { readFileSync } = require('fs') + +// this network gets used if none is specified via the +// COSMOS_NETWORK env var +let DEFAULT_NETWORK = join(__dirname, '../networks/gaia-2') +let networkPath = process.env.COSMOS_NETWORK || DEFAULT_NETWORK + +let genesisText = readFileSync(join(networkPath, 'genesis.json'), 'utf8') +let genesis = JSON.parse(genesisText) +let networkName = genesis.chain_id + +module.exports = { + path: networkPath, + name: networkName +} diff --git a/app/src/root.js b/app/src/root.js index 214f20c282..c9a4b03a4e 100644 --- a/app/src/root.js +++ b/app/src/root.js @@ -1,10 +1,16 @@ const { COSMOS_HOME, NODE_ENV } = process.env + if (COSMOS_HOME) { module.exports = COSMOS_HOME } else { const home = require('user-home') const { join } = require('path') + const networkName = require('./network.js').name + const pkg = require('../package.json') const DEV = NODE_ENV === 'development' - module.exports = join(home, `.${pkg.name}${DEV ? '-dev' : ''}`) + const appName = pkg.name.toLowerCase() + const appDirName = `.${appName}${DEV ? '-dev' : ''}` + + module.exports = join(home, appDirName, networkName) } From 3438f23b68ee5192ba47f85b3079c83b73ecb2eb Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 5 Feb 2018 11:03:28 -0800 Subject: [PATCH 2/5] Error and exit if we have existing incompatible data, removes need to back up data since we never replace it --- app/src/main/index.js | 57 ++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 39 deletions(-) diff --git a/app/src/main/index.js b/app/src/main/index.js index 42c407926f..2bc00ad561 100644 --- a/app/src/main/index.js +++ b/app/src/main/index.js @@ -10,7 +10,6 @@ let semver = require('semver') let event = require('event-to-promise') let toml = require('toml') let axios = require('axios') -var glob = require('glob') let pkg = require('../../../package.json') let relayServer = require('./relayServer.js') @@ -263,36 +262,6 @@ async function initBaseserver (chainId, home, node) { await expectCleanExit(child, 'gaia init exited unplanned') } -async function backupData (root) { - let i = 1 - let path - do { - path = `${root}_backup_${i}` - i++ - } while (exists(path)) - - log(`backing up data to "${path}"`) - - // ATTENTION: mainLog stream is still open at this point, so we can't move it arround (at least on windows) - fs.copySync(root, path, { - overwrite: false, - errorOnExist: true, - filter: file => file.indexOf('main.log') === -1 - }) - await new Promise((resolve, reject) => { - glob(root + '/**/*', (err, files) => { - if (err) { - return reject(err) - } - - files - .filter(file => file.indexOf('main.log') === -1) - .forEach(file => fs.removeSync(file)) - resolve() - }) - }) -} - /* * log to file */ @@ -414,23 +383,34 @@ async function main () { if (rootExists) { log(`root exists (${root})`) + // NOTE: when changing this code, always make sure the app can never + // overwrite/delete existing data without at least backing it up, + // since it may contain the user's private keys and they might not + // have written down their seed words. + // they might get pretty mad if the app deletes their money! + // check if the existing data came from a compatible app version - // if not, backup the data and re-initialize + // if not, fail with an error if (consistentConfigDir(appVersionPath, genesisPath, configPath, gaiaVersionPath)) { - let existingVersion = fs.readFileSync(appVersionPath, 'utf8') + let existingVersion = fs.readFileSync(appVersionPath, 'utf8').trim() let compatible = semver.diff(existingVersion, pkg.version) !== 'major' if (compatible) { log('configs are compatible with current app version') init = false } else { - await backupData(root) + // TODO: versions of the app with different data formats will need to learn how to + // migrate old data + logError(`Data was created with an incompatible app version + data=${existingVersion} app=${pkg.version}`) + return process.exit(1) } } else { - await backupData(root) + logError(`The data directory (${root}) has missing files`) + return process.exit(1) } // check to make sure the genesis.json we want to use matches the one - // we already have. if it has changed, back up the old data + // we already have. if it has changed, exit with an error if (!init) { let existingGenesis = fs.readFileSync(genesisPath, 'utf8') let genesisJSON = JSON.parse(existingGenesis) @@ -438,9 +418,8 @@ async function main () { if (genesisJSON.chain_id !== 'local') { let specifiedGenesis = fs.readFileSync(join(networkPath, 'genesis.json'), 'utf8') if (existingGenesis.trim() !== specifiedGenesis.trim()) { - log('genesis has changed') - await backupData(root) - init = true + logError('Genesis has changed') + return process.exit(1) } } } From 66b0a0e51bd1e408db807b4b5012724c1c900d75 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 5 Feb 2018 11:12:46 -0800 Subject: [PATCH 3/5] Throw instead of exiting process --- app/src/main/index.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/src/main/index.js b/app/src/main/index.js index 2bc00ad561..ce2c996f92 100644 --- a/app/src/main/index.js +++ b/app/src/main/index.js @@ -400,13 +400,11 @@ async function main () { } else { // TODO: versions of the app with different data formats will need to learn how to // migrate old data - logError(`Data was created with an incompatible app version + throw Error(`Data was created with an incompatible app version data=${existingVersion} app=${pkg.version}`) - return process.exit(1) } } else { - logError(`The data directory (${root}) has missing files`) - return process.exit(1) + throw Error(`The data directory (${root}) has missing files`) } // check to make sure the genesis.json we want to use matches the one @@ -418,8 +416,7 @@ async function main () { if (genesisJSON.chain_id !== 'local') { let specifiedGenesis = fs.readFileSync(join(networkPath, 'genesis.json'), 'utf8') if (existingGenesis.trim() !== specifiedGenesis.trim()) { - logError('Genesis has changed') - return process.exit(1) + throw Error('Genesis has changed') } } } From 6059a0c3ea239747c8d20d253412c32c64f7293d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 5 Feb 2018 11:13:06 -0800 Subject: [PATCH 4/5] Updated main tests for new behavior of data dir changes --- test/unit/specs/main.spec.js | 47 +++++++++++++++--------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/test/unit/specs/main.spec.js b/test/unit/specs/main.spec.js index a9983422f6..6075f9c891 100644 --- a/test/unit/specs/main.spec.js +++ b/test/unit/specs/main.spec.js @@ -266,54 +266,45 @@ describe('Startup Process', () => { describe('Update app version', function () { mainSetup() - it('should backup the genesis.json', async function () { + it('should not replace the existing data', async function () { resetModulesKeepingFS() // alter the version so the main thread assumes an update jest.mock(root + 'package.json', () => ({ version: '1.1.1' })) - await require(appRoot + 'src/main/index.js') - - expect(fs.existsSync(testRoot.substr(0, testRoot.length - 1) + '_backup_1/genesis.json')).toBe(true) - }) - - it('should not backup main log', async function () { - expect(fs.existsSync(testRoot.substr(0, testRoot.length - 1) + '_backup_1/genesis.json')).toBe(true) - }) - }) - - describe('Keep main log on update', function () { - mainSetup() - - it('should not backup main log', async function () { - resetModulesKeepingFS() - fs.writeFile(testRoot + 'main.log', 'I AM A LOGFILE') - - // alter the version so the main thread assumes an update - jest.mock(root + 'package.json', () => ({ - version: '1.1.1' - })) - await require(appRoot + 'src/main/index.js') + let err + try { + await require(appRoot + 'src/main/index.js') + } catch (_err) { + err = _err + } + expect(err.message).toBe(`Data was created with an incompatible app version + data=0.1.0 app=1.1.1`) - expect(fs.existsSync(testRoot.substr(0, testRoot.length - 1) + '_backup_1/main.log')).toBe(false) - expect(fs.readFileSync(testRoot + 'main.log')).toContain('I AM A LOGFILE') + let appVersion = fs.readFileSync(testRoot + 'app_version', 'utf8') + expect(appVersion).toBe('0.1.0') }) }) describe('Update genesis.json', function () { mainSetup() - it('should backup the genesis.json', async function () { + it('should error on changed genesis.json', async function () { resetModulesKeepingFS() // alter the genesis so the main thread assumes a change let existingGenesis = JSON.parse(fs.readFileSync(testRoot + 'genesis.json', 'utf8')) existingGenesis.genesis_time = (new Date()).toString() fs.writeFileSync(testRoot + 'genesis.json', JSON.stringify(existingGenesis)) - await require(appRoot + 'src/main/index.js') - expect(fs.existsSync(testRoot.substr(0, testRoot.length - 1) + '_backup_1/genesis.json')).toBe(true) + let err + try { + await require(appRoot + 'src/main/index.js') + } catch (_err) { + err = _err + } + expect(err.message).toBe('Genesis has changed') }) }) From e9c6ee5c7c91e95edb8d5af3ae387c841806679d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 12 Feb 2018 08:31:40 -0800 Subject: [PATCH 5/5] Added root path test --- test/unit/specs/root.spec.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 test/unit/specs/root.spec.js diff --git a/test/unit/specs/root.spec.js b/test/unit/specs/root.spec.js new file mode 100644 index 0000000000..558ee4185d --- /dev/null +++ b/test/unit/specs/root.spec.js @@ -0,0 +1,15 @@ +const { join } = require('path') +const { homedir } = require('os') + +describe('Root UI Directory', () => { + Object.assign(process.env, { + NODE_ENV: 'development', + COSMOS_NETWORK: 'app/networks/gaia-2', + COSMOS_HOME: '' + }) + + it('should create the correct path', () => { + let root = require('../../../app/src/root.js') + expect(root).toBe(join(homedir(), '.cosmos-dev/gaia-2')) + }) +})