856 lines
23 KiB
JavaScript
856 lines
23 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('bsert');
|
|
const Network = require('../lib/protocol/network');
|
|
const consensus = require('../lib/protocol/consensus');
|
|
const MemWallet = require('./util/memwallet');
|
|
const TX = require('../lib/primitives/tx');
|
|
const NodeContext = require('./util/node-context');
|
|
const Address = require('../lib/primitives/address');
|
|
const Script = require('../lib/script/script');
|
|
|
|
const API_KEY = 'foo';
|
|
const NETWORK = 'regtest';
|
|
|
|
const ports = {
|
|
p2p: 49331,
|
|
node: 49332,
|
|
wallet: 49333
|
|
};
|
|
|
|
const nodeOptions = {
|
|
network: NETWORK,
|
|
apiKey: API_KEY,
|
|
memory: true,
|
|
workers: true,
|
|
workersSize: 2,
|
|
port: ports.p2p,
|
|
httpPort: ports.node
|
|
};
|
|
|
|
const errs = {
|
|
MISC_ERROR: -1
|
|
};
|
|
|
|
describe('RPC', function() {
|
|
this.timeout(15000);
|
|
|
|
describe('getblockchaininfo', function() {
|
|
const nodeCtx = new NodeContext(nodeOptions);
|
|
nodeCtx.init();
|
|
const nclient = nodeCtx.nclient;
|
|
|
|
before(async () => {
|
|
await nodeCtx.open();
|
|
});
|
|
|
|
after(async () => {
|
|
await nodeCtx.close();
|
|
});
|
|
|
|
it('should get blockchain info', async () => {
|
|
const info = await nclient.execute('getblockchaininfo', []);
|
|
assert.strictEqual(info.chain, NETWORK);
|
|
assert.strictEqual(info.blocks, 0);
|
|
assert.strictEqual(info.headers, 0);
|
|
assert.strictEqual(info.pruned, false);
|
|
});
|
|
});
|
|
|
|
describe('getrawmempool', function() {
|
|
const nodeCtx = new NodeContext(nodeOptions);
|
|
nodeCtx.init();
|
|
const nclient = nodeCtx.nclient;
|
|
|
|
before(async () => {
|
|
await nodeCtx.open();
|
|
});
|
|
|
|
after(async () => {
|
|
await nodeCtx.close();
|
|
});
|
|
|
|
it('should get raw mempool', async () => {
|
|
const hashes = await nclient.execute('getrawmempool', [true]);
|
|
assert.deepEqual(hashes, {});
|
|
});
|
|
});
|
|
|
|
describe('getblock', function () {
|
|
let nodeCtx, nclient, node;
|
|
|
|
before(async () => {
|
|
nodeCtx = new NodeContext({
|
|
...nodeOptions,
|
|
name: 'node-rpc-test'
|
|
});
|
|
|
|
await nodeCtx.open();
|
|
nclient = nodeCtx.nclient;
|
|
node = nodeCtx.node;
|
|
});
|
|
|
|
after(async () => {
|
|
await nodeCtx.close();
|
|
nodeCtx = null;
|
|
});
|
|
|
|
it('should rpc getblock', async () => {
|
|
const {chain} = await nclient.getInfo();
|
|
const info = await nclient.execute('getblock', [chain.tip]);
|
|
|
|
const properties = [
|
|
'hash', 'confirmations', 'strippedsize',
|
|
'size', 'weight', 'height', 'version',
|
|
'versionHex', 'merkleroot', 'witnessroot',
|
|
'treeroot', 'reservedroot', 'mask',
|
|
'coinbase', 'tx', 'time', 'mediantime',
|
|
'nonce', 'bits', 'difficulty', 'chainwork',
|
|
'nTx', 'previousblockhash', 'nextblockhash'
|
|
];
|
|
|
|
for (const property of properties)
|
|
assert(property in info);
|
|
|
|
assert.deepEqual(chain.height, info.height);
|
|
assert.deepEqual(chain.tip, info.hash);
|
|
assert.deepEqual(chain.treeRoot, info.treeroot);
|
|
});
|
|
|
|
it('should return correct height', async () => {
|
|
const address = 'rs1qjjpnmnrzfvxgqlqf5j48j50jmq9pyqjz0a7ytz';
|
|
|
|
// Mine two blocks.
|
|
await nodeCtx.mineBlocks(2, address);
|
|
|
|
const {chain} = await nclient.getInfo();
|
|
const info = await nclient.execute('getblock', [chain.tip]);
|
|
|
|
// Assert the heights match.
|
|
assert.deepEqual(chain.height, info.height);
|
|
});
|
|
|
|
it('should return confirmations (main chain)', async () => {
|
|
const {chain} = await nclient.getInfo();
|
|
|
|
const {genesis} = node.network;
|
|
const hash = genesis.hash.toString('hex');
|
|
|
|
const info = await nclient.execute('getblock', [hash]);
|
|
assert.deepEqual(chain.height, info.confirmations - 1);
|
|
});
|
|
|
|
it('should return confirmations (post reorg)', async () => {
|
|
// Get the current chain state
|
|
const {chain} = await nclient.getInfo();
|
|
|
|
// Get the chain entry associated with
|
|
// the genesis block.
|
|
const {genesis} = node.network;
|
|
let entry = await node.chain.getEntry(genesis.hash);
|
|
|
|
// Reorg from the genesis block.
|
|
for (let i = 0; i < chain.height + 1; i++) {
|
|
const block = await node.miner.mineBlock(entry);
|
|
await node.chain.add(block);
|
|
entry = await node.chain.getEntry(block.hash());
|
|
}
|
|
|
|
// Call getblock using the previous tip
|
|
const info = await nclient.execute('getblock', [chain.tip]);
|
|
assert.deepEqual(info.confirmations, -1);
|
|
});
|
|
|
|
it('should return confirmations (alternate)', async () => {
|
|
// Get a previous blockheight
|
|
const height = node.chain.height - 2;
|
|
assert(height > 0);
|
|
|
|
// Get the entry and mine on it.
|
|
const entry = await node.chain.getEntryByHeight(height);
|
|
|
|
const block = await node.miner.mineBlock(entry);
|
|
assert(await node.chain.add(block));
|
|
|
|
const hash = block.hash().toString('hex');
|
|
const info = await nclient.execute('getblock', [hash]);
|
|
assert.deepEqual(info.confirmations, -1);
|
|
});
|
|
});
|
|
|
|
describe('pruneblockchain', function() {
|
|
const network = Network.get(NETWORK);
|
|
const PRUNE_AFTER_HEIGHT = network.block.pruneAfterHeight;
|
|
const KEEP_BLOCKS = network.KEEP_BLOCKS;
|
|
|
|
const TEST_KEEP_BLOCKS = 10;
|
|
const TEST_PRUNED_BLOCKS = 10;
|
|
const TEST_PRUNE_AFTER_HEIGHT = 10;
|
|
|
|
let nodeCtx;
|
|
|
|
before(() => {
|
|
network.block.pruneAfterHeight = TEST_PRUNE_AFTER_HEIGHT;
|
|
network.block.keepBlocks = TEST_KEEP_BLOCKS;
|
|
});
|
|
|
|
after(() => {
|
|
network.block.pruneAfterHeight = PRUNE_AFTER_HEIGHT;
|
|
network.block.keepBlocks = KEEP_BLOCKS;
|
|
});
|
|
|
|
afterEach(async () => {
|
|
if (nodeCtx)
|
|
await nodeCtx.close();
|
|
});
|
|
|
|
it('should fail with wrong arguments', async () => {
|
|
nodeCtx = new NodeContext(nodeOptions);
|
|
await nodeCtx.open();
|
|
|
|
await assert.rejects(async () => {
|
|
await nodeCtx.nclient.execute('pruneblockchain', [1]);
|
|
}, {
|
|
code: errs.MISC_ERROR,
|
|
type: 'RPCError',
|
|
message: 'pruneblockchain'
|
|
});
|
|
});
|
|
|
|
it('should not work for spvnode', async () => {
|
|
nodeCtx = new NodeContext({
|
|
...nodeOptions,
|
|
spv: true
|
|
});
|
|
|
|
await nodeCtx.open();
|
|
|
|
await assert.rejects(async () => {
|
|
await nodeCtx.nclient.execute('pruneblockchain');
|
|
}, {
|
|
type: 'RPCError',
|
|
message: 'Cannot prune chain in SPV mode.',
|
|
code: errs.MISC_ERROR
|
|
});
|
|
});
|
|
|
|
it('should fail for pruned node', async () => {
|
|
nodeCtx = new NodeContext({
|
|
...nodeOptions,
|
|
prune: true
|
|
});
|
|
await nodeCtx.open();
|
|
|
|
await assert.rejects(async () => {
|
|
await nodeCtx.nclient.execute('pruneblockchain');
|
|
}, {
|
|
type: 'RPCError',
|
|
code: errs.MISC_ERROR,
|
|
message: 'Chain is already pruned.'
|
|
});
|
|
});
|
|
|
|
it('should fail for short chain', async () => {
|
|
nodeCtx = new NodeContext(nodeOptions);
|
|
await nodeCtx.open();
|
|
|
|
await assert.rejects(async () => {
|
|
await nodeCtx.nclient.execute('pruneblockchain');
|
|
}, {
|
|
type: 'RPCError',
|
|
code: errs.MISC_ERROR,
|
|
message: 'Chain is too short for pruning.'
|
|
});
|
|
});
|
|
|
|
it('should prune chain', async () => {
|
|
// default - prune: false
|
|
nodeCtx = new NodeContext(nodeOptions);
|
|
await nodeCtx.open();
|
|
const {miner, nclient} = nodeCtx;
|
|
|
|
const addr = 'rs1q4rvs9pp9496qawp2zyqpz3s90fjfk362q92vq8';
|
|
miner.addAddress(addr);
|
|
|
|
let genBlocks = TEST_PRUNE_AFTER_HEIGHT;
|
|
genBlocks += TEST_PRUNED_BLOCKS;
|
|
genBlocks += TEST_KEEP_BLOCKS;
|
|
|
|
// generate 30 blocks.
|
|
// similar to chain-rpc-test
|
|
const blocks = await nclient.execute('generate', [genBlocks]);
|
|
|
|
// make sure we have all the blocks.
|
|
for (let i = 0; i < genBlocks; i++) {
|
|
const block = await nclient.execute('getblock', [blocks[i]]);
|
|
assert(block);
|
|
}
|
|
|
|
// now prune..
|
|
await nclient.execute('pruneblockchain');
|
|
|
|
let i = 0;
|
|
|
|
// behind height check
|
|
let to = TEST_PRUNE_AFTER_HEIGHT;
|
|
for (; i < to; i++) {
|
|
const block = await nclient.execute('getblock', [blocks[i]]);
|
|
assert(block, 'could not get block before height check.');
|
|
}
|
|
|
|
// pruned blocks.
|
|
to += TEST_PRUNED_BLOCKS;
|
|
for (; i < to; i++) {
|
|
await assert.rejects(async () => {
|
|
await nclient.execute('getblock', [blocks[i]]);
|
|
}, {
|
|
type: 'RPCError',
|
|
code: errs.MISC_ERROR,
|
|
message: 'Block not available (pruned data)'
|
|
});
|
|
}
|
|
|
|
// keep blocks
|
|
to += TEST_KEEP_BLOCKS;
|
|
for (; i < to; i++) {
|
|
const block = await nclient.execute('getblock', [blocks[i]]);
|
|
assert(block, `block ${i} was pruned.`);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('mining', function() {
|
|
const nodeCtx = new NodeContext(nodeOptions);
|
|
nodeCtx.init();
|
|
const {
|
|
miner,
|
|
chain,
|
|
mempool,
|
|
nodeRPC,
|
|
nclient
|
|
} = nodeCtx;
|
|
|
|
const wallet = new MemWallet({
|
|
network: NETWORK
|
|
});
|
|
|
|
let mtx1, mtx2;
|
|
|
|
before(async () => {
|
|
await nodeCtx.open();
|
|
});
|
|
|
|
after(async () => {
|
|
await nodeCtx.close();
|
|
});
|
|
|
|
it('should get a block template', async () => {
|
|
const {network, chain} = nodeCtx;
|
|
const json = await nclient.execute('getblocktemplate', []);
|
|
assert.deepStrictEqual(json, {
|
|
capabilities: ['proposal'],
|
|
mutable: ['time', 'transactions', 'prevblock'],
|
|
version: 0,
|
|
rules: [],
|
|
vbavailable: {},
|
|
vbrequired: 0,
|
|
height: 1,
|
|
previousblockhash: network.genesis.hash.toString('hex'),
|
|
treeroot: network.genesis.treeRoot.toString('hex'),
|
|
reservedroot: consensus.ZERO_HASH.toString('hex'),
|
|
mask: json.mask,
|
|
target:
|
|
'7fffff0000000000000000000000000000000000000000000000000000000000',
|
|
bits: '207fffff',
|
|
noncerange: ''
|
|
+ '000000000000000000000000000000000000000000000000'
|
|
+ 'ffffffffffffffffffffffffffffffffffffffffffffffff',
|
|
curtime: json.curtime,
|
|
mintime: 1580745081,
|
|
maxtime: json.maxtime,
|
|
expires: json.expires,
|
|
sigoplimit: 80000,
|
|
sizelimit: 1000000,
|
|
weightlimit: 4000000,
|
|
longpollid: chain.tip.hash.toString('hex') + '00000000',
|
|
submitold: false,
|
|
coinbaseaux: { flags: '6d696e656420627920687364' },
|
|
coinbasevalue: 2000000000,
|
|
claims: [],
|
|
airdrops: [],
|
|
transactions: []
|
|
});
|
|
});
|
|
|
|
it('should send a block template proposal', async () => {
|
|
const {node} = nodeCtx;
|
|
const attempt = await node.miner.createBlock();
|
|
const block = attempt.toBlock();
|
|
const hex = block.toHex();
|
|
const json = await nclient.execute('getblocktemplate', [{
|
|
mode: 'proposal',
|
|
data: hex
|
|
}]);
|
|
assert.strictEqual(json, null);
|
|
});
|
|
|
|
it('should submit a block', async () => {
|
|
const block = await miner.mineBlock();
|
|
const hex = block.toHex();
|
|
|
|
const result = await nclient.execute('submitblock', [hex]);
|
|
|
|
assert.strictEqual(result, null);
|
|
assert.bufferEqual(chain.tip.hash, block.hash());
|
|
});
|
|
|
|
it('should add transactions to mempool', async () => {
|
|
// Fund MemWallet
|
|
miner.addresses.length = 0;
|
|
miner.addAddress(wallet.getReceive());
|
|
for (let i = 0; i < 10; i++) {
|
|
const block = await miner.mineBlock();
|
|
const entry = await chain.add(block);
|
|
wallet.addBlock(entry, block.txs);
|
|
}
|
|
|
|
// High fee
|
|
mtx1 = await wallet.send({
|
|
rate: 100000,
|
|
outputs: [{
|
|
value: 100000,
|
|
address: wallet.getReceive()
|
|
}]
|
|
});
|
|
await mempool.addTX(mtx1.toTX());
|
|
|
|
// Low fee
|
|
mtx2 = await wallet.send({
|
|
rate: 10000,
|
|
outputs: [{
|
|
value: 100000,
|
|
address: wallet.getReceive()
|
|
}]
|
|
});
|
|
await mempool.addTX(mtx2.toTX());
|
|
|
|
assert.strictEqual(mempool.map.size, 2);
|
|
});
|
|
|
|
it('should get a block template', async () => {
|
|
nodeRPC.refreshBlock();
|
|
|
|
const result = await nclient.execute(
|
|
'getblocktemplate',
|
|
[{rules: []}]
|
|
);
|
|
|
|
let fees = 0;
|
|
let weight = 0;
|
|
|
|
for (const item of result.transactions) {
|
|
fees += item.fee;
|
|
weight += item.weight;
|
|
}
|
|
|
|
assert.strictEqual(result.transactions.length, 2);
|
|
assert.strictEqual(fees, mtx1.getFee() + mtx2.getFee());
|
|
assert.strictEqual(weight, mtx1.getWeight() + mtx2.getWeight());
|
|
assert.strictEqual(result.transactions[0].txid, mtx1.txid());
|
|
assert.strictEqual(result.transactions[1].txid, mtx2.txid());
|
|
assert.strictEqual(result.coinbasevalue, 2000 * consensus.COIN + fees);
|
|
});
|
|
|
|
it('should prioritise transaction', async () => {
|
|
const result = await nclient.execute(
|
|
'prioritisetransaction',
|
|
[mtx2.txid(), 0, 10000000]
|
|
);
|
|
|
|
assert.strictEqual(result, true);
|
|
});
|
|
|
|
it('should get a block template', async () => {
|
|
let fees = 0;
|
|
let weight = 0;
|
|
|
|
nodeRPC.refreshBlock();
|
|
|
|
const result = await nclient.execute(
|
|
'getblocktemplate',
|
|
[{rules: []}]
|
|
);
|
|
|
|
for (const item of result.transactions) {
|
|
fees += item.fee;
|
|
weight += item.weight;
|
|
}
|
|
|
|
assert.strictEqual(result.transactions.length, 2);
|
|
assert.strictEqual(fees, mtx1.getFee() + mtx2.getFee());
|
|
assert.strictEqual(weight, mtx1.getWeight() + mtx2.getWeight());
|
|
// TX order is swapped from last test due to priortization
|
|
assert.strictEqual(result.transactions[1].txid, mtx1.txid());
|
|
assert.strictEqual(result.transactions[0].txid, mtx2.txid());
|
|
assert.strictEqual(result.coinbasevalue, 2000 * consensus.COIN + fees);
|
|
});
|
|
|
|
it('should mine a block', async () => {
|
|
const block = await miner.mineBlock();
|
|
assert(block);
|
|
await chain.add(block);
|
|
});
|
|
});
|
|
|
|
describe('transactions', function() {
|
|
const nodeCtx = new NodeContext({
|
|
...nodeOptions,
|
|
indexTX: true
|
|
});
|
|
nodeCtx.init();
|
|
|
|
const {
|
|
miner,
|
|
chain,
|
|
mempool,
|
|
nclient
|
|
} = nodeCtx;
|
|
|
|
const wallet = new MemWallet({
|
|
network: NETWORK
|
|
});
|
|
|
|
let tx1;
|
|
|
|
before(async () => {
|
|
await nodeCtx.open();
|
|
});
|
|
|
|
after(async () => {
|
|
await nodeCtx.close();
|
|
});
|
|
|
|
it('should confirm a transaction in a block', async () => {
|
|
// Fund MemWallet
|
|
miner.addresses.length = 0;
|
|
miner.addAddress(wallet.getReceive());
|
|
for (let i = 0; i < 10; i++) {
|
|
const block = await miner.mineBlock();
|
|
const entry = await chain.add(block);
|
|
wallet.addBlock(entry, block.txs);
|
|
}
|
|
|
|
const mtx1 = await wallet.send({
|
|
rate: 100000,
|
|
outputs: [{
|
|
value: 100000,
|
|
address: wallet.getReceive()
|
|
}]
|
|
});
|
|
tx1 = mtx1.toTX();
|
|
|
|
await mempool.addTX(tx1);
|
|
|
|
assert.strictEqual(mempool.map.size, 1);
|
|
|
|
const block = await miner.mineBlock();
|
|
assert(block);
|
|
assert.strictEqual(block.txs.length, 2);
|
|
await chain.add(block);
|
|
});
|
|
|
|
it('should get raw transaction', async () => {
|
|
const result = await nclient.execute(
|
|
'getrawtransaction',
|
|
[tx1.txid()]
|
|
);
|
|
|
|
const tx = TX.fromHex(result);
|
|
assert.strictEqual(tx.txid(), tx1.txid());
|
|
});
|
|
|
|
it('should get raw transaction (verbose=true)', async () => {
|
|
const result = await nclient.execute(
|
|
'getrawtransaction',
|
|
[tx1.txid(), true]
|
|
);
|
|
|
|
const tx = TX.fromHex(result.hex);
|
|
|
|
assert.equal(result.vin.length, tx.inputs.length);
|
|
assert.equal(result.vout.length, tx.outputs.length);
|
|
|
|
for (const [i, vout] of result.vout.entries()) {
|
|
const output = tx.output(i);
|
|
assert.equal(vout.address.version, output.address.version);
|
|
assert.equal(vout.address.string, output.address.toString(nodeCtx.network));
|
|
assert.equal(vout.address.hash, output.address.hash.toString('hex'));
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('networking', function() {
|
|
const nodeCtx = new NodeContext({ ...nodeOptions, bip37: true });
|
|
nodeCtx.init();
|
|
const nclient = nodeCtx.nclient;
|
|
|
|
before(async () => {
|
|
await nodeCtx.open();
|
|
});
|
|
|
|
after(async () => {
|
|
await nodeCtx.close();
|
|
});
|
|
|
|
it('should get service names for rpc getnetworkinfo', async () => {
|
|
const result = await nclient.execute('getnetworkinfo', []);
|
|
|
|
assert.deepEqual(result.localservicenames, ['NETWORK', 'BLOOM']);
|
|
});
|
|
});
|
|
|
|
describe('DNS Utility', function() {
|
|
const nodeCtx = new NodeContext(nodeOptions);
|
|
nodeCtx.init();
|
|
const nclient = nodeCtx.nclient;
|
|
|
|
before(async () => {
|
|
await nodeCtx.open();
|
|
});
|
|
|
|
after(async () => {
|
|
await nodeCtx.close();
|
|
});
|
|
|
|
it('should decode resource', async () => {
|
|
// .p resource at mainnet height 118700
|
|
const result = await nclient.execute(
|
|
'decoderesource',
|
|
[
|
|
'0002036e733101700022858d9e01c00200c625080220d96' +
|
|
'65e9952988fc27b1d4098491b37d83a3b6fb2cdf5fc9787' +
|
|
'd4dda67f1408ed001383080220ae8ea2f2800727f9ad3b6' +
|
|
'd7c802dac2a0790bdc8ea717bf5f6dc21f83fb8cc4e'
|
|
]
|
|
);
|
|
|
|
assert.deepEqual(
|
|
result,
|
|
{
|
|
records: [
|
|
{
|
|
type: 'GLUE4',
|
|
ns: 'ns1.p.',
|
|
address: '34.133.141.158'
|
|
},
|
|
{
|
|
type: 'NS',
|
|
ns: 'ns1.p.'
|
|
},
|
|
{
|
|
type: 'DS',
|
|
keyTag: 50725,
|
|
algorithm: 8,
|
|
digestType: 2,
|
|
digest: 'd9665e9952988fc27b1d4098491b37d83a3b6fb2cdf5fc9787d4dda67f1408ed'
|
|
},
|
|
{
|
|
type: 'DS',
|
|
keyTag: 4995,
|
|
algorithm: 8,
|
|
digestType: 2,
|
|
digest: 'ae8ea2f2800727f9ad3b6d7c802dac2a0790bdc8ea717bf5f6dc21f83fb8cc4e'
|
|
}
|
|
]
|
|
}
|
|
);
|
|
});
|
|
|
|
it('should validateresource (valid)', async () => {
|
|
const records = [
|
|
[{type: 'NS', ns: 'ns1.handshake.org.'}],
|
|
[{type: 'DS', keyTag: 0xffff, algorithm: 0xff, digestType: 0xff, digest: '00'.repeat(32)}],
|
|
[{type: 'TXT', txt: ['i like turtles', 'then who phone']}],
|
|
[{type: 'GLUE4', ns: 'ns1.nam.ek.', address: '192.168.0.1'}],
|
|
[{type: 'GLUE6', ns: 'ns2.nam.ek.', address: '::'}],
|
|
[{type: 'SYNTH4', address: '192.168.0.1'}],
|
|
[{type: 'SYNTH6', address: '::'}]
|
|
];
|
|
|
|
for (const record of records) {
|
|
const data = {records: record};
|
|
const info = await nclient.execute('validateresource', [data]);
|
|
assert.deepEqual(info, data);
|
|
}
|
|
});
|
|
|
|
it('should validateresource (invalid)', async () => {
|
|
const records = [
|
|
[
|
|
// No trailing dot
|
|
[{type: 'NS', ns: 'ns1.handshake.org'}],
|
|
'Invalid NS record. ns must be a valid name.'
|
|
],
|
|
[
|
|
[{type: 'DS', keyTag: 0xffffff}],
|
|
'Invalid DS record. KeyTag must be a uint16.'
|
|
],
|
|
[
|
|
[{type: 'DS', keyTag: 0xffff, algorithm: 0xffff}],
|
|
'Invalid DS record. Algorithm must be a uint8.'
|
|
],
|
|
[
|
|
[{type: 'DS', keyTag: 0xffff, algorithm: 0xff, digestType: 0xffff}],
|
|
'Invalid DS record. DigestType must be a uint8.'
|
|
],
|
|
[
|
|
[{type: 'DS', keyTag: 0xffff, algorithm: 0xff, digestType: 0xff, digest: Buffer.alloc(0)}],
|
|
'Invalid DS record. Digest must be a String.'
|
|
],
|
|
[
|
|
[{type: 'DS', keyTag: 0xffff, algorithm: 0xff, digestType: 0xff, digest: '00'.repeat(256)}],
|
|
'Invalid DS record. Digest is too large.'
|
|
],
|
|
[
|
|
[{type: 'TXT', txt: 'foobar'}],
|
|
'Invalid TXT record. txt must be an Array.'
|
|
],
|
|
[
|
|
[{type: 'TXT', txt: [{}]}],
|
|
'Invalid TXT record. Entries in txt Array must be type String.'
|
|
],
|
|
[
|
|
[{type: 'TXT', txt: ['0'.repeat(256)]}],
|
|
'Invalid TXT record. Entries in txt Array must be <= 255 in length.'
|
|
],
|
|
[
|
|
[{type: 'GLUE4', ns: 'ns1.nam.ek', address: '192.168.0.1'}],
|
|
'Invalid GLUE4 record. ns must be a valid name.'
|
|
],
|
|
[
|
|
[{type: 'GLUE4', ns: 'ns1.nam.ek.', address: '::'}],
|
|
'Invalid GLUE4 record. Address must be a valid IPv4 address.'
|
|
],
|
|
[
|
|
[{type: 'GLUE6', ns: 'ns1.nam.ek', address: '::'}],
|
|
'Invalid GLUE6 record. ns must be a valid name.'
|
|
],
|
|
[
|
|
[{type: 'GLUE6', ns: 'ns1.nam.ek.', address: '0.0.0.0'}],
|
|
'Invalid GLUE6 record. Address must be a valid IPv6 address.'
|
|
],
|
|
[
|
|
[{type: 'SYNTH4', address: '::'}],
|
|
'Invalid SYNTH4 record. Address must be a valid IPv4 address.'
|
|
],
|
|
[
|
|
[{type: 'SYNTH6', address: '127.0.0.1'}],
|
|
'Invalid SYNTH6 record. Address must be a valid IPv6 address.'
|
|
]
|
|
];
|
|
|
|
for (const [record, reason] of records) {
|
|
try {
|
|
const data = {records: record};
|
|
await nclient.execute('validateresource', [data]);
|
|
assert.fail();
|
|
} catch (e) {
|
|
assert.equal(e.message, reason);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Address Utility', function() {
|
|
const nodeCtx = new NodeContext({
|
|
...nodeOptions,
|
|
wallet: true
|
|
});
|
|
|
|
nodeCtx.init();
|
|
|
|
const {
|
|
node,
|
|
nclient,
|
|
wdb
|
|
} = nodeCtx;
|
|
|
|
let wallet, addr;
|
|
|
|
before(async () => {
|
|
await nodeCtx.open();
|
|
wallet = await wdb.create({ id: 'test'});
|
|
});
|
|
|
|
after(async () => {
|
|
await nodeCtx.close();
|
|
});
|
|
|
|
it('should validate an address', async () => {
|
|
addr = await wallet.receiveAddress('default');
|
|
const json = await nclient.execute('validateaddress', [
|
|
addr.toString(nodeCtx.network)
|
|
]);
|
|
|
|
assert.deepStrictEqual(json, {
|
|
isvalid: true,
|
|
isscript: false,
|
|
isspendable: true,
|
|
address: addr.toString(node.network),
|
|
witness_program: addr.hash.toString('hex'),
|
|
witness_version: addr.version
|
|
});
|
|
});
|
|
|
|
it('should not validate invalid address', async () => {
|
|
const json = await nclient.execute('validateaddress', [
|
|
addr.toString('main')
|
|
]);
|
|
assert.deepStrictEqual(json, {
|
|
isvalid: false
|
|
});
|
|
});
|
|
|
|
it('should validate a p2wsh address', async () => {
|
|
const pubkeys = [];
|
|
for (let i = 0; i < 2; i++) {
|
|
const result = await wallet.receiveKey('default');
|
|
pubkeys.push(Buffer.from(result.publicKey, 'hex'));
|
|
}
|
|
|
|
const script = Script.fromMultisig(2, 2, pubkeys);
|
|
const address = Address.fromScript(script);
|
|
|
|
const json = await nclient.execute('validateaddress', [
|
|
address.toString(node.network)
|
|
]);
|
|
|
|
assert.deepStrictEqual(json, {
|
|
address: address.toString(node.network),
|
|
isscript: true,
|
|
isspendable: true,
|
|
isvalid: true,
|
|
witness_version: address.version,
|
|
witness_program: address.hash.toString('hex')
|
|
});
|
|
});
|
|
|
|
it('should validate a null address', async () => {
|
|
const data = Buffer.from('foobar', 'ascii');
|
|
const nullAddr = Address.fromNulldata(data);
|
|
|
|
const json = await nclient.execute('validateaddress', [
|
|
nullAddr.toString(node.network)
|
|
]);
|
|
|
|
assert.deepStrictEqual(json, {
|
|
address: nullAddr.toString(node.network),
|
|
isscript: false,
|
|
isspendable: false,
|
|
isvalid: true,
|
|
witness_version: nullAddr.version,
|
|
witness_program: nullAddr.hash.toString('hex')
|
|
});
|
|
});
|
|
});
|
|
});
|