From 1b6ba5e6dc6efaf89f77b7695a18d2c3b1e2ff1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Wed, 1 Feb 2023 15:16:35 +0000 Subject: [PATCH] feat: backport remote BTC node support --- .gitignore | 1 + .sample.env | 18 +++ INSTALL.md | 6 + lib/blockchain/bitcoin.js | 48 +++++--- lib/blockchain/common.js | 20 ++- lib/blockchain/install.js | 116 ++++++++++++++---- lib/environment-helper.js | 18 +++ lib/plugins/common/json-rpc.js | 21 +++- lib/plugins/wallet/bitcoind/bitcoind.js | 11 +- .../wallet/pazuz-wallet/pazuz-wallet.js | 1 - migrations/1665418064066-set-node-env.js | 19 +++ tools/build-dev-env.js | 6 + tools/build-prod-env.js | 5 + tools/migrate-env.js | 32 +++++ tools/set-env-var.js | 5 +- 15 files changed, 272 insertions(+), 55 deletions(-) create mode 100644 migrations/1665418064066-set-node-env.js create mode 100644 tools/migrate-env.js diff --git a/.gitignore b/.gitignore index f5b53d44..640a7a13 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ scratch/ seeds/ mnemonics/ certs/ +blockchains/ test/stress/machines test/stress/config.json lamassu.json diff --git a/.sample.env b/.sample.env index 6f867352..1b4f55ec 100644 --- a/.sample.env +++ b/.sample.env @@ -1,3 +1,5 @@ +NODE_ENV= + ## Database variables # Used to describe which database to use. Possible values include: DEV, RELEASE, STRESS_TEST @@ -51,6 +53,22 @@ HOSTNAME= LOG_LEVEL= LIGHTNING_NETWORK_DAEMON= +# Crypto nodes related variables + +## Location info (can be local or remote) +BTC_NODE_LOCATION= +BTC_WALLET_LOCATION= + +## Node connection info (remote node only) +BTC_NODE_HOST= +BTC_NODE_PORT= + +## Node connection info (remote wallet only) +BTC_NODE_RPC_HOST= +BTC_NODE_RPC_PORT= +BTC_NODE_USER= +BTC_NODE_PASSWORD= + ## Deprecated or in deprecation HTTP= diff --git a/INSTALL.md b/INSTALL.md index 5630adc4..9ee31a3e 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -113,6 +113,12 @@ Click on ``+ Add Machine`` in the sidebar. Type in a name for your machine and c Now continue with lamassu-machine instructions from the ``INSTALL.md`` file in [lamassu-machine repository](https://github.com/lamassu/lamassu-machine). +### Run a local coin node (BTC supported) + +Run `node bin/lamassu-coins` in the project root and select `Bitcoin`. This process will require the existence of certain environment variables that the setup will warn about. + +Once that is done, the node needs to be run in a terminal with the following command `/bin/bitcoind -datadir=/bitcoin` + ## Subsequent runs diff --git a/lib/blockchain/bitcoin.js b/lib/blockchain/bitcoin.js index f1f9d951..12c96815 100644 --- a/lib/blockchain/bitcoin.js +++ b/lib/blockchain/bitcoin.js @@ -1,54 +1,61 @@ const path = require('path') +const _ = require('lodash/fp') const { utils: coinUtils } = require('@lamassu/coins') const common = require('./common') +const { isDevMode, isRemoteNode } = require('../environment-helper') module.exports = { setup, updateCore } const coinRec = coinUtils.getCryptoCurrency('BTC') +const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR + +const tmpDir = isDevMode() ? path.resolve(BLOCKCHAIN_DIR, 'tmp') : '/tmp' +const usrBinDir = isDevMode() ? path.resolve(BLOCKCHAIN_DIR, 'bin') : '/usr/local/bin' + function setup (dataDir) { - common.firewall([coinRec.defaultPort]) + !isDevMode() && common.firewall([coinRec.defaultPort]) const config = buildConfig() common.writeFile(path.resolve(dataDir, coinRec.configFile), config) - const cmd = `/usr/local/bin/${coinRec.daemon} -datadir=${dataDir}` - common.writeSupervisorConfig(coinRec, cmd) + const cmd = `${usrBinDir}/${coinRec.daemon} -datadir=${dataDir}` + !isDevMode() && common.writeSupervisorConfig(coinRec, cmd) } function updateCore (coinRec, isCurrentlyRunning) { common.logger.info('Updating Bitcoin Core. This may take a minute...') - common.es(`sudo supervisorctl stop bitcoin`) + !isDevMode() && common.es(`sudo supervisorctl stop bitcoin`) common.es(`curl -#o /tmp/bitcoin.tar.gz ${coinRec.url}`) common.es(`tar -xzf /tmp/bitcoin.tar.gz -C /tmp/`) common.logger.info('Updating wallet...') - common.es(`cp /tmp/${coinRec.dir}/* /usr/local/bin/`) - common.es(`rm -r /tmp/${coinRec.dir.replace('/bin', '')}`) - common.es(`rm /tmp/bitcoin.tar.gz`) + common.es(`cp ${tmpDir}/${coinRec.dir}/* ${usrBinDir}/`) + common.es(`rm -r ${tmpDir}/${coinRec.dir.replace('/bin', '')}`) + common.es(`rm ${tmpDir}/bitcoin.tar.gz`) - if (common.es(`grep "addresstype=p2sh-segwit" /mnt/blockchains/bitcoin/bitcoin.conf || true`)) { + if (common.es(`grep "addresstype=p2sh-segwit" ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf || true`)) { common.logger.info(`Enabling bech32 receiving addresses in config file..`) - common.es(`sed -i 's/addresstype=p2sh-segwit/addresstype=bech32/g' /mnt/blockchains/bitcoin/bitcoin.conf`) + common.es(`sed -i 's/addresstype=p2sh-segwit/addresstype=bech32/g' ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf`) } else { common.logger.info(`bech32 receiving addresses already defined, skipping...`) } - if (common.es(`grep "changetype=" /mnt/blockchains/bitcoin/bitcoin.conf || true`)) { + if (common.es(`grep "changetype=" ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf || true`)) { common.logger.info(`changetype already defined, skipping...`) } else { common.logger.info(`Enabling bech32 change addresses in config file..`) - common.es(`echo "\nchangetype=bech32" >> /mnt/blockchains/bitcoin/bitcoin.conf`) + common.es(`echo "\nchangetype=bech32" >> ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf`) } - if (common.es(`grep "listenonion=" /mnt/blockchains/bitcoin/bitcoin.conf || true`)) { + if (common.es(`grep "listenonion=" ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf || true`)) { common.logger.info(`listenonion already defined, skipping...`) } else { common.logger.info(`Setting 'listenonion=0' in config file...`) - common.es(`echo "\nlistenonion=0" >> /mnt/blockchains/bitcoin/bitcoin.conf`) + common.es(`echo "\nlistenonion=0" >> ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf`) } - if (isCurrentlyRunning) { + if (isCurrentlyRunning && !isDevMode()) { common.logger.info('Starting wallet...') common.es(`sudo supervisorctl start bitcoin`) } @@ -59,6 +66,7 @@ function updateCore (coinRec, isCurrentlyRunning) { function buildConfig () { return `rpcuser=lamassuserver rpcpassword=${common.randomPass()} +${isDevMode() ? `regtest=1` : ``} dbcache=500 server=1 connections=40 @@ -68,8 +76,16 @@ daemon=0 addresstype=bech32 changetype=bech32 walletrbf=1 -bind=0.0.0.0:8332 -rpcport=8333 listenonion=0 +fallbackfee=0.00005 +rpcworkqueue=2000 +${isDevMode() + ? `[regtest] +rpcport=18333 +bind=0.0.0.0:18332 +${isRemoteNode(coinRec) ? `connect=${process.env.BTC_NODE_HOST}:${process.env.BTC_NODE_PORT}` : ``}` + : `rpcport=8333 +bind=0.0.0.0:8332 +${isRemoteNode(coinRec) ? `connect=${process.env.BTC_NODE_HOST}:${process.env.BTC_NODE_PORT}` : ``}`} ` } diff --git a/lib/blockchain/common.js b/lib/blockchain/common.js index 85739b41..540c44c6 100644 --- a/lib/blockchain/common.js +++ b/lib/blockchain/common.js @@ -3,11 +3,16 @@ const os = require('os') const path = require('path') const cp = require('child_process') const fs = require('fs') +const makeDir = require('make-dir') const _ = require('lodash/fp') const logger = require('console-log-level')({level: 'info'}) +const { isDevMode } = require('../environment-helper') + +const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR + module.exports = { es, writeSupervisorConfig, @@ -106,6 +111,11 @@ function writeSupervisorConfig (coinRec, cmd, walletCmd = '') { } function isInstalledSoftware (coinRec) { + if (isDevMode()) { + return fs.existsSync(`${BLOCKCHAIN_DIR}/${coinRec.code}/${coinRec.configFile}`) + && fs.existsSync(`${BLOCKCHAIN_DIR}/bin/${coinRec.daemon}`) + } + const nodeInstalled = fs.existsSync(`/etc/supervisor/conf.d/${coinRec.code}.conf`) const walletInstalled = _.isNil(coinRec.wallet) ? true @@ -127,13 +137,19 @@ function fetchAndInstall (coinRec) { es(`wget -q ${url}`) es(`tar -xf ${downloadFile}`) + const usrBinDir = isDevMode() ? path.resolve(BLOCKCHAIN_DIR, 'bin') : '/usr/local/bin' + + if (isDevMode()) { + makeDir.sync(usrBinDir) + } + if (_.isEmpty(binaries.files)) { - es(`sudo cp ${binDir}/* /usr/local/bin`) + es(`sudo cp ${binDir}/* ${usrBinDir}`) return } _.forEach(([source, target]) => { - es(`sudo cp ${binDir}/${source} /usr/local/bin/${target}`) + es(`sudo cp ${binDir}/${source} ${usrBinDir}/${target}`) }, binaries.files) } diff --git a/lib/blockchain/install.js b/lib/blockchain/install.js index 71434198..2cbd31b5 100644 --- a/lib/blockchain/install.js +++ b/lib/blockchain/install.js @@ -10,6 +10,7 @@ const _ = require('lodash/fp') const { utils: coinUtils } = require('@lamassu/coins') const settingsLoader = require('../new-settings-loader') const wallet = require('../wallet') +const { isDevMode, isRemoteNode, isRemoteWallet } = require('../environment-helper') const common = require('./common') const doVolume = require('./do-volume') @@ -30,7 +31,10 @@ const PLUGINS = { const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR -module.exports = {run} +module.exports = { + isEnvironmentValid, + run +} function installedVolumeFilePath (crypto) { return path.resolve(coinUtils.cryptoDir(crypto, BLOCKCHAIN_DIR), '.installed') @@ -52,43 +56,94 @@ function processCryptos (codes) { logger.info('Thanks! Installing: %s. Will take a while...', _.join(', ', codes)) - const goodVolume = doVolume.prepareVolume() - - if (!goodVolume) { - logger.error('There was an error preparing the disk volume. Exiting.') - process.exit(1) - } - const selectedCryptos = _.map(code => _.find(['code', code], cryptos), codes) - _.forEach(setupCrypto, selectedCryptos) - common.es('sudo supervisorctl reread') - common.es('sudo supervisorctl update') - const blockchainDir = BLOCKCHAIN_DIR - const backupDir = path.resolve(os.homedir(), 'backups') - const rsyncCmd = `( \ - (crontab -l 2>/dev/null || echo -n "") | grep -v "@daily rsync ".*"wallet.dat"; \ - echo "@daily rsync -r --prune-empty-dirs --include='*/' \ - --include='wallet.dat' \ - --exclude='*' ${blockchainDir} ${backupDir} > /dev/null" \ - ) | crontab -` - common.es(rsyncCmd) + if (isDevMode()) { + _.forEach(setupCrypto, selectedCryptos) + } else { + const goodVolume = doVolume.prepareVolume() - _.forEach(c => { - updateCrypto(c) - common.es(`sudo supervisorctl start ${c.code}`) - }, selectedCryptos) + if (!goodVolume) { + logger.error('There was an error preparing the disk volume. Exiting.') + process.exit(1) + } + + _.forEach(setupCrypto, selectedCryptos) + common.es('sudo supervisorctl reread') + common.es('sudo supervisorctl update') + + const blockchainDir = BLOCKCHAIN_DIR + const backupDir = path.resolve(os.homedir(), 'backups') + const rsyncCmd = `( \ + (crontab -l 2>/dev/null || echo -n "") | grep -v "@daily rsync ".*"wallet.dat"; \ + echo "@daily rsync -r --prune-empty-dirs --include='*/' \ + --include='wallet.dat' \ + --exclude='*' ${blockchainDir} ${backupDir} > /dev/null" \ + ) | crontab -` + common.es(rsyncCmd) + + _.forEach(c => { + updateCrypto(c) + common.es(`sudo supervisorctl start ${c.code}`) + }, selectedCryptos) + } logger.info('Installation complete.') } +function isEnvironmentValid (crypto) { + if (_.isEmpty(process.env[`${crypto.cryptoCode}_NODE_LOCATION`])) + throw new Error(`The environment variable for ${crypto.cryptoCode}_NODE_LOCATION is not set!`) + + if (_.isEmpty(process.env[`${crypto.cryptoCode}_WALLET_LOCATION`])) + throw new Error(`The environment variable for ${crypto.cryptoCode}_WALLET_LOCATION is not set!`) + + if (isRemoteWallet(crypto) && !isRemoteNode(crypto)) + throw new Error(`Invalid environment setup for ${crypto.display}: It's not possible to use a remote wallet without using a remote node!`) + + if (isRemoteNode(crypto) && !isRemoteWallet(crypto)) { + if (_.isEmpty(process.env[`${crypto.cryptoCode}_NODE_HOST`])) + throw new Error(`The environment variable for ${crypto.cryptoCode}_NODE_HOST is not set!`) + + if (_.isEmpty(process.env[`${crypto.cryptoCode}_NODE_PORT`])) + throw new Error(`The environment variable for ${crypto.cryptoCode}_NODE_PORT is not set!`) + + if (_.isEmpty(process.env.BLOCKCHAIN_DIR)) + throw new Error(`The environment variable for BLOCKCHAIN_DIR is not set!`) + } + + if (isRemoteWallet(crypto)) { + if (_.isEmpty(process.env[`${crypto.cryptoCode}_NODE_RPC_HOST`])) + throw new Error(`The environment variable for ${crypto.cryptoCode}_NODE_RPC_HOST is not set!`) + + if (_.isEmpty(process.env[`${crypto.cryptoCode}_NODE_RPC_PORT`])) + throw new Error(`The environment variable for ${crypto.cryptoCode}_NODE_RPC_PORT is not set!`) + + if (_.isEmpty(process.env[`${crypto.cryptoCode}_NODE_USER`])) + throw new Error(`The environment variable for ${crypto.cryptoCode}_NODE_USER is not set!`) + + if (_.isEmpty(process.env[`${crypto.cryptoCode}_NODE_PASSWORD`])) + throw new Error(`The environment variable for ${crypto.cryptoCode}_NODE_PASSWORD is not set!`) + } + + return true +} + function setupCrypto (crypto) { logger.info(`Installing ${crypto.display}...`) + + if (!isEnvironmentValid(crypto)) throw new Error(`Environment error for ${crypto.display}`) + + if (isRemoteWallet(crypto)) { + logger.info(`Environment variable ${crypto.cryptoCode}_WALLET_LOCATION is set as 'remote', so there's no need to install a node in the system. Exiting...`) + return + } + const cryptoDir = coinUtils.cryptoDir(crypto, BLOCKCHAIN_DIR) makeDir.sync(cryptoDir) const cryptoPlugin = plugin(crypto) const oldDir = process.cwd() - const tmpDir = '/tmp/blockchain-install' + const tmpDir = isDevMode() ? path.resolve(BLOCKCHAIN_DIR, 'tmp', 'blockchain-install') : '/tmp/blockchain-install' makeDir.sync(tmpDir) process.chdir(tmpDir) @@ -97,7 +152,10 @@ function setupCrypto (crypto) { cryptoPlugin.setup(cryptoDir) - common.writeFile(installedVolumeFilePath(crypto), '') + if (!isDevMode()) { + common.writeFile(installedVolumeFilePath(crypto), '') + } + process.chdir(oldDir) } @@ -121,6 +179,8 @@ function plugin (crypto) { function getBlockchainSyncStatus (cryptoList) { return settingsLoader.loadLatest() .then(settings => { + if (isDevMode()) return new Array(_.size(cryptoList)).fill('ready') + const blockchainStatuses = _.reduce((acc, value) => { const processStatus = common.es(`sudo supervisorctl status ${value.code} | awk '{ print $2 }'`).trim() return acc.then(a => { @@ -140,7 +200,9 @@ function getBlockchainSyncStatus (cryptoList) { } function isInstalled (crypto) { - return isInstalledSoftware(crypto) && isInstalledVolume(crypto) + return isDevMode() + ? isInstalledSoftware(crypto) + : isInstalledSoftware(crypto) && isInstalledVolume(crypto) } function isDisabled (crypto) { diff --git a/lib/environment-helper.js b/lib/environment-helper.js index 6027fbc7..777dcef4 100644 --- a/lib/environment-helper.js +++ b/lib/environment-helper.js @@ -1,3 +1,21 @@ const path = require('path') +const isDevMode = () => process.env.NODE_ENV === 'development' +const isProdMode = () => process.env.NODE_ENV === 'production' + require('dotenv').config({ path: path.resolve(__dirname, '../.env') }) + +function isRemoteNode (crypto) { + return process.env[`${crypto.cryptoCode}_NODE_LOCATION`] === 'remote' +} + +function isRemoteWallet (crypto) { + return process.env[`${crypto.cryptoCode}_WALLET_LOCATION`] === 'remote' +} + +module.exports = { + isDevMode, + isProdMode, + isRemoteNode, + isRemoteWallet +} diff --git a/lib/plugins/common/json-rpc.js b/lib/plugins/common/json-rpc.js index ca393439..41a5ef37 100644 --- a/lib/plugins/common/json-rpc.js +++ b/lib/plugins/common/json-rpc.js @@ -7,6 +7,8 @@ const request = require('request-promise') const { utils: coinUtils } = require('@lamassu/coins') const logger = require('../../logger') +const { isRemoteNode, isRemoteWallet } = require('../../environment-helper') +const { isEnvironmentValid } = require('../../blockchain/install') const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR @@ -28,7 +30,7 @@ function fetch (account = {}, method, params) { if (_.isNil(account.port)) throw new Error('port attribute required for jsonRpc') - const url = _.defaultTo(`http://localhost:${account.port}`, account.url) + const url = _.defaultTo(`http://${account.host}:${account.port}`, account.url) return axios({ method: 'post', @@ -109,15 +111,30 @@ function parseConf (confPath) { function rpcConfig (cryptoRec) { try { + if (isRemoteWallet(cryptoRec) && isEnvironmentValid(cryptoRec)) { + return { + username: process.env[`${cryptoRec.cryptoCode}_NODE_USER`], + password: process.env[`${cryptoRec.cryptoCode}_NODE_PASSWORD`], + host: process.env[`${cryptoRec.cryptoCode}_NODE_RPC_HOST`], + port: process.env[`${cryptoRec.cryptoCode}_NODE_RPC_PORT`] + } + } + const configPath = coinUtils.configPath(cryptoRec, BLOCKCHAIN_DIR) const config = parseConf(configPath) + return { username: config.rpcuser, password: config.rpcpassword, + host: 'localhost', port: config.rpcport || cryptoRec.defaultPort } } catch (err) { - logger.error('Wallet is currently not installed!') + if (!isEnvironmentValid(cryptoRec)) { + logger.error('Environment is not correctly setup for remote wallet usage!') + } else { + logger.error('Wallet is currently not installed!') + } return { port: cryptoRec.defaultPort } diff --git a/lib/plugins/wallet/bitcoind/bitcoind.js b/lib/plugins/wallet/bitcoind/bitcoind.js index cc30e4b4..803f4115 100644 --- a/lib/plugins/wallet/bitcoind/bitcoind.js +++ b/lib/plugins/wallet/bitcoind/bitcoind.js @@ -5,6 +5,7 @@ const BN = require('../../../bn') const E = require('../../../error') const logger = require('../../../logger') const { utils: coinUtils } = require('@lamassu/coins') +const { isDevMode } = require('../../../environment-helper') const cryptoRec = coinUtils.getCryptoCurrency('BTC') const unitScale = cryptoRec.unitScale @@ -36,15 +37,15 @@ function checkCryptoCode (cryptoCode) { function accountBalance (cryptoCode) { return checkCryptoCode(cryptoCode) - .then(() => fetch('getwalletinfo')) - .then(({ balance }) => new BN(balance).shiftedBy(unitScale).decimalPlaces(0)) + .then(() => fetch('getbalances')) + .then(({ mine }) => new BN(mine.trusted).shiftedBy(unitScale).decimalPlaces(0)) .catch(errorHandle) } function accountUnconfirmedBalance (cryptoCode) { return checkCryptoCode(cryptoCode) - .then(() => fetch('getwalletinfo')) - .then(({ unconfirmed_balance: balance }) => new BN(balance).shiftedBy(unitScale).decimalPlaces(0)) + .then(() => fetch('getbalances')) + .then(({ mine }) => new BN(mine.untrusted_pending).plus(mine.immature).shiftedBy(unitScale).decimalPlaces(0)) .catch(errorHandle) } @@ -62,7 +63,7 @@ function estimateFee () { function calculateFeeDiscount (feeMultiplier) { // 0 makes bitcoind do automatic fee selection - const AUTOMATIC_FEE = 0 + const AUTOMATIC_FEE = isDevMode() ? 0.01 : 0 if (!feeMultiplier || feeMultiplier.eq(1)) return AUTOMATIC_FEE return estimateFee() .then(estimatedFee => { diff --git a/lib/plugins/wallet/pazuz-wallet/pazuz-wallet.js b/lib/plugins/wallet/pazuz-wallet/pazuz-wallet.js index 6fe38387..582b7d40 100644 --- a/lib/plugins/wallet/pazuz-wallet/pazuz-wallet.js +++ b/lib/plugins/wallet/pazuz-wallet/pazuz-wallet.js @@ -3,7 +3,6 @@ const BN = require('../../../bn') const E = require('../../../error') const _ = require('lodash/fp') -const ENV = process.env.NODE_ENV === undefined || process.env.NODE_ENV === 'development' ? 'development' : 'production' const SUPPORTED_COINS = ['BTC'] const axios = require('axios').create({ diff --git a/migrations/1665418064066-set-node-env.js b/migrations/1665418064066-set-node-env.js new file mode 100644 index 00000000..604955bc --- /dev/null +++ b/migrations/1665418064066-set-node-env.js @@ -0,0 +1,19 @@ +const fs = require('fs') +const path = require('path') + +const migrateEnv = require('../tools/migrate-env') + +exports.up = function (next) { + try { + // NODE_ENV defaults to undefined on some environments, best to check the existence of the production environment file + migrateEnv([ + ['NODE_ENV', fs.existsSync(path.resolve('/etc', 'lamassu', '.env')) ? 'production' : 'development'] + ]) + } finally { + next() + } +} + +exports.down = function (next) { + next() +} diff --git a/tools/build-dev-env.js b/tools/build-dev-env.js index ddc89de2..f95f3919 100644 --- a/tools/build-dev-env.js +++ b/tools/build-dev-env.js @@ -6,6 +6,8 @@ const setEnvVariable = require('./set-env-var') fs.copyFileSync(path.resolve(__dirname, '../.sample.env'), path.resolve(__dirname, '../.env')) +setEnvVariable('NODE_ENV', 'development') + setEnvVariable('LAMASSU_DB', 'DEV') setEnvVariable('POSTGRES_USER', 'postgres') setEnvVariable('POSTGRES_PASSWORD', 'postgres123') @@ -20,6 +22,7 @@ setEnvVariable('KEY_PATH', `${process.env.PWD}/certs/Lamassu_OP.key`) setEnvVariable('MNEMONIC_PATH', `${process.env.HOME}/.lamassu/mnemonics/mnemonic.txt`) setEnvVariable('MIGRATE_STATE_PATH', `${process.env.HOME}/.lamassu/.migrate`) +setEnvVariable('BLOCKCHAIN_DIR', `${process.env.PWD}/blockchains`) setEnvVariable('OFAC_DATA_DIR', `${process.env.HOME}/.lamassu/ofac`) setEnvVariable('ID_PHOTO_CARD_DIR', `${process.env.HOME}/.lamassu/idphotocard`) setEnvVariable('FRONT_CAMERA_DIR', `${process.env.HOME}/.lamassu/frontcamera`) @@ -28,5 +31,8 @@ setEnvVariable('OPERATOR_DATA_DIR', `${process.env.HOME}/.lamassu/operatordata`) setEnvVariable('OFAC_SOURCES_NAMES', 'sdn_advanced,cons_advanced') setEnvVariable('OFAC_SOURCES_URLS', 'https://www.treasury.gov/ofac/downloads/sanctions/1.0/sdn_advanced.xml,https://www.treasury.gov/ofac/downloads/sanctions/1.0/cons_advanced.xml') +setEnvVariable('BTC_NODE_LOCATION', 'remote') +setEnvVariable('BTC_WALLET_LOCATION', 'local') + setEnvVariable('HOSTNAME', 'localhost') setEnvVariable('LOG_LEVEL', 'debug') diff --git a/tools/build-prod-env.js b/tools/build-prod-env.js index a98fec35..39b333aa 100644 --- a/tools/build-prod-env.js +++ b/tools/build-prod-env.js @@ -14,6 +14,8 @@ if (!_.isEqual(_.intersection(_.keys(argv), requiredParams), requiredParams)) { fs.copyFileSync(path.resolve(__dirname, '../.sample.env'), path.resolve('/etc', 'lamassu', '.env')) +setEnvVariable('NODE_ENV', 'production') + setEnvVariable('POSTGRES_USER', 'lamassu_pg') setEnvVariable('POSTGRES_PASSWORD', `${argv['db-password']}`) setEnvVariable('POSTGRES_HOST', 'localhost') @@ -40,5 +42,8 @@ setEnvVariable('COIN_ATM_RADAR_URL', `https://coinatmradar.info/api/lamassu/`) setEnvVariable('OFAC_SOURCES_NAMES', 'sdn_advanced,cons_advanced') setEnvVariable('OFAC_SOURCES_URLS', 'https://www.treasury.gov/ofac/downloads/sanctions/1.0/sdn_advanced.xml,https://www.treasury.gov/ofac/downloads/sanctions/1.0/cons_advanced.xml') +setEnvVariable('BTC_NODE_LOCATION', 'local') +setEnvVariable('BTC_WALLET_LOCATION', 'local') + setEnvVariable('HOSTNAME', `${argv.hostname}`) setEnvVariable('LOG_LEVEL', 'info') diff --git a/tools/migrate-env.js b/tools/migrate-env.js new file mode 100644 index 00000000..4029c906 --- /dev/null +++ b/tools/migrate-env.js @@ -0,0 +1,32 @@ +const fs = require('fs') +const path = require('path') + +const _ = require('lodash/fp') +const setEnvVariable = require('./set-env-var') + +const ENV_PATH = process.env.NODE_ENV === 'production' ? path.resolve('/etc', 'lamassu', '.env') : path.resolve(__dirname, '../.env') +const BACKUP_TIMESTAMP = Date.now() +const BACKUP_PATH = `${ENV_PATH}-${BACKUP_TIMESTAMP}` + +const migrateEnv = newVars => { + try { + fs.copyFileSync(ENV_PATH, BACKUP_PATH) + _.forEach(it => { + setEnvVariable(it[0], it[1], { ENV_PATH }) + }, newVars) + fs.unlinkSync(BACKUP_PATH) + console.log('Environment migration successful') + } catch (e) { + // Rollback the migration and restore the backup file + if (fs.existsSync(BACKUP_PATH)) { + console.log('Rolling back the environment migration...') + fs.copyFileSync(BACKUP_PATH, ENV_PATH) + fs.unlinkSync(BACKUP_PATH) + console.log('Rollback finished') + } + console.log('Environment migration failed') + throw e + } +} + +module.exports = migrateEnv diff --git a/tools/set-env-var.js b/tools/set-env-var.js index 8aa5f0d0..c4008aff 100644 --- a/tools/set-env-var.js +++ b/tools/set-env-var.js @@ -1,9 +1,10 @@ const fs = require('fs') const os = require('os') const path = require('path') +const _ = require('lodash/fp') -const setEnvVariable = (key, value) => { - const ENV_PATH = path.resolve(__dirname, '../.env') +const setEnvVariable = (key, value, opts) => { + const ENV_PATH = !_.isNil(opts.ENV_PATH) ? opts.ENV_PATH : process.env.NODE_ENV === 'production' ? path.resolve('/etc', 'lamassu', '.env') : path.resolve(__dirname, '../.env') const ENV_VARIABLES = fs.readFileSync(ENV_PATH, 'utf-8').split(os.EOL) const target = ENV_VARIABLES.indexOf(ENV_VARIABLES.find(line => line.match(new RegExp(`^${key}=`))))