itns-sidechain/test/util/node-context.js
Nodari Chkuaselidze 6aeceed719
wdb: Add timeCache to the interactive rescan callback.
test: fix NodeContext usage and update tests.
2024-08-29 15:36:25 +04:00

387 lines
8.2 KiB
JavaScript

'use strict';
const assert = require('bsert');
const common = require('./common');
const fs = require('bfile');
const Network = require('../../lib/protocol/network');
const SPVNode = require('../../lib/node/spvnode');
const FullNode = require('../../lib/node/fullnode');
const WalletNode = require('../../lib/wallet/node');
const plugin = require('../../lib/wallet/plugin');
const {NodeClient, WalletClient} = require('../../lib/client');
const Logger = require('blgr');
class NodeContext {
/** @type {FullNode|SPVNode} */
node;
/** @type {WalletClient} */
wclient;
/** @type {NodeClient} */
nclient;
constructor(options = {}) {
this.name = 'node-test';
this.options = {};
this.prefix = null;
this.opened = false;
this.logger = new Logger({
console: true,
filename: null,
level: 'none'
});
this.initted = false;
this.node = null;
this.walletNode = null;
this.nclient = null;
this.wclient = null;
this.clients = [];
this.fromOptions(options);
}
fromOptions(options) {
const fnodeOptions = {
...options,
memory: true,
network: 'regtest',
listen: false,
wallet: false,
spv: false,
logger: this.logger,
// wallet plugin options
walletHttpPort: null
};
if (options.name != null) {
assert(typeof options.name === 'string');
this.name = options.name;
}
if (options.network != null)
fnodeOptions.network = Network.get(options.network).type;
if (options.name != null)
fnodeOptions.name = options.name;
if (options.listen != null) {
assert(typeof options.listen === 'boolean');
fnodeOptions.listen = options.listen;
}
if (options.prefix != null) {
fnodeOptions.prefix = options.prefix;
fnodeOptions.memory = false;
this.prefix = fnodeOptions.prefix;
}
if (options.memory != null) {
assert(typeof options.memory === 'boolean');
assert(!(options.memory && options.prefix),
'Can not set prefix with memory.');
fnodeOptions.memory = options.memory;
}
if (!fnodeOptions.memory && !fnodeOptions.prefix) {
fnodeOptions.prefix = common.testdir(this.name);
this.prefix = fnodeOptions.prefix;
}
if (options.wallet != null)
fnodeOptions.wallet = options.wallet;
if (options.spv != null) {
assert(typeof options.spv === 'boolean');
fnodeOptions.spv = options.spv;
}
if (options.httpPort != null) {
assert(typeof options.httpPort === 'number');
fnodeOptions.httpPort = options.httpPort;
}
if (options.walletHttpPort != null) {
assert(typeof options.walletHttpPort === 'number');
fnodeOptions.walletHttpPort = options.walletHttpPort;
}
if (options.timeout != null) {
assert(typeof options.timeout === 'number');
fnodeOptions.timeout = options.timeout;
}
if (options.standalone != null) {
assert(typeof options.standalone === 'boolean');
fnodeOptions.standalone = options.standalone;
}
this.options = fnodeOptions;
}
init() {
if (this.initted)
return;
if (this.options.spv)
this.node = new SPVNode(this.options);
else
this.node = new FullNode(this.options);
if (this.options.wallet && !this.options.standalone) {
this.node.use(plugin);
} else if (this.options.wallet && this.options.standalone) {
this.walletNode = new WalletNode({
...this.options,
nodeHost: '127.0.0.1',
nodePort: this.options.httpPort,
nodeApiKey: this.options.apiKey,
httpPort: this.options.walletHttpPort,
apiKey: this.options.apiKey
});
}
// Initial wallets.
this.nclient = this.nodeClient();
if (this.options.wallet)
this.wclient = this.walletClient();
this.initted = true;
}
get network() {
return this.node.network;
}
get miner() {
return this.node.miner;
}
get mempool() {
return this.node.mempool;
}
get chain() {
return this.node.chain;
}
get nodeRPC() {
return this.node.rpc;
}
get height() {
return this.chain.tip.height;
}
get wdb() {
if (!this.options.wallet)
return null;
if (this.walletNode)
return this.walletNode.wdb;
return this.node.get('walletdb').wdb;
}
/*
* Event Listeners wrappers
*/
on(event, listener) {
this.node.on(event, listener);
}
once(event, listener) {
this.node.once(event, listener);
}
addListener(event, listener) {
this.node.addListener(event, listener);
}
removeListener(event, listener) {
this.node.removeListener(event, listener);
}
removeAllListeners(event) {
this.node.removeAllListeners(event);
}
/*
* Life Cycle
*/
async open() {
this.init();
if (this.opened)
return;
if (this.prefix)
await fs.mkdirp(this.prefix);
const open = common.forEvent(this.node, 'open');
await this.node.ensure();
await this.node.open();
await this.node.connect();
this.node.startSync();
await open;
if (this.walletNode) {
const walletOpen = common.forEvent(this.walletNode, 'open');
await this.walletNode.open();
await walletOpen;
}
if (this.wclient)
await this.wclient.open();
await this.nclient.open();
this.opened = true;
}
async close() {
if (!this.opened)
return;
const closeClients = [];
for (const client of this.clients) {
if (client.opened)
closeClients.push(client.close());
}
await Promise.all(closeClients);
if (this.walletNode) {
const walletClose = common.forEvent(this.walletNode, 'close');
await this.walletNode.close();
await walletClose;
}
const close = common.forEvent(this.node, 'close');
await this.node.close();
await close;
this.node = null;
this.wclient = null;
this.nclient = null;
this.opened = false;
this.initted = false;
}
async destroy() {
if (this.prefix)
await fs.rimraf(this.prefix);
}
/*
* Helpers
*/
enableLogging(level = 'debug') {
this.logger.setLevel(level);
}
disableLogging() {
this.logger.setLevel('none');
}
/**
* Execute an RPC using the node client.
* @param {String} method - RPC method
* @param {Array} params - method parameters
* @returns {Promise<Array>} - Returns a two item array with the
* RPC's return value or null as the first item and an error or
* null as the second item.
*/
async nrpc(method, params) {
return this.nclient.execute(method, params);
}
/**
* Execute an RPC using the wallet client.
* @param {String} method - RPC method
* @param {Array} params - method parameters
* @returns {Promise} - Returns a two item array with the RPC's return value
* or null as the first item and an error or null as the second item.
*/
async wrpc(method, params) {
return this.wclient.execute(method, params);
};
/**
* Create new client
* @param {Object} [options]
* @returns {NodeClient}
*/
nodeClient(options = {}) {
const client = new NodeClient({
timeout: this.options.timeout,
apiKey: this.options.apiKey,
port: this.options.httpPort || this.network.rpcPort,
...options
});
this.clients.push(client);
return client;
}
/**
* Create new wallet client.
* @param {Object} [options]
* @returns {WalletClient}
*/
walletClient(options = {}) {
const client = new WalletClient({
timeout: this.options.timeout,
apiKey: this.options.apiKey,
port: this.options.walletHttpPort || this.network.walletPort,
...options
});
this.clients.push(client);
return client;
}
/**
* Mine blocks and wait for connect.
* @param {Number} count
* @param {Address} address
* @param {ChainEntry} [tip=chain.tip] - Tip to mine on
* @returns {Promise<Buffer[]>} - Block hashes
*/
async mineBlocks(count, address, tip) {
assert(this.open);
if (!tip)
tip = this.chain.tip;
const blocks = [];
for (let i = 0; i < count; i++) {
const block = await this.miner.mineBlock(tip, address);
tip = await this.chain.add(block);
blocks.push(block);
}
return blocks;
}
}
module.exports = NodeContext;