fcoin/lib/mempool/addrindexer.js
Braydon Fuller b4be8574e5
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.
2019-05-15 12:11:32 -07:00

236 lines
4.3 KiB
JavaScript

/*!
* 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;