chore: use monorepo organization

This commit is contained in:
Rafael Taranto 2025-05-12 10:52:54 +01:00
parent deaf7d6ecc
commit a687827f7e
1099 changed files with 8184 additions and 11535 deletions

9
packages/server/bin/bip39 Executable file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env node
'use strict'
const mnemonicHelpers = require('../lib/mnemonic-helpers')
const seed = process.argv[2]
console.log(mnemonicHelpers.fromSeed(seed))

17
packages/server/bin/hkdf Executable file
View file

@ -0,0 +1,17 @@
#!/usr/bin/env node
'use strict'
const hkdf = require('futoin-hkdf')
const label = process.argv[2]
const masterSeedHex = process.argv[3].trim()
if (process.argv.length !== 4) {
console.error('hdkf <label> <masterKey>')
console.error('masterKey should be in hex encoding.')
process.exit(3)
}
const masterSeed = Buffer.from(masterSeedHex, 'hex')
console.log(hkdf(masterSeed, 32, { salt: 'lamassu-server-salt', info: label }).toString('hex'))

View file

@ -0,0 +1,5 @@
#!/usr/bin/env node
const adminServer = require('../lib/new-admin/admin-server')
adminServer.run()

View file

@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -e
CERT_FILES=(
/lamassu-data/certs/{Lamassu_CA,Lamassu_OP,Lamassu_OP_Root_CA}.pem
/lamassu-data/certs/Lamassu_OP_Root_CA.srl
/lamassu-data/private/{Lamassu_OP,Lamassu_OP_Root_CA}.key
)
if ! (( ${#CERT_FILES[@]} == $(ls "${CERT_FILES[@]}" 2>/dev/null | wc -l) )); then
echo "Some certificates are missing. Retrying in 5 seconds"
sleep 5
exit 1
fi
echo "Update certs on alpine"
cp /lamassu-data/certs/Lamassu_CA.pem /usr/local/share/ca-certificates
cp /lamassu-data/certs/Lamassu_OP_Root_CA.pem /usr/local/share/ca-certificates
update-ca-certificates
if [ "${LAMASSU_DEV_MODE}" = "true" ]; then
echo "Starting in dev mode"
node /lamassu-server/bin/lamassu-admin-server --lamassuDev
else
node /lamassu-server/bin/lamassu-admin-server
fi

View file

@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -e
if [ "$(whoami)" != "root" ]; then
echo -e "This script has to be run as \033[1mroot\033[0m user"
exit 3
fi
DAYS_TO_KEEP=3
DATE=$(date --utc +%F_%H-%M)
BACKUP_DIR=/var/backups/postgresql
BACKUP_FILE=$BACKUP_DIR/backup-$DATE.sql.gz
cd ~postgres
su postgres -c "pg_dump lamassu" | gzip > $BACKUP_FILE
cd
find $BACKUP_DIR -maxdepth 1 -mtime +$DAYS_TO_KEEP -exec rm -f '{}' ';'

View file

@ -0,0 +1,47 @@
#!/usr/bin/env node
const inquirer = require('inquirer')
const bitcoind = require('../lib/plugins/wallet/bitcoind/bitcoind')
const BN = require('../lib/bn')
const mempool = require('../lib/blockexplorers/mempool.space')
const txId = process.argv[2]
if (!txId) {
console.error('Please provide a BTC transaction hash as input.')
process.exit(1)
}
const bumpTransactionFee = async (txId) => {
const txData = await bitcoind.fetch('gettransaction', [txId, true, true])
const fee = new BN(txData.fee).abs().shiftedBy(8).decimalPlaces(0)
const size = txData.decoded.vsize
const satPerVb = fee.div(size)
console.log(`Current fee: ${satPerVb.toFixed(2).toString()} sat/vB`)
const recommendedFees = await mempool.getSatBEstimateFees()
console.log('Recommended fees (sat/vB):', recommendedFees)
const { selectedFee } = await inquirer.prompt([
{
type: 'list',
name: 'selectedFee',
message: 'Select a fee higher than the current one:',
choices: Object.entries(recommendedFees)
.filter(([_, value]) => satPerVb.lt(value))
.map(([key, value]) => ({name: `${key}: ${value} sat/vB`, value})),
},
])
const { txid } = await bitcoind.fetch('bumpfee', [txId, {fee_rate: selectedFee}])
console.log(`
Fee bumped to ${selectedFee.toFixed(2)} sat/vB
Transaction ID: ${txid}
`)
}
bumpTransactionFee(txId)

View file

@ -0,0 +1,43 @@
#!/usr/bin/env node
require('../lib/environment-helper')
const argv = require('minimist')(process.argv.slice(2))
const _ = require('lodash')
const db = require('../lib/db')
const txId = argv.tx
const customerId = argv.customer
if ((!txId && !customerId) || (txId && customerId)) {
console.log('Usage: lamassu-clean-parsed-id [--tx <txId> | --customer <customerId>]')
console.log('The command can only be run with EITHER --tx OR --customer, NOT BOTH')
process.exit(2)
}
if (!_.isNil(txId)) {
db.oneOrNone('SELECT * FROM (SELECT id, customer_id FROM cash_in_txs UNION SELECT id, customer_id FROM cash_out_txs) as txs WHERE txs.id = $1', [txId])
.then(res => {
return db.none('UPDATE customers SET id_card_data = null WHERE id = $1', [res.customer_id])
.then(() => {
console.log(`ID card data from customer ${res.customer_id} was cleared with success`)
process.exit(0)
})
})
.catch(() => {
console.log('A transaction with that ID was not found')
process.exit(0)
})
}
if (!_.isNil(customerId)) {
db.none('UPDATE customers SET id_card_data = null WHERE id = $1', [customerId])
.then(() => {
console.log(`ID card data from customer ${customerId} was cleared with success`)
process.exit(0)
})
.catch(() => {
console.log('A customer with that ID was not found')
process.exit(0)
})
}

View file

@ -0,0 +1,11 @@
#!/usr/bin/env bash
echo
echo "Here is the 'External ID' of your paired machine(s), for use under the 'ATM / Teller details' of your CoinATMRadar listing:"
echo
su - postgres -c "psql \"lamassu\" -Atc \"select regexp_replace(device_id, '$', ' '),regexp_replace(name, '^', ' ') from devices\""
echo
echo "If speaking with CoinATMRadar directly, it may be helpful to let them know your 'Operator ID':"
echo
$(npm root -g)/lamassu-server/bin/lamassu-operator
echo

View file

@ -0,0 +1,7 @@
#!/usr/bin/env node
require('../lib/environment-helper')
const install = require('../lib/blockchain/install')
install.run()

View file

@ -0,0 +1,11 @@
#!/usr/bin/env node
'use strict'
require('../lib/environment-helper')
const setEnvVariable = require('../tools/set-env-var')
if (!process.env.FRONT_CAMERA_DIR) {
setEnvVariable('FRONT_CAMERA_DIR', '/opt/lamassu-server/frontcamera')
}

View file

@ -0,0 +1,7 @@
#!/usr/bin/env bash
echo
echo "Your list of paired machines and their Device IDs and names:"
echo
su - postgres -c "psql \"lamassu\" -Atc \"select device_id, name from devices\""
echo

View file

@ -0,0 +1,67 @@
#!/usr/bin/env node
require('../lib/environment-helper')
const hdkey = require('ethereumjs-wallet/hdkey')
const hkdf = require('futoin-hkdf')
const db = require('../lib/db')
const _ = require('lodash/fp')
const mnemonicHelpers = require('../lib/mnemonic-helpers')
const pify = require('pify')
const fs = pify(require('fs'))
const os = require('os')
const MNEMONIC_PATH = process.env.MNEMONIC_PATH
const defaultPrefixPath = "m/44'/60'/1'/0'"
const paymentPrefixPath = "m/44'/60'/0'/0'"
const address = process.argv[2]
if (!MNEMONIC_PATH) {
console.error(`Unable to fetch mnemonic from your account!`)
process.exit(1)
}
if (!address) {
console.log('Usage: lamassu-eth-recovery <cash-out address>')
process.exit(2)
}
function run (address) {
Promise.all([fetchMnemonic(), searchForHdIndex(address)])
.then(([mnemonic, hdIndex]) => {
try {
const prefix = !_.isNil(hdIndex) ? paymentPrefixPath : defaultPrefixPath
console.log(`Private key: `, defaultHdNode(mnemonic, prefix).deriveChild(hdIndex).getWallet().getPrivateKeyString())
process.exit(0)
} catch (err) {
console.error(`Error while retrieving private key!`)
process.exit(3)
}
})
}
function searchForHdIndex (address) {
const sql = `SELECT hd_index FROM cash_out_txs WHERE to_address = $1`
return db.oneOrNone(sql, [address])
.then(result => _.get('hd_index', result))
}
function fetchMnemonic () {
return fs.readFile(MNEMONIC_PATH, 'utf8')
.then(mnemonic => computeSeed(mnemonic))
}
function computeSeed (seed) {
const masterSeed = mnemonicHelpers.toEntropyBuffer(seed)
return hkdf(masterSeed, 32, { salt: 'lamassu-server-salt', info: 'wallet-seed' })
}
function defaultHdNode (masterSeed, prefix) {
if (!masterSeed) throw new Error('No master seed!')
const key = hdkey.fromMasterSeed(masterSeed)
return key.derivePath(prefix)
}
run(address)

View file

@ -0,0 +1,298 @@
#!/usr/bin/env node
require('../lib/environment-helper')
const hdkey = require('ethereumjs-wallet/hdkey')
const hkdf = require('futoin-hkdf')
const crypto = require('crypto')
const path = require('path')
const pify = require('pify')
const fs = pify(require('fs'))
const _ = require('lodash/fp')
const { BigNumber } = require('bignumber.js')
const coins = require('@lamassu/coins')
const Web3 = require('web3')
const web3 = new Web3()
const Tx = require('ethereumjs-tx')
const mnemonicHelpers = require('../lib/mnemonic-helpers')
const settingsLoader = require('../lib/new-settings-loader')
const BN = require('../lib/bn')
const ph = require('../lib/plugin-helper')
const configManager = require('../lib/new-config-manager')
const walletI = require('../lib/wallet')
const LOCKFILE_PATH = '/var/lock/lamassu-eth-pending-sweep'
const defaultPrefixPath = "m/44'/60'/1'/0'"
let lastUsedNonces = {}
const hex = bigNum => '0x' + bigNum.integerValue(BN.ROUND_DOWN).toString(16)
const MNEMONIC_PATH = process.env.MNEMONIC_PATH
function writeNewMnemonic (mnemonic) {
return fs.writeFile(`${MNEMONIC_PATH}-new-temp`, mnemonic)
.then(() => `${MNEMONIC_PATH}-new-temp`)
}
function renameNewMnemonic () {
return fs.rename(`${MNEMONIC_PATH}-new-temp`, `${MNEMONIC_PATH}`)
.then(() => MNEMONIC_PATH)
}
function backupMnemonic () {
const folderPath = path.dirname(MNEMONIC_PATH)
const fileName = path.resolve(folderPath, `mnemonic-${Date.now()}.txt`)
return fs.copyFile(MNEMONIC_PATH, fileName)
.then(() => fileName)
}
function computeSeed (seed) {
const masterSeed = mnemonicHelpers.toEntropyBuffer(seed)
return hkdf(masterSeed, 32, { salt: 'lamassu-server-salt', info: 'wallet-seed' })
}
function computeOperatorId (masterSeed) {
return hkdf(masterSeed, 16, { salt: 'lamassu-server-salt', info: 'operator-id' }).toString('hex')
}
function generateRandomSeed () {
const seed = crypto
.randomBytes(32)
.toString('hex')
return Buffer.from(seed, 'hex')
}
function generateNewMnemonic (newSeed) {
return mnemonicHelpers.fromSeed(newSeed)
}
function defaultWallet (seed) {
return defaultHdNode(seed).deriveChild(0).getWallet()
}
function defaultWalletAcc (account) {
return defaultHdNodeAcc(account).deriveChild(0).getWallet()
}
function defaultAddress (seed) {
return defaultWallet(seed).getChecksumAddressString()
}
function defaultHdNode (seed) {
const key = hdkey.fromMasterSeed(seed)
return key.derivePath(defaultPrefixPath)
}
function defaultHdNodeAcc (account) {
const key = hdkey.fromMasterSeed(account.seed)
return key.derivePath(defaultPrefixPath)
}
function getAllBalance () {
return settingsLoader.loadLatest()
.then(settings => walletI.balance(settings, 'ETH'))
.then(r => r.balance)
}
function isInfuraRunning (settings) {
const isInfuraSelected = settings.config.wallets_ETH_wallet === 'infura'
const isInfuraConfigured =
!_.isNil(settings.accounts.infura)
&& !_.isNil(settings.accounts.infura.apiKey)
&& !_.isNil(settings.accounts.infura.apiSecret)
&& !_.isNil(settings.accounts.infura.endpoint)
return isInfuraSelected && isInfuraConfigured
}
function isGethRunning (settings) {
return walletI.checkBlockchainStatus(settings, 'ETH')
.then(res => res === 'ready')
.catch(() => false)
}
function connect (url) {
if (!web3.isConnected()) {
web3.setProvider(new web3.providers.HttpProvider(url))
}
}
function sendCoins (account, tx, settings, operatorId, feeMultiplier, _opts) {
const { toAddress, cryptoAtoms, cryptoCode } = tx
const opts = { ..._opts, includesFee: _.defaultTo(false, _opts?.includesFee) }
return generateTx(toAddress, defaultWalletAcc(account), cryptoAtoms, cryptoCode, opts)
.then(pify(web3.eth.sendRawTransaction))
.then(txid => {
return pify(web3.eth.getTransaction)(txid)
.then(tx => {
if (!tx) return { txid }
const fee = new BN(tx.gas).times(new BN(tx.gasPrice)).decimalPlaces(0)
return { txid, fee }
})
})
}
function generateTx (_toAddress, wallet, amount, cryptoCode, opts) {
const fromAddress = '0x' + wallet.getAddress().toString('hex')
const isErc20Token = coins.utils.isErc20Token(cryptoCode)
const toAddress = isErc20Token ? coins.utils.getErc20Token(cryptoCode).contractAddress : _toAddress.toLowerCase()
let contract, contractData
if (isErc20Token) {
contract = web3.eth.contract(ABI.ERC20).at(toAddress)
contractData = isErc20Token && contract.transfer.getData(_toAddress.toLowerCase(), hex(toSend))
}
const txTemplate = {
from: fromAddress,
to: toAddress,
value: amount.toString()
}
if (isErc20Token) txTemplate.data = contractData
const promises = [
pify(web3.eth.estimateGas)(txTemplate),
pify(web3.eth.getGasPrice)(),
pify(web3.eth.getTransactionCount)(fromAddress)
]
return Promise.all(promises)
.then(([gas, gasPrice, txCount]) => [
BN(gas),
BN(gasPrice),
_.max([0, txCount, lastUsedNonces[fromAddress] + 1])
])
.then(([gas, gasPrice, txCount]) => {
lastUsedNonces[fromAddress] = txCount
const toSend = opts.includesFee
? amount.minus(gasPrice.times(gas))
: amount
const rawTx = {
chainId: _.defaultTo(1, opts?.chainId),
nonce: _.defaultTo(txCount, opts?.nonce),
gasPrice: hex(gasPrice),
gasLimit: hex(gas),
to: toAddress,
from: fromAddress,
value: isErc20Token ? hex(BN(0)) : hex(toSend)
}
if (isErc20Token) {
rawTx.data = contractData
}
const tx = new Tx(rawTx)
const privateKey = wallet.getPrivateKey()
tx.sign(privateKey)
return '0x' + tx.serialize().toString('hex')
})
}
function fetchWallet (settings, cryptoCode) {
return fs.readFile(MNEMONIC_PATH, 'utf8')
.then(mnemonic => {
const computeSeed = masterSeed =>
hkdf(masterSeed, 32, { salt: 'lamassu-server-salt', info: 'wallet-seed' })
const masterSeed = mnemonicHelpers.toEntropyBuffer(mnemonic)
const plugin = configManager.getWalletSettings(cryptoCode, settings.config).wallet
const wallet = ph.load(ph.WALLET, plugin)
const rawAccount = settings.accounts[plugin]
const account = _.set('seed', computeSeed(masterSeed), rawAccount)
if (_.isFunction(wallet.run)) wallet.run(account)
const operatorId = computeOperatorId(masterSeed)
return { wallet, account, operatorId }
})
}
fs.exists(LOCKFILE_PATH, function(exists) {
if (!exists) {
console.log('Couldn\'t find the lamassu-eth-pending-sweep lock file, exiting...')
process.exit(1)
}
})
const seed = generateRandomSeed()
const mnemonic = generateNewMnemonic(seed)
const mnemonicSeed = computeSeed(mnemonic)
const newAddress = defaultAddress(mnemonicSeed)
settingsLoader.loadLatest()
.then(settings => Promise.all([isInfuraRunning(settings), isGethRunning(settings), settings]))
.then(([infuraIsRunning, gethIsRunning, settings]) => {
if (!infuraIsRunning && !gethIsRunning) {
console.log('Neither geth nor Infura are running, so the script cannot be executed.')
process.exit(2)
}
console.log(`Backing up old mnemonic...`)
return Promise.all([backupMnemonic(), infuraIsRunning, settings])
})
.then(([fileName, infuraIsRunning, settings]) => {
console.log(`Successfully backed up the old mnemonic, new location is ${fileName}`)
return Promise.all([writeNewMnemonic(mnemonic), infuraIsRunning, settings])
})
.then(([tempMnemonicFileName, infuraIsRunning, settings]) => {
console.log(`New mnemonic stored temporarily in ${tempMnemonicFileName}`)
console.log(`Starting funds transfer...`)
return Promise.all([infuraIsRunning, settings])
})
.then(([infuraIsRunning, settings]) => {
if (infuraIsRunning) {
const endpoint = _.startsWith('https://')(settings.accounts.infura.endpoint)
? settings.accounts.infura.endpoint
: `https://${settings.accounts.infura.endpoint}`
connect(endpoint)
} else {
connect(`http://localhost:${coins.utils.getCryptoCurrency('ETH').defaultPort}`)
}
return Promise.all([getAllBalance(), settings, fetchWallet(settings, 'ETH')])
})
.then(([balance, settings, { account, operatorId }]) => {
const tx = {
cryptoCode: 'ETH',
toAddress: newAddress,
cryptoAtoms: BN(balance.times(0.99999).toFixed(0, BigNumber.ROUND_DOWN))
}
const opts = {
chainId: 1,
nonce: 0,
includesFee: true
}
return sendCoins(account, tx, settings, operatorId, null, opts)
})
.then(resTx => {
console.log('Successfully moved funds from the old wallet to the new one.')
console.log('Information about the transaction', resTx)
console.log('Moving the current mnemonic to the default file...')
return renameNewMnemonic()
})
.then(() => {
console.log('New mnemonic stored successfully! All your funds (minus the transaction fee) should be available in the next few minutes.')
return fs.rmdir(LOCKFILE_PATH)
})
.then(() => {
console.log('lamassu-eth-pending-sweep lock file successfully removed')
return fs.mkdir(`${LOCKFILE_PATH}-finished`)
})
.then(() => {
console.log('lamassu-eth-pending-sweep-finished lock file successfully created, this will automatically be deleted once the upgrade script finishes running')
console.log('Process finished successfully! You may now execute the upgrade script again')
process.exit(0)
})
.catch(err => {
console.error(err)
process.exit(1)
})

View file

@ -0,0 +1,25 @@
#!/usr/bin/env node
const _ = require('lodash/fp')
require('../lib/environment-helper')
const db = require('../lib/db')
const migrate = require('../lib/migrate')
const createMigration = `CREATE TABLE IF NOT EXISTS migrations (
id serial PRIMARY KEY,
data json NOT NULL
)`
// no need to log the migration process
process.env.SKIP_SERVER_LOGS = true
db.none(createMigration)
.then(() => migrate.run())
.then(() => {
console.log('DB Migration succeeded.')
process.exit(0)
})
.catch(err => {
console.error('DB Migration failed: %s', err)
process.exit(1)
})

View file

@ -0,0 +1,9 @@
#!/usr/bin/env node
const fs = require('fs')
require('../lib/environment-helper')
const MNEMONIC_PATH = process.env.MNEMONIC_PATH
const mnemonic = fs.readFileSync(MNEMONIC_PATH, 'utf8').trim()
console.log(mnemonic)

View file

@ -0,0 +1,11 @@
#!/usr/bin/env node
require('../lib/environment-helper')
const ofac = require('../lib/ofac/update')
console.log('Updating OFAC databases.')
ofac.update()
.then(() => console.log('Success.'))
.catch(console.log)

View file

@ -0,0 +1,14 @@
#!/usr/bin/env node
const fs = require('fs')
const hkdf = require('futoin-hkdf')
require('../lib/environment-helper')
const mnemonicHelpers = require('../lib/mnemonic-helpers')
const MNEMONIC_PATH = process.env.MNEMONIC_PATH
const mnemonic = fs.readFileSync(MNEMONIC_PATH, 'utf8').trim()
const masterSeed = mnemonicHelpers.toEntropyBuffer(mnemonic)
console.log(hkdf(masterSeed, 16, { salt: 'lamassu-server-salt', info: 'operator-id' }).toString('hex'))

View file

@ -0,0 +1,51 @@
#!/usr/bin/env node
require('../lib/environment-helper')
const userManagement = require('../lib/new-admin/graphql/modules/userManagement')
const authErrors = require('../lib/new-admin/graphql/errors')
const name = process.argv[2]
const role = process.argv[3]
const domain = process.env.HOSTNAME
if (!domain) {
console.error('No hostname configured in the environment')
process.exit(1)
}
if (!name || !role) {
console.log('Usage: lamassu-register <email> <role>')
console.log('<role> must be \'user\' or \'superuser\'')
process.exit(2)
}
const emailRegex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
if (!emailRegex.test(name)) {
console.log('Usage: <email> must be in an email format')
process.exit(2)
}
if (role !== 'user' && role !== 'superuser') {
console.log('Usage: <role> must be \'user\' or \'superuser\'')
process.exit(2)
}
userManagement.createRegisterToken(name, role).then(token => {
if (domain === 'localhost' && process.env.NODE_ENV !== 'production') {
console.log(`https://${domain}:3001/register?t=${token.token}`)
} else {
console.log(`https://${domain}/register?t=${token.token}`)
}
process.exit(0)
}).catch(err => {
if (err instanceof authErrors.UserAlreadyExistsError){
console.log(`A user with email ${name} already exists!`)
process.exit(2)
}
console.log('Error: %s', err)
process.exit(3)
})

View file

@ -0,0 +1,90 @@
#!/usr/bin/env node
require('../lib/environment-helper')
const settingsLoader = require('../lib/new-settings-loader')
const configManager = require('../lib/new-config-manager')
const wallet = require('../lib/wallet')
const { utils: coinUtils } = require('@lamassu/coins')
const BN = require('../lib/bn')
const inquirer = require('inquirer')
const ticker = require('../lib/ticker')
const [toAddress, cryptoValue, cryptoCode] = process.argv.slice(2)
function computeCrypto (cryptoCode, value) {
try {
const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode)
const unitScale = cryptoRec.unitScale
return new BN(value).shiftedBy(unitScale)
} catch (err) {
return null
}
}
if (!toAddress || !cryptoValue || !cryptoCode) {
console.log('Usage: lamassu-send-coins <address> <amount> <coin>')
console.log('Example: lamassu-send-coins 3FUv7vKaP11idnsUKyQ2pxdWxCDMyr5HKJ 0.009 BTC')
process.exit(1)
}
const cryptoAtoms = computeCrypto(cryptoCode, cryptoValue)
if (!cryptoAtoms) {
console.log(`Unsupported coin: ${cryptoCode}.\n`)
console.log('Usage: lamassu-send-coins <address> <amount> <coin>')
console.log('Example: lamassu-send-coins 3FUv7vKaP11idnsUKyQ2pxdWxCDMyr5HKJ 0.009 BTC')
process.exit(2)
}
console.log('Loading ticker...')
settingsLoader.loadLatest()
.then(settings => {
const fiatCode = configManager.getGlobalLocale(settings.config).fiatCurrency
return wallet.isStrictAddress(settings, cryptoCode, toAddress)
.then(isValid => {
if (!isValid) {
console.log(`Invalid ${cryptoCode} address: ${toAddress}.`)
console.log('Please check your command.\n')
console.log('Usage: lamassu-send-coins <address> <amount> <coin>')
console.log('Example: lamassu-send-coins 3FUv7vKaP11idnsUKyQ2pxdWxCDMyr5HKJ 0.009 BTC')
process.exit(3)
}
})
.then(() => ticker.getRates(settings, fiatCode, cryptoCode))
.then(rates => {
const fiatAmount = rates.rates.ask.times(cryptoValue).toFixed(2)
const questions = [
{
type: 'confirm',
name: 'confirm',
message: `Are you sure you want to send ${cryptoValue} ${cryptoCode} (${fiatAmount} ${fiatCode}) to the address ${toAddress}?`,
default: false
}
]
console.log('\nPlease look over this transaction carefully!')
return inquirer.prompt(questions)
.then(answers => {
if (!answers.confirm) {
console.log('Transaction cancelled.')
process.exit(0)
}
console.log('Sending...')
return wallet.sendCoins(settings, { toAddress, cryptoAtoms, cryptoCode })
.then(() => {
console.log('Success.')
process.exit(0)
})
.catch(e => console.log(e.message))
})
.catch(e => console.log(e.message))
})
.catch(e => console.log(e.message))
})

View file

@ -0,0 +1,12 @@
#!/usr/bin/env node
const app = require('../lib/app')
process.on('unhandledRejection', err => {
console.log('Unhandled rejection')
console.dir(err)
console.log(err.stack)
})
app.run()
.catch(console.log)

View file

@ -0,0 +1,25 @@
#!/usr/bin/env bash
CERT_FILES=(
/lamassu-data/certs/{Lamassu_CA,Lamassu_OP,Lamassu_OP_Root_CA}.pem
/lamassu-data/certs/Lamassu_OP_Root_CA.srl
/lamassu-data/private/{Lamassu_OP,Lamassu_OP_Root_CA}.key
)
echo "Checking for Lamassu certificates..."
if ! (( ${#CERT_FILES[@]} == $(ls "${CERT_FILES[@]}" 2>/dev/null | wc -l) )); then
echo "Some certificates are missing. Building them..."
bash /lamassu-server/tools/build-docker-certs.sh
fi
echo "Upcate certs on alpine"
cp /lamassu-data/certs/Lamassu_CA.pem /usr/local/share/ca-certificates
cp /lamassu-data/certs/Lamassu_OP_Root_CA.pem /usr/local/share/ca-certificates
update-ca-certificates
echo "Executing migrations..."
node /lamassu-server/bin/lamassu-migrate
echo "Starting server..."
node /lamassu-server/bin/lamassu-server

View file

@ -0,0 +1,53 @@
#!/usr/bin/env node
require('../lib/environment-helper')
const TronWeb = require('tronweb')
const db = require('../lib/db')
const _ = require('lodash/fp')
const pify = require('pify')
const fs = pify(require('fs'))
const MNEMONIC_PATH = process.env.MNEMONIC_PATH
const defaultPrefixPath = "m/44'/195'/0'/0"
const paymentPrefixPath = "m/44'/195'/1'/0"
const address = process.argv[2]
if (!MNEMONIC_PATH) {
console.error(`Unable to fetch mnemonic from your account!`)
process.exit(1)
}
if (!address) {
console.log('Usage: lamassu-trx-recovery <cash-out address>')
process.exit(2)
}
function run (address) {
Promise.all([fetchMnemonic(), searchForHdIndex(address)])
.then(([mnemonic, hdIndex]) => {
try {
const prefix = !_.isNil(hdIndex) ? `${paymentPrefixPath}/${hdIndex}` : `${defaultPrefixPath}/0`
const privKey = TronWeb.fromMnemonic(mnemonic.replace(/[\r\n]/gm, ' ').trim(), prefix).privateKey
console.log(`Private key: `, privKey.slice(2))
process.exit(0)
} catch (err) {
console.error(`Error while retrieving private key!`)
process.exit(3)
}
})
}
function searchForHdIndex (address) {
const sql = `SELECT hd_index FROM cash_out_txs WHERE to_address = $1`
return db.oneOrNone(sql, [address])
.then(result => _.get('hd_index', result))
}
function fetchMnemonic () {
return fs.readFile(MNEMONIC_PATH, 'utf8')
}
run(address)

View file

@ -0,0 +1,36 @@
#!/usr/bin/env node
require('../lib/environment-helper')
const _ = require('lodash')
const db = require('../lib/db')
if (process.argv.length !== 4) {
console.log('Usage: lamassu-update-cassettes <device_id> <number_of_cassettes>')
process.exit(1)
}
if (!_.isFinite(parseInt(process.argv[3]))) {
console.log('Error: <number_of_cassettes> is not a valid number (%s)', err)
process.exit(3)
}
if (parseInt(process.argv[3]) > 4 || parseInt(process.argv[3]) < 2) {
console.log('Error: <number_of_cassettes> is out of range. Should be a number between 2 and 4')
process.exit(3)
}
const deviceId = process.argv[2]
const numberOfCassettes = parseInt(process.argv[3])
const query = `UPDATE devices SET number_of_cassettes = $1 WHERE device_id = $2`
db.none(query, [numberOfCassettes, deviceId])
.then(() => {
console.log('Success! Device %s updated to %s cassettes', deviceId, numberOfCassettes)
process.exit(0)
})
.catch(err => {
console.log('Error: %s', err)
process.exit(3)
})

View file

@ -0,0 +1,36 @@
#!/usr/bin/env node
require('../lib/environment-helper')
const _ = require('lodash')
const db = require('../lib/db')
if (process.argv.length !== 4) {
console.log('Usage: lamassu-update-recyclers <device_id> <number_of_recyclers>')
process.exit(1)
}
if (!_.isFinite(parseInt(process.argv[3]))) {
console.log('Error: <number_of_recyclers> is not a valid number (%s)', err)
process.exit(3)
}
if (parseInt(process.argv[3]) > 6 || parseInt(process.argv[3]) < 1) {
console.log('Error: <number_of_recyclers> is out of range. Should be a number between 1 and 3')
process.exit(3)
}
const deviceId = process.argv[2]
const numberOfRecyclers = parseInt(process.argv[3])
const query = `UPDATE devices SET number_of_recyclers = $1 WHERE device_id = $2`
db.none(query, [numberOfRecyclers, deviceId])
.then(() => {
console.log('Success! Device %s updated to %s recyclers', deviceId, numberOfRecyclers)
process.exit(0)
})
.catch(err => {
console.log('Error: %s', err)
process.exit(3)
})

View file

@ -0,0 +1,30 @@
#!/usr/bin/env node
'use strict'
const fs = require('fs')
const path = require('path')
const os = require('os')
require('../lib/environment-helper')
const mnemonicHelpers = require('../lib/mnemonic-helpers')
const setEnvVariable = require('../tools/set-env-var')
if (!process.env.MNEMONIC_PATH && process.env.SEED_PATH) {
const seed = fs.readFileSync(process.env.SEED_PATH, 'utf8').trim()
const mnemonic = mnemonicHelpers.fromSeed(seed)
if (process.argv[2] === '--prod') {
setEnvVariable('MNEMONIC_PATH', path.resolve('/etc', 'lamassu', 'mnemonics', 'mnemonic.txt'))
} else {
setEnvVariable('MNEMONIC_PATH', path.resolve(os.homedir(), '.lamassu', 'mnemonics', 'mnemonic.txt'))
}
if (!fs.existsSync(path.dirname(process.env.MNEMONIC_PATH))) {
fs.mkdirSync(path.dirname(process.env.MNEMONIC_PATH))
}
if (!fs.existsSync(process.env.MNEMONIC_PATH)) {
fs.writeFileSync(process.env.MNEMONIC_PATH, mnemonic, 'utf8')
}
}

View file

@ -0,0 +1,43 @@
#!/usr/bin/env node
require('../lib/environment-helper')
const _ = require('lodash/fp')
const common = require('../lib/blockchain/common')
const { utils: coinUtils } = require('@lamassu/coins')
const cryptos = coinUtils.cryptoCurrencies()
const PLUGINS = {
BTC: require('../lib/blockchain/bitcoin.js'),
BCH: require('../lib/blockchain/bitcoincash.js'),
DASH: require('../lib/blockchain/dash.js'),
ETH: require('../lib/blockchain/ethereum.js'),
LTC: require('../lib/blockchain/litecoin.js'),
XMR: require('../lib/blockchain/monero.js'),
ZEC: require('../lib/blockchain/zcash.js')
}
function plugin (crypto) {
const plugin = PLUGINS[crypto.cryptoCode]
if (!plugin) throw new Error(`No such plugin: ${crypto.cryptoCode}`)
return plugin
}
function isWalletNodeInstalled (status) {
// From http://supervisord.org/subprocess.html#process-states
return _.includes(status, ['STARTING', 'RUNNING', 'STOPPED', 'BACKOFF', 'STOPPING', 'EXITED', 'FATAL'])
}
function run () {
_.forEach((crypto) => {
if (!_.includes(crypto.cryptoCode, _.keys(PLUGINS))) return
const cryptoPlugin = plugin(crypto)
const status = common.es(`sudo supervisorctl status ${crypto.code} | awk '{ print $2 }'`).trim()
if (!isWalletNodeInstalled(status)) return
cryptoPlugin.updateCore(common.getBinaries(crypto.cryptoCode), _.includes(status, ['RUNNING', 'STARTING']))
}, cryptos)
}
run()