Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to data directory #448

Merged
merged 8 commits into from
Feb 12, 2018
61 changes: 17 additions & 44 deletions app/src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -26,6 +25,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'
Expand All @@ -41,11 +42,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) {
Expand Down Expand Up @@ -266,36 +262,6 @@ async function initBaseserver (chainId, home, node) {
await expectCleanExit(child, 'gaia init exited unplanned')
}

async function backupData (root) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't we need to backup anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were previously backing up data when we switched networks, since we had to re-init and we didn't want to just delete the old data. Now we use separate folders for different networks, so we shouldn't ever have to do this.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense!

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
*/
Expand Down Expand Up @@ -417,33 +383,40 @@ 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
throw Error(`Data was created with an incompatible app version
data=${existingVersion} app=${pkg.version}`)
}
} else {
await backupData(root)
throw Error(`The data directory (${root}) has missing files`)
}

// 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)
// skip this check for local testnet
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
throw Error('Genesis has changed')
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions app/src/network.js
Original file line number Diff line number Diff line change
@@ -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
}
8 changes: 7 additions & 1 deletion app/src/root.js
Original file line number Diff line number Diff line change
@@ -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)
}
47 changes: 19 additions & 28 deletions test/unit/specs/main.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
})
})

Expand Down
15 changes: 15 additions & 0 deletions test/unit/specs/root.spec.js
Original file line number Diff line number Diff line change
@@ -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'))
})
})