wallet: refactor and add global zap.

This commit is contained in:
Christopher Jeffrey 2016-10-05 19:12:21 -07:00
parent acae838059
commit 5bb21070c4
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
7 changed files with 140 additions and 18 deletions

View File

@ -303,6 +303,12 @@ CLI.prototype.rescan = co(function* rescan() {
this.log('Rescanning...');
});
CLI.prototype.zapAll = co(function* zapAll() {
var age = +this.argv[0] || 72 * 3600;
yield this.client.zapAll(age);
this.log('Zapped.');
});
CLI.prototype.backup = co(function* backup() {
var path = this.argv[0];
@ -493,6 +499,8 @@ CLI.prototype.handleNode = co(function* handleNode() {
return yield this.getBlock();
case 'rescan':
return yield this.rescan();
case 'zap':
return yield this.zapAll();
case 'backup':
return yield this.backup();
case 'rpc':
@ -507,6 +515,7 @@ CLI.prototype.handleNode = co(function* handleNode() {
this.log(' $ coin [hash+index/address]: View coins.');
this.log(' $ block [hash/height]: View block.');
this.log(' $ rescan [height/hash]: Rescan for transactions.');
this.log(' $ zap [age?]: Zap stale transactions from walletdb.');
this.log(' $ backup [path]: Backup the wallet db.');
this.log(' $ rpc [command] [args]: Execute RPC command.');
return;

View File

@ -358,6 +358,17 @@ HTTPClient.prototype.rescan = function rescan(hash) {
return this._post('/rescan', options);
};
/**
* Zap stale transactions.
* @param {Number} age
* @returns {Promise}
*/
HTTPClient.prototype.zapAll = function zapAll(age) {
var options = { age: age };
return this._post('/zap', options);
};
/**
* Backup the walletdb.
* @param {String} path

View File

@ -698,6 +698,18 @@ HTTPServer.prototype._init = function _init() {
yield this.node.scan(height);
}));
// Zap
this.post('/zap', con(function* (req, res, send, next) {
var options = req.options;
var age = options.age;
enforce(age != null, 'Age is required.');
yield this.walletdb.zap(age);
send(200, { success: true });
}));
// Backup WalletDB
this.post('/backup', con(function* (req, res, send, next) {
var options = req.options;

View File

@ -245,6 +245,9 @@ Fullnode.prototype._open = co(function* open() {
// Ensure primary wallet.
yield this.openWallet();
// Zap stale txs.
yield this.walletdb.zap(72 * 3600);
// Rescan for any missed transactions.
yield this.rescan();

View File

@ -168,6 +168,9 @@ SPVNode.prototype._open = co(function* open(callback) {
// Load bloom filter.
yield this.openFilter();
// Zap stale txs.
yield this.walletdb.zap(72 * 3600);
// Rescan for any missed transactions.
yield this.rescan();

View File

@ -1918,6 +1918,7 @@ TXDB.prototype.getAccountBalance = co(function* getBalance(account) {
*/
TXDB.prototype.zap = co(function* zap(account, age) {
var hashes = [];
var i, txs, tx, hash;
if (!utils.isUInt32(age))
@ -1937,6 +1938,9 @@ TXDB.prototype.zap = co(function* zap(account, age) {
this.wallet.start();
this.logger.debug('Zapping TX: %s (%s)',
hash, this.wallet.id);
try {
yield this._remove(hash);
} catch (e) {
@ -1944,8 +1948,12 @@ TXDB.prototype.zap = co(function* zap(account, age) {
throw e;
}
hashes.push(hash);
yield this.wallet.commit();
}
return hashes;
});
/**

View File

@ -25,6 +25,7 @@ var ldb = require('../db/ldb');
var Bloom = require('../utils/bloom');
var Logger = require('../node/logger');
var TX = require('../primitives/tx');
var TXDB = require('./txdb');
/*
* Database Layout:
@ -743,7 +744,7 @@ WalletDB.prototype.saveAccount = function saveAccount(account) {
var wallet = account.wallet;
var index = account.accountIndex;
var name = account.name;
var batch = this.batch(account.wallet);
var batch = this.batch(wallet);
var buf = new Buffer(4);
buf.writeUInt32LE(index, 0, true);
@ -1043,12 +1044,10 @@ WalletDB.prototype._rescan = co(function* rescan(chaindb, height) {
*/
WalletDB.prototype.getPendingKeys = co(function* getPendingKeys() {
var layout = require('./txdb').layout;
var layout = TXDB.layout;
var dummy = new Buffer(0);
var uniq = {};
var keys = [];
var result = [];
var i, iter, item, key, wid, hash;
var iter, item;
iter = yield this.db.iterator({
gte: layout.prefix(0x00000000, dummy),
@ -1065,6 +1064,22 @@ WalletDB.prototype.getPendingKeys = co(function* getPendingKeys() {
keys.push(item.key);
}
return keys;
});
/**
* Get keys of all pending transactions
* in the wallet db (for resending).
* @returns {Promise}
*/
WalletDB.prototype.getPendingTX = co(function* getPendingTX() {
var layout = TXDB.layout;
var keys = yield this.getPendingKeys();
var uniq = {};
var result = [];
var i, key, wid, hash;
for (i = 0; i < keys.length; i++) {
key = keys[i];
@ -1083,13 +1098,41 @@ WalletDB.prototype.getPendingKeys = co(function* getPendingKeys() {
return result;
});
/**
* Get all wallet IDs with pending txs in them.
* @returns {Promise}
*/
WalletDB.prototype.getPendingWallets = co(function* getPendingWallets() {
var layout = TXDB.layout;
var keys = yield this.getPendingKeys();
var uniq = {};
var result = [];
var i, key, wid;
for (i = 0; i < keys.length; i++) {
key = keys[i];
wid = layout.pre(key);
if (uniq[wid])
continue;
uniq[wid] = true;
result.push(wid);
}
return result;
});
/**
* Resend all pending transactions.
* @returns {Promise}
*/
WalletDB.prototype.resend = co(function* resend() {
var keys = yield this.getPendingKeys();
var keys = yield this.getPendingTX();
var i, key, data, tx;
if (keys.length > 0)
@ -1114,7 +1157,7 @@ WalletDB.prototype.resend = co(function* resend() {
* @returns {Promise}
*/
WalletDB.prototype.getWidsByHashes = co(function* getWidsByHashes(hashes) {
WalletDB.prototype.getWalletsByHashes = co(function* getWalletsByHashes(hashes) {
var result = [];
var i, j, hash, wids;
@ -1126,10 +1169,16 @@ WalletDB.prototype.getWidsByHashes = co(function* getWidsByHashes(hashes) {
wids = yield this.getWalletsByHash(hash);
if (!wids)
continue;
for (j = 0; j < wids.length; j++)
utils.binaryInsert(result, wids[j], cmp, true);
}
if (result.length === 0)
return;
return result;
});
@ -1398,13 +1447,10 @@ WalletDB.prototype._addTX = co(function* addTX(tx) {
assert(!tx.mutable, 'Cannot add mutable TX to wallet.');
// Atomicity doesn't matter here. If we crash,
// the automatic rescan will get the database
// back in the correct state.
hashes = tx.getHashes('hex');
wallets = yield this.getWidsByHashes(hashes);
wallets = yield this.getWalletsByHashes(hashes);
if (wallets.length === 0)
if (!wallets)
return;
this.logger.info(
@ -1415,8 +1461,7 @@ WalletDB.prototype._addTX = co(function* addTX(tx) {
wid = wallets[i];
wallet = yield this.get(wid);
if (!wallet)
continue;
assert(wallet);
this.logger.debug('Adding tx to wallet: %s', wallet.id);
@ -1459,14 +1504,45 @@ WalletDB.prototype._unconfirmTX = co(function* unconfirmTX(hash) {
for (i = 0; i < wallets.length; i++) {
wid = wallets[i];
wallet = yield this.get(wid);
if (!wallet)
continue;
assert(wallet);
yield wallet.unconfirm(hash);
}
});
/**
* Zap stale transactions.
* @param {Number} age
* @returns {Promise}
*/
WalletDB.prototype.zap = co(function* zap(age) {
var unlock = yield this.txLock.lock();
try {
return yield this._zap(age);
} finally {
unlock();
}
});
/**
* Zap stale transactions without a lock.
* @private
* @param {Number} age
* @returns {Promise}
*/
WalletDB.prototype._zap = co(function* zap(age) {
var wallets = yield this.getPendingWallets();
var i, wid, wallet;
for (i = 0; i < wallets.length; i++) {
wid = wallets[i];
wallet = yield this.get(wid);
assert(wallet);
yield wallet.zap(age);
}
});
/**
* Wallet Block
* @constructor