feat: backport remote BTC node support
This commit is contained in:
parent
24473de6d5
commit
1b6ba5e6dc
15 changed files with 272 additions and 55 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -32,6 +32,7 @@ scratch/
|
||||||
seeds/
|
seeds/
|
||||||
mnemonics/
|
mnemonics/
|
||||||
certs/
|
certs/
|
||||||
|
blockchains/
|
||||||
test/stress/machines
|
test/stress/machines
|
||||||
test/stress/config.json
|
test/stress/config.json
|
||||||
lamassu.json
|
lamassu.json
|
||||||
|
|
|
||||||
18
.sample.env
18
.sample.env
|
|
@ -1,3 +1,5 @@
|
||||||
|
NODE_ENV=
|
||||||
|
|
||||||
## Database variables
|
## Database variables
|
||||||
|
|
||||||
# Used to describe which database to use. Possible values include: DEV, RELEASE, STRESS_TEST
|
# Used to describe which database to use. Possible values include: DEV, RELEASE, STRESS_TEST
|
||||||
|
|
@ -51,6 +53,22 @@ HOSTNAME=
|
||||||
LOG_LEVEL=
|
LOG_LEVEL=
|
||||||
LIGHTNING_NETWORK_DAEMON=
|
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
|
## Deprecated or in deprecation
|
||||||
|
|
||||||
HTTP=
|
HTTP=
|
||||||
|
|
|
||||||
|
|
@ -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).
|
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 `<YOUR_BLOCKCHAIN_DIR_ENV_VAR>/bin/bitcoind -datadir=<YOUR_BLOCKCHAIN_DIR_ENV_VAR>/bitcoin`
|
||||||
|
|
||||||
|
|
||||||
## Subsequent runs
|
## Subsequent runs
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,54 +1,61 @@
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const _ = require('lodash/fp')
|
||||||
|
|
||||||
const { utils: coinUtils } = require('@lamassu/coins')
|
const { utils: coinUtils } = require('@lamassu/coins')
|
||||||
|
|
||||||
const common = require('./common')
|
const common = require('./common')
|
||||||
|
const { isDevMode, isRemoteNode } = require('../environment-helper')
|
||||||
|
|
||||||
module.exports = { setup, updateCore }
|
module.exports = { setup, updateCore }
|
||||||
|
|
||||||
const coinRec = coinUtils.getCryptoCurrency('BTC')
|
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) {
|
function setup (dataDir) {
|
||||||
common.firewall([coinRec.defaultPort])
|
!isDevMode() && common.firewall([coinRec.defaultPort])
|
||||||
const config = buildConfig()
|
const config = buildConfig()
|
||||||
common.writeFile(path.resolve(dataDir, coinRec.configFile), config)
|
common.writeFile(path.resolve(dataDir, coinRec.configFile), config)
|
||||||
const cmd = `/usr/local/bin/${coinRec.daemon} -datadir=${dataDir}`
|
const cmd = `${usrBinDir}/${coinRec.daemon} -datadir=${dataDir}`
|
||||||
common.writeSupervisorConfig(coinRec, cmd)
|
!isDevMode() && common.writeSupervisorConfig(coinRec, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCore (coinRec, isCurrentlyRunning) {
|
function updateCore (coinRec, isCurrentlyRunning) {
|
||||||
common.logger.info('Updating Bitcoin Core. This may take a minute...')
|
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(`curl -#o /tmp/bitcoin.tar.gz ${coinRec.url}`)
|
||||||
common.es(`tar -xzf /tmp/bitcoin.tar.gz -C /tmp/`)
|
common.es(`tar -xzf /tmp/bitcoin.tar.gz -C /tmp/`)
|
||||||
|
|
||||||
common.logger.info('Updating wallet...')
|
common.logger.info('Updating wallet...')
|
||||||
common.es(`cp /tmp/${coinRec.dir}/* /usr/local/bin/`)
|
common.es(`cp ${tmpDir}/${coinRec.dir}/* ${usrBinDir}/`)
|
||||||
common.es(`rm -r /tmp/${coinRec.dir.replace('/bin', '')}`)
|
common.es(`rm -r ${tmpDir}/${coinRec.dir.replace('/bin', '')}`)
|
||||||
common.es(`rm /tmp/bitcoin.tar.gz`)
|
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.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 {
|
} else {
|
||||||
common.logger.info(`bech32 receiving addresses already defined, skipping...`)
|
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...`)
|
common.logger.info(`changetype already defined, skipping...`)
|
||||||
} else {
|
} else {
|
||||||
common.logger.info(`Enabling bech32 change addresses in config file..`)
|
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...`)
|
common.logger.info(`listenonion already defined, skipping...`)
|
||||||
} else {
|
} else {
|
||||||
common.logger.info(`Setting 'listenonion=0' in config file...`)
|
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.logger.info('Starting wallet...')
|
||||||
common.es(`sudo supervisorctl start bitcoin`)
|
common.es(`sudo supervisorctl start bitcoin`)
|
||||||
}
|
}
|
||||||
|
|
@ -59,6 +66,7 @@ function updateCore (coinRec, isCurrentlyRunning) {
|
||||||
function buildConfig () {
|
function buildConfig () {
|
||||||
return `rpcuser=lamassuserver
|
return `rpcuser=lamassuserver
|
||||||
rpcpassword=${common.randomPass()}
|
rpcpassword=${common.randomPass()}
|
||||||
|
${isDevMode() ? `regtest=1` : ``}
|
||||||
dbcache=500
|
dbcache=500
|
||||||
server=1
|
server=1
|
||||||
connections=40
|
connections=40
|
||||||
|
|
@ -68,8 +76,16 @@ daemon=0
|
||||||
addresstype=bech32
|
addresstype=bech32
|
||||||
changetype=bech32
|
changetype=bech32
|
||||||
walletrbf=1
|
walletrbf=1
|
||||||
bind=0.0.0.0:8332
|
|
||||||
rpcport=8333
|
|
||||||
listenonion=0
|
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}` : ``}`}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,16 @@ const os = require('os')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const cp = require('child_process')
|
const cp = require('child_process')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
|
const makeDir = require('make-dir')
|
||||||
|
|
||||||
const _ = require('lodash/fp')
|
const _ = require('lodash/fp')
|
||||||
|
|
||||||
const logger = require('console-log-level')({level: 'info'})
|
const logger = require('console-log-level')({level: 'info'})
|
||||||
|
|
||||||
|
const { isDevMode } = require('../environment-helper')
|
||||||
|
|
||||||
|
const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
es,
|
es,
|
||||||
writeSupervisorConfig,
|
writeSupervisorConfig,
|
||||||
|
|
@ -106,6 +111,11 @@ function writeSupervisorConfig (coinRec, cmd, walletCmd = '') {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isInstalledSoftware (coinRec) {
|
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 nodeInstalled = fs.existsSync(`/etc/supervisor/conf.d/${coinRec.code}.conf`)
|
||||||
const walletInstalled = _.isNil(coinRec.wallet)
|
const walletInstalled = _.isNil(coinRec.wallet)
|
||||||
? true
|
? true
|
||||||
|
|
@ -127,13 +137,19 @@ function fetchAndInstall (coinRec) {
|
||||||
es(`wget -q ${url}`)
|
es(`wget -q ${url}`)
|
||||||
es(`tar -xf ${downloadFile}`)
|
es(`tar -xf ${downloadFile}`)
|
||||||
|
|
||||||
|
const usrBinDir = isDevMode() ? path.resolve(BLOCKCHAIN_DIR, 'bin') : '/usr/local/bin'
|
||||||
|
|
||||||
|
if (isDevMode()) {
|
||||||
|
makeDir.sync(usrBinDir)
|
||||||
|
}
|
||||||
|
|
||||||
if (_.isEmpty(binaries.files)) {
|
if (_.isEmpty(binaries.files)) {
|
||||||
es(`sudo cp ${binDir}/* /usr/local/bin`)
|
es(`sudo cp ${binDir}/* ${usrBinDir}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_.forEach(([source, target]) => {
|
_.forEach(([source, target]) => {
|
||||||
es(`sudo cp ${binDir}/${source} /usr/local/bin/${target}`)
|
es(`sudo cp ${binDir}/${source} ${usrBinDir}/${target}`)
|
||||||
}, binaries.files)
|
}, binaries.files)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ const _ = require('lodash/fp')
|
||||||
const { utils: coinUtils } = require('@lamassu/coins')
|
const { utils: coinUtils } = require('@lamassu/coins')
|
||||||
const settingsLoader = require('../new-settings-loader')
|
const settingsLoader = require('../new-settings-loader')
|
||||||
const wallet = require('../wallet')
|
const wallet = require('../wallet')
|
||||||
|
const { isDevMode, isRemoteNode, isRemoteWallet } = require('../environment-helper')
|
||||||
|
|
||||||
const common = require('./common')
|
const common = require('./common')
|
||||||
const doVolume = require('./do-volume')
|
const doVolume = require('./do-volume')
|
||||||
|
|
@ -30,7 +31,10 @@ const PLUGINS = {
|
||||||
|
|
||||||
const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR
|
const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR
|
||||||
|
|
||||||
module.exports = {run}
|
module.exports = {
|
||||||
|
isEnvironmentValid,
|
||||||
|
run
|
||||||
|
}
|
||||||
|
|
||||||
function installedVolumeFilePath (crypto) {
|
function installedVolumeFilePath (crypto) {
|
||||||
return path.resolve(coinUtils.cryptoDir(crypto, BLOCKCHAIN_DIR), '.installed')
|
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))
|
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)
|
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
|
if (isDevMode()) {
|
||||||
const backupDir = path.resolve(os.homedir(), 'backups')
|
_.forEach(setupCrypto, selectedCryptos)
|
||||||
const rsyncCmd = `( \
|
} else {
|
||||||
(crontab -l 2>/dev/null || echo -n "") | grep -v "@daily rsync ".*"wallet.dat"; \
|
const goodVolume = doVolume.prepareVolume()
|
||||||
echo "@daily rsync -r --prune-empty-dirs --include='*/' \
|
|
||||||
--include='wallet.dat' \
|
|
||||||
--exclude='*' ${blockchainDir} ${backupDir} > /dev/null" \
|
|
||||||
) | crontab -`
|
|
||||||
common.es(rsyncCmd)
|
|
||||||
|
|
||||||
_.forEach(c => {
|
if (!goodVolume) {
|
||||||
updateCrypto(c)
|
logger.error('There was an error preparing the disk volume. Exiting.')
|
||||||
common.es(`sudo supervisorctl start ${c.code}`)
|
process.exit(1)
|
||||||
}, selectedCryptos)
|
}
|
||||||
|
|
||||||
|
_.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.')
|
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) {
|
function setupCrypto (crypto) {
|
||||||
logger.info(`Installing ${crypto.display}...`)
|
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)
|
const cryptoDir = coinUtils.cryptoDir(crypto, BLOCKCHAIN_DIR)
|
||||||
makeDir.sync(cryptoDir)
|
makeDir.sync(cryptoDir)
|
||||||
const cryptoPlugin = plugin(crypto)
|
const cryptoPlugin = plugin(crypto)
|
||||||
const oldDir = process.cwd()
|
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)
|
makeDir.sync(tmpDir)
|
||||||
process.chdir(tmpDir)
|
process.chdir(tmpDir)
|
||||||
|
|
@ -97,7 +152,10 @@ function setupCrypto (crypto) {
|
||||||
|
|
||||||
cryptoPlugin.setup(cryptoDir)
|
cryptoPlugin.setup(cryptoDir)
|
||||||
|
|
||||||
common.writeFile(installedVolumeFilePath(crypto), '')
|
if (!isDevMode()) {
|
||||||
|
common.writeFile(installedVolumeFilePath(crypto), '')
|
||||||
|
}
|
||||||
|
|
||||||
process.chdir(oldDir)
|
process.chdir(oldDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,6 +179,8 @@ function plugin (crypto) {
|
||||||
function getBlockchainSyncStatus (cryptoList) {
|
function getBlockchainSyncStatus (cryptoList) {
|
||||||
return settingsLoader.loadLatest()
|
return settingsLoader.loadLatest()
|
||||||
.then(settings => {
|
.then(settings => {
|
||||||
|
if (isDevMode()) return new Array(_.size(cryptoList)).fill('ready')
|
||||||
|
|
||||||
const blockchainStatuses = _.reduce((acc, value) => {
|
const blockchainStatuses = _.reduce((acc, value) => {
|
||||||
const processStatus = common.es(`sudo supervisorctl status ${value.code} | awk '{ print $2 }'`).trim()
|
const processStatus = common.es(`sudo supervisorctl status ${value.code} | awk '{ print $2 }'`).trim()
|
||||||
return acc.then(a => {
|
return acc.then(a => {
|
||||||
|
|
@ -140,7 +200,9 @@ function getBlockchainSyncStatus (cryptoList) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isInstalled (crypto) {
|
function isInstalled (crypto) {
|
||||||
return isInstalledSoftware(crypto) && isInstalledVolume(crypto)
|
return isDevMode()
|
||||||
|
? isInstalledSoftware(crypto)
|
||||||
|
: isInstalledSoftware(crypto) && isInstalledVolume(crypto)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDisabled (crypto) {
|
function isDisabled (crypto) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,21 @@
|
||||||
const path = require('path')
|
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') })
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ const request = require('request-promise')
|
||||||
const { utils: coinUtils } = require('@lamassu/coins')
|
const { utils: coinUtils } = require('@lamassu/coins')
|
||||||
|
|
||||||
const logger = require('../../logger')
|
const logger = require('../../logger')
|
||||||
|
const { isRemoteNode, isRemoteWallet } = require('../../environment-helper')
|
||||||
|
const { isEnvironmentValid } = require('../../blockchain/install')
|
||||||
|
|
||||||
const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR
|
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')
|
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({
|
return axios({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
|
@ -109,15 +111,30 @@ function parseConf (confPath) {
|
||||||
|
|
||||||
function rpcConfig (cryptoRec) {
|
function rpcConfig (cryptoRec) {
|
||||||
try {
|
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 configPath = coinUtils.configPath(cryptoRec, BLOCKCHAIN_DIR)
|
||||||
const config = parseConf(configPath)
|
const config = parseConf(configPath)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
username: config.rpcuser,
|
username: config.rpcuser,
|
||||||
password: config.rpcpassword,
|
password: config.rpcpassword,
|
||||||
|
host: 'localhost',
|
||||||
port: config.rpcport || cryptoRec.defaultPort
|
port: config.rpcport || cryptoRec.defaultPort
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} 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 {
|
return {
|
||||||
port: cryptoRec.defaultPort
|
port: cryptoRec.defaultPort
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ const BN = require('../../../bn')
|
||||||
const E = require('../../../error')
|
const E = require('../../../error')
|
||||||
const logger = require('../../../logger')
|
const logger = require('../../../logger')
|
||||||
const { utils: coinUtils } = require('@lamassu/coins')
|
const { utils: coinUtils } = require('@lamassu/coins')
|
||||||
|
const { isDevMode } = require('../../../environment-helper')
|
||||||
|
|
||||||
const cryptoRec = coinUtils.getCryptoCurrency('BTC')
|
const cryptoRec = coinUtils.getCryptoCurrency('BTC')
|
||||||
const unitScale = cryptoRec.unitScale
|
const unitScale = cryptoRec.unitScale
|
||||||
|
|
@ -36,15 +37,15 @@ function checkCryptoCode (cryptoCode) {
|
||||||
|
|
||||||
function accountBalance (cryptoCode) {
|
function accountBalance (cryptoCode) {
|
||||||
return checkCryptoCode(cryptoCode)
|
return checkCryptoCode(cryptoCode)
|
||||||
.then(() => fetch('getwalletinfo'))
|
.then(() => fetch('getbalances'))
|
||||||
.then(({ balance }) => new BN(balance).shiftedBy(unitScale).decimalPlaces(0))
|
.then(({ mine }) => new BN(mine.trusted).shiftedBy(unitScale).decimalPlaces(0))
|
||||||
.catch(errorHandle)
|
.catch(errorHandle)
|
||||||
}
|
}
|
||||||
|
|
||||||
function accountUnconfirmedBalance (cryptoCode) {
|
function accountUnconfirmedBalance (cryptoCode) {
|
||||||
return checkCryptoCode(cryptoCode)
|
return checkCryptoCode(cryptoCode)
|
||||||
.then(() => fetch('getwalletinfo'))
|
.then(() => fetch('getbalances'))
|
||||||
.then(({ unconfirmed_balance: balance }) => new BN(balance).shiftedBy(unitScale).decimalPlaces(0))
|
.then(({ mine }) => new BN(mine.untrusted_pending).plus(mine.immature).shiftedBy(unitScale).decimalPlaces(0))
|
||||||
.catch(errorHandle)
|
.catch(errorHandle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,7 +63,7 @@ function estimateFee () {
|
||||||
|
|
||||||
function calculateFeeDiscount (feeMultiplier) {
|
function calculateFeeDiscount (feeMultiplier) {
|
||||||
// 0 makes bitcoind do automatic fee selection
|
// 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
|
if (!feeMultiplier || feeMultiplier.eq(1)) return AUTOMATIC_FEE
|
||||||
return estimateFee()
|
return estimateFee()
|
||||||
.then(estimatedFee => {
|
.then(estimatedFee => {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ const BN = require('../../../bn')
|
||||||
const E = require('../../../error')
|
const E = require('../../../error')
|
||||||
const _ = require('lodash/fp')
|
const _ = require('lodash/fp')
|
||||||
|
|
||||||
const ENV = process.env.NODE_ENV === undefined || process.env.NODE_ENV === 'development' ? 'development' : 'production'
|
|
||||||
const SUPPORTED_COINS = ['BTC']
|
const SUPPORTED_COINS = ['BTC']
|
||||||
|
|
||||||
const axios = require('axios').create({
|
const axios = require('axios').create({
|
||||||
|
|
|
||||||
19
migrations/1665418064066-set-node-env.js
Normal file
19
migrations/1665418064066-set-node-env.js
Normal file
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,8 @@ const setEnvVariable = require('./set-env-var')
|
||||||
|
|
||||||
fs.copyFileSync(path.resolve(__dirname, '../.sample.env'), path.resolve(__dirname, '../.env'))
|
fs.copyFileSync(path.resolve(__dirname, '../.sample.env'), path.resolve(__dirname, '../.env'))
|
||||||
|
|
||||||
|
setEnvVariable('NODE_ENV', 'development')
|
||||||
|
|
||||||
setEnvVariable('LAMASSU_DB', 'DEV')
|
setEnvVariable('LAMASSU_DB', 'DEV')
|
||||||
setEnvVariable('POSTGRES_USER', 'postgres')
|
setEnvVariable('POSTGRES_USER', 'postgres')
|
||||||
setEnvVariable('POSTGRES_PASSWORD', 'postgres123')
|
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('MNEMONIC_PATH', `${process.env.HOME}/.lamassu/mnemonics/mnemonic.txt`)
|
||||||
setEnvVariable('MIGRATE_STATE_PATH', `${process.env.HOME}/.lamassu/.migrate`)
|
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('OFAC_DATA_DIR', `${process.env.HOME}/.lamassu/ofac`)
|
||||||
setEnvVariable('ID_PHOTO_CARD_DIR', `${process.env.HOME}/.lamassu/idphotocard`)
|
setEnvVariable('ID_PHOTO_CARD_DIR', `${process.env.HOME}/.lamassu/idphotocard`)
|
||||||
setEnvVariable('FRONT_CAMERA_DIR', `${process.env.HOME}/.lamassu/frontcamera`)
|
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_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('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('HOSTNAME', 'localhost')
|
||||||
setEnvVariable('LOG_LEVEL', 'debug')
|
setEnvVariable('LOG_LEVEL', 'debug')
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ if (!_.isEqual(_.intersection(_.keys(argv), requiredParams), requiredParams)) {
|
||||||
|
|
||||||
fs.copyFileSync(path.resolve(__dirname, '../.sample.env'), path.resolve('/etc', 'lamassu', '.env'))
|
fs.copyFileSync(path.resolve(__dirname, '../.sample.env'), path.resolve('/etc', 'lamassu', '.env'))
|
||||||
|
|
||||||
|
setEnvVariable('NODE_ENV', 'production')
|
||||||
|
|
||||||
setEnvVariable('POSTGRES_USER', 'lamassu_pg')
|
setEnvVariable('POSTGRES_USER', 'lamassu_pg')
|
||||||
setEnvVariable('POSTGRES_PASSWORD', `${argv['db-password']}`)
|
setEnvVariable('POSTGRES_PASSWORD', `${argv['db-password']}`)
|
||||||
setEnvVariable('POSTGRES_HOST', 'localhost')
|
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_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('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('HOSTNAME', `${argv.hostname}`)
|
||||||
setEnvVariable('LOG_LEVEL', 'info')
|
setEnvVariable('LOG_LEVEL', 'info')
|
||||||
|
|
|
||||||
32
tools/migrate-env.js
Normal file
32
tools/migrate-env.js
Normal file
|
|
@ -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
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const os = require('os')
|
const os = require('os')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const _ = require('lodash/fp')
|
||||||
|
|
||||||
const setEnvVariable = (key, value) => {
|
const setEnvVariable = (key, value, opts) => {
|
||||||
const ENV_PATH = path.resolve(__dirname, '../.env')
|
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 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}=`))))
|
const target = ENV_VARIABLES.indexOf(ENV_VARIABLES.find(line => line.match(new RegExp(`^${key}=`))))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue