add blockchain install scripts

This commit is contained in:
Josh Harvey 2017-07-01 16:02:46 +03:00
parent 0e9e27b97b
commit 178f576cfb
39 changed files with 3938 additions and 750 deletions

3
.gitignore vendored
View file

@ -33,3 +33,6 @@ scratch/
seeds/
certs/
lamassu.json
terraform.*
.terraform

8
Vagrantfile vendored
View file

@ -11,7 +11,7 @@ Vagrant.configure(2) do |config|
# vagrantup.com
# Every Vagrant virtual environment requires a box to build off of.
config.vm.box = "ubuntu/trusty64"
config.vm.box = "ubuntu/xenial64"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
@ -46,13 +46,13 @@ Vagrant.configure(2) do |config|
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
# config.vm.provider "virtualbox" do |vb|
config.vm.provider "virtualbox" do |vb|
# # Don't boot with headless mode
# vb.gui = true
#
# # Use VBoxManage to customize the VM. For example to change memory:
# vb.customize ["modifyvm", :id, "--memory", "1024"]
# end
vb.customize ["modifyvm", :id, "--memory", "4096"]
end
#
# View the documentation for the provider you're using for more
# information on available options.

54
blockchain/blockchain.tf Normal file
View file

@ -0,0 +1,54 @@
variable "name" {}
variable "blockchain_cmd" {}
variable "blockchain_conf" {}
variable "ssh_key" {}
variable "size" {
default = "2gb"
}
variable "blockchain_dir" {
default = "./scratch/blockchains"
}
data "template_file" "supervisor_conf" {
template = "${file("./blockchains/supervisor.conf")}"
vars {
blockchain = "${var.name}"
blockchain_cmd = "${var.blockchain_cmd}"
}
}
resource "digitalocean_droplet" "blockchain_server" {
image = "debian-9-x64"
name = "${var.name}"
region = "ams2"
size = "${var.size}"
ssh_keys = ["${var.ssh_key}"]
connection {
type = "ssh"
user = "root"
private_key = "${file("${pathexpand("~/.ssh/id_rsa")}")}"
}
provisioner "file" {
content = "${data.template_file.supervisor_conf.rendered}"
destination = "/tmp/supervisor-${var.name}.conf"
}
provisioner "file" {
source = "${var.blockchain_dir}/${var.blockchain_conf}"
destination = "/tmp/${var.blockchain_conf}"
}
provisioner "remote-exec" {
script = "./blockchains/${var.name}/install.sh"
}
}
output "ip_address" {
value = "${digitalocean_droplet.blockchain_server.ipv4_address}"
}

View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -e
LOG_FILE=/tmp/install.log
apt-get update -y >> $LOG_FILE 2>&1
apt-get install -y ufw supervisor >> $LOG_FILE 2>&1
ufw allow 8332 >> $LOG_FILE 2>&1
ufw allow 22 >> $LOG_FILE 2>&1
ufw --force enable >> $LOG_FILE 2>&1
ufw status >> $LOG_FILE 2>&1
wget -q https://bitcoin.org/bin/bitcoin-core-0.14.2/bitcoin-0.14.2-x86_64-linux-gnu.tar.gz >> $LOG_FILE 2>&1
tar -xzf bitcoin-0.14.2-x86_64-linux-gnu.tar.gz >> $LOG_FILE 2>&1
cp bitcoin-0.14.2/bin/* /usr/local/bin >> $LOG_FILE 2>&1
mkdir ~/.bitcoin >> $LOG_FILE 2>&1
mv /tmp/bitcoin.conf ~/.bitcoin
mv /tmp/supervisor-bitcoin.conf /etc/supervisor/conf.d/bitcoin.conf >> $LOG_FILE 2>&1
service supervisor restart >> $LOG_FILE 2>&1

View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -e
LOG_FILE=/tmp/install.log
apt-get update -y >> $LOG_FILE 2>&1
apt-get install -y ufw supervisor >> $LOG_FILE 2>&1
ufw allow 8332 >> $LOG_FILE 2>&1
ufw allow 22 >> $LOG_FILE 2>&1
ufw --force enable >> $LOG_FILE 2>&1
ufw status >> $LOG_FILE 2>&1
wget -q https://www.dash.org/binaries/dashcore-0.12.1.5-linux64.tar.gz >> $LOG_FILE 2>&1
tar -xzf dashcore-0.12.1.5-linux64.tar.gz >> $LOG_FILE 2>&1
cp dashcore-0.12.1/bin/* /usr/local/bin >> $LOG_FILE 2>&1
mkdir ~/.dash >> $LOG_FILE 2>&1
mv /tmp/dash.conf ~/.bitocoin
mv /tmp/supervisor-dash.conf /etc/supervisor/conf.d/dash.conf >> $LOG_FILE 2>&1
service supervisor restart >> $LOG_FILE 2>&1

View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -e
LOG_FILE=/tmp/install.log
apt-get update -y >> $LOG_FILE 2>&1
apt-get install -y ufw supervisor >> $LOG_FILE 2>&1
ufw allow 8332 >> $LOG_FILE 2>&1
ufw allow 22 >> $LOG_FILE 2>&1
ufw --force enable >> $LOG_FILE 2>&1
ufw status >> $LOG_FILE 2>&1
wget -q https://download.litecoin.org/litecoin-0.13.2/linux/litecoin-0.13.2-x86_64-linux-gnu.tar.gz >> $LOG_FILE 2>&1
tar -xzf litecoin-0.13.2-x86_64-linux-gnu.tar.gz >> $LOG_FILE 2>&1
cp litecoin-0.13.2/bin/* /usr/local/bin >> $LOG_FILE 2>&1
mkdir ~/.litecoin >> $LOG_FILE 2>&1
mv /tmp/litecoin.conf ~/.litecoin
mv /tmp/supervisor-litecoin.conf /etc/supervisor/conf.d/litecoin.conf >> $LOG_FILE 2>&1
service supervisor restart >> $LOG_FILE 2>&1

View file

@ -0,0 +1,7 @@
[program:${blockchain}]
command=/usr/local/bin/${blockchain_cmd} -datadir=/mnt/blockchain/${blockchain}
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/${blockchain}.err.log
stdout_logfile=/var/log/supervisor/${blockchain}.out.log
environment=HOME="/root"

7
blockchains/tunnel.conf Normal file
View file

@ -0,0 +1,7 @@
[program:${blockchain}-tunnel]
command=autossh -L 8232:localhost:8232 -o "ExitOnForwardFailure yes" -fN -o "ServerAliveInterval 45" -o "ServerAliveCountMax 2" root@188.226.149.173
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/${blockchain}-tunnel.err.log
stdout_logfile=/var/log/supervisor/${blockchain}-tunnel.out.log
environment="AUTOSSH_GATETIME=0"

View file

@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -e
LOG_FILE=/tmp/install.log
apt-get update -y >> $LOG_FILE 2>&1
apt-get install -y ufw supervisor libgomp1 >> $LOG_FILE 2>&1
ufw allow 8233 >> $LOG_FILE 2>&1
ufw allow 22 >> $LOG_FILE 2>&1
ufw --force enable >> $LOG_FILE 2>&1
ufw status >> $LOG_FILE 2>&1
wget -q https://z.cash/downloads/zcash-1.0.10-1-linux64.tar.gz >> $LOG_FILE 2>&1
tar -xzf zcash-1.0.10-1-linux64.tar.gz >> $LOG_FILE 2>&1
cp zcash-1.0.10-1/bin/* /usr/local/bin >> $LOG_FILE 2>&1
zcash-fetch-params >> $LOG_FILE 2>&1
mkdir ~/.zcash >> $LOG_FILE 2>&1
mv /tmp/zcash.conf ~/.zcash
mv /tmp/supervisor-zcash.conf /etc/supervisor/conf.d/zcash.conf >> $LOG_FILE 2>&1
service supervisor restart >> $LOG_FILE 2>&1

View file

View file

@ -0,0 +1,20 @@
#!/usr/bin/env node
const path = require('path')
const crypto = require('crypto')
const fs = require('fs')
const options = require('../../lib/options')
const blockchainDir = options.blockchainDir
const confPath = path.resolve(blockchainDir, 'zcash.conf')
const password = crypto.randomBytes(32).toString('hex')
const conf = `rpcuser=username
rpcpassword=${password}
mainnet=1
addnode=mainnet.z.cash
dbcache=500
`
fs.writeFileSync(confPath, conf)

View file

@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e
LOG_FILE=/tmp/install.log
wget -q $UPGRADE_URL >> $LOG_FILE 2>&1
tar -xzf $UPGRADE_PACKAGE >> $LOG_FILE 2>&1
supervisorctl stop zcash >> $LOG_FILE 2>&1
cp $UPGRADE_PACKAGE/bin/* /usr/local/bin/ >> $LOG_FILE 2>&1
supervisorctl start zcash >> $LOG_FILE 2>&1

View file

@ -0,0 +1,4 @@
rpcuser=username
mainnet=1
addnode=mainnet.z.cash
dbcache=500

View file

@ -1,12 +1,44 @@
const _ = require('lodash/fp')
const BN = require('../bn')
const settingsLoader = require('../settings-loader')
const configManager = require('../config-manager')
const wallet = require('../wallet')
const ticker = require('../ticker')
const coinUtils = require('../coin-utils')
const machineLoader = require('../machine-loader')
module.exports = {getFunding}
function allScopes (cryptoScopes, machineScopes) {
const scopes = []
cryptoScopes.forEach(c => {
machineScopes.forEach(m => scopes.push([c, m]))
})
return scopes
}
function allMachineScopes (machineList, machineScope) {
const machineScopes = []
if (machineScope === 'global' || machineScope === 'both') machineScopes.push('global')
if (machineScope === 'specific' || machineScope === 'both') machineList.forEach(r => machineScopes.push(r))
return machineScopes
}
function getCryptos (config, machineList) {
const scopes = allScopes(['global'], allMachineScopes(machineList, 'both'))
const scoped = scope => configManager.scopedValue(scope[0], scope[1], 'cryptoCurrencies', config)
return _.uniq(_.flatten(_.map(scoped, scopes)))
}
function fetchMachines () {
return machineLoader.getMachines()
.then(machineList => machineList.map(r => r.deviceId))
}
function computeCrypto (cryptoCode, _balance) {
const unitScale = coinUtils.coins[cryptoCode].unitScale
@ -19,15 +51,17 @@ function computeFiat (rate, cryptoCode, _balance) {
return BN(_balance).shift(-unitScale).mul(rate).round(5)
}
function getFunding (cryptoCode) {
cryptoCode = cryptoCode || 'BTC'
const cryptoDisplays = coinUtils.cryptoDisplays
function getFunding (_cryptoCode) {
return Promise.all([settingsLoader.loadLatest(), fetchMachines()])
.then(([settings, machineList]) => {
const config = configManager.unscoped(settings.config)
const cryptoCodes = getCryptos(settings.config, machineList)
const cryptoCode = _cryptoCode || cryptoCodes[0]
const fiatCode = config.fiatCurrency
const pareCoins = c => _.includes(c.cryptoCode, cryptoCodes)
const cryptoDisplays = _.filter(pareCoins, coinUtils.cryptoDisplays)
if (!coinUtils.coins[cryptoCode]) throw new Error(`Unsupported coin: ${cryptoCode}`)
return settingsLoader.loadLatest()
.then(settings => {
const config = configManager.unscoped(settings.config)
const fiatCode = config.fiatCurrency
const promises = [
wallet.newFunding(settings, cryptoCode),

33
lib/blockchain/bitcoin.js Normal file
View file

@ -0,0 +1,33 @@
const fs = require('fs')
const path = require('path')
const coinUtils = require('../coin-utils')
const common = require('./common')
module.exports = {setup}
const es = common.es
function setup (dataDir) {
const coinRec = coinUtils.getCryptoCurrency('BTC')
common.firewall([coinRec.defaultPort])
const config = buildConfig()
fs.writeFileSync(path.resolve(dataDir, coinRec.configFile), config)
setupPm2(dataDir)
}
function buildConfig () {
return `rpcuser=lamassuserver
rpcpassword=${common.randomPass()}
dbcache=500
server=1
connections=40
keypool=10000
prune=4000
daemon=0`
}
function setupPm2 (dataDir) {
es(`pm2 start /usr/local/bin/bitcoind -- -datadir=${dataDir}`)
}

61
lib/blockchain/common.js Normal file
View file

@ -0,0 +1,61 @@
const crypto = require('crypto')
const os = require('os')
const path = require('path')
const cp = require('child_process')
const logger = require('console-log-level')({level: 'info'})
module.exports = {es, firewall, randomPass, fetchAndInstall, logger}
const BINARIES = {
BTC: {
url: 'https://bitcoin.org/bin/bitcoin-core-0.14.2/bitcoin-0.14.2-x86_64-linux-gnu.tar.gz',
dir: 'bitcoin-0.14.2/bin'
},
ETH: {
url: 'https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.6.6-10a45cb5.tar.gz',
dir: 'geth-linux-amd64-1.6.6-10a45cb5'
},
ZEC: {
url: 'https://z.cash/downloads/zcash-1.0.10-1-linux64.tar.gz',
dir: 'zcash-1.0.10-1/bin'
},
DASH: {
url: 'https://www.dash.org/binaries/dashcore-0.12.1.5-linux64.tar.gz',
dir: 'dashcore-0.12.1/bin'
},
LTC: {
url: 'https://download.litecoin.org/litecoin-0.13.2/linux/litecoin-0.13.2-x86_64-linux-gnu.tar.gz',
dir: 'litecoin-0.13.2/bin'
}
}
function firewall (ports) {
if (!ports || ports.length === 0) throw new Error('No ports supplied')
const portsString = ports.join(',')
es(`sudo ufw allow ${portsString}`)
}
function randomPass () {
return crypto.randomBytes(32).toString('hex')
}
function es (cmd) {
const env = {HOME: os.userInfo().homedir}
const options = {encoding: 'utf8', env}
const res = cp.execSync(cmd, options)
logger.debug(res)
return res.toString()
}
function fetchAndInstall (crypto) {
const binaries = BINARIES[crypto.cryptoCode]
if (!binaries) throw new Error(`No such coin: ${crypto.code}`)
const url = binaries.url
const downloadFile = path.basename(url)
const binDir = binaries.dir
es(`wget -q ${url}`)
es(`tar -xzf ${downloadFile}`)
es(`sudo cp ${binDir}/* /usr/local/bin`)
}

28
lib/blockchain/dash.js Normal file
View file

@ -0,0 +1,28 @@
const fs = require('fs')
const path = require('path')
const coinUtils = require('../coin-utils')
const common = require('./common')
module.exports = {setup}
const es = common.es
function setup (dataDir) {
const coinRec = coinUtils.getCryptoCurrency('DASH')
common.firewall([coinRec.defaultPort])
const config = buildConfig()
fs.writeFileSync(path.resolve(dataDir, coinRec.configFile), config)
setupPm2(dataDir)
}
function buildConfig () {
return `rpcuser=lamassuserver
rpcpassword=${common.randomPass()}
dbcache=500`
}
function setupPm2 (dataDir) {
es(`pm2 start /usr/local/bin/dashd -- -datadir=${dataDir}`)
}

0
lib/blockchain/dashd.js Normal file
View file

View file

@ -0,0 +1,67 @@
const fs = require('fs')
const common = require('./common')
const MOUNT_POINT = '/mnt/blockchains'
module.exports = {prepareVolume}
const logger = common.logger
function isMounted () {
return fs.existsSync(MOUNT_POINT)
}
function isFormatted (volumePath) {
const res = common.es(`file --dereference -s ${volumePath}`)
return res !== `${volumePath}: data`
}
function formatVolume (volumePath) {
if (isFormatted(volumePath)) {
logger.info('Volume is already formatted.')
return
}
logger.info('Formatting...')
common.es(`sudo mkfs.ext4 ${volumePath}`)
}
function mountVolume (volumePath) {
if (isMounted()) {
logger.info('Volume is already mounted.')
return
}
logger.info('Mounting...')
common.es(`sudo mkdir -p ${MOUNT_POINT}`)
common.es(`sudo mount -o discard,defaults ${volumePath} ${MOUNT_POINT}`)
common.es(`echo ${volumePath} ${MOUNT_POINT} ext4 defaults,nofail,discard 0 0 | sudo tee -a /etc/fstab`)
}
function locateVolume () {
const res = common.es('ls /dev/disk/by-id/*')
const lines = res.trim().split('\n')
if (lines.length > 1) {
logger.error('More than one volume present, cannot prepare.')
return null
}
if (lines.length === 0) {
logger.error('No available volumes. You might need to attach one.')
return null
}
return lines[0].trim()
}
function prepareVolume () {
const volumePath = locateVolume()
if (!volumePath) return false
formatVolume(volumePath)
mountVolume(volumePath)
return true
}

View file

@ -0,0 +1,17 @@
const coinUtils = require('../coin-utils')
const common = require('./common')
module.exports = {setup}
const es = common.es
function setup (dataDir) {
const coinRec = coinUtils.getCryptoCurrency('ETH')
common.firewall([coinRec.defaultPort])
setupPm2(dataDir)
}
function setupPm2 (dataDir) {
es(`pm2 start /usr/local/bin/geth -- --datadir "${dataDir}" --cache 500`)
}

95
lib/blockchain/install.js Normal file
View file

@ -0,0 +1,95 @@
const fs = require('fs')
const path = require('path')
const process = require('process')
const makeDir = require('make-dir')
const inquirer = require('inquirer')
const _ = require('lodash/fp')
const options = require('../options')
const coinUtils = require('../coin-utils')
const common = require('./common')
const doVolume = require('./do-volume')
const cryptos = coinUtils.cryptoCurrencies()
const logger = common.logger
const PLUGINS = {
BTC: require('./bitcoin.js'),
LTC: require('./litecoin.js'),
ETH: require('./ethereum.js'),
DASH: require('./dash.js'),
ZEC: require('./zcash.js')
}
function installedFilePath (crypto) {
return path.resolve(options.blockchainDir, crypto.code, '.installed')
}
function isInstalled (crypto) {
return fs.existsSync(installedFilePath(crypto))
}
const choices = _.map(c => {
const checked = isInstalled(c)
return {
name: c.display,
value: c.code,
checked,
disabled: checked && 'Installed'
}
}, cryptos)
const questions = []
questions.push({
type: 'checkbox',
name: 'crypto',
message: 'Which cryptocurrencies would you like to install?',
choices
})
function processCryptos (codes) {
if (_.isEmpty(codes)) {
logger.info('No cryptos selected. Exiting.')
process.exit(0)
}
doVolume.prepareVolume()
logger.info('Thanks! Installing: %s. Will take a while...', _.join(', ', codes))
const selectedCryptos = _.map(code => _.find(['code', code], cryptos), codes)
_.forEach(setupCrypto, selectedCryptos)
common.es('pm2 save')
logger.info('Installation complete.')
}
inquirer.prompt(questions)
.then(answers => processCryptos(answers.crypto))
function plugin (crypto) {
const plugin = PLUGINS[crypto.cryptoCode]
if (!plugin) throw new Error(`No such plugin: ${crypto.cryptoCode}`)
return plugin
}
function setupCrypto (crypto) {
logger.info(`Installing ${crypto.display}...`)
const cryptoDir = path.resolve(options.blockchainDir, crypto.code)
makeDir.sync(cryptoDir)
const cryptoPlugin = plugin(crypto)
const oldDir = process.cwd()
const tmpDir = '/tmp/blockchain-install'
makeDir.sync(tmpDir)
process.chdir(tmpDir)
common.es('rm -rf *')
common.fetchAndInstall(crypto)
cryptoPlugin.setup(cryptoDir)
fs.writeFileSync(installedFilePath(crypto), '')
process.chdir(oldDir)
}

View file

@ -0,0 +1,33 @@
const fs = require('fs')
const path = require('path')
const coinUtils = require('../coin-utils')
const common = require('./common')
module.exports = {setup}
const es = common.es
function setup (dataDir) {
const coinRec = coinUtils.getCryptoCurrency('LTC')
common.firewall([coinRec.defaultPort])
const config = buildConfig()
fs.writeFileSync(path.resolve(dataDir, coinRec.configFile), config)
setupPm2(dataDir)
}
function buildConfig () {
return `rpcuser=lamassuserver
rpcpassword=${common.randomPass()}
dbcache=500
server=1
connections=40
keypool=10000
prune=4000
daemon=0`
}
function setupPm2 (dataDir) {
es(`pm2 start /usr/local/bin/litecoind -- -datadir=${dataDir}`)
}

37
lib/blockchain/zcash.js Normal file
View file

@ -0,0 +1,37 @@
const fs = require('fs')
const path = require('path')
const coinUtils = require('../coin-utils')
const common = require('./common')
module.exports = {setup}
const es = common.es
const logger = common.logger
function setup (dataDir) {
es('sudo apt-get update')
es('sudo apt-get install libgomp1 -y')
const coinRec = coinUtils.getCryptoCurrency('ZEC')
common.firewall([coinRec.defaultPort])
logger.info('Fetching Zcash proofs, will take a while...')
es('zcash-fetch-params 2>&1')
logger.info('Finished fetching proofs.')
const config = buildConfig()
fs.writeFileSync(path.resolve(dataDir, 'zcash.conf'), config)
setupPm2(dataDir)
}
function buildConfig () {
return `mainnet=1
addnode=mainnet.z.cash
rpcuser=lamassuserver
rpcpassword=${common.randomPass()}
dbcache=500`
}
function setupPm2 (dataDir) {
es(`pm2 start /usr/local/bin/zcashd -- -datadir=${dataDir}`)
}

0
lib/blockchain/zcashd.js Normal file
View file

View file

@ -1,22 +1,64 @@
const _ = require('lodash/fp')
const coins = {
BTC: {unitScale: 8},
ETH: {unitScale: 18},
ZEC: {unitScale: 8},
LTC: {unitScale: 8},
DASH: {unitScale: 8}
const CRYPTO_CURRENCIES = [
{
cryptoCode: 'BTC',
display: 'Bitcoin',
code: 'bitcoin',
configFile: 'bitcoin.conf',
daemon: 'bitcoind',
defaultPort: 8332,
unitScale: 8
},
{
cryptoCode: 'ETH',
display: 'Ethereum',
code: 'ethereum',
configFile: 'geth.conf',
daemon: 'geth',
defaultPort: 8545,
unitScale: 18
},
{
cryptoCode: 'LTC',
display: 'Litecoin',
code: 'litecoin',
configFile: 'litecoin.conf',
daemon: 'litecoind',
defaultPort: 9332,
unitScale: 8
},
{
cryptoCode: 'DASH',
display: 'Dash',
code: 'dash',
configFile: 'dash.conf',
daemon: 'dashd',
defaultPort: 9998,
unitScale: 8
},
{
cryptoCode: 'ZEC',
display: 'Zcash',
code: 'zcash',
configFile: 'zcash.conf',
daemon: 'zcashd',
defaultPort: 8232,
unitScale: 8
}
const cryptoDisplays = [
{cryptoCode: 'BTC', display: 'Bitcoin'},
{cryptoCode: 'ETH', display: 'Ethereum'},
{cryptoCode: 'ZEC', display: 'Zcash'},
{cryptoCode: 'LTC', display: 'Litecoin'},
{cryptoCode: 'DASH', display: 'Dash'}
]
module.exports = {coins, cryptoDisplays, buildUrl, unitScale, display}
module.exports = {buildUrl, cryptoCurrencies, getCryptoCurrency}
function getCryptoCurrency (cryptoCode) {
const cryptoCurrency = _.find(['cryptoCode', cryptoCode], CRYPTO_CURRENCIES)
if (!cryptoCurrency) throw new Error(`Unsupported crypto: ${cryptoCode}`)
return cryptoCurrency
}
function cryptoCurrencies () {
return CRYPTO_CURRENCIES
}
function buildUrl (cryptoCode, address) {
switch (cryptoCode) {
@ -28,15 +70,3 @@ function buildUrl (cryptoCode, address) {
default: throw new Error(`Unsupported crypto: ${cryptoCode}`)
}
}
function display (cryptoCode) {
const rec = _.find(['cryptoCode', cryptoCode], cryptoDisplays)
if (!rec) throw new Error(`Unsupported crypto: ${cryptoCode}`)
return rec.display
}
function unitScale (cryptoCode) {
const scaleRec = coins[cryptoCode]
if (!scaleRec) throw new Error(`Unsupported crypto: ${cryptoCode}`)
return scaleRec.unitScale
}

View file

@ -6,13 +6,13 @@ const argv = require('minimist')(process.argv.slice(2))
let serverConfig
try {
const homeConfigPath = path.resolve(os.homedir(), '.lamassu', 'lamassu.json')
serverConfig = JSON.parse(fs.readFileSync(homeConfigPath))
} catch (_) {
try {
const globalConfigPath = path.resolve('/etc', 'lamassu', 'lamassu.json')
serverConfig = JSON.parse(fs.readFileSync(globalConfigPath))
} catch (_) {
try {
const homeConfigPath = path.resolve(os.homedir(), '.lamassu', 'lamassu.json')
serverConfig = JSON.parse(fs.readFileSync(homeConfigPath))
} catch (_) {
console.error("Couldn't open lamassu.json config file.")
process.exit(1)

View file

@ -171,7 +171,9 @@ function plugins (settings, deviceId) {
.then(row => row.id)
}
function mapCoinSettings (coin) {
function mapCoinSettings (coinParams) {
const coin = coinParams[0]
const cryptoNetwork = coinParams[1]
const config = configManager.scoped(coin, deviceId, settings.config)
const minimumTx = BN(config.minimumTx)
const cashInFee = BN(config.cashInFee)
@ -180,7 +182,8 @@ function plugins (settings, deviceId) {
cryptoCode: coin,
display: coinUtils.display(coin),
minimumTx: BN.max(minimumTx, cashInFee),
cashInFee: cashInFee
cashInFee: cashInFee,
cryptoNetwork
}
}
@ -191,6 +194,7 @@ function plugins (settings, deviceId) {
const tickerPromises = cryptoCodes.map(c => ticker.getRates(settings, fiatCode, c))
const balancePromises = cryptoCodes.map(c => fiatBalance(fiatCode, c))
const testnetPromises = cryptoCodes.map(c => wallet.cryptoNetwork(settings, c))
const pingPromise = recordPing(serialNumber, deviceTime, deviceRec)
const currentConfigVersionPromise = fetchCurrentConfigVersion()
@ -198,20 +202,25 @@ function plugins (settings, deviceId) {
buildAvailableCassettes(),
pingPromise,
currentConfigVersionPromise
].concat(tickerPromises, balancePromises)
].concat(tickerPromises, balancePromises, testnetPromises)
return Promise.all(promises)
.then(arr => {
const cassettes = arr[0]
const configVersion = arr[2]
const tickers = arr.slice(3, cryptoCodes.length + 3)
const balances = arr.slice(cryptoCodes.length + 3)
const cryptoCodesCount = cryptoCodes.length
const tickers = arr.slice(3, cryptoCodesCount + 3)
const balances = arr.slice(cryptoCodesCount + 3, 2 * cryptoCodesCount + 3)
const testNets = arr.slice(2 * cryptoCodesCount + 3)
const coinParams = _.zip(cryptoCodes, testNets)
console.log('DEBUG200: %j', cryptoCodes)
console.log('DEBUG201: %j', coinParams)
return {
cassettes,
rates: buildRates(tickers),
balances: buildBalances(balances),
coins: _.map(mapCoinSettings, cryptoCodes),
coins: _.map(mapCoinSettings, coinParams),
configVersion
}
})

View file

@ -23,18 +23,27 @@ function fetch (account, method, params) {
return r.data.result
})
.catch(err => {
console.log(err.message)
try {
console.log(err.response.data.error)
} catch (__) {}
throw err
})
}
function split (str) {
const i = str.indexOf('=')
if (i === -1) return []
return [str.slice(0, i), str.slice(i + 1)]
}
function parseConf (confPath) {
const conf = fs.readFileSync(confPath)
const lines = conf.toString().split('\n', 2)
const lines = conf.toString().split('\n')
const res = {}
for (let i = 0; i < lines.length; i++) {
const keyVal = lines[i].split('=')
const keyVal = split(lines[i])
// skip when value is empty
if (!keyVal[1]) continue

View file

@ -39,11 +39,10 @@ function balance (account, cryptoCode) {
}
function sendCoins (account, address, cryptoAtoms, cryptoCode) {
const confirmations = 1
const bitcoins = cryptoAtoms.shift(-SATOSHI_SHIFT).toFixed(8)
const coins = cryptoAtoms.shift(-SATOSHI_SHIFT).toFixed(8)
return checkCryptoCode(cryptoCode)
.then(() => fetch('sendfrom', [address, bitcoins, confirmations]))
.then(() => fetch('sendtoaddress', [address, coins]))
.catch(err => {
if (err.code === -6) throw new E.InsufficientFundsError()
throw err
@ -103,10 +102,16 @@ function newFunding (account, cryptoCode) {
}))
}
function cryptoNetwork (account, cryptoCode) {
return checkCryptoCode(cryptoCode)
.then(() => parseInt(rpcConfig.port, 10) === 18332 ? 'test' : 'main')
}
module.exports = {
balance,
sendCoins,
newAddress,
getStatus,
newFunding
newFunding,
cryptoNetwork
}

View file

@ -103,11 +103,17 @@ function newFunding (account, cryptoCode) {
})
}
function cryptoNetwork (account, cryptoCode) {
return checkCryptoCode(cryptoCode)
.then(() => account.environment === 'test' ? 'test' : 'main')
}
module.exports = {
NAME,
balance,
sendCoins,
newAddress,
getStatus,
newFunding
newFunding,
cryptoNetwork
}

View file

@ -22,7 +22,8 @@ module.exports = {
sweep,
defaultAddress,
supportsHd: true,
newFunding
newFunding,
privateKey
}
if (!web3.isConnected()) {
@ -31,6 +32,10 @@ if (!web3.isConnected()) {
const hex = bigNum => '0x' + bigNum.truncated().toString(16)
function privateKey (account) {
return defaultWallet(account).getPrivateKey()
}
function sendCoins (account, toAddress, cryptoAtoms, cryptoCode) {
return generateTx(toAddress, defaultWallet(account), cryptoAtoms, false)
.then(_.tap(r => console.log('DEBUG113: %s', r)))

View file

@ -18,6 +18,9 @@ const rpcConfig = {
port: config.rpcport || DEFAULT_PORT
}
console.log('DEBUG101: %j', configPath)
console.log('DEBUG100: %j', rpcConfig)
function fetch (method, params) {
return jsonRpc.fetch(rpcConfig, method, params)
}
@ -40,11 +43,10 @@ function balance (account, cryptoCode) {
}
function sendCoins (account, address, cryptoAtoms, cryptoCode) {
const confirmations = 1
const bitcoins = cryptoAtoms.shift(-SATOSHI_SHIFT).toFixed(8)
const coins = cryptoAtoms.shift(-SATOSHI_SHIFT).toFixed(8)
return checkCryptoCode(cryptoCode)
.then(() => fetch('sendfrom', [address, bitcoins, confirmations]))
.then(() => fetch('sendtoaddress', [address, coins]))
.catch(err => {
if (err.code === -6) throw new E.InsufficientFundsError()
throw err

View file

@ -40,11 +40,10 @@ function balance (account, cryptoCode) {
}
function sendCoins (account, address, cryptoAtoms, cryptoCode) {
const confirmations = 1
const bitcoins = cryptoAtoms.shift(-SATOSHI_SHIFT).toFixed(8)
const coins = cryptoAtoms.shift(-SATOSHI_SHIFT).toFixed(8)
return checkCryptoCode(cryptoCode)
.then(() => fetch('sendfrom', [address, bitcoins, confirmations]))
.then(() => fetch('sendtoaddress', [address, coins]))
.catch(err => {
if (err.code === -6) throw new E.InsufficientFundsError()
throw err

View file

@ -139,6 +139,15 @@ function isHd (settings, cryptoCode) {
.then(r => r.wallet.supportsHd)
}
function cryptoNetwork (settings, cryptoCode) {
const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet
const wallet = ph.load(ph.WALLET, plugin)
const account = settings.accounts[plugin]
if (!wallet.cryptoNetwork) return Promise.resolve(false)
return wallet.cryptoNetwork(account, cryptoCode)
}
module.exports = {
balance: mem(balance, {maxAge: FETCH_INTERVAL}),
sendCoins,
@ -146,5 +155,6 @@ module.exports = {
getStatus,
sweep,
isHd,
newFunding
newFunding,
cryptoNetwork
}

37
main.tf Normal file
View file

@ -0,0 +1,37 @@
module "zcash" {
source = "./blockchain"
name = "zcash"
blockchain_cmd = "zcashd"
blockchain_conf = "zcash.conf"
ssh_key = "${digitalocean_ssh_key.default.id}"
}
module "litecoin" {
source = "./blockchain"
name = "litecoin"
blockchain_cmd = "litecoind"
blockchain_conf = "litecoin.conf"
ssh_key = "${digitalocean_ssh_key.default.id}"
}
module "dash" {
source = "./blockchain"
name = "dash"
blockchain_cmd = "dashd"
blockchain_conf = "dash.conf"
ssh_key = "${digitalocean_ssh_key.default.id}"
}
module "bitcoin" {
source = "./blockchain"
name = "bitcoin"
blockchain_cmd = "bitcoind"
blockchain_conf = "bitcoin.conf"
ssh_key = "${digitalocean_ssh_key.default.id}"
}
resource "digitalocean_ssh_key" "default" {
name = "Lamassu Server"
public_key = "${file("${pathexpand("~/.ssh/id_rsa.pub")}")}"
}

3707
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,7 @@
"bitcoind-rpc": "^0.7.0",
"bitgo": "^3.4.0",
"body-parser": "^1.15.1",
"console-log-level": "^1.4.0",
"cookie-parser": "^1.4.3",
"ethereumjs-tx": "^1.3.0",
"ethereumjs-wallet": "^0.6.0",
@ -23,8 +24,10 @@
"express-ws": "^3.0.0",
"got": "^7.0.0",
"helmet": "^3.6.1",
"inquirer": "^3.1.1",
"kraken-api": "DeX3/npm-kraken-api",
"lodash": "^4.17.2",
"make-dir": "^1.0.0",
"mem": "^1.1.0",
"migrate": "^0.2.2",
"minimist": "^1.2.0",
@ -68,6 +71,7 @@
"lamassu-nuke-db": "./bin/lamassu-nuke-db"
},
"scripts": {
"start": "node bin/lamassu-server",
"test": "ava"
},
"devDependencies": {

View file

@ -21384,6 +21384,12 @@ var _pablohirafuji$elm_qrcode$QRCode_View$rectView = function (_p1) {
ctor: '::',
_0: _elm_lang$svg$Svg_Attributes$y(
_elm_lang$core$Basics$toString(_p2.row * _pablohirafuji$elm_qrcode$QRCode_View$moduleSize)),
_1: {
ctor: '::',
_0: _elm_lang$svg$Svg_Attributes$rx('0'),
_1: {
ctor: '::',
_0: _elm_lang$svg$Svg_Attributes$ry('0'),
_1: {
ctor: '::',
_0: _elm_lang$svg$Svg_Attributes$width(
@ -21400,6 +21406,8 @@ var _pablohirafuji$elm_qrcode$QRCode_View$rectView = function (_p1) {
}
}
}
}
}
},
{ctor: '[]'});
};

107
yarn.lock
View file

@ -102,6 +102,10 @@ ansi-align@^2.0.0:
dependencies:
string-width "^2.0.0"
ansi-escapes@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-2.0.0.tgz#5bae52be424878dd9783e8910e3fc2922e83c81b"
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
@ -1327,6 +1331,10 @@ cli-truncate@^1.0.0:
slice-ansi "0.0.4"
string-width "^2.0.0"
cli-width@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
cliui@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1"
@ -1458,6 +1466,10 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
console-log-level@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/console-log-level/-/console-log-level-1.4.0.tgz#4035818beea37e5850a0c03c8d450ca5f2cd1217"
content-disposition@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
@ -2152,6 +2164,14 @@ extend@3, extend@^3.0.0, extend@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4"
external-editor@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.4.tgz#1ed9199da9cbfe2ef2f7a31b2fde8b0d12368972"
dependencies:
iconv-lite "^0.4.17"
jschardet "^1.4.2"
tmp "^0.0.31"
extglob@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
@ -2679,10 +2699,14 @@ hullabaloo-config-manager@^1.0.0:
resolve-from "^3.0.0"
safe-buffer "^5.0.1"
iconv-lite@0.4.15, iconv-lite@^0.4.5:
iconv-lite@0.4.15:
version "0.4.15"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
iconv-lite@^0.4.17, iconv-lite@^0.4.5:
version "0.4.18"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2"
ienoopen@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ienoopen/-/ienoopen-1.0.0.tgz#346a428f474aac8f50cf3784ea2d0f16f62bda6b"
@ -2732,6 +2756,25 @@ ini@~1.3.0:
version "1.3.4"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
inquirer@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.1.1.tgz#87621c4fba4072f48a8dd71c9f9df6f100b2d534"
dependencies:
ansi-escapes "^2.0.0"
chalk "^1.0.0"
cli-cursor "^2.1.0"
cli-width "^2.0.0"
external-editor "^2.0.4"
figures "^2.0.0"
lodash "^4.3.0"
mute-stream "0.0.7"
run-async "^2.2.0"
rx-lite "^4.0.8"
rx-lite-aggregates "^4.0.8"
string-width "^2.0.0"
strip-ansi "^3.0.0"
through "^2.3.6"
invariant@^2.2.0:
version "2.2.2"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
@ -3012,6 +3055,10 @@ jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
jschardet@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.4.2.tgz#2aa107f142af4121d145659d44f50830961e699a"
jsesc@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
@ -3229,7 +3276,7 @@ lodash@^3.1.0, lodash@^3.10.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
lodash@^4.12.0, lodash@^4.17.2, lodash@^4.2.0:
lodash@^4.12.0, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.3.0:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
@ -3464,6 +3511,10 @@ multimatch@^2.1.0:
arrify "^1.0.0"
minimatch "^3.0.0"
mute-stream@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
nan@^2.0.5, nan@^2.0.8, nan@^2.2.1, nan@^2.3.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45"
@ -3662,7 +3713,7 @@ os-locale@^1.4.0:
dependencies:
lcid "^1.0.0"
os-tmpdir@^1.0.0, os-tmpdir@^1.0.1:
os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
@ -4509,6 +4560,22 @@ rootpath@0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/rootpath/-/rootpath-0.1.2.tgz#5b379a87dca906e9b91d690a599439bef267ea6b"
run-async@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
dependencies:
is-promise "^2.1.0"
rx-lite-aggregates@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be"
dependencies:
rx-lite "*"
rx-lite@*, rx-lite@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
safe-buffer@^5.0.1, safe-buffer@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223"
@ -4678,7 +4745,7 @@ socket.io-adapter@~1.1.0:
dependencies:
debug "2.3.3"
socket.io-client@^2.0.3:
socket.io-client@^2.0.3, socket.io-client@~2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.0.3.tgz#6caf4aff9f85b19fd91b6ce13d69adb564f8873b"
dependencies:
@ -4696,24 +4763,6 @@ socket.io-client@^2.0.3:
socket.io-parser "~3.1.1"
to-array "0.1.4"
socket.io-client@~2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.0.2.tgz#86f59db59ba1168724220f39be281ad49af742c1"
dependencies:
backo2 "1.0.2"
base64-arraybuffer "0.1.5"
component-bind "1.0.0"
component-emitter "1.2.1"
debug "~2.6.4"
engine.io-client "~3.1.0"
has-cors "1.1.0"
indexof "0.0.1"
object-component "0.0.3"
parseqs "0.0.5"
parseuri "0.0.5"
socket.io-parser "~3.1.1"
to-array "0.1.4"
socket.io-parser@~3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.1.2.tgz#dbc2282151fc4faebbe40aeedc0772eba619f7f2"
@ -5033,7 +5082,7 @@ through2@^2.0.0:
readable-stream "^2.1.5"
xtend "~4.0.1"
through@2, through@~2.3.8:
through@2, through@^2.3.6, through@~2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@ -5054,6 +5103,12 @@ timed-out@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
tmp@^0.0.31:
version "0.0.31"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
dependencies:
os-tmpdir "~1.0.1"
to-array@0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890"
@ -5228,11 +5283,7 @@ uuid@^2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
uuid@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"
uuid@^3.1.0:
uuid@^3.0.0, uuid@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"