UNPKG

bitcore-node

Version:

A blockchain indexing node with extended capabilities using bitcore

338 lines 16 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const bitcore_client_1 = require("bitcore-client"); const chai_1 = require("chai"); const io = __importStar(require("socket.io-client")); const config_1 = __importDefault(require("../../src/config")); const coin_1 = require("../../src/models/coin"); const transaction_1 = require("../../src/models/transaction"); const wallet_1 = require("../../src/models/wallet"); const walletAddress_1 = require("../../src/models/walletAddress"); const p2p_1 = require("../../src/modules/bitcoin/p2p"); const rpc_1 = require("../../src/rpc"); const api_1 = require("../../src/services/api"); const event_1 = require("../../src/services/event"); const utils_1 = require("../../src/utils"); const wallet_benchmark_1 = require("../benchmark/wallet-benchmark"); const helpers_1 = require("../helpers"); const integration_1 = require("../helpers/integration"); const chain = 'BTC'; const network = 'regtest'; const chainConfig = config_1.default.chains[chain][network]; const creds = chainConfig.rpc; const rpc = new rpc_1.AsyncRPC(creds.username, creds.password, creds.host, creds.port); async function checkWalletExists(pubKey, expectedAddress) { // Check the database for the first wallet const dbWallet = await wallet_1.WalletStorage.collection.findOne({ chain, network, pubKey }); // Verify the addresses match const foundAddresses = await walletAddress_1.WalletAddressStorage.collection .find({ chain, network, wallet: dbWallet._id }) .toArray(); (0, chai_1.expect)(foundAddresses.length).to.eq(1); (0, chai_1.expect)(foundAddresses[0].address).to.eq(expectedAddress); return dbWallet; } async function getWalletUtxos(wallet) { const utxos = new Array(); return new Promise(resolve => wallet .getUtxos() .pipe(new bitcore_client_1.ParseApiStream()) .on('data', (utxo) => { utxos.push(utxo); }) .on('end', () => resolve(utxos))); } async function checkWalletUtxos(wallet, expectedAddress) { const utxos = await getWalletUtxos(wallet); (0, chai_1.expect)(utxos.length).to.eq(1); (0, chai_1.expect)(utxos[0].address).to.eq(expectedAddress); return utxos; } async function verifyCoinSpent(coin, spentTxid, wallet) { const wallet1Coin = await coin_1.CoinStorage.collection.findOne({ chain: coin.chain, network: coin.network, mintTxid: coin.mintTxid, mintIndex: coin.mintIndex, }); (0, chai_1.expect)(wallet1Coin.spentTxid).to.eq(spentTxid); (0, chai_1.expect)(wallet1Coin.wallets[0].toHexString()).to.eq(wallet._id.toHexString()); } async function checkWalletReceived(receivingWallet, txid, address, sendingWallet) { const broadcastedOutput = await coin_1.CoinStorage.collection.findOne({ chain, network, mintTxid: txid, address }); (0, chai_1.expect)(broadcastedOutput.address).to.eq(address); (0, chai_1.expect)(broadcastedOutput.wallets.length).to.eq(1); (0, chai_1.expect)(broadcastedOutput.wallets[0].toHexString()).to.eq(receivingWallet._id.toHexString()); const broadcastedTransaction = await transaction_1.TransactionStorage.collection.findOne({ chain, network, txid }); (0, chai_1.expect)(broadcastedTransaction.txid).to.eq(txid); (0, chai_1.expect)(broadcastedTransaction.fee).gt(0); const txWallets = broadcastedTransaction.wallets.map(w => w.toHexString()); (0, chai_1.expect)(txWallets.length).to.eq(2); (0, chai_1.expect)(txWallets).to.include(receivingWallet._id.toHexString()); (0, chai_1.expect)(txWallets).to.include(sendingWallet._id.toHexString()); } describe('Wallet Benchmark', function () { const suite = this; this.timeout(5000000); let p2pWorker; before(async () => { await (0, integration_1.intBeforeHelper)(); await event_1.Event.start(); await api_1.Api.start(); }); after(async () => { await event_1.Event.stop(); await api_1.Api.stop(); await (0, integration_1.intAfterHelper)(suite); }); beforeEach(async () => { await (0, helpers_1.resetDatabase)(); }); afterEach(async () => { if (p2pWorker) { await p2pWorker.stop(); } }); describe('Wallet import', () => { it('should be able to create two wallets and have them interact', async () => { const seenCoins = new Set(); const socket = io.connect('http://localhost:3000', { transports: ['websocket'] }); const connected = new Promise(r => { socket.on('connect', () => { const room = `/${chain}/${network}/inv`; socket.emit('room', room); console.log('Connected to socket'); r(); }); }); await connected; socket.on('coin', (coin) => { seenCoins.add(coin.mintTxid); }); p2pWorker = new p2p_1.BitcoinP2PWorker({ chain, network, chainConfig }); await p2pWorker.start(); const address1 = await rpc.getnewaddress(''); const address2 = await rpc.getnewaddress(''); const anAddress = 'mkzAfSHtmTh5Xsc352jf6TBPj55Lne5g21'; try { await rpc.call('generatetoaddress', [1, address1]); await rpc.call('generatetoaddress', [1, address2]); await rpc.call('generatetoaddress', [100, anAddress]); await p2pWorker.syncDone(); const wallet1 = await (0, wallet_benchmark_1.createWallet)([address1], 0, network); const wallet2 = await (0, wallet_benchmark_1.createWallet)([address2], 1, network); const dbWallet1 = await checkWalletExists(wallet1.authPubKey, address1); const dbWallet2 = await checkWalletExists(wallet2.authPubKey, address2); const utxos = await checkWalletUtxos(wallet1, address1); await checkWalletUtxos(wallet2, address2); const tx = await rpc.call('createrawtransaction', [ utxos.map(utxo => ({ txid: utxo.mintTxid, vout: utxo.mintIndex })), { [address1]: 0.1, [address2]: 0.1 } ]); const fundedTx = await rpc.call('fundrawtransaction', [tx]); const signedTx = await rpc.signrawtx(fundedTx.hex); const broadcastedTx = await rpc.call('sendrawtransaction', [signedTx.hex]); while (!seenCoins.has(broadcastedTx)) { console.log('...WAITING...'); // TODO await (0, utils_1.wait)(1000); } await verifyCoinSpent(utxos[0], broadcastedTx, dbWallet1); await checkWalletReceived(dbWallet1, broadcastedTx, address1, dbWallet2); await checkWalletReceived(dbWallet2, broadcastedTx, address2, dbWallet1); await (0, utils_1.wait)(1000); await socket.disconnect(); await p2pWorker.stop(); } catch (e) { console.log('Error : ', e); (0, chai_1.expect)(e).to.be.undefined; } }); it('should be able to create two wallets and have them interact, while syncing', async () => { const seenCoins = new Set(); const socket = io.connect('http://localhost:3000', { transports: ['websocket'] }); const connected = new Promise(r => { socket.on('connect', () => { const room = `/${chain}/${network}/inv`; socket.emit('room', room); console.log('Connected to socket'); r(); }); }); await connected; socket.on('coin', (coin) => { seenCoins.add(coin.mintTxid); }); p2pWorker = new p2p_1.BitcoinP2PWorker({ chain, network, chainConfig }); await p2pWorker.start(); const address1 = await rpc.getnewaddress(''); const address2 = await rpc.getnewaddress(''); const anAddress = 'mkzAfSHtmTh5Xsc352jf6TBPj55Lne5g21'; try { await rpc.call('generatetoaddress', [1, address1]); await rpc.call('generatetoaddress', [1, address2]); // mature coins await rpc.call('generatetoaddress', [100, anAddress]); await p2pWorker.syncDone(); const wallet1 = await (0, wallet_benchmark_1.createWallet)([address1], 2, network); const wallet2 = await (0, wallet_benchmark_1.createWallet)([address2], 3, network); const dbWallet1 = await checkWalletExists(wallet1.authPubKey, address1); const dbWallet2 = await checkWalletExists(wallet2.authPubKey, address2); const utxos = await checkWalletUtxos(wallet1, address1); await checkWalletUtxos(wallet2, address2); const tx = await rpc.call('createrawtransaction', [ utxos.map(utxo => ({ txid: utxo.mintTxid, vout: utxo.mintIndex })), { [address1]: 0.1, [address2]: 0.1 } ]); const fundedTx = await rpc.call('fundrawtransaction', [tx]); const signedTx = await rpc.signrawtx(fundedTx.hex); await rpc.call('generatetoaddress', [100, anAddress]); p2pWorker.sync(); (0, chai_1.expect)(p2pWorker.isSyncing).to.be.true; // Generate some blocks for the node to process const broadcastedTx = await rpc.call('sendrawtransaction', [signedTx.hex]); (0, chai_1.expect)(p2pWorker.isSyncing).to.be.true; while (!seenCoins.has(broadcastedTx)) { console.log('...WAITING...'); // TODO await (0, utils_1.wait)(1000); } await verifyCoinSpent(utxos[0], broadcastedTx, dbWallet1); await checkWalletReceived(dbWallet1, broadcastedTx, address1, dbWallet2); await checkWalletReceived(dbWallet2, broadcastedTx, address2, dbWallet1); await (0, utils_1.wait)(1000); await socket.disconnect(); await p2pWorker.stop(); } catch (e) { console.log('Error : ', e); (0, chai_1.expect)(e).to.be.undefined; } }); it('should import all addresses and verify in database while below 300 mb of heapUsed memory', async () => { let smallAddressBatch = new Array(); let mediumAddressBatch = new Array(); let largeAddressBatch = new Array(); console.log('Generating small batch of addresses'); for (let i = 0; i < 10; i++) { let address = await rpc.getnewaddress(''); smallAddressBatch.push(address); } console.log('Generating medium batch of addresses'); (0, chai_1.expect)(smallAddressBatch.length).to.deep.equal(10); for (let i = 0; i < 100; i++) { let address = await rpc.getnewaddress(''); mediumAddressBatch.push(address); } (0, chai_1.expect)(mediumAddressBatch.length).to.deep.equal(100); console.log('Generating large batch of addresses'); for (let i = 0; i < 1000; i++) { let address = await rpc.getnewaddress(''); largeAddressBatch.push(address); } (0, chai_1.expect)(largeAddressBatch.length).to.deep.equal(1000); console.log('Checking'); const importedWallet1 = await (0, wallet_benchmark_1.createWallet)(smallAddressBatch, 0, network); const importedWallet2 = await (0, wallet_benchmark_1.createWallet)(mediumAddressBatch, 1, network); const importedWallet3 = await (0, wallet_benchmark_1.createWallet)(largeAddressBatch, 2, network); (0, chai_1.expect)(importedWallet1).to.not.be.null; (0, chai_1.expect)(importedWallet2).to.not.be.null; (0, chai_1.expect)(importedWallet3).to.not.be.null; const foundSmallAddressBatch = await walletAddress_1.WalletAddressStorage.collection .find({ chain, network, address: { $in: smallAddressBatch } }) .toArray(); const smallAddresses = foundSmallAddressBatch.map(wa => wa.address); for (let address of smallAddressBatch) { (0, chai_1.expect)(smallAddresses.includes(address)).to.be.true; } (0, chai_1.expect)(foundSmallAddressBatch.length).to.have.deep.equal(smallAddressBatch.length); const foundMediumAddressBatch = await walletAddress_1.WalletAddressStorage.collection .find({ chain, network, address: { $in: mediumAddressBatch } }) .toArray(); const mediumAddresses = foundMediumAddressBatch.map(wa => wa.address); for (let address of mediumAddressBatch) { (0, chai_1.expect)(mediumAddresses.includes(address)).to.be.true; } (0, chai_1.expect)(foundMediumAddressBatch.length).to.have.deep.equal(mediumAddressBatch.length); const foundLargeAddressBatch = await walletAddress_1.WalletAddressStorage.collection .find({ chain, network, address: { $in: largeAddressBatch } }) .toArray(); const largeAddresses = foundLargeAddressBatch.map(wa => wa.address); for (let address of largeAddressBatch) { (0, chai_1.expect)(largeAddresses.includes(address)).to.be.true; } (0, chai_1.expect)(foundLargeAddressBatch.length).to.have.deep.equal(largeAddressBatch.length); const { heapUsed } = process.memoryUsage(); (0, chai_1.expect)(heapUsed).to.be.below(3e8); }); }); }); //# sourceMappingURL=wallet-benchmark.spec.js.map