From f74c94ef50c768beef45caa00e5ae4a9febc6479 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 24 Dec 2022 07:34:54 -0800 Subject: [PATCH] fix(sweep): Adding fullstack-sweep-wallet.js for faster wallet sweeping --- package-lock.json | 5 +- production/scripts/bulk-sweep-wallet.js | 153 +++++++++++++++++++ production/scripts/fullstack-sweep-wallet.js | 111 ++++++++++++++ production/scripts/sweep-wallet.js | 2 - src/adapters/wallet.js | 1 + 5 files changed, 269 insertions(+), 3 deletions(-) create mode 100644 production/scripts/bulk-sweep-wallet.js create mode 100644 production/scripts/fullstack-sweep-wallet.js diff --git a/package-lock.json b/package-lock.json index 130852b..031bb4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7157,7 +7157,8 @@ }, "node_modules/jwt-bch-lib": { "version": "1.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jwt-bch-lib/-/jwt-bch-lib-1.3.0.tgz", + "integrity": "sha512-CZs3jUHJlWvConvoTeLDogDEwUysKeojYJxfUhbwJKJrtDCSfi3ZWbsZZ8WN30Xs28aZPDB+qlHPJlTXQmup0w==", "dependencies": { "axios": "^0.21.1" } @@ -20527,6 +20528,8 @@ }, "jwt-bch-lib": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jwt-bch-lib/-/jwt-bch-lib-1.3.0.tgz", + "integrity": "sha512-CZs3jUHJlWvConvoTeLDogDEwUysKeojYJxfUhbwJKJrtDCSfi3ZWbsZZ8WN30Xs28aZPDB+qlHPJlTXQmup0w==", "requires": { "axios": "^0.21.1" }, diff --git a/production/scripts/bulk-sweep-wallet.js b/production/scripts/bulk-sweep-wallet.js new file mode 100644 index 0000000..3729685 --- /dev/null +++ b/production/scripts/bulk-sweep-wallet.js @@ -0,0 +1,153 @@ +/* + This script is similar ot sweep-wallet.js, but it can do a much faster (bulk) + scanning of addresses. However, this requires a subscription to a FullStack.cash + or access to a web2 Cash Stack. + + This script will scan 20 addresses at a time. If a balance is found in any + of the addresses, it will sweep the funds from that address. +*/ +/* +// Public npm libraries +import BchTokenSweep from 'bch-token-sweep/index.js' + +// Local libraries +import WalletAdapter from '../../src/adapters/wallet.js' +import BCHJS from '@psf/bch-js' +import JwtLib from 'jwt-bch-lib' + +// Constants +// const EMTPY_ADDR_CUTOFF = 100 +const LAST_ADDR_INDEX = 100 + +if (!process.env.BCHJSTOKEN) { + console.log('You will need a JWT token from FullStack.cash to execute this script. Export it to the BCHJSTOKEN environment variable and try again.') + exit(0) +} + +async function sweepFunds () { + try { + // Configure the wallet library to use a FullStack.cash or a local Cash Stack + const walletLib = new WalletAdapter() + walletLib.config.useFullStackCash = true + walletLib.config.apiServer = 'http://192.168.2.129:3000/v5/' + + // const walletInfo = await walletLib.openWallet() + const walletInfo = await openWallet(walletLib) + const wallet = await walletLib.instanceWallet(walletInfo) + console.log('walletInfo: ', walletInfo) + + const rootAddr = walletInfo.cashAddress + const rootWif = walletInfo.privateKey + console.log(`Sweeping all funds into root address ${rootAddr}...`) + + // Instantiate bch-js, and use the JWT token saved to environment variable. + + // Generate an HD tree + // const bchjs = wallet.bchjs + const bchjs = new BCHJS() + const rootSeed = await bchjs.Mnemonic.toSeed(walletInfo.mnemonic) + const masterHDNode = bchjs.HDNode.fromSeed(rootSeed) + + const emptyAddrCnt = 0 + const hdIndex = 1 + + // Divide all the addresses by 20, since that's how many will be checked at once. + const numOfLoops = Math.ceil(LAST_ADDR_INDEX / 20) + + for (let i = 1; i < numOfLoops; i++) { + const startIndex = i + const stopIndex = i * 20 + console.log(`startIndex: ${startIndex}, stopIndex: ${stopIndex}`) + + // Generate the next block of 20 key pairs to scan. + const addrsToScan = [] + const wifsToScan = [] + for (let j = startIndex; j < stopIndex; j++) { + // Generate a keypair from the HD wallet. + const childNode = masterHDNode.derivePath(`m/44'/245'/0'/0/${j}`) + const cashAddress = bchjs.HDNode.toCashAddress(childNode) + const wifToSweep = bchjs.HDNode.toWIF(childNode) + + addrsToScan.push(cashAddress) + wifsToScan.push(wifToSweep) + } + console.log(`Scanning these addresses for a balance: ${JSON.stringify(addrsToScan, null, 2)}`) + + // Scan a block of 20 addresses for a balance. + const balances = await bchjs.Electrumx.balance(addrsToScan) + console.log(`balances: ${JSON.stringify(balances, null, 2)}`) + + // Scan through the results and look for a balance. + for (let j = 0; j < balances.balances.length; j++) { + const thisBalance = balances.balances[j] + + // const combinedBal = thisBalance.balance.confirmed + thisBalance.balance. + + // if(thisBalance.) + } + } + + // do { + // // Generate a keypair from the HD wallet. + // const childNode = masterHDNode.derivePath(`m/44'/245'/0'/0/${hdIndex}`) + // const cashAddress = bchjs.HDNode.toCashAddress(childNode) + // const wifToSweep = bchjs.HDNode.toWIF(childNode) + // + // console.log(`\nSweeping ${cashAddress} with private key ${wifToSweep}`) + // + // try { + // // Sweep tokens from address + // const sweeper = new BchTokenSweep( + // wifToSweep, + // rootWif, + // wallet, + // 550, + // rootAddr + // ) + // await sweeper.populateObjectFromNetwork() + // + // const hex = await sweeper.sweepTo(rootAddr) + // // console.log(`hex: ${hex}`) + // + // const txid = await sweeper.blockchain.broadcast(hex) + // + // // console.log('Transaction ID', txid) + // console.log(`Swept HD index ${hdIndex}. TXID: ${txid}`) + // + // emptyAddrCnt = 0 + // + // // Wait between loop iterations. + // await bchjs.Util.sleep(3000) + // } catch (err) { + // console.log(`error message with index ${hdIndex}: ${err.message}`) + // // console.log(err) + // emptyAddrCnt++ + // } + // + // hdIndex++ + // } while (emptyAddrCnt < EMTPY_ADDR_CUTOFF) + // + // console.log(`${EMTPY_ADDR_CUTOFF} empty addresses detected. Exiting.`) + // + // console.log('\n\nDo not forget to reset the nextAddress property in the wallet.json file!\n\n') + } catch (err) { + console.error('Error in sweepFunds(): ', err) + } +} +sweepFunds() + +// Open the wallet file, or create one if the file doesn't exist. +// Does not instance the wallet. The output of this function is expected to +// be passed to instanceWallet(). +async function openWallet (walletLib) { + try { + // console.log('this.WALLET_FILE: ', this.WALLET_FILE) + const walletData = await walletLib.jsonFiles.readJSON('./wallet.json') + + return walletData + } catch (err) { + console.error('Error in openWallet()') + throw err + } +} +*/ diff --git a/production/scripts/fullstack-sweep-wallet.js b/production/scripts/fullstack-sweep-wallet.js new file mode 100644 index 0000000..5dd21df --- /dev/null +++ b/production/scripts/fullstack-sweep-wallet.js @@ -0,0 +1,111 @@ +/* + This script is the same as sweep-wallet.js, but it requires a FullStack.cash + account, which makes the scanning much faster. +*/ + +// Public npm libraries +// const BCHJS = require('@psf/bch-js') +import BchTokenSweep from 'bch-token-sweep/index.js' + +// Local libraries +import WalletAdapter from '../../src/adapters/wallet.js' + +// Constants +// const EMTPY_ADDR_CUTOFF = 10 +const START_INDEX = 1 +const LAST_ADDR_INDEX = 100 + +if (!process.env.BCHJSTOKEN) { + console.log('You will need a JWT token from FullStack.cash to execute this script. Export it to the BCHJSTOKEN environment variable and try again.') + process.exit(0) +} + +async function sweepFunds () { + try { + // Configure the wallet library to use a FullStack.cash or a local Cash Stack + const walletLib = new WalletAdapter() + walletLib.config.useFullStackCash = true + // walletLib.config.apiServer = 'http://192.168.2.129:3000/v5/' + walletLib.config.apiServer = 'https://api.fullstack.cash/v5/' + + // Open the wallet files. + const walletInfo = await openWallet(walletLib) + const wallet = await walletLib.instanceWallet(walletInfo) + console.log('walletInfo: ', walletInfo) + + const rootAddr = walletInfo.cashAddress + const rootWif = walletInfo.privateKey + console.log(`Sweeping all funds into root address ${rootAddr}...`) + + // Generate an HD tree + const bchjs = wallet.bchjs + const rootSeed = await bchjs.Mnemonic.toSeed(walletInfo.mnemonic) + const masterHDNode = bchjs.HDNode.fromSeed(rootSeed) + + let emptyAddrCnt = 0 + let hdIndex = START_INDEX + + do { + // Generate a keypair from the HD wallet. + const childNode = masterHDNode.derivePath(`m/44'/245'/0'/0/${hdIndex}`) + const cashAddress = bchjs.HDNode.toCashAddress(childNode) + const wifToSweep = bchjs.HDNode.toWIF(childNode) + + console.log(`\nSweeping ${cashAddress} with private key ${wifToSweep}`) + + try { + // Sweep tokens from address + const sweeper = new BchTokenSweep( + wifToSweep, + rootWif, + wallet, + 550, + rootAddr + ) + await sweeper.populateObjectFromNetwork() + + const hex = await sweeper.sweepTo(rootAddr) + // console.log(`hex: ${hex}`) + + const txid = await sweeper.blockchain.broadcast(hex) + + // console.log('Transaction ID', txid) + console.log(`Swept HD index ${hdIndex}. TXID: ${txid}`) + + emptyAddrCnt = 0 + + // Wait between loop iterations. + await bchjs.Util.sleep(3000) + } catch (err) { + console.log(`error message with index ${hdIndex}: ${err.message}`) + // console.log(err) + emptyAddrCnt++ + } + + hdIndex++ + // } while (emptyAddrCnt < EMTPY_ADDR_CUTOFF) + // console.log(`${EMTPY_ADDR_CUTOFF} empty addresses detected. Exiting.`) + } while (hdIndex < LAST_ADDR_INDEX) + console.log(`Last index of ${LAST_ADDR_INDEX} reached. emptyAddrCnt: ${emptyAddrCnt}`) + + console.log('\n\nDo not forget to reset the nextAddress property in the wallet.json file!\n\n') + } catch (err) { + console.error('Error in sweepFunds(): ', err) + } +} +sweepFunds() + +// Open the wallet file, or create one if the file doesn't exist. +// Does not instance the wallet. The output of this function is expected to +// be passed to instanceWallet(). +async function openWallet (walletLib) { + try { + // console.log('this.WALLET_FILE: ', this.WALLET_FILE) + const walletData = await walletLib.jsonFiles.readJSON('./wallet.json') + + return walletData + } catch (err) { + console.error('Error in openWallet()') + throw err + } +} diff --git a/production/scripts/sweep-wallet.js b/production/scripts/sweep-wallet.js index f4ba533..1ffcfd9 100644 --- a/production/scripts/sweep-wallet.js +++ b/production/scripts/sweep-wallet.js @@ -2,8 +2,6 @@ This script will travers the HD wallet and sweep funds and tokens back into the root address (index 0). That root address needs to have funds to pay for the transactions. - - This script will sweep BCH and fungible (Type 1) tokens, but not NFTs. */ // Public npm libraries diff --git a/src/adapters/wallet.js b/src/adapters/wallet.js index 3d1fd97..a40e99b 100644 --- a/src/adapters/wallet.js +++ b/src/adapters/wallet.js @@ -34,6 +34,7 @@ class WalletAdapter { this.bitcoinJs = bitcoinJs this.BchWallet = BchWallet this.bchWallet = {} // Will be replaced when initialized. + // this.advancedConfig = localConfig.advancedConfig // Bind the 'this' object this.moveTokens = this.moveTokens.bind(this)