test: improve addrindex tests

- Vectors for p2wsh and p2sh.
- Edge case for witness program.
- Improve determinism by not comparing mtime.
- Various edge and error cases.
This commit is contained in:
Braydon Fuller 2019-04-18 10:12:04 -07:00
parent 69e9844f82
commit b4be8574e5
No known key found for this signature in database
GPG Key ID: F24F232D108B3AD4
6 changed files with 579 additions and 275 deletions

View File

@ -138,8 +138,7 @@ class AddrIndexer extends Indexer {
const hash = tx.hash();
const count = new Count(height, i);
this.put(layout.C.encode(height, i), hash);
this.put(layout.c.encode(hash), count.toRaw());
let hasAddress = false;
for (const addr of tx.getAddresses(view)) {
const prefix = addr.getPrefix();
@ -150,6 +149,13 @@ class AddrIndexer extends Indexer {
const addrHash = addr.getHash();
this.put(layout.A.encode(prefix, addrHash, height, i), null);
hasAddress = true;
}
if (hasAddress) {
this.put(layout.C.encode(height, i), hash);
this.put(layout.c.encode(hash), count.toRaw());
}
}
}
@ -169,8 +175,7 @@ class AddrIndexer extends Indexer {
const tx = block.txs[i];
const hash = tx.hash();
this.del(layout.C.encode(height, i));
this.del(layout.c.encode(hash));
let hasAddress = false;
for (const addr of tx.getAddresses(view)) {
const prefix = addr.getPrefix();
@ -181,6 +186,13 @@ class AddrIndexer extends Indexer {
const addrHash = addr.getHash();
this.del(layout.A.encode(prefix, addrHash, height, i));
hasAddress = true;
}
if (hasAddress) {
this.del(layout.C.encode(height, i));
this.del(layout.c.encode(hash));
}
}
}
@ -222,13 +234,19 @@ class AddrIndexer extends Indexer {
}
};
// Determine if the hash -> height + index mapping exists.
const hasAfter = (after && await this.db.has(layout.c.encode(after)));
const skip = (after && !hasAfter && !reverse);
// Check to see if results should be skipped because
// the after hash is expected to be within a following
// mempool query.
const skip = (after && !hasAfter && !reverse);
if (skip)
return [];
if (after && hasAfter) {
// Give results starting from after
// the tx hash for the address.
const raw = await this.db.get(layout.c.encode(after));
const count = Count.fromRaw(raw);
const {height, index} = count;
@ -241,6 +259,8 @@ class AddrIndexer extends Indexer {
opts.lt = layout.A.max(prefix, hash, height, index);
}
} else {
// Give earliest or latest results
// for the address.
opts.gte = layout.A.min(prefix, hash);
opts.lte = layout.A.max(prefix, hash);
}

235
lib/mempool/addrindexer.js Normal file
View File

@ -0,0 +1,235 @@
/*!
* mempool.js - mempool for bcoin
* Copyright (c) 2018-2019, the bcoin developers (MIT License).
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
const assert = require('bsert');
const {BufferMap} = require('buffer-map');
const TXMeta = require('../primitives/txmeta');
/**
* Address Indexer
* @ignore
*/
class AddrIndexer {
/**
* Create TX address index.
* @constructor
*/
constructor() {
// Map of addr->entries.
this.index = new BufferMap();
// Map of txid->addrs.
this.map = new BufferMap();
}
reset() {
this.index.clear();
this.map.clear();
}
getKey(addr) {
const prefix = addr.getPrefix();
if (prefix < 0)
return null;
const raw = Buffer.allocUnsafe(1);
raw.writeUInt8(prefix);
return Buffer.concat([raw, addr.getHash()]);
}
/**
* Get transactions by address.
* @param {Address} addr
* @param {Object} options
* @param {Number} options.limit
* @param {Number} options.reverse
* @param {Buffer} options.after
*/
get(addr, options = {}) {
const values = this.getEntries(addr, options);
const out = [];
for (const entry of values)
out.push(entry.tx);
return out;
}
/**
* Get transaction meta by address.
* @param {Address} addr
* @param {Object} options
* @param {Number} options.limit
* @param {Number} options.reverse
* @param {Buffer} options.after
*/
getMeta(addr, options = {}) {
const values = this.getEntries(addr, options);
const out = [];
for (const entry of values) {
const meta = TXMeta.fromTX(entry.tx);
meta.mtime = entry.time;
out.push(meta);
}
return out;
}
/**
* Get entries by address.
* @param {Address} addr
* @param {Object} options
* @param {Number} options.limit
* @param {Number} options.reverse
* @param {Buffer} options.after
*/
getEntries(addr, options = {}) {
const {limit, reverse, after} = options;
const key = this.getKey(addr);
if (!key)
return [];
const items = this.index.get(key);
if (!items)
return [];
let values = [];
// Check to see if results should be skipped because
// the after hash is expected to be within a following
// confirmed query.
const skip = (after && !items.has(after) && reverse);
if (skip)
return values;
if (after && items.has(after)) {
// Give results starting from after
// the tx hash for the address.
let index = 0;
for (const k of items.keys()) {
if (k.compare(after) === 0)
break;
index += 1;
}
values = Array.from(items.values());
let start = index + 1;
let end = values.length;
if (end - start > limit)
end = start + limit;
if (reverse) {
start = 0;
end = index;
if (end > limit)
start = end - limit;
}
values = values.slice(start, end);
} else {
// Give earliest or latest results
// for the address.
values = Array.from(items.values());
if (values.length > limit) {
let start = 0;
let end = limit;
if (reverse) {
start = values.length - limit;
end = values.length;
}
values = values.slice(start, end);
}
}
if (reverse)
values.reverse();
return values;
}
insert(entry, view) {
const tx = entry.tx;
const hash = tx.hash();
const addrs = tx.getAddresses(view);
if (addrs.length === 0)
return;
for (const addr of addrs) {
const key = this.getKey(addr);
if (!key)
continue;
let items = this.index.get(key);
if (!items) {
items = new BufferMap();
this.index.set(key, items);
}
assert(!items.has(hash));
items.set(hash, entry);
}
this.map.set(hash, addrs);
}
remove(hash) {
const addrs = this.map.get(hash);
if (!addrs)
return;
for (const addr of addrs) {
const key = this.getKey(addr);
if (!key)
continue;
const items = this.index.get(key);
assert(items);
assert(items.has(hash));
items.delete(hash);
if (items.size === 0)
this.index.delete(key);
}
this.map.delete(hash);
}
}
/*
* Expose
*/
module.exports = AddrIndexer;

View File

@ -27,6 +27,7 @@ const TXMeta = require('../primitives/txmeta');
const MempoolEntry = require('./mempoolentry');
const Network = require('../protocol/network');
const layout = require('./layout');
const AddrIndexer = require('./addrindexer');
const Fees = require('./fees');
const CoinView = require('../coins/coinview');
@ -72,7 +73,7 @@ class Mempool extends EventEmitter {
this.spents = new BufferMap();
this.rejects = new RollingFilter(120000, 0.000001);
this.addrindex = new AddrIndex();
this.addrindex = new AddrIndexer();
}
/**
@ -2118,216 +2119,6 @@ class MempoolOptions {
}
}
/**
* Address Index
* @ignore
*/
class AddrIndex {
/**
* Create TX address index.
* @constructor
*/
constructor() {
// Map of addr->entries.
this.index = new BufferMap();
// Map of txid->addrs.
this.map = new BufferMap();
}
reset() {
this.index.clear();
this.map.clear();
}
getKey(addr) {
const prefix = addr.getPrefix();
if (prefix < 0)
return null;
const raw = Buffer.allocUnsafe(1);
raw.writeUInt8(prefix);
return Buffer.concat([raw, addr.getHash()]);
}
/**
* Get transactions by address.
* @param {Address} addr
* @param {Object} options
* @param {Number} options.limit
* @param {Number} options.reverse
* @param {Buffer} options.after
*/
get(addr, options = {}) {
const values = this.getEntries(addr, options);
const out = [];
for (const entry of values)
out.push(entry.tx);
return out;
}
/**
* Get transaction meta by address.
* @param {Address} addr
* @param {Object} options
* @param {Number} options.limit
* @param {Number} options.reverse
* @param {Buffer} options.after
*/
getMeta(addr, options = {}) {
const values = this.getEntries(addr, options);
const out = [];
for (const entry of values) {
const meta = TXMeta.fromTX(entry.tx);
meta.mtime = entry.time;
out.push(meta);
}
return out;
}
/**
* Get entries by address.
* @param {Address} addr
* @param {Object} options
* @param {Number} options.limit
* @param {Number} options.reverse
* @param {Buffer} options.after
*/
getEntries(addr, options = {}) {
const {limit, reverse, after} = options;
const key = this.getKey(addr);
if (!key)
return [];
const items = this.index.get(key);
if (!items)
return [];
let values = [];
const skip = (after && !items.has(after) && reverse);
if (skip)
return values;
if (after && items.has(after)) {
let index = 0;
for (const k of items.keys()) {
if (k.compare(after) === 0)
break;
index += 1;
}
values = Array.from(items.values());
let start = index + 1;
let end = values.length;
if (end - start > limit)
end = start + limit;
if (reverse) {
start = 0;
end = index;
if (end - start > limit)
start = end - limit;
}
values = values.slice(start, end);
} else {
values = Array.from(items.values());
if (values.length > limit) {
let start = 0;
let end = limit;
if (reverse) {
start = values.length - limit;
end = values.length;
}
values = values.slice(start, end);
}
}
if (reverse)
values.reverse();
return values;
}
insert(entry, view) {
const tx = entry.tx;
const hash = tx.hash();
const addrs = tx.getAddresses(view);
if (addrs.length === 0)
return;
for (const addr of addrs) {
const key = this.getKey(addr);
if (!key)
continue;
let items = this.index.get(key);
if (!items) {
items = new BufferMap();
this.index.set(key, items);
}
assert(!items.has(hash));
items.set(hash, entry);
}
this.map.set(hash, addrs);
}
remove(hash) {
const addrs = this.map.get(hash);
if (!addrs)
return;
for (const addr of addrs) {
const key = this.getKey(addr);
if (!key)
continue;
const items = this.index.get(key);
assert(items);
assert(items.has(hash));
items.delete(hash);
if (items.size === 0)
this.index.delete(key);
}
this.map.delete(hash);
}
}
/**
* Orphan
* @ignore

View File

@ -5,6 +5,9 @@
const assert = require('./util/assert');
const reorg = require('./util/reorg');
const Script = require('../lib/script/script');
const Opcode = require('../lib/script/opcode');
const Address = require('../lib/primitives/address');
const Chain = require('../lib/blockchain/chain');
const WorkerPool = require('../lib/workers/workerpool');
const Miner = require('../lib/mining/miner');
@ -18,6 +21,34 @@ const network = Network.get('regtest');
const {NodeClient, WalletClient} = require('bclient');
const {forValue} = require('./util/common');
const vectors = [
// Secret for the public key vectors:
// cVDJUtDjdaM25yNVVDLLX3hcHUfth4c7tY3rSc4hy9e8ibtCuj6G
{
addr: 'bcrt1qngw83fg8dz0k749cg7k3emc7v98wy0c7azaa6h',
amount: 19.99,
label: 'p2wpkh'
},
{
addr: 'muZpTpBYhxmRFuCjLc7C6BBDF32C8XVJUi',
amount: 1.99,
label: 'p2pkh'
},
// Secrets for 1 of 2 multisig vectors:
// cVDJUtDjdaM25yNVVDLLX3hcHUfth4c7tY3rSc4hy9e8ibtCuj6G
// 93KCDD4LdP4BDTNBXrvKUCVES2jo9dAKKvhyWpNEMstuxDauHty
{
addr: 'bcrt1q2nj8e2nhmsa4hl9qw3xas7l5n2547h5uhlj47nc3pqfxaeq5rtjs9g328g',
amount: 0.99,
label: 'p2wsh'
},
{
addr: '2Muy8nSQaMsMFAZwPyiXSEMTVFJv9iYuhwT',
amount: 0.11,
label: 'p2sh'
}
];
const workers = new WorkerPool({
enabled: true
});
@ -79,7 +110,89 @@ describe('Indexer', function() {
await addrindexer.close();
});
describe('index 10 blocks', function() {
describe('Unit', function() {
it('should not index transaction w/ invalid address', async () => {
const indexer = new AddrIndexer({
blocks: {},
chain: {}
});
const ops = [];
indexer.put = (key, value) => ops.push([key, value]);
indexer.del = (key, value) => ops.push([key, value]);
// Create a witness program version 1 with
// 40 byte data push.
const script = new Script();
script.push(Opcode.fromSmall(1));
script.push(Opcode.fromData(Buffer.alloc(40)));
script.compile();
const addr = Address.fromScript(script);
const tx = {
getAddresses: () => [addr],
hash: () => Buffer.alloc(32)
};
const entry = {height: 323549};
const block = {txs: [tx]};
const view = {};
indexer.indexBlock(entry, block, view);
indexer.unindexBlock(entry, block, view);
assert.equal(ops.length, 0);
});
it('should index transaction w/ valid address', async () => {
const indexer = new AddrIndexer({
blocks: {},
chain: {}
});
const ops = [];
indexer.put = (key, value) => ops.push([key, value]);
indexer.del = (key, value) => ops.push([key, value]);
// Create a witness program version 0 with
// 20 byte data push.
const script = new Script();
script.push(Opcode.fromSmall(0));
script.push(Opcode.fromData(Buffer.alloc(20)));
script.compile();
const addr = Address.fromScript(script);
const tx = {
getAddresses: () => [addr],
hash: () => Buffer.alloc(32)
};
const entry = {height: 323549};
const block = {txs: [tx]};
const view = {};
indexer.indexBlock(entry, block, view);
indexer.unindexBlock(entry, block, view);
assert.equal(ops.length, 6);
});
it('should error with limits', async () => {
const indexer = new AddrIndexer({
blocks: {},
chain: {},
maxTxs: 10
});
await assert.asyncThrows(async () => {
await indexer.getHashesByAddress(vectors[0].addr, {limit: 11});
}, 'Limit above max');
});
});
describe('Index 10 blocks', function() {
let addr = null;
before(async () => {
@ -189,7 +302,7 @@ describe('Indexer', function() {
});
});
describe('rescan and reorg', function() {
describe('Rescan and reorg', function() {
it('should rescan and reindex 10 missed blocks', async () => {
for (let i = 0; i < 10; i++) {
const block = await cpu.mineBlock();
@ -226,26 +339,11 @@ describe('Indexer', function() {
});
});
describe('http', function() {
describe('HTTP', function() {
this.timeout(120000);
let node, nclient, wclient = null;
const vectors = [
// Secret for the vectors:
// cVDJUtDjdaM25yNVVDLLX3hcHUfth4c7tY3rSc4hy9e8ibtCuj6G
{
addr: 'bcrt1qngw83fg8dz0k749cg7k3emc7v98wy0c7azaa6h',
amount: 19.99,
label: 'p2wpkh'
},
{
addr: 'muZpTpBYhxmRFuCjLc7C6BBDF32C8XVJUi',
amount: 1.99,
label: 'p2pkh'
}
];
const confirmed = [];
const unconfirmed = [];
@ -255,6 +353,15 @@ describe('Indexer', function() {
wallet: 49333
};
function sanitize(txs) {
return txs.map((tx) => {
// Remove mtime from the results for deep
// comparisons as it can be variable.
delete tx.mtime;
return tx;
});
}
before(async () => {
this.timeout(120000);
@ -326,7 +433,7 @@ describe('Indexer', function() {
await forValue(node.chain, 'height', 160);
// Send unconfirmed to the vector addresses.
for (let i = 0; i < 3; i++) {
for (let i = 0; i < 5; i++) {
for (const v of vectors) {
const txid = await wclient.execute(
'sendtoaddress', [v.addr, v.amount]);
@ -335,7 +442,7 @@ describe('Indexer', function() {
}
}
await forValue(node.mempool.map, 'size', 6);
await forValue(node.mempool.map, 'size', 20);
});
after(async () => {
@ -345,20 +452,20 @@ describe('Indexer', function() {
});
for (const v of vectors) {
it(`txs by ${v.label} address`, async () => {
it(`txs by ${v.label} addr`, async () => {
const res = await nclient.request(
'GET', `/tx/address/${v.addr}`, {});
assert.equal(res.length, 13);
assert.equal(res.length, 15);
for (let i = 0; i < 10; i++)
assert(confirmed.includes(res[i].hash));
for (let i = 10; i < 13; i++)
for (let i = 10; i < 15; i++)
assert(unconfirmed.includes(res[i].hash));
});
it(`txs by ${v.label} address (limit)`, async () => {
it(`txs by ${v.label} addr (limit)`, async () => {
const res = await nclient.request(
'GET', `/tx/address/${v.addr}`, {limit: 3});
@ -368,7 +475,7 @@ describe('Indexer', function() {
assert(confirmed.includes(tx.hash));
});
it(`txs by ${v.label} address (limit w/ unconf)`, async () => {
it(`txs by ${v.label} addr (limit w/ unconf)`, async () => {
const res = await nclient.request(
'GET', `/tx/address/${v.addr}`, {limit: 11});
@ -381,21 +488,21 @@ describe('Indexer', function() {
assert(unconfirmed.includes(res[i].hash));
});
it(`txs by ${v.label} address (reverse)`, async () => {
it(`txs by ${v.label} addr (reverse)`, async () => {
const asc = await nclient.request(
'GET', `/tx/address/${v.addr}`, {reverse: false});
assert.equal(asc.length, 13);
assert.equal(asc.length, 15);
const dsc = await nclient.request(
'GET', `/tx/address/${v.addr}`, {reverse: true});
assert.equal(asc.length, 13);
assert.equal(dsc.length, 15);
for (let i = 0; i < 10; i++)
assert(confirmed.includes(asc[i].hash));
for (let i = 10; i < 13; i++)
for (let i = 10; i < 15; i++)
assert(unconfirmed.includes(asc[i].hash));
// Check the the results are reverse
@ -407,11 +514,16 @@ describe('Indexer', function() {
}
});
it(`txs by ${v.label} address (after)`, async () => {
it(`txs by ${v.label} addr (after)`, async () => {
const one = await nclient.request(
'GET', `/tx/address/${v.addr}`, {limit: 3});
assert.strictEqual(one.length, 3);
for (let i = 0; i < 3; i++)
assert(confirmed.includes(one[i].hash));
// The after hash is within the
// confirmed transactions.
const hash = one[2].hash;
const two = await nclient.request(
@ -422,10 +534,10 @@ describe('Indexer', function() {
'GET', `/tx/address/${v.addr}`, {limit: 6});
assert.strictEqual(one.length, 3);
assert.deepEqual(one.concat(two), all);
assert.deepEqual(sanitize(one.concat(two)), sanitize(all));
});
it(`txs by ${v.label} address (after w/ unconf)`, async () => {
it(`txs by ${v.label} addr (after w/ unconf)`, async () => {
const one = await nclient.request(
'GET', `/tx/address/${v.addr}`, {limit: 11});
assert.strictEqual(one.length, 11);
@ -449,24 +561,113 @@ describe('Indexer', function() {
'GET', `/tx/address/${v.addr}`, {limit: 12});
assert.strictEqual(all.length, 12);
assert.deepEqual(one.concat(two), all);
assert.deepEqual(sanitize(one.concat(two)), sanitize(all));
});
it(`txs by ${v.label} address (after, reverse)`, async () => {
it(`txs by ${v.label} addr (after w/ unconf 2)`, async () => {
const one = await nclient.request(
'GET', `/tx/address/${v.addr}`, {limit: 12});
assert.strictEqual(one.length, 12);
for (let i = 0; i < 10; i++)
assert(confirmed.includes(one[i].hash));
for (let i = 10; i < 12; i++)
assert(unconfirmed.includes(one[i].hash));
const hash = one[11].hash;
const two = await nclient.request(
'GET', `/tx/address/${v.addr}`, {after: hash, limit: 10});
assert.strictEqual(two.length, 3);
for (let i = 0; i < 3; i++)
assert(unconfirmed.includes(two[i].hash));
const all = await nclient.request(
'GET', `/tx/address/${v.addr}`, {limit: 100});
assert.strictEqual(all.length, 15);
assert.deepEqual(sanitize(one.concat(two)), sanitize(all));
});
it(`txs by ${v.label} addr (after w/ unconf 3)`, async () => {
const one = await nclient.request(
'GET', `/tx/address/${v.addr}`, {limit: 13});
assert.strictEqual(one.length, 13);
for (let i = 0; i < 10; i++)
assert(confirmed.includes(one[i].hash));
for (let i = 10; i < 13; i++)
assert(unconfirmed.includes(one[i].hash));
const hash = one[12].hash;
const two = await nclient.request(
'GET', `/tx/address/${v.addr}`, {after: hash, limit: 1});
assert.strictEqual(two.length, 1);
assert(unconfirmed.includes(two[0].hash));
const all = await nclient.request(
'GET', `/tx/address/${v.addr}`, {limit: 14});
assert.strictEqual(all.length, 14);
assert.deepEqual(sanitize(one.concat(two)), sanitize(all));
});
it(`txs by ${v.label} addr (after, reverse)`, async () => {
const one = await nclient.request(
'GET', `/tx/address/${v.addr}`,
{limit: 8, reverse: true});
assert.strictEqual(one.length, 8);
for (let i = 0; i < 5; i++)
assert(unconfirmed.includes(one[i].hash));
for (let i = 5; i < 8; i++)
assert(confirmed.includes(one[i].hash));
// The after hash is within the
// confirmed transactions.
const hash = one[7].hash;
const two = await nclient.request(
'GET', `/tx/address/${v.addr}`,
{after: hash, limit: 3, reverse: true});
assert.strictEqual(two.length, 3);
for (let i = 0; i < 3; i++)
assert(confirmed.includes(two[i].hash));
const all = await nclient.request(
'GET', `/tx/address/${v.addr}`,
{limit: 11, reverse: true});
assert.strictEqual(all.length, 11);
for (let i = 0; i < 5; i++)
assert(unconfirmed.includes(all[i].hash));
for (let i = 5; i < 11; i++)
assert(confirmed.includes(all[i].hash));
assert.deepEqual(sanitize(one.concat(two)), sanitize(all));
});
it(`txs by ${v.label} addr (after, reverse w/ unconf)`, async () => {
const one = await nclient.request(
'GET', `/tx/address/${v.addr}`,
{limit: 5, reverse: true});
assert.strictEqual(one.length, 5);
for (let i = 0; i < 3; i++)
for (let i = 0; i < 5; i++)
assert(unconfirmed.includes(one[i].hash));
for (let i = 3; i < 5; i++)
assert(confirmed.includes(one[i].hash));
// The after hash is within the
// confirmed transactions.
// unconfirmed transactions.
const hash = one[4].hash;
const two = await nclient.request(
@ -474,6 +675,7 @@ describe('Indexer', function() {
{after: hash, limit: 3, reverse: true});
assert.strictEqual(two.length, 3);
for (let i = 0; i < 3; i++)
assert(confirmed.includes(two[i].hash));
@ -483,16 +685,16 @@ describe('Indexer', function() {
assert.strictEqual(all.length, 8);
for (let i = 0; i < 3; i++)
for (let i = 0; i < 5; i++)
assert(unconfirmed.includes(all[i].hash));
for (let i = 3; i < 8; i++)
for (let i = 5; i < 8; i++)
assert(confirmed.includes(all[i].hash));
assert.deepEqual(one.concat(two), all);
assert.deepEqual(sanitize(one.concat(two)), sanitize(all));
});
it(`txs by ${v.label} address (after, reverse w/ unconf)`, async () => {
it(`txs by ${v.label} addr (after, reverse w/ unconf 2)`, async () => {
const one = await nclient.request(
'GET', `/tx/address/${v.addr}`,
{limit: 3, reverse: true});
@ -501,32 +703,49 @@ describe('Indexer', function() {
for (let i = 0; i < 3; i++)
assert(unconfirmed.includes(one[i].hash));
// The after hash is within the
// unconfirmed transactions.
const hash = one[2].hash;
const two = await nclient.request(
'GET', `/tx/address/${v.addr}`,
{after: hash, limit: 3, reverse: true});
{after: hash, limit: 1, reverse: true});
assert.strictEqual(two.length, 3);
for (let i = 0; i < 3; i++)
assert(confirmed.includes(two[i].hash));
assert.strictEqual(two.length, 1);
assert(unconfirmed.includes(two[0].hash));
const all = await nclient.request(
'GET', `/tx/address/${v.addr}`,
{limit: 6, reverse: true});
{limit: 4, reverse: true});
assert.strictEqual(all.length, 6);
assert.strictEqual(all.length, 4);
for (let i = 0; i < 3; i++)
for (let i = 0; i < 4; i++)
assert(unconfirmed.includes(all[i].hash));
for (let i = 3; i < 6; i++)
assert(confirmed.includes(all[i].hash));
assert.deepEqual(one.concat(two), all);
assert.deepEqual(sanitize(one.concat(two)), sanitize(all));
});
}
describe('Errors', function() {
it('will give error if limit is exceeded', async () => {
await assert.asyncThrows(async () => {
await nclient.request(
'GET', `/tx/address/${vectors[0].addr}`, {limit: 101});
}, 'Limit above max');
});
it('will give error with invalid after hash', async () => {
await assert.asyncThrows(async () => {
await nclient.request(
'GET', `/tx/address/${vectors[0].addr}`, {after: 'deadbeef'});
});
});
it('will give error with invalid reverse', async () => {
await assert.asyncThrows(async () => {
await nclient.request(
'GET', `/tx/address/${vectors[0].addr}`, {reverse: 'sure'});
});
});
});
});
});

View File

@ -9,6 +9,7 @@ const common = require('../lib/blockchain/common');
const Block = require('../lib/primitives/block');
const MempoolEntry = require('../lib/mempool/mempoolentry');
const Mempool = require('../lib/mempool/mempool');
const AddrIndexer = require('../lib/mempool/addrindexer');
const WorkerPool = require('../lib/workers/workerpool');
const Chain = require('../lib/blockchain/chain');
const MTX = require('../lib/primitives/mtx');
@ -18,6 +19,7 @@ const Address = require('../lib/primitives/address');
const Outpoint = require('../lib/primitives/outpoint');
const Input = require('../lib/primitives/input');
const Script = require('../lib/script/script');
const Opcode = require('../lib/script/opcode');
const opcodes = Script.opcodes;
const Witness = require('../lib/script/witness');
const MemWallet = require('./util/memwallet');
@ -780,6 +782,40 @@ describe('Mempool', function() {
});
});
describe('AddrIndexer', function () {
it('will not get key for witness program v1', function() {
const addrindex = new AddrIndexer();
// Create a witness program version 1 with
// 40 byte data push.
const script = new Script();
script.push(Opcode.fromSmall(1));
script.push(Opcode.fromData(Buffer.alloc(40)));
script.compile();
const addr = Address.fromScript(script);
const key = addrindex.getKey(addr);
assert.strictEqual(key, null);
});
it('will get key for witness program v0', function() {
const addrindex = new AddrIndexer();
// Create a witness program version 0 with
// 32 byte data push.
const script = new Script();
script.push(Opcode.fromSmall(0));
script.push(Opcode.fromData(Buffer.alloc(32)));
script.compile();
const addr = Address.fromScript(script);
const key = addrindex.getKey(addr);
assert.bufferEqual(key, Buffer.from('0a' + '00'.repeat(32), 'hex'));
});
});
describe('Mempool persistent cache', function () {
const workers = new WorkerPool({
enabled: true

View File

@ -115,8 +115,11 @@ assert.asyncThrows = async function asyncThrows(func, expectedError) {
} catch (e) {
err = e;
}
const re = new RegExp('^' + expectedError);
assert(re.test(err.message));
assert(err, 'Expected error.');
if (expectedError) {
const re = new RegExp('^' + expectedError);
assert(re.test(err.message), err.message);
}
};
function _isString(value, message, stackStartFunction) {