bitcore-node
Version:
A blockchain indexing node with extended capabilities using bitcore
292 lines (264 loc) • 9.33 kB
text/typescript
import { expect } from 'chai';
import sinon from 'sinon';
import * as io from 'socket.io-client';
import config from '../../src/config';
import { BitcoinP2PWorker } from '../../src/modules/bitcoin/p2p';
import { AsyncRPC } from '../../src/rpc';
import { Api } from '../../src/services/api';
import { Event } from '../../src/services/event';
import { IUtxoNetworkConfig } from '../../src/types/Config';
import { resetDatabase } from '../helpers';
const { PrivateKey } = require('bitcore-lib');
const chain = 'BTC';
const network = 'regtest';
const chainConfig = config.chains[chain][network] as IUtxoNetworkConfig;
const creds = chainConfig.rpc;
const rpc = new AsyncRPC(creds.username, creds.password, creds.host, creds.port);
import { Client } from 'bitcore-client';
import { WalletStorage } from '../../src/models/wallet';
import { WalletAddressStorage } from '../../src/models/walletAddress';
import { Socket } from '../../src/services/socket';
import { wait } from '../../src/utils';
import { intAfterHelper, intBeforeHelper } from '../helpers/integration';
function getSocket() {
const socket = io.connect('http://localhost:3000', { transports: ['websocket'] });
return socket;
}
let p2pWorker: BitcoinP2PWorker;
let socket = getSocket();
const bwsPrivKey = new PrivateKey();
const bwsKey = bwsPrivKey.publicKey.toString('hex');
const authKey = new PrivateKey();
const pubKey = authKey.publicKey.toString('hex');
const address = '2MuYKLUaKCenkEpwPkWUwYpBoDBNA2dgY3t';
const sandbox = sinon.createSandbox();
describe('Websockets', function() {
const suite = this;
this.timeout(60000);
before(async () => {
intBeforeHelper();
sandbox.stub(Socket.serviceConfig, 'bwsKeys').value([bwsKey]);
await resetDatabase();
await Event.start();
await Api.start();
const inserted = await WalletStorage.collection.insertOne({
chain,
network,
name: 'WalletSocketTest',
singleAddress: false,
pubKey,
path: ''
});
await WalletAddressStorage.collection.insertOne({
address,
chain,
network,
processed: true,
wallet: inserted.insertedId
});
});
after(async () => {
await Event.stop();
await Api.stop();
await resetDatabase();
await intAfterHelper(suite);
});
beforeEach(async () => {
socket = getSocket();
const connected = new Promise<void>(r => {
socket.on('connect', () => {
console.log('Socket connected');
r();
});
});
await connected;
p2pWorker = new BitcoinP2PWorker({
chain,
network,
chainConfig
});
console.log('Starting p2p worker');
p2pWorker.start();
if (p2pWorker.isSyncing) {
console.log('Worker is syncing. Waiting til done');
await p2pWorker.syncDone();
}
console.log('Waiting til p2p done');
await p2pWorker.waitTilSync();
console.log('P2P done');
});
afterEach(async () => {
try {
console.log('Stopping p2p worker');
await p2pWorker.stop();
console.log('Disconnecting socket');
await socket.disconnect();
} catch (e) {
console.log('Error stopping p2p worker');
}
});
it('should get websocket events', async () => {
socket.emit('room', '/BTC/regtest/inv');
let hasSeenTxEvent = false;
let hasSeenBlockEvent = false;
let hasSeenCoinEvent = false;
const anAddress = await rpc.getnewaddress('');
let sawEvents = new Promise<void>(async resolve => {
socket.on('block', () => {
hasSeenBlockEvent = true;
console.log('Block event received');
if (hasSeenTxEvent && hasSeenCoinEvent && hasSeenBlockEvent) {
resolve();
}
});
socket.on('tx', () => {
hasSeenTxEvent = true;
console.log('Transaction event received');
if (hasSeenTxEvent && hasSeenCoinEvent && hasSeenBlockEvent) {
resolve();
}
});
socket.on('coin', () => {
hasSeenCoinEvent = true;
console.log('Coin event received');
if (hasSeenTxEvent && hasSeenCoinEvent && hasSeenBlockEvent) {
resolve();
}
});
while (!hasSeenBlockEvent) {
console.log('WAITING FOR BLOCK EVENT ON SOCKET');
if (hasSeenTxEvent && hasSeenCoinEvent && hasSeenBlockEvent) {
return resolve();
}
await wait(1000);
}
});
console.log('Generating 100 blocks');
await rpc.call('generatetoaddress', [101, anAddress]);
await p2pWorker.syncDone();
console.log('Sync done, generating new block');
await rpc.call('generatetoaddress', [1, anAddress]);
console.log('Sending bitcoin');
await rpc.sendtoaddress(address, 0.1);
await sawEvents;
expect(hasSeenBlockEvent).to.equal(true);
expect(hasSeenTxEvent).to.equal(true);
expect(hasSeenCoinEvent).to.equal(true);
});
it('should get wallet events', async () => {
const authClient = new Client({ apiUrl: 'http://localhost:3000/api', authKey });
const payload = { method: 'socket', url: 'http://localhost:3000/api' };
const authPayload = { pubKey, message: authClient.getMessage(payload), signature: authClient.sign(payload) };
const chain = 'BTC';
const network = 'regtest';
const roomPrefix = `/${chain}/${network}/`;
socket.emit('room', roomPrefix + 'wallet', authPayload);
let hasSeenTxEvent = false;
let hasSeenCoinEvent = false;
let sawEvents = new Promise<void>(async resolve => {
socket.on('tx', () => {
hasSeenTxEvent = true;
console.log('Transaction event received');
if (hasSeenTxEvent && hasSeenCoinEvent) {
resolve();
}
});
socket.on('coin', () => {
hasSeenCoinEvent = true;
console.log('Coin event received');
if (hasSeenTxEvent && hasSeenCoinEvent) {
resolve();
}
});
while (!hasSeenTxEvent) {
console.log('WAITING FOR TX EVENT ON SOCKET');
if (hasSeenTxEvent && hasSeenCoinEvent) {
return resolve();
}
await wait(1000);
}
});
await rpc.sendtoaddress(address, 0.1);
await sawEvents;
expect(hasSeenTxEvent).to.equal(true);
expect(hasSeenCoinEvent).to.equal(true);
});
it('should get all wallet events', async () => {
const authClient = new Client({ apiUrl: 'http://localhost:3000/api', authKey: bwsPrivKey });
const payload = { method: 'socket', url: 'http://localhost:3000/api' };
const authPayload = {
pubKey: bwsKey,
message: authClient.getMessage(payload),
signature: authClient.sign(payload)
};
const chain = 'BTC';
const network = 'regtest';
const roomPrefix = `/${chain}/${network}/`;
socket.emit('room', roomPrefix + 'wallets', authPayload);
let hasSeenTxEvent = false;
let hasSeenCoinEvent = false;
let sawEvents = new Promise<void>(async resolve => {
socket.on('tx', () => {
hasSeenTxEvent = true;
console.log('Transaction event received');
if (hasSeenTxEvent && hasSeenCoinEvent) {
resolve();
}
});
socket.on('coin', () => {
hasSeenCoinEvent = true;
console.log('Coin event received');
if (hasSeenTxEvent && hasSeenCoinEvent) {
resolve();
}
});
while (!hasSeenTxEvent) {
console.log('WAITING FOR Wallet-TX EVENT ON SOCKET');
if (hasSeenTxEvent && hasSeenCoinEvent) {
return resolve();
}
await wait(1000);
}
});
await rpc.sendtoaddress(address, 0.1);
await sawEvents;
expect(hasSeenTxEvent).to.equal(true);
expect(hasSeenCoinEvent).to.equal(true);
sandbox.restore();
});
it('should get an error when the key does not match the bwsKey', async () => {
const pubKey = authKey.publicKey.toString('hex');
const wrongKey = new PrivateKey();
const authClient = new Client({ apiUrl: 'http://localhost:3000/api', authKey: wrongKey });
const payload = { method: 'socket', url: 'http://localhost:3000/api' };
const authPayload = { pubKey, message: authClient.getMessage(payload), signature: authClient.sign(payload) };
const chain = 'BTC';
const network = 'regtest';
const roomPrefix = `/${chain}/${network}/`;
let failed = new Promise<void>(resolve => {
socket.on('failure', e => {
expect(e.message).to.include('Authentication failed');
resolve();
});
});
socket.emit('room', roomPrefix + 'wallets', authPayload);
await failed;
});
it('should get an error when the signature is invalid', async () => {
const wrongKey = new PrivateKey();
const authClient = new Client({ apiUrl: 'http://localhost:3000/api', authKey: wrongKey });
const payload = { method: 'socket', url: 'http://localhost:3000/api' };
const authPayload = { pubKey, message: authClient.getMessage(payload), signature: 'invalid' };
const chain = 'BTC';
const network = 'regtest';
const roomPrefix = `/${chain}/${network}/`;
let failed = new Promise<void>(resolve => {
socket.on('failure', e => {
expect(e.message).to.include('Authentication failed');
resolve();
});
});
socket.emit('room', roomPrefix + 'wallet', authPayload);
await failed;
});
});