Skip to content

Commit

Permalink
test: add utility for test fs cleanup
Browse files Browse the repository at this point in the history
test: add utility to sleep for desired ms

wallet: add test cases for wallet tx count and time indexing

wallet: add db layout for tx count and monotonic time

wallet: implement monotonic time

wallet: add tx count index for confirmed txs

wallet: add rpc methods for tx count and monotonic time

test: restore count to generateBlocks regtest util

docs: after is exclusive

wallet: monotonic time indexing

test: add reorg to wallet time test

test: monotonic time reorg, incomplete

pkg: update bdb package to support reverse

wallet: rollback monotonic time

wallet: add spv monotonic time reorg

wallet: reorg tx monotonic time and count indexes

wallet: refactor monotonic index method

wallet: options for list history methods

wallet: add indexing for unconfirmed count and time

wallet: add methods to query for unconfirmed txs

test: confirm and unconfirm txs

wallet: combine `getPending` and `listUnconfirmed`

wallet: http endpoints for history and unconfirmed txs

wallet: test spv node http endpoints and w/ fixes

test: remove `.skip` function call, it's not implemented in bmocha

test: wallet rescan test

pkg: update bdb

wallet: use height + index for count index

test: remove spv from wallet rescan test

test: assertions for wallet rescan test

wallet: use median time past for time indexing

wallet: linting

wallet: use brhash for http after param

test: fix spv test when cpu is loaded

wallet: minor cleanup

wallet: cleanup indexes

wallet: make configurable max transactions limits

wallet: minor cleanup

wallet: fix configurable txs limits

test: bump wallet http before hook time

wallet: fix http time tx queries

wallet: minor linting

wallet: check rpc min args length

test: minor linting

wallet: fix sorting of time indexing

wallet: use brhash instead of manually reversing

docs: minor linting

wallet: fix sorting of confirmed time index

wallet: refactor date handling for api

test: refactor utils

wallet: minor cleanup and adjustments

wallet: minor cleanup and update

wallet: update rpc methods with new history functions

includes necessary fixes from bcoin-org#550

wallet: use unix epoch time in seconds for rpc history cmds

test: increase lookahead for test wallet
  • Loading branch information
braydonf authored and nodech committed Jan 17, 2019
1 parent bd7a094 commit ce948cf
Show file tree
Hide file tree
Showing 23 changed files with 3,307 additions and 477 deletions.
2 changes: 1 addition & 1 deletion lib/node/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ class Node extends EventEmitter {
*/

error(err) {
this.logger.error(err);
this.logger.error(err.stack);
this.emit('error', err);
}

Expand Down
12 changes: 0 additions & 12 deletions lib/wallet/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,6 @@ common.isName = function isName(key) {
return key.length >= 1 && key.length <= 40;
};

/**
* Sort an array of transactions by time.
* @param {TX[]} txs
* @returns {TX[]}
*/

common.sortTX = function sortTX(txs) {
return txs.sort((a, b) => {
return a.mtime - b.mtime;
});
};

/**
* Sort an array of coins by height.
* @param {Coin[]} txs
Expand Down
114 changes: 80 additions & 34 deletions lib/wallet/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const KeyRing = require('../primitives/keyring');
const Mnemonic = require('../hd/mnemonic');
const HDPrivateKey = require('../hd/private');
const HDPublicKey = require('../hd/public');
const util = require('../utils/util');
const common = require('./common');

/**
Expand Down Expand Up @@ -765,9 +766,40 @@ class HTTP extends Server {
this.get('/wallet/:id/tx/history', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const txs = await req.wallet.getHistory(acct);
const reverse = valid.bool('reverse', false);
const limit = valid.u32('limit', 10);
const after = valid.brhash('after');
const date = valid.str('date');
let time = valid.u32('time');

if (date && !time) {
time = util.time(date);
enforce(time, 'Invalid date.');
}

enforce(limit <= this.options.maxTxs,
`Limit above max of ${this.options.maxTxs}.`);

let txs = [];

common.sortTX(txs);
if (after) {
txs = await req.wallet.listHistoryAfter(acct, {
hash: after,
limit,
reverse
});
} else if (time) {
txs = await req.wallet.listHistoryByTime(acct, {
time,
limit,
reverse
});
} else {
txs = await req.wallet.listHistory(acct, {
limit,
reverse
});
}

const details = await req.wallet.toDetails(txs);

Expand All @@ -783,9 +815,40 @@ class HTTP extends Server {
this.get('/wallet/:id/tx/unconfirmed', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const txs = await req.wallet.getPending(acct);
const reverse = valid.bool('reverse', false);
const limit = valid.u32('limit', 10);
const after = valid.brhash('after');
const date = valid.str('date');
let time = valid.u32('time');

if (date && !time) {
time = util.time(date);
enforce(time, 'Invalid date.');
}

enforce(limit <= this.options.maxTxs,
`Limit above max of ${this.options.maxTxs}.`);

common.sortTX(txs);
let txs = [];

if (after) {
txs = await req.wallet.listUnconfirmedAfter(acct, {
hash: after,
limit,
reverse
});
} else if (time) {
txs = await req.wallet.listUnconfirmedByTime(acct, {
time,
limit,
reverse
});
} else {
txs = await req.wallet.listUnconfirmed(acct, {
limit,
reverse
});
}

const details = await req.wallet.toDetails(txs);
const result = [];
Expand All @@ -796,41 +859,18 @@ class HTTP extends Server {
res.json(200, result);
});

// Wallet TXs within time range
// Wallet TXs within time range.
this.get('/wallet/:id/tx/range', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');

const options = {
start: valid.u32('start'),
end: valid.u32('end'),
limit: valid.u32('limit'),
reverse: valid.bool('reverse')
};

const txs = await req.wallet.getRange(acct, options);
const details = await req.wallet.toDetails(txs);
const result = [];

for (const item of details)
result.push(item.toJSON(this.network, this.wdb.height));

res.json(200, result);
throw new Error('Deprecated: `/wallet/:id/tx/range`. ' +
'Use `/wallet/:id/tx/history` or ' +
'`/wallet/:id/tx/unconfirmed`.');
});

// Last Wallet TXs
this.get('/wallet/:id/tx/last', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const limit = valid.u32('limit');
const txs = await req.wallet.getLast(acct, limit);
const details = await req.wallet.toDetails(txs);
const result = [];

for (const item of details)
result.push(item.toJSON(this.network, this.wdb.height));

res.json(200, result);
throw new Error('Deprecated: `/wallet/:id/tx/last`. ' +
'Use `/wallet/:id/tx/history` or ' +
'`/wallet/:id/tx/unconfirmed`.');
});

// Wallet TX
Expand Down Expand Up @@ -1049,6 +1089,7 @@ class HTTPOptions {
this.noAuth = false;
this.cors = false;
this.walletAuth = false;
this.maxTxs = 100;

this.prefix = null;
this.host = '127.0.0.1';
Expand Down Expand Up @@ -1156,6 +1197,11 @@ class HTTPOptions {
this.certFile = options.certFile;
}

if (options.maxTxs != null) {
assert(Number.isSafeInteger(options.maxTxs));
this.maxTxs = options.maxTxs;
}

// Allow no-auth implicitly
// if we're listening locally.
if (!options.apiKey) {
Expand Down
72 changes: 59 additions & 13 deletions lib/wallet/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const bdb = require('bdb');
* i[wid][name] -> account index
* n[wid][index] -> account name
* h[height] -> recent block hash
* m[hash] -> block median time past
* b[height] -> block->wid map
* o[hash][index] -> outpoint->wid map
* T[hash] -> tx->wid map
Expand All @@ -45,6 +46,7 @@ exports.wdb = {
i: bdb.key('i', ['uint32', 'ascii']),
n: bdb.key('n', ['uint32', 'uint32']),
h: bdb.key('h', ['uint32']),
m: bdb.key('m', ['hash256']),
b: bdb.key('b', ['uint32']),
o: bdb.key('o', ['hash256', 'uint32']),
T: bdb.key('T', ['hash256']),
Expand All @@ -53,38 +55,82 @@ exports.wdb = {

/*
* TXDB Database Layout:
* Balance
* -------
* R -> wallet balance
* r[account] -> account balance
*
* Transactions
* ------------
* t[hash] -> extended tx
* T[account][hash] -> dummy (tx by account)
* Coins
* -----
* c[hash][index] -> coin
* C[account][hash][index] -> dummy (coin by account)
* d[hash][index] -> undo coin
* s[hash][index] -> spent by hash
* p[hash] -> dummy (pending flag)
* m[time][hash] -> dummy (tx by time)
*
* Confirmed
* ---------
* g[time][height][index][hash] -> dummy (tx by time)
* G[account][time][height][index][hash] -> dummy (tx by time + account)
* z[height][index] -> dummy (tx by count)
* Z[account][height][index]-> dummy (tx by count + account)
* y[hash] -> count (count for tx)
* h[height][hash] -> dummy (tx by height)
* T[account][hash] -> dummy (tx by account)
* P[account][hash] -> dummy (pending tx by account)
* M[account][time][hash] -> dummy (tx by time + account)
* H[account][height][hash] -> dummy (tx by height + account)
* C[account][hash][index] -> dummy (coin by account)
* b[height] -> block record
*
* Unconfirmed
* -----------
* w[time][count][hash] -> dummy (tx by time)
* W[account][time][count][hash] -> dummy (tx by time + account)
* e[hash] -> time (unconfirmed time for tx)
* u[count] -> dummy (tx by unconfirmed count)
* U[account][count] -> dummy (tx by unconfirmed count + account)
* v[hash] -> count (unconfirmed count for tx)
* V[account][hash] -> count (unconfirmed count for tx + account)
* p[hash] -> dummy (pending flag)
* P[account][hash] -> dummy (pending tx by account)
*/

exports.txdb = {
prefix: bdb.key('t', ['uint32']),

// Balance
R: bdb.key('R'),
r: bdb.key('r', ['uint32']),

// Transactions
t: bdb.key('t', ['hash256']),
T: bdb.key('T', ['uint32', 'hash256']),

// Coins
c: bdb.key('c', ['hash256', 'uint32']),
C: bdb.key('C', ['uint32', 'hash256', 'uint32']),
d: bdb.key('d', ['hash256', 'uint32']),
s: bdb.key('s', ['hash256', 'uint32']),
p: bdb.key('p', ['hash256']),
m: bdb.key('m', ['uint32', 'hash256']),

// Confirmed
g: bdb.key('g', ['uint32', 'uint32', 'uint32', 'hash256']),
G: bdb.key('G', ['uint32', 'uint32', 'uint32', 'uint32', 'hash256']),
z: bdb.key('z', ['uint32', 'uint32']),
Z: bdb.key('Z', ['uint32', 'uint32', 'uint32']),
y: bdb.key('y', ['hash256']),
h: bdb.key('h', ['uint32', 'hash256']),
T: bdb.key('T', ['uint32', 'hash256']),
P: bdb.key('P', ['uint32', 'hash256']),
M: bdb.key('M', ['uint32', 'uint32', 'hash256']),
H: bdb.key('H', ['uint32', 'uint32', 'hash256']),
C: bdb.key('C', ['uint32', 'hash256', 'uint32']),
b: bdb.key('b', ['uint32'])
b: bdb.key('b', ['uint32']),

// Unconfirmed
w: bdb.key('w', ['uint32', 'uint32', 'hash256']),
W: bdb.key('W', ['uint32', 'uint32', 'uint32', 'hash256']),
e: bdb.key('e', ['hash256']),
u: bdb.key('u', ['uint32']),
U: bdb.key('U', ['uint32', 'uint32']),
v: bdb.key('v', ['hash256']),
V: bdb.key('V', ['uint32', 'hash256']),
p: bdb.key('p', ['hash256']),
P: bdb.key('P', ['uint32', 'hash256'])
};
15 changes: 15 additions & 0 deletions lib/wallet/nodeclient.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,21 @@ class NodeClient extends AsyncEmitter {
return entry;
}

/**
* Get MTP for a block hash.
* @param {Hash} blockhash
* @returns {Promise}
*/

async getMedianTime(blockhash) {
const entry = await this.node.chain.getEntry(blockhash);

if (!entry)
return null;

return await this.node.chain.getMedianTime(entry);
}

/**
* Send a transaction. Do not wait for promise.
* @param {TX} tx
Expand Down
11 changes: 11 additions & 0 deletions lib/wallet/nullclient.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

const assert = require('bsert');
const EventEmitter = require('events');
const util = require('../utils/util');

/**
* Null Client
Expand Down Expand Up @@ -91,6 +92,16 @@ class NullClient extends EventEmitter {
return { hash, height: 0, time: 0 };
}

/**
* Get MTP for a block hash.
* @param {Hash} blockhash
* @returns {Promise}
*/

async getMedianTime(blockhash) {
return util.now();
}

/**
* Send a transaction. Do not wait for promise.
* @param {TX} tx
Expand Down
6 changes: 4 additions & 2 deletions lib/wallet/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ class Plugin extends EventEmitter {
witness: this.config.bool('witness'),
checkpoints: this.config.bool('checkpoints'),
wipeNoReally: this.config.bool('wipe-no-really'),
spv: node.spv
spv: node.spv,
maxTxs: this.config.uint('max-txs')
});

this.rpc = new RPC(this);
Expand All @@ -73,7 +74,8 @@ class Plugin extends EventEmitter {
walletAuth: this.config.bool('wallet-auth'),
noAuth: this.config.bool('no-auth'),
cors: this.config.bool('cors'),
adminToken: this.config.str('admin-token')
adminToken: this.config.str('admin-token'),
maxTxs: this.config.uint('max-txs')
});

this.init();
Expand Down
Loading

0 comments on commit ce948cf

Please sign in to comment.