bbqpool-stratum
Version:
High performance Stratum poolserver in Node.js. Optimized to build with GCC 10 and O3 / bugfixes
1,496 lines (1,442 loc) • 91.7 kB
JavaScript
/*
*
* Pool (Updated)
*
*/
const events = require('events');
const nock = require('nock');
const Pool = require('../main/pool');
const rpcData = {
'capabilities': [
'proposal'
],
'version': 536870912,
'rules': [],
'vbavailable': {},
'vbrequired': 0,
'previousblockhash': '9719aefb83ef6583bd4c808bbe7d49b629a60b375fc6e36bee039530bc7727e2',
'transactions': [{
'data': '0100000001cba672d0bfdbcc441d171ef0723a191bf050932c6f8adc8a05b0cac2d1eb022f010000006c493046022100a23472410d8fd7eabf5c739bdbee5b6151ff31e10d5cb2b52abeebd5e9c06977022100c2cdde5c632eaaa1029dff2640158aaf9aab73fa021ed4a48b52b33ba416351801210212ee0e9c79a72d88db7af3fed18ae2b7ca48eaed995d9293ae0f94967a70cdf6ffffffff02905f0100000000001976a91482db4e03886ee1225fefaac3ee4f6738eb50df9188ac00f8a093000000001976a914c94f5142dd7e35f5645735788d0fe1343baf146288ac00000000',
'hash': '7c90a5087ac4d5b9361d47655812c89b4ad0dee6ecd5e08814d00ce7385aa317',
'depends': [],
'fee': 10000,
'sigops': 2
}],
'coinbaseaux': {
'flags': ''
},
'coinbasevalue': 5000000000,
'longpollid': '9719aefb83ef6583bd4c808bbe7d49b629a60b375fc6e36bee039530bc7727e22',
'target': '00000ffff0000000000000000000000000000000000000000000000000000000',
'mintime': 1614044921,
'mutable': [
'time',
'transactions',
'prevblock'
],
'noncerange': '00000000ffffffff',
'sigoplimit': 20000,
'sizelimit': 1000000,
'curtime': 1614201893,
'bits': '1e0ffff0',
'height': 1,
'default_witness_commitment': '6a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf9'
};
const auxData = {
'chainid': 1,
'hash': '8719aefb83ef6583bd4c808bbe7d49b629a60b375fc6e36bee039530bc7727e2',
'target': Buffer.from('1', 'hex'),
};
const blockchainData = {
'chain': 'main',
'blocks': 1,
'headers': 1,
'bestblockhash': '1d5af7e2ad9aeccb110401761938c07a5895d85711c9c5646661a10407c82769',
'difficulty': 0.000244140625,
'mediantime': 1614202191,
'verificationprogress': 3.580678270509504e-08,
'initialblockdownload': false,
'chainwork': '0000000000000000000000000000000000000000000000000000000000200020',
'size_on_disk': 472,
'pruned': false,
'softforks': [
{
'id': 'bip34',
'version': 2,
'reject': {
'status': false
}
},
{
'id': 'bip66',
'version': 3,
'reject': {
'status': false
}
},
{
'id': 'bip65',
'version': 4,
'reject': {
'status': false
}
}
],
'bip9_softforks': {
'csv': {
'status': 'defined',
'startTime': 1485561600,
'timeout': 1517356801,
'since': 0
},
'segwit': {
'status': 'defined',
'startTime': 1485561600,
'timeout': 1517356801,
'since': 0
}
},
'warnings': ''
};
const peerData = {
'id': 20,
'addr': '18.213.13.51:9333',
'addrlocal': '173.73.155.96:61108',
'addrbind': '192.168.1.155:61108',
'services': '000000000000040d',
'relaytxes': true,
'lastsend': 1615676709,
'lastrecv': 1615676709,
'bytessent': 1793,
'bytesrecv': 1782,
'conntime': 1615674308,
'timeoffset': 0,
'pingtime': 0.007751,
'minping': 0.00522,
'version': 70015,
'subver': '/LitecoinCore:0.18.1/',
'inbound': false,
'addnode': false,
'startingheight': 1,
'banscore': 0,
'synced_headers': 1,
'synced_blocks': 1,
'inflight': [],
'whitelisted': false,
'minfeefilter': 0.00001000,
'bytessent_per_msg': {
'addr': 55,
'feefilter': 32,
'getaddr': 24,
'getheaders': 93,
'ping': 672,
'pong': 672,
'sendcmpct': 66,
'sendheaders': 24,
'verack': 24,
'version': 131
},
'bytesrecv_per_msg': {
'addr': 55,
'feefilter': 32,
'headers': 106,
'ping': 672,
'pong': 672,
'sendcmpct': 66,
'sendheaders': 24,
'verack': 24,
'version': 131
}
};
const poolConfig = {
'name': 'Pool1',
'coins': ['Bitcoin', 'Namecoin'],
'debug': true,
'banning': {
'enabled': true,
'time': 600,
'invalidPercent': 0.5,
'checkThreshold': 500,
'purgeInterval': 300
},
'ports': [{
'port': 3001,
'enabled': true,
'difficulty': {
'initial': 32,
'minimum': 8,
'maximum': 512,
'targetTime': 15,
'retargetTime': 90,
'variance': 0.3
}
}],
'p2p': {
'enabled': true,
'host': '127.0.0.1',
'port': 8333,
},
'settings': {
'connectionTimeout': 600,
'jobRebroadcastTimeout': 60,
'tcpProxyProtocol': false,
},
'primary': {
'address': 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq',
'coin': {
'name': 'Bitcoin',
'symbol': 'BTC',
'getinfo': false,
'segwit': true,
'rewards': {},
'algorithms': {
'mining': 'sha256d',
'block': 'sha256d',
'coinbase': 'sha256d',
},
'mainnet': {
'bech32': 'bc',
'bip32': {
'public': 0x0488b21e,
'private': 0x0488ade4,
},
'peerMagic': 'f9beb4d9',
'pubKeyHash': 0x00,
'scriptHash': 0x05,
'wif': 0x80,
'coin': 'btc',
},
'testnet': {
'bech32': 'tb',
'bip32': {
'public': 0x043587cf,
'private': 0x04358394,
},
'peerMagic': '0b110907',
'pubKeyHash': 0x6f,
'scriptHash': 0xc4,
'wif': 0xef,
'coin': 'btc',
}
},
'daemons': [{
'host': '127.0.0.1',
'port': 8332,
'user': '',
'password': ''
}],
'recipients': [{
'address': '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2',
'percentage': 0.05,
}],
},
'auxiliary': {
'enabled': false,
'coin': {
'name': 'Namecoin',
'symbol': 'NMC',
'header': 'fabe6d6d'
},
'daemons': [{
'host': '127.0.0.1',
'port': 8336,
'user': '',
'password': ''
}],
}
};
const portalConfig = {
'identifier': 'master',
'tls': {
'rootCA': 'rootCA.crt',
'serverKey': 'server.key',
'serverCert': 'server.crt',
},
};
nock.disableNetConnect();
nock.enableNetConnect('127.0.0.1');
process.env.forkId = '0';
////////////////////////////////////////////////////////////////////////////////
function mockSetupDaemon(pool, callback) {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getpeerinfo')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: null,
}));
nock('http://127.0.0.1:8336')
.post('/', body => body.method === 'getpeerinfo')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: null,
}));
pool.setupDaemonInterface(() => callback());
}
function mockSetupData(pool, callback) {
nock('http://127.0.0.1:8332')
.post('/').reply(200, JSON.stringify([
{ id: 'nocktest', error: null, result: { isvalid: true, address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq' }},
{ id: 'nocktest', error: null, result: { networkhashps: 0 }},
{ id: 'nocktest', error: true, result: { code: -1 }},
{ id: 'nocktest', error: null, result: { chain: 'main', difficulty: 0 }},
{ id: 'nocktest', error: null, result: { protocolversion: 1, connections: 1 }},
]));
pool.setupPoolData(() => callback());
}
function mockSetupTestnetData(pool, callback) {
nock('http://127.0.0.1:8332')
.post('/').reply(200, JSON.stringify([
{ id: 'nocktest', error: null, result: { isvalid: true, address: 'tb1qprvwwfr5cey54e4353t9dmker7zd9w4uhvkz5p' }},
{ id: 'nocktest', error: null, result: { networkhashps: 0 }},
{ id: 'nocktest', error: true, result: { code: -1 }},
{ id: 'nocktest', error: null, result: { chain: 'test', difficulty: 0 }},
{ id: 'nocktest', error: null, result: { protocolversion: 1, connections: 1 }},
]));
pool.setupPoolData(() => callback());
}
function mockSetupBlockchain(pool, callback) {
const rpcDataCopy = Object.assign({}, rpcData);
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: rpcDataCopy,
}));
pool.setupBlockchain(() => callback());
}
function mockSetupFirstJob(pool, callback) {
const rpcDataCopy = Object.assign({}, rpcData);
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: rpcDataCopy,
}));
pool.setupFirstJob(() => callback());
}
////////////////////////////////////////////////////////////////////////////////
describe('Test pool functionality', () => {
let poolConfigCopy, configCopy, rpcDataCopy;
beforeEach(() => {
poolConfigCopy = JSON.parse(JSON.stringify(poolConfig));
configCopy = JSON.parse(JSON.stringify(portalConfig));
rpcDataCopy = JSON.parse(JSON.stringify(rpcData));
});
test('Test initialization of pool', () => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
expect(typeof pool).toBe('object');
});
test('Test initialization of port difficulty', () => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.setupDifficulty();
expect(typeof pool.difficulty).toBe('object');
expect(typeof pool.difficulty['3001']).toBe('object');
expect(typeof pool.difficulty['3001'].manageClient).toBe('function');
expect(pool.difficulty['3001']._eventsCount).toBe(1);
});
test('Test initialization of daemon', () => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.setupDaemonInterface(() => {});
expect(typeof pool.primary.daemon).toBe('object');
expect(typeof pool.primary.daemon.indexDaemons).toBe('function');
expect(typeof pool.primary.daemon.isOnline).toBe('function');
expect(typeof pool.primary.daemon.initDaemons).toBe('function');
expect(pool.primary.daemon._eventsCount).toBe(3);
});
test('Test pool daemon events [1]', (done) => {
poolConfigCopy.primary.daemons = [];
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
expect(type).toBe('error');
expect(text).toBe('No primary daemons have been configured - pool cannot start');
done();
});
pool.setupDaemonInterface(() => {});
});
test('Test pool daemon events [2]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getpeerinfo')
.reply(401, {});
pool.on('log', (type, text) => {
expect(type).toBe('error');
expect(text).toBe('Unauthorized RPC access - invalid RPC username or password');
done();
});
pool.setupDaemonInterface(() => {});
pool.primary.daemon.cmd('getpeerinfo', [], true, () => {});
});
test('Test pool daemon events [3]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getpeerinfo')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: null,
}));
pool.setupDaemonInterface(() => done());
});
/* eslint-disable no-useless-escape */
test('Test pool daemon events [4]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getpeerinfo')
.reply(200, JSON.stringify({
id: 'nocktest',
error: true,
result: null,
}));
pool.on('log', (type, text) => {
expect(type).toBe('error');
expect(text).toBe('Failed to connect daemon(s): [{"error":true,"response":null,"instance":{"host":"127.0.0.1","port":8332,"user":"","password":"","index":0},\"data\":\"{\\\"id\\\":\\\"nocktest\\\",\\\"error\\\":true,\\\"result\\\":null}\"}]');
done();
});
pool.setupDaemonInterface(() => {});
});
test('Test pool daemon events [5]', (done) => {
poolConfigCopy.auxiliary.enabled = true;
poolConfigCopy.auxiliary.daemons = [];
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
expect(type).toBe('error');
expect(text).toBe('No auxiliary daemons have been configured - pool cannot start');
done();
});
pool.setupDaemonInterface(() => {});
});
test('Test pool daemon events [6]', (done) => {
poolConfigCopy.auxiliary.enabled = true;
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getpeerinfo')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: null,
}));
nock('http://127.0.0.1:8336')
.post('/', body => body.method === 'getpeerinfo')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: null,
}));
pool.setupDaemonInterface(() => done());
});
test('Test pool batch data events [1]', (done) => {
poolConfigCopy.primary.coin.getinfo = true;
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
expect(type).toBe('error');
expect(text).toBe('Could not start pool, error with init batch RPC call');
expect(poolConfigCopy.primary.coin.getinfo).toBe(true);
done();
});
mockSetupDaemon(pool, () => {
pool.setupPoolData(() => {});
});
});
test('Test pool batch data events [2]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
expect(type).toBe('error');
expect(text).toBe('Could not start pool, error with init batch RPC call');
done();
});
mockSetupDaemon(pool, () => {
pool.setupPoolData(() => {});
});
});
test('Test pool batch data events [3]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
mockSetupDaemon(pool, () => {
nock('http://127.0.0.1:8332')
.post('/').reply(200, JSON.stringify([
{ id: 'nocktest', error: null, result: { isvalid: true, address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq' }},
{ id: 'nocktest', error: null, result: { networkhashps: 0 }},
{ id: 'nocktest', error: true, result: { code: -1 }},
{ id: 'nocktest', error: null, result: { chain: 'main', difficulty: 0 }},
{ id: 'nocktest', error: null, result: { protocolversion: 1, connections: 1 }},
]));
pool.setupPoolData(() => {
expect(poolConfigCopy.primary.address).toBe('bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq');
expect(poolConfigCopy.settings.testnet).toBe(false);
expect(poolConfigCopy.settings.protocolVersion).toBe(1);
expect(poolConfigCopy.settings.hasSubmitMethod).toBe(true);
expect(typeof poolConfigCopy.statistics).toBe('object');
done();
});
});
});
test('Test pool batch data events [4]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
expect(type).toBe('error');
expect(text).toBe('Could not start pool, error with init RPC call: validateaddress - true');
done();
});
mockSetupDaemon(pool, () => {
nock('http://127.0.0.1:8332')
.post('/').reply(200, JSON.stringify([
{ id: 'nocktest', error: true, result: { isvalid: true, address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq' }},
{ id: 'nocktest', error: null, result: { networkhashps: 0 }},
{ id: 'nocktest', error: true, result: { code: -1 }},
{ id: 'nocktest', error: null, result: { chain: 'main', difficulty: 0 }},
{ id: 'nocktest', error: null, result: { protocolversion: 1, connections: 1 }},
]));
pool.setupPoolData(() => {});
});
});
test('Test pool batch data events [5]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
expect(type).toBe('error');
expect(text).toBe('Daemon reports address is not valid');
done();
});
mockSetupDaemon(pool, () => {
nock('http://127.0.0.1:8332')
.post('/').reply(200, JSON.stringify([
{ id: 'nocktest', error: null, result: { isvalid: false, address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq' }},
{ id: 'nocktest', error: null, result: { networkhashps: 0 }},
{ id: 'nocktest', error: true, result: { code: -1 }},
{ id: 'nocktest', error: null, result: { chain: 'main', difficulty: 0 }},
{ id: 'nocktest', error: null, result: { protocolversion: 1, connections: 1 }},
]));
pool.setupPoolData(() => {});
});
});
test('Test pool batch data events [6]', (done) => {
poolConfigCopy.primary.coin.getinfo = true;
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
mockSetupDaemon(pool, () => {
nock('http://127.0.0.1:8332')
.post('/').reply(200, JSON.stringify([
{ id: 'nocktest', error: null, result: { isvalid: true, address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq' }},
{ id: 'nocktest', error: null, result: { networkhashps: 0 }},
{ id: 'nocktest', error: true, result: { code: -1 }},
{ id: 'nocktest', error: null, result: { testnet: false, difficulty: { 'proof-of-work': 0 }, protocolversion: 1, connections: 0 }},
]));
pool.setupPoolData(() => {
expect(poolConfigCopy.primary.address).toBe('bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq');
expect(poolConfigCopy.settings.testnet).toBe(false);
expect(poolConfigCopy.settings.protocolVersion).toBe(1);
expect(poolConfigCopy.settings.hasSubmitMethod).toBe(true);
expect(typeof poolConfigCopy.statistics).toBe('object');
done();
});
});
});
test('Test pool batch data events [7]', (done) => {
poolConfigCopy.primary.coin.getinfo = false;
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
mockSetupDaemon(pool, () => {
nock('http://127.0.0.1:8332')
.post('/').reply(200, JSON.stringify([
{ id: 'nocktest', error: null, result: { isvalid: true, address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq' }},
{ id: 'nocktest', error: null, result: { networkhashps: 0 }},
{ id: 'nocktest', error: true, result: { code: -1 }},
{ id: 'nocktest', error: null, result: { chain: 'test', difficulty: { 'proof-of-work': 0 }}},
{ id: 'nocktest', error: null, result: { protocolversion: 1, connections: 1 }},
]));
pool.setupPoolData(() => {
expect(poolConfigCopy.primary.address).toBe('bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq');
expect(poolConfigCopy.settings.testnet).toBe(true);
expect(poolConfigCopy.settings.protocolVersion).toBe(1);
expect(poolConfigCopy.settings.hasSubmitMethod).toBe(true);
expect(typeof poolConfigCopy.statistics).toBe('object');
done();
});
});
});
test('Test pool batch data events [8]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
mockSetupDaemon(pool, () => {
nock('http://127.0.0.1:8332')
.post('/').reply(200, JSON.stringify([
{ id: 'nocktest', error: null, result: { isvalid: true, address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq' }},
{ id: 'nocktest', error: null, result: { networkhashps: 0 }},
{ id: 'nocktest', error: true, result: { message: 'Method not found' }},
{ id: 'nocktest', error: null, result: { chain: 'main', difficulty: 0 }},
{ id: 'nocktest', error: null, result: { protocolversion: 1, connections: 1 }},
]));
pool.setupPoolData(() => {
expect(poolConfigCopy.primary.address).toBe('bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq');
expect(poolConfigCopy.settings.testnet).toBe(false);
expect(poolConfigCopy.settings.protocolVersion).toBe(1);
expect(poolConfigCopy.settings.hasSubmitMethod).toBe(false);
expect(typeof poolConfigCopy.statistics).toBe('object');
done();
});
});
});
test('Test pool batch data events [9]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
expect(type).toBe('error');
expect(text).toBe('Could not detect block submission RPC method');
done();
});
mockSetupDaemon(pool, () => {
nock('http://127.0.0.1:8332')
.post('/').reply(200, JSON.stringify([
{ id: 'nocktest', error: null, result: { isvalid: true, address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq' }},
{ id: 'nocktest', error: null, result: { networkhashps: 0 }},
{ id: 'nocktest', error: true, result: {}},
{ id: 'nocktest', error: null, result: { chain: 'main', difficulty: 0 }},
{ id: 'nocktest', error: null, result: { protocolversion: 1, connections: 1 }},
]));
pool.setupPoolData(() => {});
});
});
test('Test pool recipient setup [1]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
pool.setupRecipients();
expect(poolConfigCopy.settings.feePercentage).toBe(0.05);
done();
});
});
});
test('Test pool recipient setup [2]', (done) => {
poolConfigCopy.primary.recipients = [];
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
expect(type).toBe('warning');
expect(text).toBe('No recipients have been added which means that no fees will be taken');
done();
});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
pool.setupRecipients();
});
});
});
test('Test initialization of manager', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
pool.setupJobManager();
expect(typeof pool.manager).toBe('object');
expect(typeof pool.manager.updateCurrentJob).toBe('function');
expect(pool.manager._eventsCount).toBe(3);
done();
});
});
});
test('Test pool manager events [1]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
pool.setupJobManager();
pool.manager.emit('newBlock', rpcDataCopy);
done();
});
});
});
test('Test pool manager events [2]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
pool.setupJobManager();
pool.manager.emit('updatedBlock', rpcDataCopy);
done();
});
});
});
test('Test pool manager events [3]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
expect(type).toBe('error');
expect(text).toBe('RPC error with primary daemon instance 0 when submitting block with submitblock true');
done();
});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'submitblock')
.reply(200, JSON.stringify({
id: 'nocktest',
error: true,
result: null,
}));
pool.setupJobManager();
const shareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'primary',
coinbase: null,
difficulty: 1,
hash: 'example blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
height: 1,
identifier:'master',
reward: 5000000000,
shareDiff: 1,
};
const auxShareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'auxiliary',
coinbase: null,
difficulty: 1,
hash: 'example auxiliary blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
identifier:'master',
shareDiff: 1,
};
pool.manager.emit('share', shareData, auxShareData, true);
});
});
});
//
test('Test pool manager events [4]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
expect(type).toBe('error');
expect(text).toBe('Primary daemon instance 0 rejected a supposedly valid block');
done();
});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'submitblock')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: 'rejected',
}));
pool.setupJobManager();
const shareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'primary',
coinbase: null,
difficulty: 1,
hash: 'example blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
height: 1,
identifier:'master',
reward: 5000000000,
shareDiff: 1,
};
const auxShareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'auxiliary',
coinbase: null,
difficulty: 1,
hash: 'example auxiliary blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
identifier:'master',
shareDiff: 1,
};
pool.manager.emit('share', shareData, auxShareData, true);
});
});
});
test('Test pool manager events [5]', (done) => {
const response = [];
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
response.push([type, text]);
if (response.length === 3) {
expect(response[0][0]).toBe('special');
expect(response[0][1]).toBe('Submitted primary block successfully to Bitcoin\'s daemon instance(s)');
expect(response[1][0]).toBe('error');
expect(response[1][1]).toBe('Block was rejected by the network');
expect(response[2][0]).toBe('special');
expect(response[2][1]).toBe('Block notification via RPC after primary block submission');
done();
}
});
pool.on('share', () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: rpcDataCopy,
}));
});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'submitblock')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: null,
}));
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblock')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: null,
}));
pool.setupJobManager();
const shareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'primary',
coinbase: null,
difficulty: 1,
hash: 'example blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
height: 1,
identifier:'master',
reward: 5000000000,
shareDiff: 1,
};
const auxShareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'auxiliary',
coinbase: null,
difficulty: 1,
hash: 'example auxiliary blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
identifier:'master',
shareDiff: 1,
};
pool.manager.emit('share', shareData, auxShareData, true);
});
});
});
test('Test pool manager events [6]', (done) => {
const response = [];
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
response.push([type, text]);
if (response.length === 3) {
expect(response[0][0]).toBe('special');
expect(response[0][1]).toBe('Submitted primary block successfully to Bitcoin\'s daemon instance(s)');
expect(response[1][0]).toBe('error');
expect(response[1][1]).toBe('Block was rejected by the network');
expect(response[2][0]).toBe('special');
expect(response[2][1]).toBe('Block notification via RPC after primary block submission');
done();
}
});
pool.on('share', () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: rpcDataCopy,
}));
});
mockSetupDaemon(pool, () => {
nock('http://127.0.0.1:8332')
.post('/').reply(200, JSON.stringify([
{ id: 'nocktest', error: null, result: { isvalid: true, address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq' }},
{ id: 'nocktest', error: null, result: { networkhashps: 0 }},
{ id: 'nocktest', error: true, result: { message: 'Method not found' }},
{ id: 'nocktest', error: null, result: { chain: 'main', difficulty: 0 }},
{ id: 'nocktest', error: null, result: { protocolversion: 1, connections: 1 }},
]));
pool.setupPoolData(() => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: null,
}));
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblock')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: null,
}));
pool.setupJobManager();
const shareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'primary',
coinbase: null,
difficulty: 1,
hash: 'example blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
height: 1,
identifier:'master',
reward: 5000000000,
shareDiff: 1,
};
const auxShareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'auxiliary',
coinbase: null,
difficulty: 1,
hash: 'example auxiliary blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
identifier:'master',
shareDiff: 1,
};
pool.manager.emit('share', shareData, auxShareData, true);
});
});
});
test('Test pool manager events [7]', (done) => {
const response = [];
poolConfigCopy.primary.coin.segwit = false;
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
response.push([type, text]);
if (response.length === 3) {
expect(response[0][0]).toBe('special');
expect(response[0][1]).toBe('Submitted primary block successfully to Bitcoin\'s daemon instance(s)');
expect(response[1][0]).toBe('error');
expect(response[1][1]).toBe('Block was rejected by the network');
expect(response[2][0]).toBe('special');
expect(response[2][1]).toBe('Block notification via RPC after primary block submission');
done();
}
});
pool.on('share', () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: rpcDataCopy,
}));
});
mockSetupDaemon(pool, () => {
nock('http://127.0.0.1:8332')
.post('/').reply(200, JSON.stringify([
{ id: 'nocktest', error: null, result: { isvalid: true, address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq' }},
{ id: 'nocktest', error: null, result: { networkhashps: 0 }},
{ id: 'nocktest', error: true, result: { message: 'Method not found' }},
{ id: 'nocktest', error: null, result: { chain: 'main', difficulty: 0 }},
{ id: 'nocktest', error: null, result: { protocolversion: 1, connections: 1 }},
]));
pool.setupPoolData(() => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: null,
}));
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblock')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: null,
}));
pool.setupJobManager();
const shareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'primary',
coinbase: null,
difficulty: 1,
hash: 'example blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
height: 1,
identifier:'master',
reward: 5000000000,
shareDiff: 1,
};
const auxShareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'auxiliary',
coinbase: null,
difficulty: 1,
hash: 'example auxiliary blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
identifier:'master',
shareDiff: 1,
};
pool.manager.emit('share', shareData, auxShareData, true);
});
});
});
test('Test pool manager events [8]', (done) => {
const response = [];
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
response.push([type, text]);
if (response.length === 2) {
expect(response[0][0]).toBe('special');
expect(response[0][1]).toBe('Submitted primary block successfully to Bitcoin\'s daemon instance(s)');
expect(response[1][0]).toBe('special');
expect(response[1][1]).toBe('Block notification via RPC after primary block submission');
done();
}
});
pool.on('share', () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: rpcDataCopy,
}));
});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'submitblock')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: null,
}));
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblock')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: {
hash: 'example blockhash',
tx: 'example transaction',
confirmations: 1,
},
}));
pool.setupJobManager();
const shareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'primary',
coinbase: null,
difficulty: 1,
hash: 'example blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
height: 1,
identifier:'master',
reward: 5000000000,
shareDiff: 1,
};
const auxShareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'auxiliary',
coinbase: null,
difficulty: 1,
hash: 'example auxiliary blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
identifier:'master',
shareDiff: 1,
};
pool.manager.emit('share', shareData, auxShareData, true);
});
});
});
test('Test pool manager events [9]', (done) => {
const response = [];
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
response.push([type, text]);
if (response.length === 3) {
expect(response[0][0]).toBe('special');
expect(response[0][1]).toBe('Submitted primary block successfully to Bitcoin\'s daemon instance(s)');
expect(response[1][0]).toBe('error');
expect(response[1][1]).toBe('Block was rejected by the network');
expect(response[2][0]).toBe('special');
expect(response[2][1]).toBe('Block notification via RPC after primary block submission');
done();
}
});
pool.on('share', () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: rpcDataCopy,
}));
});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'submitblock')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: null,
}));
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblock')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: {
hash: 'example blockhash',
tx: 'example transaction',
confirmations: -1,
},
}));
pool.setupJobManager();
const shareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'primary',
coinbase: null,
difficulty: 1,
hash: 'example blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
height: 1,
identifier:'master',
reward: 5000000000,
shareDiff: 1,
};
const auxShareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'auxiliary',
coinbase: null,
difficulty: 1,
hash: 'example auxiliary blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
identifier:'master',
shareDiff: 1,
};
pool.manager.emit('share', shareData, auxShareData, true);
});
});
});
test('Test pool manager events [10]', (done) => {
const response = [];
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
response.push([type, text]);
if (response.length === 3) {
expect(response[0][0]).toBe('special');
expect(response[0][1]).toBe('Submitted primary block successfully to Bitcoin\'s daemon instance(s)');
expect(response[1][0]).toBe('error');
expect(response[1][1]).toBe('Block was rejected by the network');
expect(response[2][0]).toBe('error');
expect(response[2][1]).toBe('getblocktemplate call failed for daemon instance 0 with error true');
done();
}
});
pool.on('share', () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: true,
result: null,
}));
});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'submitblock')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: null,
}));
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblock')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: {
hash: 'example blockhash',
tx: 'example transaction',
confirmations: -1,
},
}));
pool.setupJobManager();
const shareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'primary',
coinbase: null,
difficulty: 1,
hash: 'example blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
height: 1,
identifier:'master',
reward: 5000000000,
shareDiff: 1,
};
const auxShareData = {
job: 1,
ip: 'ip_addr',
port: 'port',
addrPrimary: 'addr1',
addrAuxiliary: 'addr2',
blockDiff : 1,
blockDiffActual: 1,
blockType: 'auxiliary',
coinbase: null,
difficulty: 1,
hash: 'example auxiliary blockhash',
hex: Buffer.from('000011110000111100001111', 'hex'),
header: null,
headerDiff: null,
identifier:'master',
shareDiff: 1,
};
pool.manager.emit('share', shareData, auxShareData, true);
});
});
});
test('Test pool blockchain events [1]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: rpcDataCopy,
}));
pool.setupBlockchain(() => done());
});
});
});
test('Test pool blockchain events [2]', (done) => {
const response = [];
const blockchainDataCopy = JSON.parse(JSON.stringify(blockchainData));
const peerDataCopy = JSON.parse(JSON.stringify(peerData));
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
response.push([type, text]);
if (response.length === 2) {
expect(response[0][0]).toBe('error');
expect(response[0][1]).toBe('Daemon is still syncing with the network. The server will be started once synced');
expect(response[1][0]).toBe('warning');
expect(response[1][1]).toBe('Downloaded 100.00% of blockchain from 1 peers');
done();
}
});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: { code: -10 },
result: null,
}));
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblockchaininfo')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: blockchainDataCopy,
}));
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getpeerinfo')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: [peerDataCopy],
}));
pool.setupBlockchain(() => done());
});
});
});
test('Test pool job events [1]', (done) => {
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
pool.setupJobManager();
mockSetupBlockchain(pool, () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: rpcDataCopy,
}));
pool.setupFirstJob(() => {
expect(typeof pool.manager.currentJob).toBe('object');
expect(pool.manager.currentJob.rpcData.height).toBe(1);
done();
});
});
});
});
});
test('Test pool job events [2]', (done) => {
const response = [];
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('log', (type, text) => {
response.push([type, text]);
if (response.length === 2) {
expect(response[0][0]).toBe('error');
expect(response[0][1]).toBe('getblocktemplate call failed for daemon instance 0 with error true');
expect(response[1][0]).toBe('error');
expect(response[1][1]).toBe('Error with getblocktemplate on creating first job, server cannot start');
done();
}
});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
pool.setupJobManager();
mockSetupBlockchain(pool, () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: true,
result: null,
}));
pool.setupFirstJob(() => {});
});
});
});
});
test('Test pool job events [3]', (done) => {
poolConfigCopy.auxiliary.enabled = true;
rpcDataCopy.auxData = auxData;
const auxDataCopy = JSON.parse(JSON.stringify(auxData));
const pool = new Pool(poolConfigCopy, configCopy, null, () => {});
pool.on('share', (shareData, shareType, blockValid) => {
expect(shareData.identifier).toBe('master');
if (shareData.blockType === 'auxiliary') {
expect(shareType).toBe('valid');
expect(blockValid).toBe(false);
expect(shareData.job).toBe(1);
expect(shareData.hash).toBe('example auxiliary blockhash');
expect(shareData.blockType).toBe('auxiliary');
nock.cleanAll();
done();
}
});
mockSetupDaemon(pool, () => {
mockSetupData(pool, () => {
pool.setupJobManager();
mockSetupBlockchain(pool, () => {
nock('http://127.0.0.1:8332')
.post('/', body => body.method === 'getblocktemplate')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: rpcDataCopy,
}));
nock('http://127.0.0.1:8336')
.persist()
.post('/', body => body.method === 'getauxblock')
.reply(200, JSON.stringify({
id: 'nocktest',
error: null,
result: auxDataCopy,
}));
pool.setupFirstJob(() => {
const shareData = {