From c16e0ea776ead5454fbecb4cd1c67be6c6e0f403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Sun, 12 Jun 2022 16:10:18 +0100 Subject: [PATCH 1/8] feat: add script to sweep all ETH balance to a new wallet --- bin/lamassu-eth-sweep-to-new-wallet | 130 ++++++++++++++++++++++++++++ lib/blockchain/install.js | 3 +- lib/plugins/wallet/geth/base.js | 19 ++-- lib/wallet.js | 4 +- 4 files changed, 145 insertions(+), 11 deletions(-) create mode 100644 bin/lamassu-eth-sweep-to-new-wallet diff --git a/bin/lamassu-eth-sweep-to-new-wallet b/bin/lamassu-eth-sweep-to-new-wallet new file mode 100644 index 00000000..38b4c3c0 --- /dev/null +++ b/bin/lamassu-eth-sweep-to-new-wallet @@ -0,0 +1,130 @@ +#!/usr/bin/env node +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 mnemonicHelpers = require('../lib/mnemonic-helpers') +const options = require('../lib/options') +const settingsLoader = require('../lib/new-settings-loader') +const wallet = require('../lib/wallet') +const BN = require('../lib/bn') + +const defaultPrefixPath = "m/44'/60'/1'/0'" +const paymentPrefixPath = "m/44'/60'/0'/0'" + +function writeNewMnemonic (mnemonic) { + return fs.writeFile(options.mnemonicPath, mnemonic) +} + +function backupMnemonic () { + const folderPath = path.dirname(options.mnemonicPath) + const fileName = path.resolve(folderPath, `mnemonic-${Date.now()}.txt`) + return fs.copyFile(options.mnemonicPath, fileName) + .then(() => fileName) +} + +function computeSeed (seed) { + const masterSeed = mnemonicHelpers.toEntropyBuffer(seed) + return hkdf(masterSeed, 32, { salt: 'lamassu-server-salt', info: 'wallet-seed' }) +} + +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 defaultAddress (seed) { + return defaultWallet(seed).getChecksumAddressString() +} + +function defaultHdNode (seed) { + const key = hdkey.fromMasterSeed(seed) + return key.derivePath(defaultPrefixPath) +} + +function getAllBalance () { + return settingsLoader.loadLatest() + .then(settings => wallet.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 wallet.checkBlockchainStatus(settings, 'ETH') + .then(res => res === 'ready') +} + +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) + } + + return Promise.all([getAllBalance(), settings]) + }) + .then(([balance, settings]) => { + 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 wallet.sendCoins(settings, tx, opts) + }) + .then(resTx => { + console.log('Successfully moved funds from the old wallet to the new one.') + console.log('Information about the transaction', resTx) + return backupMnemonic() + }) + .then(fileName => { + console.log(`Successfully backed up the old mnemonic, new location is ${fileName}`) + return writeNewMnemonic(mnemonic) + }) + .then(() => { + console.log('New mnemonic stored successfully! All your funds (minus the transaction fee) should be available in the next few minutes.') + console.log('Process finished successfully!') + process.exit(0) + }) + .catch(err => { + console.error(err) + process.exit(1) + }) diff --git a/lib/blockchain/install.js b/lib/blockchain/install.js index 1191f20e..d5789549 100644 --- a/lib/blockchain/install.js +++ b/lib/blockchain/install.js @@ -158,7 +158,8 @@ function run () { const validateAnswers = async (answers) => { if (_.size(answers) > 2) return { message: `Please insert a maximum of two coins to install.`, isValid: false } return getBlockchainSyncStatus(cryptos) - .then(blockchainStatuses => { + .then(_blockchainStatuses => { + const blockchainStatuses = _.filter(it => it !== 'disconnected', _blockchainStatuses) const result = _.reduce((acc, value) => ({ ...acc, [value]: _.isNil(acc[value]) ? 1 : acc[value] + 1 }), {}, _.values(blockchainStatuses)) if (_.size(answers) + result.syncing > 2) { return { message: `Installing these coins would pass the 2 parallel blockchain synchronization limit. Please try again with fewer coins or try again later.`, isValid: false } diff --git a/lib/plugins/wallet/geth/base.js b/lib/plugins/wallet/geth/base.js index d37227a6..144d12a0 100644 --- a/lib/plugins/wallet/geth/base.js +++ b/lib/plugins/wallet/geth/base.js @@ -50,9 +50,10 @@ function isStrictAddress (cryptoCode, toAddress, settings, operatorId) { return cryptoCode === 'ETH' && util.isValidChecksumAddress(toAddress) } -function sendCoins (account, tx, settings, operatorId) { +function sendCoins (account, tx, settings, operatorId, feeMultiplier, _opts) { const { toAddress, cryptoAtoms, cryptoCode } = tx - return generateTx(toAddress, defaultWallet(account), cryptoAtoms, false, cryptoCode) + const opts = { ..._opts, includesFee: _.defaultTo(false, _opts?.includesFee) } + return generateTx(toAddress, defaultWallet(account), cryptoAtoms, cryptoCode, opts) .then(pify(web3.eth.sendRawTransaction)) .then(txid => { return pify(web3.eth.getTransaction)(txid) @@ -95,7 +96,7 @@ function _balance (includePending, address, cryptoCode) { .then(balance => balance ? BN(balance) : BN(0)) } -function generateTx (_toAddress, wallet, amount, includesFee, cryptoCode) { +function generateTx (_toAddress, wallet, amount, cryptoCode, opts) { const fromAddress = '0x' + wallet.getAddress().toString('hex') const isErc20Token = coins.utils.isErc20Token(cryptoCode) @@ -125,18 +126,18 @@ function generateTx (_toAddress, wallet, amount, includesFee, cryptoCode) { .then(([gas, gasPrice, txCount]) => [ BN(gas), BN(gasPrice), - _.max([0, txCount, lastUsedNonces[fromAddress] + 1]) + _.max([1, txCount, lastUsedNonces[fromAddress] + 1]) ]) .then(([gas, gasPrice, txCount]) => { lastUsedNonces[fromAddress] = txCount - const toSend = includesFee + const toSend = opts.includesFee ? amount.minus(gasPrice.times(gas)) : amount const rawTx = { - chainId: 1, - nonce: txCount, + chainId: _.defaultTo(1, opts?.chainId), + nonce: _.defaultTo(txCount, opts?.nonce), gasPrice: hex(gasPrice), gasLimit: hex(gas), to: toAddress, @@ -172,8 +173,9 @@ function sweep (account, cryptoCode, hdIndex, settings, operatorId) { return confirmedBalance(fromAddress, cryptoCode) .then(r => { if (r.eq(0)) return + const opts = { includesFee: true } - return generateTx(defaultAddress(account), wallet, r, true, cryptoCode) + return generateTx(defaultAddress(account), wallet, r, cryptoCode, opts) .then(signedTx => pify(web3.eth.sendRawTransaction)(signedTx)) }) } @@ -237,4 +239,5 @@ function checkBlockchainStatus (cryptoCode) { .then(() => connect(`http://localhost:${coins.utils.getCryptoCurrency(cryptoCode).defaultPort}`)) .then(() => web3.eth.syncing) .then(res => res === false ? 'ready' : 'syncing') + .catch(() => 'disconnected') } diff --git a/lib/wallet.js b/lib/wallet.js index 1073a8a0..eddeb7f4 100644 --- a/lib/wallet.js +++ b/lib/wallet.js @@ -59,11 +59,11 @@ function _balance (settings, cryptoCode) { }) } -function sendCoins (settings, tx) { +function sendCoins (settings, tx, opts) { return fetchWallet(settings, tx.cryptoCode) .then(r => { const feeMultiplier = new BN(configManager.getWalletSettings(tx.cryptoCode, settings.config).feeMultiplier) - return r.wallet.sendCoins(r.account, tx, settings, r.operatorId, feeMultiplier) + return r.wallet.sendCoins(r.account, tx, settings, r.operatorId, feeMultiplier, opts) .then(res => { mem.clear(module.exports.balance) return res From 6287464ceac062cb7f94f2e5af466613e44b3423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Mon, 13 Jun 2022 16:57:19 +0100 Subject: [PATCH 2/8] feat: switch to a standalone script --- bin/lamassu-eth-sweep-to-new-wallet | 161 ++++++++++++++++++++++++++-- lib/blockchain/install.js | 3 +- lib/plugins/wallet/geth/base.js | 19 ++-- lib/wallet.js | 4 +- 4 files changed, 164 insertions(+), 23 deletions(-) diff --git a/bin/lamassu-eth-sweep-to-new-wallet b/bin/lamassu-eth-sweep-to-new-wallet index 38b4c3c0..8f72f438 100644 --- a/bin/lamassu-eth-sweep-to-new-wallet +++ b/bin/lamassu-eth-sweep-to-new-wallet @@ -7,15 +7,24 @@ 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 options = require('../lib/options') const settingsLoader = require('../lib/new-settings-loader') -const wallet = require('../lib/wallet') 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'" -const paymentPrefixPath = "m/44'/60'/0'/0'" +let lastUsedNonces = {} + +const hex = bigNum => '0x' + bigNum.integerValue(BN.ROUND_DOWN).toString(16) function writeNewMnemonic (mnemonic) { return fs.writeFile(options.mnemonicPath, mnemonic) @@ -33,6 +42,10 @@ function computeSeed (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) @@ -49,6 +62,10 @@ 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() } @@ -58,9 +75,14 @@ function defaultHdNode (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 => wallet.balance(settings, 'ETH')) + .then(settings => walletI.balance(settings, 'ETH')) .then(r => r.balance) } @@ -76,10 +98,120 @@ function isInfuraRunning (settings) { } function isGethRunning (settings) { - return wallet.checkBlockchainStatus(settings, 'ETH') + 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(options.mnemonicPath, '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) @@ -93,9 +225,18 @@ settingsLoader.loadLatest() process.exit(2) } - return Promise.all([getAllBalance(), 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]) => { + .then(([balance, settings, { account, operatorId }]) => { const tx = { cryptoCode: 'ETH', toAddress: newAddress, @@ -103,12 +244,12 @@ settingsLoader.loadLatest() } const opts = { - chainId: 1, + chainId: 3, nonce: 0, includesFee: true } - return wallet.sendCoins(settings, tx, opts) + return sendCoins(account, tx, settings, operatorId, null, opts) }) .then(resTx => { console.log('Successfully moved funds from the old wallet to the new one.') @@ -121,6 +262,10 @@ settingsLoader.loadLatest() }) .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') console.log('Process finished successfully!') process.exit(0) }) diff --git a/lib/blockchain/install.js b/lib/blockchain/install.js index d5789549..1191f20e 100644 --- a/lib/blockchain/install.js +++ b/lib/blockchain/install.js @@ -158,8 +158,7 @@ function run () { const validateAnswers = async (answers) => { if (_.size(answers) > 2) return { message: `Please insert a maximum of two coins to install.`, isValid: false } return getBlockchainSyncStatus(cryptos) - .then(_blockchainStatuses => { - const blockchainStatuses = _.filter(it => it !== 'disconnected', _blockchainStatuses) + .then(blockchainStatuses => { const result = _.reduce((acc, value) => ({ ...acc, [value]: _.isNil(acc[value]) ? 1 : acc[value] + 1 }), {}, _.values(blockchainStatuses)) if (_.size(answers) + result.syncing > 2) { return { message: `Installing these coins would pass the 2 parallel blockchain synchronization limit. Please try again with fewer coins or try again later.`, isValid: false } diff --git a/lib/plugins/wallet/geth/base.js b/lib/plugins/wallet/geth/base.js index 144d12a0..d9cee46c 100644 --- a/lib/plugins/wallet/geth/base.js +++ b/lib/plugins/wallet/geth/base.js @@ -50,10 +50,9 @@ function isStrictAddress (cryptoCode, toAddress, settings, operatorId) { return cryptoCode === 'ETH' && util.isValidChecksumAddress(toAddress) } -function sendCoins (account, tx, settings, operatorId, feeMultiplier, _opts) { +function sendCoins (account, tx, settings, operatorId, feeMultiplier) { const { toAddress, cryptoAtoms, cryptoCode } = tx - const opts = { ..._opts, includesFee: _.defaultTo(false, _opts?.includesFee) } - return generateTx(toAddress, defaultWallet(account), cryptoAtoms, cryptoCode, opts) + return generateTx(toAddress, defaultWallet(account), cryptoAtoms, false, cryptoCode) .then(pify(web3.eth.sendRawTransaction)) .then(txid => { return pify(web3.eth.getTransaction)(txid) @@ -96,7 +95,7 @@ function _balance (includePending, address, cryptoCode) { .then(balance => balance ? BN(balance) : BN(0)) } -function generateTx (_toAddress, wallet, amount, cryptoCode, opts) { +function generateTx (_toAddress, wallet, amount, includesFee, cryptoCode) { const fromAddress = '0x' + wallet.getAddress().toString('hex') const isErc20Token = coins.utils.isErc20Token(cryptoCode) @@ -126,18 +125,18 @@ function generateTx (_toAddress, wallet, amount, cryptoCode, opts) { .then(([gas, gasPrice, txCount]) => [ BN(gas), BN(gasPrice), - _.max([1, txCount, lastUsedNonces[fromAddress] + 1]) + _.max([0, txCount, lastUsedNonces[fromAddress] + 1]) ]) .then(([gas, gasPrice, txCount]) => { lastUsedNonces[fromAddress] = txCount - const toSend = opts.includesFee + const toSend = includesFee ? amount.minus(gasPrice.times(gas)) : amount const rawTx = { - chainId: _.defaultTo(1, opts?.chainId), - nonce: _.defaultTo(txCount, opts?.nonce), + chainId: 1, + nonce: txCount, gasPrice: hex(gasPrice), gasLimit: hex(gas), to: toAddress, @@ -173,9 +172,8 @@ function sweep (account, cryptoCode, hdIndex, settings, operatorId) { return confirmedBalance(fromAddress, cryptoCode) .then(r => { if (r.eq(0)) return - const opts = { includesFee: true } - return generateTx(defaultAddress(account), wallet, r, cryptoCode, opts) + return generateTx(defaultAddress(account), wallet, r, true, cryptoCode) .then(signedTx => pify(web3.eth.sendRawTransaction)(signedTx)) }) } @@ -239,5 +237,4 @@ function checkBlockchainStatus (cryptoCode) { .then(() => connect(`http://localhost:${coins.utils.getCryptoCurrency(cryptoCode).defaultPort}`)) .then(() => web3.eth.syncing) .then(res => res === false ? 'ready' : 'syncing') - .catch(() => 'disconnected') } diff --git a/lib/wallet.js b/lib/wallet.js index eddeb7f4..1073a8a0 100644 --- a/lib/wallet.js +++ b/lib/wallet.js @@ -59,11 +59,11 @@ function _balance (settings, cryptoCode) { }) } -function sendCoins (settings, tx, opts) { +function sendCoins (settings, tx) { return fetchWallet(settings, tx.cryptoCode) .then(r => { const feeMultiplier = new BN(configManager.getWalletSettings(tx.cryptoCode, settings.config).feeMultiplier) - return r.wallet.sendCoins(r.account, tx, settings, r.operatorId, feeMultiplier, opts) + return r.wallet.sendCoins(r.account, tx, settings, r.operatorId, feeMultiplier) .then(res => { mem.clear(module.exports.balance) return res From f95d3e99920a30bd020ab4ccb124768a70afc781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Mon, 13 Jun 2022 23:21:54 +0100 Subject: [PATCH 3/8] chore: add lockfile once the sweeping finishes --- bin/lamassu-eth-sweep-to-new-wallet | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bin/lamassu-eth-sweep-to-new-wallet b/bin/lamassu-eth-sweep-to-new-wallet index 8f72f438..514425ed 100644 --- a/bin/lamassu-eth-sweep-to-new-wallet +++ b/bin/lamassu-eth-sweep-to-new-wallet @@ -265,7 +265,11 @@ settingsLoader.loadLatest() return fs.rmdir(LOCKFILE_PATH) }) .then(() => { - console.log('lamassu-eth-pending-sweep lock file successfully removed') + console.log('lamassu-eth-pending-sweep lock file successfully removed, you may now execute the upgrade script again') + 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!') process.exit(0) }) From fb46d6e7a96664960674de47ca7519afb2ba2184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Thu, 16 Jun 2022 18:42:20 +0100 Subject: [PATCH 4/8] fix: chain ID --- bin/lamassu-eth-sweep-to-new-wallet | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/lamassu-eth-sweep-to-new-wallet b/bin/lamassu-eth-sweep-to-new-wallet index 514425ed..225075fd 100644 --- a/bin/lamassu-eth-sweep-to-new-wallet +++ b/bin/lamassu-eth-sweep-to-new-wallet @@ -244,7 +244,7 @@ settingsLoader.loadLatest() } const opts = { - chainId: 3, + chainId: 1, nonce: 0, includesFee: true } @@ -265,12 +265,12 @@ settingsLoader.loadLatest() return fs.rmdir(LOCKFILE_PATH) }) .then(() => { - console.log('lamassu-eth-pending-sweep lock file successfully removed, you may now execute the upgrade script again') + 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!') + console.log('Process finished successfully! You may now execute the upgrade script again') process.exit(0) }) .catch(err => { From 4af07a360901cb7eb026a49258dec52ba0e965f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Tue, 21 Jun 2022 08:30:52 +0100 Subject: [PATCH 5/8] fix: always set new web3 providers despite one being previously set feat: separate eth wallet names --- lib/plugins/wallet/geth/base.js | 6 +----- lib/plugins/wallet/geth/geth.js | 10 ++++++++-- lib/plugins/wallet/infura/infura.js | 4 +++- new-lamassu-admin/src/pages/Wallet/Wallet.js | 2 -- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/plugins/wallet/geth/base.js b/lib/plugins/wallet/geth/base.js index d37227a6..2f37b933 100644 --- a/lib/plugins/wallet/geth/base.js +++ b/lib/plugins/wallet/geth/base.js @@ -11,7 +11,6 @@ const pify = require('pify') const BN = require('../../../bn') const ABI = require('../../tokens') -const NAME = 'geth' exports.SUPPORTED_MODULES = ['wallet'] const paymentPrefixPath = "m/44'/60'/0'/0'" @@ -19,7 +18,6 @@ const defaultPrefixPath = "m/44'/60'/1'/0'" let lastUsedNonces = {} module.exports = { - NAME, balance, sendCoins, newAddress, @@ -35,9 +33,7 @@ module.exports = { } function connect (url) { - if (!web3.isConnected()) { - web3.setProvider(new web3.providers.HttpProvider(url)) - } + return web3.setProvider(new web3.providers.HttpProvider(url)) } const hex = bigNum => '0x' + bigNum.integerValue(BN.ROUND_DOWN).toString(16) diff --git a/lib/plugins/wallet/geth/geth.js b/lib/plugins/wallet/geth/geth.js index 53266d90..61b28e73 100644 --- a/lib/plugins/wallet/geth/geth.js +++ b/lib/plugins/wallet/geth/geth.js @@ -1,9 +1,15 @@ +const _ = require('lodash/fp') + const base = require('./base') const { utils: coinUtils } = require('@lamassu/coins') const cryptoRec = coinUtils.getCryptoCurrency('ETH') const defaultPort = cryptoRec.defaultPort -base.connect(`http://localhost:${defaultPort}`) +const NAME = 'geth' -module.exports = base +function run (account) { + base.connect(`http://localhost:${defaultPort}`) +} + +module.exports = _.merge(base, { NAME, run }) diff --git a/lib/plugins/wallet/infura/infura.js b/lib/plugins/wallet/infura/infura.js index 8cff40b4..47d4e35c 100644 --- a/lib/plugins/wallet/infura/infura.js +++ b/lib/plugins/wallet/infura/infura.js @@ -1,6 +1,8 @@ const _ = require('lodash/fp') const base = require('../geth/base') +const NAME = 'infura' + function run (account) { if (!account.endpoint) throw new Error('Need to configure API endpoint for Infura') @@ -10,4 +12,4 @@ function run (account) { base.connect(endpoint) } -module.exports = _.merge(base, {run}) +module.exports = _.merge(base, { NAME, run }) diff --git a/new-lamassu-admin/src/pages/Wallet/Wallet.js b/new-lamassu-admin/src/pages/Wallet/Wallet.js index 31e12c25..b0a9c0c8 100644 --- a/new-lamassu-admin/src/pages/Wallet/Wallet.js +++ b/new-lamassu-admin/src/pages/Wallet/Wallet.js @@ -61,8 +61,6 @@ const Wallet = ({ name: SCREEN_KEY }) => { const [advancedSettings, setAdvancedSettings] = useState(false) const { data } = useQuery(GET_INFO) - console.log(data) - const [saveConfig, { error }] = useMutation(SAVE_CONFIG, { onCompleted: () => setWizard(false), refetchQueries: () => ['getData'] From fa4cfdfcd306bbe89607ffb6e446cd8e7bfac6d9 Mon Sep 17 00:00:00 2001 From: Neal Date: Tue, 21 Jun 2022 16:30:26 -0400 Subject: [PATCH 6/8] fix: update geth, disable new installs --- lib/blockchain/common.js | 4 ++-- lib/blockchain/install.js | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/blockchain/common.js b/lib/blockchain/common.js index 6004dda3..6b9d59f2 100644 --- a/lib/blockchain/common.js +++ b/lib/blockchain/common.js @@ -29,8 +29,8 @@ const BINARIES = { dir: 'bitcoin-23.0/bin' }, ETH: { - url: 'https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.17-25c9b49f.tar.gz', - dir: 'geth-linux-amd64-1.10.17-25c9b49f' + url: 'https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.19-23bee162.tar.gz', + dir: 'geth-linux-amd64-1.10.19-23bee162' }, ZEC: { url: 'https://z.cash/downloads/zcash-5.0.0-linux64-debian-bullseye.tar.gz', diff --git a/lib/blockchain/install.js b/lib/blockchain/install.js index 1191f20e..377d7952 100644 --- a/lib/blockchain/install.js +++ b/lib/blockchain/install.js @@ -148,7 +148,9 @@ function run () { name, value: c.code, checked, - disabled: checked && 'Installed' + disabled: c.cryptoCode === 'ETH' + ? 'Use admin\'s Infura plugin' + : checked && 'Installed' } }), ])(cryptos) From 6826b9df6399cee832e181cb285c69a9b04ba331 Mon Sep 17 00:00:00 2001 From: Neal Date: Tue, 21 Jun 2022 16:42:51 -0400 Subject: [PATCH 7/8] chore: update BCH, LTC --- lib/blockchain/bitcoincash.js | 8 ++++++++ lib/blockchain/common.js | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/blockchain/bitcoincash.js b/lib/blockchain/bitcoincash.js index ef5a2995..d8ac0efe 100644 --- a/lib/blockchain/bitcoincash.js +++ b/lib/blockchain/bitcoincash.js @@ -28,6 +28,13 @@ function updateCore (coinRec, isCurrentlyRunning) { common.es(`rm -r /tmp/${coinRec.dir.replace('/bin', '')}`) common.es(`rm /tmp/bitcoincash.tar.gz`) + if (common.es(`grep "listenonion=" /mnt/blockchains/bitcoincash/bitcoincash.conf || true`)) { + common.logger.info(`listenonion already defined, skipping...`) + } else { + common.logger.info(`Setting 'listenonion=0' in config file...`) + common.es(`echo "\nlistenonion=0" >> /mnt/blockchains/bitcoincash/bitcoincash.conf`) + } + if (isCurrentlyRunning) { common.logger.info('Starting wallet...') common.es(`sudo supervisorctl start bitcoincash`) @@ -47,5 +54,6 @@ prune=4000 daemon=0 bind=0.0.0.0:8335 rpcport=8336 +listenonion=0 ` } diff --git a/lib/blockchain/common.js b/lib/blockchain/common.js index 6004dda3..4c9be42d 100644 --- a/lib/blockchain/common.js +++ b/lib/blockchain/common.js @@ -43,12 +43,12 @@ const BINARIES = { LTC: { defaultUrl: 'https://download.litecoin.org/litecoin-0.18.1/linux/litecoin-0.18.1-x86_64-linux-gnu.tar.gz', defaultDir: 'litecoin-0.18.1/bin', - url: 'https://download.litecoin.org/litecoin-0.21.2/linux/litecoin-0.21.2-x86_64-linux-gnu.tar.gz', - dir: 'litecoin-0.21.2/bin' + url: 'https://download.litecoin.org/litecoin-0.21.2.1/linux/litecoin-0.21.2.1-x86_64-linux-gnu.tar.gz', + dir: 'litecoin-0.21.2.1/bin' }, BCH: { - url: 'https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v24.0.0/bitcoin-cash-node-24.0.0-x86_64-linux-gnu.tar.gz', - dir: 'bitcoin-cash-node-24.0.0/bin', + url: 'https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v24.1.0/bitcoin-cash-node-24.1.0-x86_64-linux-gnu.tar.gz', + dir: 'bitcoin-cash-node-24.1.0/bin', files: [['bitcoind', 'bitcoincashd'], ['bitcoin-cli', 'bitcoincash-cli']] }, XMR: { From b13ea40d218925f3869dd1bcbdeaed935c6ac7c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Wed, 22 Jun 2022 02:41:41 +0100 Subject: [PATCH 8/8] fix: change operation order to ensure the safety of funds in unexpected conditions --- bin/lamassu-eth-sweep-to-new-wallet | 30 ++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/bin/lamassu-eth-sweep-to-new-wallet b/bin/lamassu-eth-sweep-to-new-wallet index 225075fd..8cb2ec6b 100644 --- a/bin/lamassu-eth-sweep-to-new-wallet +++ b/bin/lamassu-eth-sweep-to-new-wallet @@ -27,7 +27,13 @@ let lastUsedNonces = {} const hex = bigNum => '0x' + bigNum.integerValue(BN.ROUND_DOWN).toString(16) function writeNewMnemonic (mnemonic) { - return fs.writeFile(options.mnemonicPath, mnemonic) + return fs.writeFile(`${options.mnemonicPath}-new-temp`, mnemonic) + .then(() => `${options.mnemonicPath}-new-temp`) +} + +function renameNewMnemonic () { + return fs.rename(`${options.mnemonicPath}-new-temp`, `${options.mnemonicPath}`) + .then(() => options.mnemonicPath) } function backupMnemonic () { @@ -225,6 +231,19 @@ settingsLoader.loadLatest() 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 @@ -244,7 +263,7 @@ settingsLoader.loadLatest() } const opts = { - chainId: 1, + chainId: 3, nonce: 0, includesFee: true } @@ -254,11 +273,8 @@ settingsLoader.loadLatest() .then(resTx => { console.log('Successfully moved funds from the old wallet to the new one.') console.log('Information about the transaction', resTx) - return backupMnemonic() - }) - .then(fileName => { - console.log(`Successfully backed up the old mnemonic, new location is ${fileName}`) - return writeNewMnemonic(mnemonic) + 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.')