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:
parent
69e9844f82
commit
b4be8574e5
@ -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
235
lib/mempool/addrindexer.js
Normal 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;
|
||||
@ -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
|
||||
|
||||
@ -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'});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user