bitcore-node
Version:
A blockchain indexing node with extended capabilities using bitcore
134 lines (117 loc) • 5.66 kB
JavaScript
const { WalletStorage } = require('../build/src/models/wallet');
const { WalletAddressStorage } = require('../build/src/models/walletAddress');
const { Storage } = require('../build/src/services/storage');
const { wait } = require('../build/src/utils');
function usage(errMsg) {
console.log('USAGE: ./migrateTestnetWallets.js [options]');
console.log('OPTIONS:');
console.log(' --chain <value> REQUIRED - e.g. BTC, BCH, DOGE, LTC...');
console.log(' --oldNetwork <value> REQUIRED - e.g. testnet3');
console.log(' --newNetwork <value> REQUIRED - e.g. testnet4');
console.log(' --batchSize <value> Number of documents to update at a time. Default: 10000');
console.log(' --deDup Delete duplicate walletAddresses (dups can happen if an updated wallet is queried before addresses are updated)');
console.log(' --doit Save the migration to the db. Make sure you stop sync services before running this script');
if (errMsg) {
console.log('\nERROR: ' + errMsg);
}
process.exit();
}
const args = process.argv.slice(2);
if (args.includes('--help') || args.includes('-h')) {
usage();
}
const chainIdx = args.indexOf('--chain');
const oldNetworkIdx = args.indexOf('--oldNetwork');
const newNetworkIdx = args.indexOf('--newNetwork');
const chain = args[chainIdx + 1]?.toUpperCase();
const oldNetwork = args[oldNetworkIdx + 1]?.toLowerCase();
const newNetwork = args[newNetworkIdx + 1]?.toLowerCase();
const deDup = args.includes('--deDup');
const dryRun = !args.includes('--doit');
if (
chainIdx === -1 ||
oldNetworkIdx === -1 ||
newNetworkIdx === -1 ||
!chain ||
!oldNetwork ||
!newNetwork
) {
usage('Missing required options.');
}
const batchSizeIdx = args.indexOf('--batchSize');
const batchSize = (batchSizeIdx > -1 && parseInt(args[batchSizeIdx + 1])) || 10000;
if (batchSize > -1 && isNaN(parseInt(args[batchSizeIdx + 1]))) {
usage('batchSize must be an integer');
}
let quit = false;
process.on('SIGINT', () => {
if (quit) {
process.exit(1);
}
console.log('Caught interrupt signal');
quit = true;
});
async function deleteOldAddress(e) {
let [_, c, n, w, a] = e.message.split('{')[1].split(':');
c = c.trim().replace(/"/g, '').replace(/,/g, '');
n = n.trim().replace(/"/g, '').replace(/,/g, '');
w = w.trim().replace('ObjectId(', '').replace(')', '').replace(/'/g, '').replace(/,/g, '');
a = a.replace('}', '').trim().replace(/"/g, '').replace(/,/g, '');
console.log('Deleting dup:', c, n, w, a);
const del1 = await WalletAddressStorage.collection.findOne({ chain, network: oldNetwork, wallet: new ObjectID(w), address: a });
const del2 = await WalletAddressStorage.collection.findOne({ chain, network: newNetwork, wallet: new ObjectID(w), address: a });
console.log(del1);
console.log(del2);
if (!!del1 && !!del2) {
const del = await WalletAddressStorage.collection.deleteOne({ chain, network: oldNetwork, wallet: new ObjectID(w), address: a });
console.log(del.deletedCount);
}
};
Storage.start()
.then(async () => {
console.log('Connected to the database');
const cnt1 = await WalletStorage.collection.countDocuments({ chain, network: oldNetwork });
const cnt2 = await WalletAddressStorage.collection.countDocuments({ chain, network: oldNetwork });
console.log(`${chain} ${oldNetwork} wallets:`, cnt1);
console.log(`${chain} ${oldNetwork} walletAddresses:`, cnt2);
if (dryRun) {
return;
}
console.log('----------------------------------------');
console.log(`Updating ${chain} ${oldNetwork} => ${newNetwork} wallets in 10 seconds...`);
!quit && await wait(10000);
console.log(`Updating ${chain} ${oldNetwork} wallets...`);
let wallets = await WalletStorage.collection.find({ chain, network: oldNetwork }).project({ _id: 1 }).limit(batchSize).toArray();
let walletsUpdated = 0;
while (wallets.length > 0 && !quit) {
const res = await WalletStorage.collection.updateMany({ _id: { $in: wallets.map(c => c._id) } }, { $set: { network: newNetwork } });
walletsUpdated += res.modifiedCount;
process.stdout.write(`Updated ${walletsUpdated} wallets (${(walletsUpdated / cnt1 * 100).toFixed(2)}%)\r`);
await wait(250);
wallets = await WalletStorage.collection.find({ chain, network: oldNetwork }).project({ _id: 1 }).limit(batchSize).toArray()
}
console.log(`Updating ${chain} ${oldNetwork} => ${newNetwork} walletAddresses in 10 seconds...`);
!quit && await wait(10000);
console.log(`Updating ${chain} ${oldNetwork} walletAddresses...`);
let walletAddresses = await WalletAddressStorage.collection.find({ chain, network: oldNetwork }).project({ _id: 1 }).limit(batchSize).toArray();
let walletAddressesUpdated = 0;
while (walletAddresses.length > 0 && !quit) {
try {
const res = await WalletAddressStorage.collection.updateMany({ _id: { $in: walletAddresses.map(c => c._id) } }, { $set: { network: newNetwork } });
walletAddressesUpdated += res.modifiedCount;
} catch (e) {
if (e.message.includes('E11000') && deDup) {
await deleteOldAddress(e);
} else { throw e; }
}
process.stdout.write(`Updated ${walletAddressesUpdated} walletAddresses (${(walletAddressesUpdated / cnt2 * 100).toFixed(2)}%)\r`);
await wait(250);
walletAddresses = await WalletAddressStorage.collection.find({ chain, network: oldNetwork }).project({ _id: 1 }).limit(batchSize).toArray()
}
})
.catch(console.error)
.finally(() => {
console.log('Closing the database connection');
Storage.stop();
});