itns-sidechain/test/node-http-test.js

420 lines
13 KiB
JavaScript

'use strict';
const assert = require('bsert');
const bio = require('bufio');
const Network = require('../lib/protocol/network');
const FullNode = require('../lib/node/fullnode');
const SPVNode = require('../lib/node/spvnode');
const Address = require('../lib/primitives/address');
const Mnemonic = require('../lib/hd/mnemonic');
const Witness = require('../lib/script/witness');
const Script = require('../lib/script/script');
const HDPrivateKey = require('../lib/hd/private');
const Output = require('../lib/primitives/output');
const Coin = require('../lib/primitives/coin');
const MTX = require('../lib/primitives/mtx');
const rules = require('../lib/covenants/rules');
const common = require('./util/common');
const NodeContext = require('./util/node');
const mnemonics = require('./data/mnemonic-english.json');
// Commonly used test mnemonic
const phrase = mnemonics[0][1];
describe('Node HTTP', function() {
describe('Chain info', function() {
let nodeCtx;
afterEach(async () => {
if (nodeCtx && nodeCtx.opened)
await nodeCtx.close();
nodeCtx = null;
});
it('should get full node chain info', async () => {
nodeCtx = new NodeContext({
network: 'regtest'
});
await nodeCtx.open();
const {chain} = await nodeCtx.nclient.getInfo();
assert.strictEqual(chain.height, 0);
assert.strictEqual(chain.tip, nodeCtx.network.genesis.hash.toString('hex'));
assert.strictEqual(chain.treeRoot, Buffer.alloc(32, 0).toString('hex'));
assert.strictEqual(chain.progress, 0);
assert.strictEqual(chain.indexers.indexTX, false);
assert.strictEqual(chain.indexers.indexAddress, false);
assert.strictEqual(chain.options.spv, false);
assert.strictEqual(chain.options.prune, false);
assert.strictEqual(chain.treeCompaction.compacted, false);
assert.strictEqual(chain.treeCompaction.compactOnInit, false);
assert.strictEqual(chain.treeCompaction.compactInterval, null);
assert.strictEqual(chain.treeCompaction.nextCompaction, null);
assert.strictEqual(chain.treeCompaction.lastCompaction, null);
});
it('should get fullnode chain info with indexers', async () => {
nodeCtx = new NodeContext({
network: 'regtest',
indexAddress: true,
indexTX: true
});
await nodeCtx.open();
const {chain} = await nodeCtx.nclient.getInfo();
assert.strictEqual(chain.indexers.indexTX, true);
assert.strictEqual(chain.indexers.indexAddress, true);
});
it('should get fullnode chain info with pruning', async () => {
nodeCtx = new NodeContext({
network: 'regtest',
prune: true
});
await nodeCtx.open();
const {chain} = await nodeCtx.nclient.getInfo();
assert.strictEqual(chain.options.prune, true);
});
it('should get fullnode chain info with compact', async () => {
nodeCtx = new NodeContext({
network: 'regtest',
compactTreeOnInit: true,
compactTreeInitInterval: 20000
});
await nodeCtx.open();
const {chain} = await nodeCtx.nclient.getInfo();
assert.strictEqual(chain.treeCompaction.compacted, false);
assert.strictEqual(chain.treeCompaction.compactOnInit, true);
assert.strictEqual(chain.treeCompaction.compactInterval, 20000);
assert.strictEqual(chain.treeCompaction.lastCompaction, null);
// last compaction height + keepBlocks + compaction interval
// regtest: 0 + 10000 + 20000
assert.strictEqual(chain.treeCompaction.nextCompaction, 30000);
});
it('should get spv node chain info', async () => {
nodeCtx = new NodeContext({
network: 'regtest',
spv: true
});
await nodeCtx.open();
const {chain} = await nodeCtx.nclient.getInfo();
assert.strictEqual(chain.options.spv, true);
});
it('should get next tree update height', async () => {
const someAddr = 'rs1q7q3h4chglps004u3yn79z0cp9ed24rfrhvrxnx';
nodeCtx = new NodeContext({
network: 'regtest'
});
const interval = nodeCtx.network.names.treeInterval;
await nodeCtx.open();
const nclient = nodeCtx.nclient;
const node = nodeCtx.node;
{
// 0th block will be 0.
const {chain} = await nclient.getInfo();
assert.strictEqual(chain.treeRootHeight, 0);
}
// blocks from 1 - 4 will be 1.
// last block commits the tree root.
for (let i = 0; i < interval - 1; i++) {
await node.rpc.generateToAddress([1, someAddr]);
const {chain} = await nclient.getInfo();
assert.strictEqual(chain.treeRootHeight, 1);
}
{
// block 5 is also 1 and it commits the new root.
await node.rpc.generateToAddress([1, someAddr]);
const {chain} = await nclient.getInfo();
assert.strictEqual(chain.treeRootHeight, 1);
}
for (let i = 0; i < interval; i++) {
await node.rpc.generateToAddress([1, someAddr]);
const {chain} = await nclient.getInfo();
assert.strictEqual(chain.treeRootHeight, interval + 1);
}
// This block will be part of the new tree batch.
await node.rpc.generateToAddress([1, someAddr]);
const {chain} = await nclient.getInfo();
assert.strictEqual(chain.treeRootHeight, interval * 2 + 1);
});
});
describe('Networking info', function() {
it('should not have public address: regtest', async () => {
const nodeCtx = new NodeContext();
await nodeCtx.open();
const {pool} = await nodeCtx.nclient.getInfo();
await nodeCtx.close();
assert.strictEqual(pool.host, '0.0.0.0');
assert.strictEqual(pool.port, nodeCtx.network.port);
assert.strictEqual(pool.brontidePort, nodeCtx.network.brontidePort);
const {public: pub} = pool;
assert.strictEqual(pub.listen, false);
assert.strictEqual(pub.host, null);
assert.strictEqual(pub.port, null);
assert.strictEqual(pub.brontidePort, null);
});
it('should not have public address: regtest, listen', async () => {
const network = Network.get('regtest');
const nodeCtx = new NodeContext({
listen: true
});
await nodeCtx.open();
const {pool} = await nodeCtx.nclient.getInfo();
await nodeCtx.close();
assert.strictEqual(pool.host, '0.0.0.0');
assert.strictEqual(pool.port, network.port);
assert.strictEqual(pool.brontidePort, network.brontidePort);
const {public: pub} = pool;
assert.strictEqual(pub.listen, true);
assert.strictEqual(pub.host, null); // we don't discover from external
assert.strictEqual(pub.port, null);
assert.strictEqual(pub.brontidePort, null);
});
it('should not have public address: main', async () => {
const nodeCtx = new NodeContext({
network: 'main'
});
const network = nodeCtx.network;
await nodeCtx.open();
const {pool} = await nodeCtx.nclient.getInfo();
await nodeCtx.close();
assert.strictEqual(pool.host, '0.0.0.0');
assert.strictEqual(pool.port, network.port);
assert.strictEqual(pool.brontidePort, network.brontidePort);
const {public: pub} = pool;
assert.strictEqual(pub.listen, false);
assert.strictEqual(pub.host, null);
assert.strictEqual(pub.port, null);
assert.strictEqual(pub.brontidePort, null);
});
it('should not have public address: main, listen', async () => {
const nodeCtx = new NodeContext({
network: 'main',
listen: true
});
const network = nodeCtx.network;
await nodeCtx.open();
const {pool} = await nodeCtx.nclient.getInfo();
await nodeCtx.close();
assert.strictEqual(pool.host, '0.0.0.0');
assert.strictEqual(pool.port, network.port);
assert.strictEqual(pool.brontidePort, network.brontidePort);
const {public: pub} = pool;
assert.strictEqual(pub.listen, true);
assert.strictEqual(pub.host, null);
assert.strictEqual(pub.port, null);
assert.strictEqual(pub.brontidePort, null);
});
it('should have public address: main, listen, publicHost', async () => {
const publicHost = '100.200.11.22';
const publicPort = 11111;
const publicBrontidePort = 22222;
const nodeCtx = new NodeContext({
network: 'main',
listen: true,
publicHost,
publicPort,
publicBrontidePort
});
const network = nodeCtx.network;
await nodeCtx.open();
const {pool} = await nodeCtx.nclient.getInfo();
await nodeCtx.close();
assert.strictEqual(pool.host, '0.0.0.0');
assert.strictEqual(pool.port, network.port);
assert.strictEqual(pool.brontidePort, network.brontidePort);
const {public: pub} = pool;
assert.strictEqual(pub.listen, true);
assert.strictEqual(pub.host, publicHost);
assert.strictEqual(pub.port, publicPort);
assert.strictEqual(pub.brontidePort, publicBrontidePort);
});
});
describe('Websockets', function () {
this.timeout(15000);
describe('tree commit', () => {
const {types} = rules;
const nodeCtx = new NodeContext({
apiKey: 'foo',
indexTx: true,
indexAddress: true,
rejectAbsurdFees: false
});
const network = nodeCtx.network;
const {treeInterval} = network.names;
const nclient = nodeCtx.nclient;
let privkey, pubkey;
let socketData, mempoolData;
let cbAddress;
// take into account race conditions
async function mineBlocks(count, address) {
for (let i = 0; i < count; i++) {
const obj = { complete: false };
nodeCtx.node.once('block', () => {
obj.complete = true;
});
await nclient.execute('generatetoaddress', [1, address]);
await common.forValue(obj, 'complete', true);
}
}
before(async () => {
await nodeCtx.open();
await nclient.call('watch chain');
const mnemonic = Mnemonic.fromPhrase(phrase);
const priv = HDPrivateKey.fromMnemonic(mnemonic);
const type = network.keyPrefix.coinType;
const key = priv.derive(44, true).derive(type, true).derive(0, true);
const xkey = key.derive(0).derive(0);
socketData = [];
mempoolData = {};
pubkey = xkey.publicKey;
privkey = xkey.privateKey;
cbAddress = Address.fromPubkey(pubkey).toString(network.type);
nclient.bind('tree commit', (root, entry, block) => {
assert.ok(root);
assert.ok(block);
assert.ok(entry);
socketData.push({root, entry, block});
});
nodeCtx.mempool.on('tx', (tx) => {
mempoolData[tx.txid()] = true;
});
});
after(async () => {
await nodeCtx.close();
});
beforeEach(() => {
socketData = [];
mempoolData = {};
});
it('should mine 1 tree interval', async () => {
await mineBlocks(treeInterval, cbAddress);
assert.equal(socketData.length, 1);
});
it('should send the correct tree root', async () => {
const name = await nclient.execute('grindname', [5]);
const rawName = Buffer.from(name, 'ascii');
const nameHash = rules.hashName(rawName);
const u32 = Buffer.alloc(4);
bio.writeU32(u32, 0, 0);
const output = new Output({
address: cbAddress,
value: 0,
covenant: {
type: types.OPEN,
items: [nameHash, u32, rawName]
}
});
const mtx = new MTX();
mtx.addOutput(output);
const coins = await nclient.getCoinsByAddresses([cbAddress]);
coins.sort((a, b) => a.height - b.height);
const coin = Coin.fromJSON(coins[0]);
assert.ok(nodeCtx.chain.height > coin.height + network.coinbaseMaturity);
mtx.addCoin(coin);
const addr = Address.fromPubkey(pubkey);
const script = Script.fromPubkeyhash(addr.hash);
const sig = mtx.signature(0, script, coin.value, privkey);
mtx.inputs[0].witness = Witness.fromItems([sig, pubkey]);
const valid = mtx.verify();
assert.ok(valid);
const tx = mtx.toTX();
await nodeCtx.node.sendTX(tx);
await common.forValue(mempoolData, tx.txid(), true);
const pre = await nclient.getInfo();
const mempool = await nclient.getMempool();
assert.equal(mempool[0], mtx.txid());
await mineBlocks(treeInterval, cbAddress);
assert.equal(socketData.length, 1);
const {root, block, entry} = socketData[0];
assert.bufferEqual(nodeCtx.chain.db.treeRoot(), root);
const info = await nclient.getInfo();
assert.notEqual(pre.chain.tip, info.chain.tip);
assert.equal(info.chain.tip, block.hash);
assert.equal(info.chain.tip, entry.hash);
});
});
});
});