bitcore-node
Version:
A blockchain indexing node with extended capabilities using bitcore
338 lines • 16 kB
JavaScript
"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