txdb and walletdb work. scanning.
This commit is contained in:
parent
74f5a8916f
commit
60d33ce9db
@ -14,11 +14,11 @@
|
||||
* p/[hash] -> dummy (pending flag)
|
||||
* m/[time]/[hash] -> dummy (tx by time)
|
||||
* h/[height]/[hash] -> dummy (tx by height)
|
||||
* T/[id]/[hash] -> dummy (tx by wallet id)
|
||||
* P/[id]/[hash] -> dummy (pending tx by wallet id)
|
||||
* M/[id]/[time]/[hash] -> dummy (tx by time + id)
|
||||
* H/[id]/[height]/[hash] -> dummy (tx by height + id)
|
||||
* C/[id]/[hash]/[index] -> dummy (coin by address)
|
||||
* T/[id]/[name]/[hash] -> dummy (tx by wallet id)
|
||||
* P/[id]/[name]/[hash] -> dummy (pending tx by wallet/account id)
|
||||
* M/[id]/[name]/[time]/[hash] -> dummy (tx by time + id/account)
|
||||
* H/[id]/[name]/[height]/[hash] -> dummy (tx by height + id/account)
|
||||
* C/[id]/[name]/[hash]/[index] -> dummy (coin by id/account)
|
||||
*/
|
||||
|
||||
var bcoin = require('./env');
|
||||
@ -40,7 +40,8 @@ var BufferWriter = require('./writer');
|
||||
* @param {Boolean?} options.indexAddress - Index addresses/IDs.
|
||||
* @param {Boolean?} options.indexExtra - Index timestamps, heights, etc.
|
||||
* @param {Boolean?} options.verify - Verify transactions as they
|
||||
* come in (note that this will not happen on the worker pool).
|
||||
* come in (note that this will not happen on the worker
|
||||
* pool -- only used for SPV).
|
||||
*/
|
||||
|
||||
function TXDB(db, options) {
|
||||
@ -52,7 +53,7 @@ function TXDB(db, options) {
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
this.wdb = db;
|
||||
this.walletdb = db;
|
||||
this.db = db.db;
|
||||
this.options = options;
|
||||
this.network = bcoin.network.get(options.network);
|
||||
@ -126,27 +127,8 @@ TXDB.prototype._testFilter = function _testFilter(addresses) {
|
||||
* @param {Function} callback - Returns [Error, {@link AddressMap}].
|
||||
*/
|
||||
|
||||
function uniq(obj) {
|
||||
var uniq = {};
|
||||
var out = [];
|
||||
var i, key, value;
|
||||
|
||||
for (i = 0; i < obj.length; i++) {
|
||||
value = obj[i];
|
||||
key = value.id + '/' + value.account;
|
||||
|
||||
if (uniq[key])
|
||||
continue;
|
||||
|
||||
uniq[key] = true;
|
||||
out.push(value);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
TXDB.prototype.getMap = function getMap(tx, callback) {
|
||||
var input, output, addresses, table, map;
|
||||
var i, input, output, address, addresses, table, map;
|
||||
|
||||
input = tx.getInputHashes();
|
||||
output = tx.getOutputHashes();
|
||||
@ -155,7 +137,7 @@ TXDB.prototype.getMap = function getMap(tx, callback) {
|
||||
if (!this._testFilter(addresses))
|
||||
return callback();
|
||||
|
||||
function cb(err, table) {
|
||||
this.mapAddresses(addresses, function(err, table) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -169,29 +151,29 @@ TXDB.prototype.getMap = function getMap(tx, callback) {
|
||||
all: []
|
||||
};
|
||||
|
||||
input.forEach(function(address) {
|
||||
for (i = 0; i < input.length; i++) {
|
||||
address = input[i];
|
||||
assert(map.table[address]);
|
||||
map.input = map.input.concat(map.table[address]);
|
||||
});
|
||||
}
|
||||
|
||||
output.forEach(function(address) {
|
||||
for (i = 0; i < output.length; i++) {
|
||||
address = output[i];
|
||||
assert(map.table[address]);
|
||||
map.output = map.output.concat(map.table[address]);
|
||||
});
|
||||
}
|
||||
|
||||
map.input = uniq(map.input);
|
||||
map.output = uniq(map.output);
|
||||
map.all = uniq(map.input.concat(map.output));
|
||||
|
||||
return callback(null, map);
|
||||
}
|
||||
|
||||
return this.mapAddresses(addresses, cb);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Map an address to a wallet ID.
|
||||
* @param {Base58Address|Base58Address[]} address
|
||||
* Map address hashes to a wallet ID.
|
||||
* @param {Hash[]} address - Address hashes.
|
||||
* @param {Function} callback - Returns [Error, {@link AddressTable}].
|
||||
*/
|
||||
|
||||
@ -201,7 +183,7 @@ TXDB.prototype.mapAddresses = function mapAddresses(address, callback) {
|
||||
var i, keys, values;
|
||||
|
||||
return utils.forEachSerial(address, function(address, next) {
|
||||
self.wdb.getAddress(address, function(err, paths) {
|
||||
self.walletdb.getAddress(address, function(err, paths) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
@ -389,6 +371,7 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
// Consume unspent money or add orphans
|
||||
utils.forEachSerial(tx.inputs, function(input, next, i) {
|
||||
var key, address;
|
||||
var prevout = input.prevout;
|
||||
|
||||
if (tx.isCoinbase())
|
||||
return next();
|
||||
@ -399,11 +382,11 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
if (!address || !map.table[address].length)
|
||||
return next();
|
||||
|
||||
self.getCoin(input.prevout.hash, input.prevout.index, function(err, coin) {
|
||||
self.getCoin(prevout.hash, prevout.index, function(err, coin) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
key = input.prevout.hash + '/' + input.prevout.index;
|
||||
key = prevout.hash + '/' + prevout.index;
|
||||
|
||||
if (coin) {
|
||||
// Add TX to inputs and spend money
|
||||
@ -432,21 +415,21 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
|
||||
input.coin = null;
|
||||
|
||||
self.isSpent(input.prevout.hash, input.prevout.index, function(err, spentBy) {
|
||||
self.isSpent(prevout.hash, prevout.index, function(err, spentBy) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
// Are we double-spending?
|
||||
// Replace older txs with newer ones.
|
||||
if (spentBy) {
|
||||
return self.getTX(input.prevout.hash, function(err, prev) {
|
||||
return self.getTX(prevout.hash, function(err, prev) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (!prev)
|
||||
return callback(new Error('Could not find double-spent coin.'));
|
||||
|
||||
input.coin = bcoin.coin.fromTX(prev, input.prevout.index);
|
||||
input.coin = bcoin.coin.fromTX(prev, prevout.index);
|
||||
|
||||
// Skip invalid transactions
|
||||
if (self.options.verify) {
|
||||
@ -577,7 +560,7 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
self.wdb.sync(tx, map, function(err) {
|
||||
self.walletdb.sync(tx, map, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -768,7 +751,7 @@ TXDB.prototype._confirm = function _confirm(tx, map, callback, force) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
self.wdb.sync(tx, map, function(err) {
|
||||
self.walletdb.sync(tx, map, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -1070,47 +1053,22 @@ TXDB.prototype._unconfirm = function unconfirm(tx, map, callback, force) {
|
||||
|
||||
/**
|
||||
* Get hashes of all transactions in the database.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {WalletID?} id
|
||||
* @param {Function} callback - Returns [Error, {@link Hash}[]].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getHistoryHashes = function getHistoryHashes(address, callback) {
|
||||
var self = this;
|
||||
var txs = [];
|
||||
|
||||
if (typeof address === 'function') {
|
||||
callback = address;
|
||||
address = null;
|
||||
}
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
if (Array.isArray(address)) {
|
||||
return utils.forEachSerial(address, function(address, next) {
|
||||
self.getHistoryHashes(address, function(err, tx) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
txs = txs.concat(tx);
|
||||
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
txs = utils.uniq(txs);
|
||||
|
||||
return callback(null, txs);
|
||||
});
|
||||
TXDB.prototype.getHistoryHashes = function getHistoryHashes(id, callback) {
|
||||
if (typeof id === 'function') {
|
||||
callback = id;
|
||||
id = null;
|
||||
}
|
||||
|
||||
this.db.iterate({
|
||||
gte: address ? 'T/' + address + '/' : 't',
|
||||
lte: address ? 'T/' + address + '/~' : 't~',
|
||||
gte: id ? 'T/' + id + '/' : 't',
|
||||
lte: id ? 'T/' + id + '/~' : 't~',
|
||||
transform: function(key) {
|
||||
key = key.split('/');
|
||||
if (address)
|
||||
if (id)
|
||||
return key[3];
|
||||
return key[1];
|
||||
}
|
||||
@ -1119,48 +1077,22 @@ TXDB.prototype.getHistoryHashes = function getHistoryHashes(address, callback) {
|
||||
|
||||
/**
|
||||
* Get hashes of all unconfirmed transactions in the database.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {WalletID?} id
|
||||
* @param {Function} callback - Returns [Error, {@link Hash}[]].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getUnconfirmedHashes = function getUnconfirmedHashes(address, callback) {
|
||||
var self = this;
|
||||
var txs = [];
|
||||
|
||||
if (typeof address === 'function') {
|
||||
callback = address;
|
||||
address = null;
|
||||
}
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
if (Array.isArray(address)) {
|
||||
return utils.forEachSerial(address, function(address, next) {
|
||||
assert(address);
|
||||
self.getUnconfirmedHashes(address, function(err, tx) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
txs = txs.concat(tx);
|
||||
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
txs = utils.uniq(txs);
|
||||
|
||||
return callback(null, txs);
|
||||
});
|
||||
TXDB.prototype.getUnconfirmedHashes = function getUnconfirmedHashes(id, callback) {
|
||||
if (typeof id === 'function') {
|
||||
callback = id;
|
||||
id = null;
|
||||
}
|
||||
|
||||
this.db.iterate({
|
||||
gte: address ? 'P/' + address + '/' : 'p',
|
||||
lte: address ? 'P/' + address + '/~' : 'p~',
|
||||
gte: id ? 'P/' + id + '/' : 'p',
|
||||
lte: id ? 'P/' + id + '/~' : 'p~',
|
||||
transform: function(key) {
|
||||
key = key.split('/');
|
||||
if (address)
|
||||
if (id)
|
||||
return key[3];
|
||||
return key[1];
|
||||
}
|
||||
@ -1169,45 +1101,22 @@ TXDB.prototype.getUnconfirmedHashes = function getUnconfirmedHashes(address, cal
|
||||
|
||||
/**
|
||||
* Get all coin hashes in the database.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {WalletID?} id
|
||||
* @param {Function} callback - Returns [Error, {@link Hash}[]].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getCoinHashes = function getCoinHashes(address, callback) {
|
||||
var self = this;
|
||||
var coins = [];
|
||||
|
||||
if (typeof address === 'function') {
|
||||
callback = address;
|
||||
address = null;
|
||||
}
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
if (Array.isArray(address)) {
|
||||
return utils.forEachSerial(address, function(address, next) {
|
||||
self.getCoinHashes(address, function(err, coin) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
coins = coins.concat(coin);
|
||||
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
return callback(null, coins);
|
||||
});
|
||||
TXDB.prototype.getCoinHashes = function getCoinHashes(id, callback) {
|
||||
if (typeof id === 'function') {
|
||||
callback = id;
|
||||
id = null;
|
||||
}
|
||||
|
||||
this.db.iterate({
|
||||
gte: address ? 'C/' + address + '/' : 'c',
|
||||
lte: address ? 'C/' + address + '/~' : 'c~',
|
||||
gte: id ? 'C/' + id + '/' : 'c',
|
||||
lte: id ? 'C/' + id + '/~' : 'c~',
|
||||
transform: function(key) {
|
||||
key = key.split('/');
|
||||
if (address)
|
||||
if (id)
|
||||
return [key[3], +key[4]];
|
||||
return [key[1], +key[2]];
|
||||
}
|
||||
@ -1216,7 +1125,7 @@ TXDB.prototype.getCoinHashes = function getCoinHashes(address, callback) {
|
||||
|
||||
/**
|
||||
* Get TX hashes by height range.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {WalletID?} id
|
||||
* @param {Object} options
|
||||
* @param {Number} options.start - Start height.
|
||||
* @param {Number} options.end - End height.
|
||||
@ -1225,27 +1134,25 @@ TXDB.prototype.getCoinHashes = function getCoinHashes(address, callback) {
|
||||
* @param {Function} callback - Returns [Error, {@link Hash}[]].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getHeightRangeHashes = function getHeightRangeHashes(address, options, callback) {
|
||||
if (typeof address !== 'string') {
|
||||
TXDB.prototype.getHeightRangeHashes = function getHeightRangeHashes(id, options, callback) {
|
||||
if (typeof id !== 'string') {
|
||||
callback = options;
|
||||
options = address;
|
||||
address = null;
|
||||
options = id;
|
||||
id = null;
|
||||
}
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
this.db.iterate({
|
||||
gte: address
|
||||
? 'H/' + address + '/' + pad32(options.start) + '/'
|
||||
gte: id
|
||||
? 'H/' + id + '/' + pad32(options.start) + '/'
|
||||
: 'h/' + pad32(options.start) + '/',
|
||||
lte: address
|
||||
? 'H/' + address + '/' + pad32(options.end) + '/~'
|
||||
lte: id
|
||||
? 'H/' + id + '/' + pad32(options.end) + '/~'
|
||||
: 'h/' + pad32(options.end) + '/~',
|
||||
limit: options.limit,
|
||||
reverse: options.reverse,
|
||||
transform: function(key) {
|
||||
key = key.split('/');
|
||||
if (address)
|
||||
if (id)
|
||||
return key[4];
|
||||
return key[2];
|
||||
}
|
||||
@ -1264,7 +1171,7 @@ TXDB.prototype.getHeightHashes = function getHeightHashes(height, callback) {
|
||||
|
||||
/**
|
||||
* Get TX hashes by timestamp range.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {WalletID?} id
|
||||
* @param {Object} options
|
||||
* @param {Number} options.start - Start height.
|
||||
* @param {Number} options.end - End height.
|
||||
@ -1273,26 +1180,24 @@ TXDB.prototype.getHeightHashes = function getHeightHashes(height, callback) {
|
||||
* @param {Function} callback - Returns [Error, {@link Hash}[]].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getRangeHashes = function getRangeHashes(address, options, callback) {
|
||||
if (typeof address === 'function') {
|
||||
callback = address;
|
||||
address = null;
|
||||
TXDB.prototype.getRangeHashes = function getRangeHashes(id, options, callback) {
|
||||
if (typeof id === 'function') {
|
||||
callback = id;
|
||||
id = null;
|
||||
}
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
this.db.iterate({
|
||||
gte: address
|
||||
? 'M/' + address + '/' + pad32(options.start) + '/'
|
||||
gte: id
|
||||
? 'M/' + id + '/' + pad32(options.start) + '/'
|
||||
: 'm/' + pad32(options.start) + '/',
|
||||
lte: address
|
||||
? 'M/' + address + '/' + pad32(options.end) + '/~'
|
||||
lte: id
|
||||
? 'M/' + id + '/' + pad32(options.end) + '/~'
|
||||
: 'm/' + pad32(options.end) + '/~',
|
||||
limit: options.limit,
|
||||
reverse: options.reverse,
|
||||
transform: function(key) {
|
||||
key = key.split('/');
|
||||
if (address)
|
||||
if (id)
|
||||
return key[4];
|
||||
return key[2];
|
||||
}
|
||||
@ -1301,7 +1206,7 @@ TXDB.prototype.getRangeHashes = function getRangeHashes(address, options, callba
|
||||
|
||||
/**
|
||||
* Get transactions by timestamp range.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {WalletID?} id
|
||||
* @param {Object} options
|
||||
* @param {Number} options.start - Start height.
|
||||
* @param {Number} options.end - End height.
|
||||
@ -1310,16 +1215,16 @@ TXDB.prototype.getRangeHashes = function getRangeHashes(address, options, callba
|
||||
* @param {Function} callback - Returns [Error, {@link TX}[]].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getRange = function getLast(address, options, callback) {
|
||||
TXDB.prototype.getRange = function getLast(id, options, callback) {
|
||||
var self = this;
|
||||
var txs = [];
|
||||
|
||||
if (typeof address === 'function') {
|
||||
callback = address;
|
||||
address = null;
|
||||
if (typeof id === 'function') {
|
||||
callback = id;
|
||||
id = null;
|
||||
}
|
||||
|
||||
return this.getRangeHashes(address, options, function(err, hashes) {
|
||||
return this.getRangeHashes(id, options, function(err, hashes) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -1346,19 +1251,19 @@ TXDB.prototype.getRange = function getLast(address, options, callback) {
|
||||
|
||||
/**
|
||||
* Get last N transactions.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {WalletID?} id
|
||||
* @param {Number} limit - Max number of transactions.
|
||||
* @param {Function} callback - Returns [Error, {@link TX}[]].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getLast = function getLast(address, limit, callback) {
|
||||
TXDB.prototype.getLast = function getLast(id, limit, callback) {
|
||||
if (typeof limit === 'function') {
|
||||
callback = limit;
|
||||
limit = address;
|
||||
address = null;
|
||||
limit = id;
|
||||
id = null;
|
||||
}
|
||||
|
||||
return this.getRange(address, {
|
||||
return this.getRange(id, {
|
||||
start: 0,
|
||||
end: 0xffffffff,
|
||||
reverse: true,
|
||||
@ -1368,20 +1273,20 @@ TXDB.prototype.getLast = function getLast(address, limit, callback) {
|
||||
|
||||
/**
|
||||
* Get all transactions.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {WalletID?} id
|
||||
* @param {Function} callback - Returns [Error, {@link TX}[]].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getHistory = function getHistory(address, callback) {
|
||||
TXDB.prototype.getHistory = function getHistory(id, callback) {
|
||||
var self = this;
|
||||
var txs = [];
|
||||
|
||||
if (typeof address === 'function') {
|
||||
callback = address;
|
||||
address = null;
|
||||
if (typeof id === 'function') {
|
||||
callback = id;
|
||||
id = null;
|
||||
}
|
||||
|
||||
return this.getHistoryHashes(address, function(err, hashes) {
|
||||
return this.getHistoryHashes(id, function(err, hashes) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -1408,32 +1313,34 @@ TXDB.prototype.getHistory = function getHistory(address, callback) {
|
||||
|
||||
/**
|
||||
* Get last active timestamp and height.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {WalletID?} id
|
||||
* @param {Function} callback - Returns [Error, Number(ts), Number(height)].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getLastTime = function getLastTime(address, callback) {
|
||||
if (typeof address === 'function') {
|
||||
callback = address;
|
||||
address = null;
|
||||
TXDB.prototype.getLastTime = function getLastTime(id, callback) {
|
||||
var i, tx, lastTs, lastHeight;
|
||||
|
||||
if (typeof id === 'function') {
|
||||
callback = id;
|
||||
id = null;
|
||||
}
|
||||
|
||||
return this.getHistory(address, function(err, txs) {
|
||||
var lastTs, lastHeight;
|
||||
|
||||
return this.getHistory(id, function(err, txs) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
lastTs = 0;
|
||||
lastHeight = -1;
|
||||
|
||||
txs.forEach(function(tx) {
|
||||
for (i = 0; i < txs.length; i++) {
|
||||
tx = txs[i];
|
||||
|
||||
if (tx.ts > lastTs)
|
||||
lastTs = tx.ts;
|
||||
|
||||
if (tx.height > lastHeight)
|
||||
lastHeight = tx.height;
|
||||
});
|
||||
}
|
||||
|
||||
return callback(null, lastTs, lastHeight);
|
||||
});
|
||||
@ -1441,20 +1348,20 @@ TXDB.prototype.getLastTime = function getLastTime(address, callback) {
|
||||
|
||||
/**
|
||||
* Get unconfirmed transactions.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {WalletID?} id
|
||||
* @param {Function} callback - Returns [Error, {@link TX}[]].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getUnconfirmed = function getUnconfirmed(address, callback) {
|
||||
TXDB.prototype.getUnconfirmed = function getUnconfirmed(id, callback) {
|
||||
var self = this;
|
||||
var txs = [];
|
||||
|
||||
if (typeof address === 'function') {
|
||||
callback = address;
|
||||
address = null;
|
||||
if (typeof id === 'function') {
|
||||
callback = id;
|
||||
id = null;
|
||||
}
|
||||
|
||||
return this.getUnconfirmedHashes(address, function(err, hashes) {
|
||||
return this.getUnconfirmedHashes(id, function(err, hashes) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -1481,20 +1388,20 @@ TXDB.prototype.getUnconfirmed = function getUnconfirmed(address, callback) {
|
||||
|
||||
/**
|
||||
* Get coins.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {WalletID?} id
|
||||
* @param {Function} callback - Returns [Error, {@link Coin}[]].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getCoins = function getCoins(address, callback) {
|
||||
TXDB.prototype.getCoins = function getCoins(id, callback) {
|
||||
var self = this;
|
||||
var coins = [];
|
||||
|
||||
if (typeof address === 'function') {
|
||||
callback = address;
|
||||
address = null;
|
||||
if (typeof id === 'function') {
|
||||
callback = id;
|
||||
id = null;
|
||||
}
|
||||
|
||||
return this.getCoinHashes(address, function(err, hashes) {
|
||||
return this.getCoinHashes(id, function(err, hashes) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -1629,8 +1536,8 @@ TXDB.prototype.hasTX = function hasTX(hash, callback) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.getCoin = function getCoin(hash, index, callback) {
|
||||
this.db.fetch('c/' + hash + '/' + index, function(coin) {
|
||||
coin = bcoin.coin.fromRaw(coin);
|
||||
this.db.fetch('c/' + hash + '/' + index, function(data) {
|
||||
var coin = bcoin.coin.fromRaw(data);
|
||||
coin.hash = hash;
|
||||
coin.index = index;
|
||||
return coin;
|
||||
@ -1649,39 +1556,21 @@ TXDB.prototype.hasCoin = function hasCoin(hash, index, callback) {
|
||||
|
||||
/**
|
||||
* Calculate balance.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {WalletID?} id
|
||||
* @param {Function} callback - Returns [Error, {@link Balance}].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getBalance = function getBalance(address, callback) {
|
||||
TXDB.prototype.getBalance = function getBalance(id, callback) {
|
||||
var self = this;
|
||||
var confirmed = 0;
|
||||
var unconfirmed = 0;
|
||||
|
||||
if (typeof address === 'function') {
|
||||
callback = address;
|
||||
address = null;
|
||||
if (typeof id === 'function') {
|
||||
callback = id;
|
||||
id = null;
|
||||
}
|
||||
|
||||
// return this.getCoins(address, function(err, coins) {
|
||||
// if (err)
|
||||
// return callback(err);
|
||||
//
|
||||
// for (i = 0; i < coins.length; i++) {
|
||||
// if (coins[i].height === -1)
|
||||
// unconfirmed += coins[i].value;
|
||||
// else
|
||||
// confirmed += coins[i].value;
|
||||
// }
|
||||
//
|
||||
// return callback(null, {
|
||||
// confirmed: confirmed,
|
||||
// unconfirmed: unconfirmed,
|
||||
// total: confirmed + unconfirmed
|
||||
// });
|
||||
// });
|
||||
|
||||
return this.getCoinHashes(address, function(err, hashes) {
|
||||
return this.getCoinHashes(id, function(err, hashes) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -1711,30 +1600,31 @@ TXDB.prototype.getBalance = function getBalance(address, callback) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {WalletID?} id
|
||||
* @param {Number} age - Age delta (delete transactions older than `now - age`).
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
TXDB.prototype.zap = function zap(address, age, callback, force) {
|
||||
TXDB.prototype.zap = function zap(id, age, callback, force) {
|
||||
var self = this;
|
||||
|
||||
if (typeof address !== 'string') {
|
||||
if (typeof age === 'function') {
|
||||
force = callback;
|
||||
callback = age;
|
||||
age = address;
|
||||
address = null;
|
||||
age = id;
|
||||
id = null;
|
||||
}
|
||||
|
||||
var unlock = this._lock(zap, [address, age, callback], force);
|
||||
var unlock = this._lock(zap, [id, age, callback], force);
|
||||
if (!unlock)
|
||||
return;
|
||||
|
||||
callback = utils.wrap(callback, unlock);
|
||||
|
||||
assert(utils.isNumber(age));
|
||||
if (!utils.isNumber(age))
|
||||
return callback(new Error('Age must be a number.'));
|
||||
|
||||
return this.getRange(address, {
|
||||
return this.getRange(id, {
|
||||
start: 0,
|
||||
end: bcoin.now() - age
|
||||
}, function(err, txs) {
|
||||
@ -1748,12 +1638,35 @@ TXDB.prototype.zap = function zap(address, age, callback, force) {
|
||||
utils.forEachSerial(txs, function(tx, next) {
|
||||
if (tx.ts !== 0)
|
||||
return next();
|
||||
self.lazyRemove(tx, next);
|
||||
self.lazyRemove(tx, next, true);
|
||||
}, callback);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function uniq(obj) {
|
||||
var uniq = {};
|
||||
var out = [];
|
||||
var i, key, value;
|
||||
|
||||
for (i = 0; i < obj.length; i++) {
|
||||
value = obj[i];
|
||||
key = value.id + '/' + value.account;
|
||||
|
||||
if (uniq[key])
|
||||
continue;
|
||||
|
||||
uniq[key] = true;
|
||||
out.push(value);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
@ -2277,14 +2277,14 @@ utils.parallel = function parallel(stack, callback) {
|
||||
|
||||
for (i = 0; i < stack.length; i++) {
|
||||
try {
|
||||
if (0 && stack[i].length >= 2) {
|
||||
stack[i](error, next);
|
||||
error = null;
|
||||
} else {
|
||||
if (error)
|
||||
continue;
|
||||
stack[i](next);
|
||||
}
|
||||
// if (stack[i].length >= 2) {
|
||||
// stack[i](error, next);
|
||||
// error = null;
|
||||
// continue;
|
||||
// }
|
||||
if (error)
|
||||
continue;
|
||||
stack[i](next);
|
||||
} catch (e) {
|
||||
pending--;
|
||||
error = e;
|
||||
@ -2306,14 +2306,13 @@ utils.serial = function serial(stack, callback) {
|
||||
if (!cb)
|
||||
return callback(err);
|
||||
|
||||
if (0)
|
||||
if (cb.length >= 2) {
|
||||
try {
|
||||
return cb(err, next);
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
}
|
||||
}
|
||||
// if (cb.length >= 2) {
|
||||
// try {
|
||||
// return cb(err, next);
|
||||
// } catch (e) {
|
||||
// return next(e);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (err)
|
||||
return utils.nextTick(next.bind(null, err));
|
||||
|
||||
@ -76,6 +76,7 @@ function Wallet(options) {
|
||||
if (!this.id)
|
||||
this.id = this.getID();
|
||||
|
||||
// An in-memory database for testing.
|
||||
if (!this.db) {
|
||||
this.db = new bcoin.walletdb({
|
||||
network: this.network,
|
||||
@ -83,9 +84,6 @@ function Wallet(options) {
|
||||
db: 'memory'
|
||||
});
|
||||
}
|
||||
|
||||
// Non-alphanumeric IDs will break leveldb sorting.
|
||||
assert(/^[a-zA-Z0-9]+$/.test(this.id), 'Wallet IDs must be alphanumeric.');
|
||||
}
|
||||
|
||||
utils.inherits(Wallet, EventEmitter);
|
||||
@ -214,6 +212,7 @@ Wallet.prototype.addKey = function addKey(account, key, callback) {
|
||||
key = account;
|
||||
account = 0;
|
||||
}
|
||||
|
||||
this.getAccount(account, function(err, account) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
@ -238,6 +237,7 @@ Wallet.prototype.removeKey = function removeKey(account, key, callback) {
|
||||
key = account;
|
||||
account = 0;
|
||||
}
|
||||
|
||||
this.getAccount(account, function(err, account) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
@ -294,7 +294,7 @@ Wallet.prototype.createAccount = function createAccount(options, callback) {
|
||||
|
||||
options = {
|
||||
network: this.network,
|
||||
wid: this.id,
|
||||
id: this.id,
|
||||
name: this.accountDepth === 0 ? 'default' : options.name,
|
||||
witness: options.witness,
|
||||
accountKey: key.hdPublicKey,
|
||||
@ -854,31 +854,31 @@ Wallet.prototype.zap = function zap(account, age, callback) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Scan for addresses.
|
||||
* @param {Function} getByAddress - Must be a callback which accepts
|
||||
* a callback and returns transactions by address.
|
||||
* @param {Function} callback - Returns [Error, Boolean, TX[]].
|
||||
* Scan for active accounts and addresses. Used for importing a wallet.
|
||||
* @param {Function} getByAddress - Must be a function which accepts
|
||||
* a {@link Base58Address} as well as a callback and returns
|
||||
* transactions by address.
|
||||
* @param {Function} callback - Return [Error, Number] (total number
|
||||
* of addresses allocated).
|
||||
*/
|
||||
|
||||
Wallet.prototype.scan = function scan(getByAddress, callback) {
|
||||
var self = this;
|
||||
var result = false;
|
||||
var txs = [];
|
||||
var total = 0;
|
||||
|
||||
(function next() {
|
||||
self.createAccount(self.options, function(err, account) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
account.scan(getByAddress, function(err, res, tx) {
|
||||
account.scan(getByAddress, function(err, result) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (!res)
|
||||
return callback(null, result, txs);
|
||||
if (result === 0)
|
||||
return callback(null, total);
|
||||
|
||||
result = true;
|
||||
txs = txs.concat(tx);
|
||||
total += result;
|
||||
|
||||
next();
|
||||
});
|
||||
@ -978,6 +978,7 @@ Wallet.prototype.addTX = function addTX(tx, callback) {
|
||||
|
||||
/**
|
||||
* Get all transactions in transaction history (accesses db).
|
||||
* @param {(String|Number)?} account
|
||||
* @param {Function} callback - Returns [Error, {@link TX}[]].
|
||||
*/
|
||||
|
||||
@ -1444,8 +1445,8 @@ Wallet.fromJSON = function fromJSON(json) {
|
||||
|
||||
Wallet.isWallet = function isWallet(obj) {
|
||||
return obj
|
||||
&& typeof obj.receiveDepth === 'number'
|
||||
&& obj.deriveAddress === 'function';
|
||||
&& typeof obj.accountDepth === 'number'
|
||||
&& obj.scriptInputs === 'function';
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1454,11 +1455,9 @@ Wallet.isWallet = function isWallet(obj) {
|
||||
* @constructor
|
||||
* @param {Object} options
|
||||
* @param {WalletDB} options.db
|
||||
* present, no coins will be available.
|
||||
* @param {(HDPrivateKey|HDPublicKey)?} options.master - Master HD key. If not
|
||||
* present, it will be generated.
|
||||
* @param {HDPublicKey} options.accountKey
|
||||
* @param {Boolean?} options.witness - Whether to use witness programs.
|
||||
* @param {Number?} options.accountIndex - The BIP44 account index (default=0).
|
||||
* @param {Number} options.accountIndex - The BIP44 account index.
|
||||
* @param {Number?} options.receiveDepth - The index of the _next_ receiving
|
||||
* address.
|
||||
* @param {Number?} options.changeDepth - The index of the _next_ change
|
||||
@ -1467,8 +1466,8 @@ Wallet.isWallet = function isWallet(obj) {
|
||||
* (default=pubkeyhash).
|
||||
* @param {Number?} options.m - `m` value for multisig.
|
||||
* @param {Number?} options.n - `n` value for multisig.
|
||||
* @param {String?} options.wid - Account ID (used for storage)
|
||||
* (default=account key "address").
|
||||
* @param {String?} options.id - Wallet ID
|
||||
* @param {String?} options.name - Account name
|
||||
*/
|
||||
|
||||
function Account(options) {
|
||||
@ -1481,19 +1480,20 @@ function Account(options) {
|
||||
|
||||
assert(options, 'Options are required.');
|
||||
assert(options.db, 'Database is required.');
|
||||
assert(options.wid, 'Wallet ID is required.');
|
||||
assert(options.id, 'Wallet ID is required.');
|
||||
assert(options.accountKey, 'Account key is required.');
|
||||
assert(utils.isNumber(options.accountIndex), 'Account index is required.');
|
||||
|
||||
this.options = options;
|
||||
this.network = bcoin.network.get(options.network);
|
||||
this.db = options.db;
|
||||
this.lookahead = options.lookahead != null ? options.lookahead : 5;
|
||||
|
||||
this.wid = options.wid;
|
||||
this.id = options.id;
|
||||
this.name = options.name;
|
||||
this.witness = options.witness || false;
|
||||
this.accountKey = options.accountKey;
|
||||
this.accountIndex = options.accountIndex || 0;
|
||||
this.accountIndex = options.accountIndex;
|
||||
this.receiveDepth = options.receiveDepth || 1;
|
||||
this.changeDepth = options.changeDepth || 1;
|
||||
this.type = options.type || 'pubkeyhash';
|
||||
@ -1949,7 +1949,7 @@ Account.prototype.save = function save(callback) {
|
||||
*/
|
||||
|
||||
Account.prototype.saveAddress = function saveAddress(address, callback) {
|
||||
this.db.saveAddress(this.wid, address, callback);
|
||||
this.db.saveAddress(this.id, address, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2032,92 +2032,62 @@ Account.prototype.setChangeDepth = function setChangeDepth(depth, callback) {
|
||||
* Scan for addresses.
|
||||
* @param {Function} getByAddress - Must be a callback which accepts
|
||||
* a callback and returns transactions by address.
|
||||
* @param {Function} callback - Returns [Boolean, TX[]].
|
||||
* @param {Function} callback - Return [Error, Number] (total number
|
||||
* of addresses allocated).
|
||||
*/
|
||||
|
||||
Account.prototype.scan = function scan(getByAddress, callback) {
|
||||
var self = this;
|
||||
var result = false;
|
||||
var total = 0;
|
||||
|
||||
return this._scan(getByAddress, function(err, depth, txs) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
if (!this.initialized)
|
||||
return callback(new Error('Account is uninitialized.'));
|
||||
|
||||
self.setChangeDepth(depth.changeDepth + 1, function(err, res) {
|
||||
function addTX(txs, calback) {
|
||||
if (!Array.isArray(txs) || txs.length === 0)
|
||||
return callback(null, false);
|
||||
|
||||
utils.forEachSerial(txs, function(tx, next) {
|
||||
self.db.addTX(tx, next);
|
||||
}, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (res)
|
||||
result = true;
|
||||
|
||||
self.setReceiveDepth(depth.receiveDepth + 1, function(err, res) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (res)
|
||||
result = true;
|
||||
|
||||
return callback(null, result, txs);
|
||||
});
|
||||
return callback(null, true);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Account.prototype._scan = function _scan(getByAddress, callback) {
|
||||
var self = this;
|
||||
var depth = { changeDepth: 0, receiveDepth: 0 };
|
||||
var all = [];
|
||||
|
||||
assert(this.initialized);
|
||||
}
|
||||
|
||||
(function chainCheck(change) {
|
||||
var addressIndex = 0;
|
||||
var total = 0;
|
||||
var gap = 0;
|
||||
|
||||
(function next() {
|
||||
var address = self.deriveAddress(change, addressIndex++);
|
||||
|
||||
getByAddress(address.getAddress(), function(err, txs) {
|
||||
var result;
|
||||
|
||||
self.createAddress(change, function(err, address) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (txs) {
|
||||
if (typeof txs === 'boolean')
|
||||
result = txs;
|
||||
else if (typeof txs === 'number')
|
||||
result = txs > 0;
|
||||
else if (Array.isArray(txs))
|
||||
result = txs.length > 0;
|
||||
else
|
||||
result = false;
|
||||
getByAddress(address.getAddress(), function(err, txs) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (Array.isArray(txs) && (txs[0] instanceof bcoin.tx))
|
||||
all = all.concat(txs);
|
||||
}
|
||||
addTX(txs, function(err, result) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (result) {
|
||||
total++;
|
||||
gap = 0;
|
||||
return next();
|
||||
}
|
||||
if (result) {
|
||||
total++;
|
||||
gap = 0;
|
||||
return next();
|
||||
}
|
||||
|
||||
if (++gap < 20)
|
||||
return next();
|
||||
if (++gap < 20)
|
||||
return next();
|
||||
|
||||
assert(depth.receiveDepth === 0 || change === true);
|
||||
if (!change)
|
||||
return chainCheck(true);
|
||||
|
||||
if (change === false)
|
||||
depth.receiveDepth = addressIndex - gap;
|
||||
else
|
||||
depth.changeDepth = addressIndex - gap;
|
||||
|
||||
if (change === false)
|
||||
return chainCheck(true);
|
||||
|
||||
return callback(null, depth, all);
|
||||
return callback(null, total);
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
})(false);
|
||||
@ -2130,9 +2100,9 @@ Account.prototype._scan = function _scan(getByAddress, callback) {
|
||||
|
||||
Account.prototype.inspect = function inspect() {
|
||||
return {
|
||||
wid: this.wid,
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
network: this.network.type,
|
||||
network: this.network,
|
||||
initialized: this.initialized,
|
||||
type: this.type,
|
||||
m: this.m,
|
||||
@ -2167,7 +2137,7 @@ Account.prototype.inspect = function inspect() {
|
||||
Account.prototype.toJSON = function toJSON() {
|
||||
return {
|
||||
network: this.network.type,
|
||||
wid: this.wid,
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
initialized: this.initialized,
|
||||
type: this.type,
|
||||
@ -2198,7 +2168,7 @@ Account.prototype.toJSON = function toJSON() {
|
||||
Account.parseJSON = function parseJSON(json) {
|
||||
return {
|
||||
network: json.network,
|
||||
wid: json.wid,
|
||||
id: json.id,
|
||||
name: json.name,
|
||||
initialized: json.initialized,
|
||||
type: json.type,
|
||||
@ -2225,7 +2195,7 @@ Account.prototype.toRaw = function toRaw(writer) {
|
||||
var i;
|
||||
|
||||
p.writeU32(this.network.magic);
|
||||
p.writeVarString(this.wid, 'utf8');
|
||||
p.writeVarString(this.id, 'utf8');
|
||||
p.writeVarString(this.name, 'utf8');
|
||||
p.writeU8(this.initialized ? 1 : 0);
|
||||
p.writeU8(this.type === 'pubkeyhash' ? 0 : 1);
|
||||
@ -2258,7 +2228,7 @@ Account.prototype.toRaw = function toRaw(writer) {
|
||||
Account.parseRaw = function parseRaw(data) {
|
||||
var p = new BufferReader(data);
|
||||
var network = bcoin.network.fromMagic(p.readU32());
|
||||
var wid = p.readVarString('utf8');
|
||||
var id = p.readVarString('utf8');
|
||||
var name = p.readVarString('utf8');
|
||||
var initialized = p.readU8() === 1;
|
||||
var type = p.readU8() === 0 ? 'pubkeyhash' : 'multisig';
|
||||
@ -2277,7 +2247,7 @@ Account.parseRaw = function parseRaw(data) {
|
||||
|
||||
return {
|
||||
network: network.type,
|
||||
wid: wid,
|
||||
id: id,
|
||||
name: name,
|
||||
initialized: initialized,
|
||||
type: type,
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
* (inherits all from txdb)
|
||||
* W/[address] -> id & path data
|
||||
* w/[id] -> wallet
|
||||
* a/[id]/[index] -> account
|
||||
* i/[id]/[name] -> account index
|
||||
*/
|
||||
|
||||
var bcoin = require('./env');
|
||||
@ -346,140 +348,12 @@ WalletDB.prototype.get = function get(id, callback) {
|
||||
*/
|
||||
|
||||
WalletDB.prototype.save = function save(wallet, callback) {
|
||||
if (!isAlpha(wallet.id))
|
||||
return callback(new Error('Wallet IDs must be alphanumeric.'));
|
||||
|
||||
this.db.put('w/' + wallet.id, wallet.toRaw(), callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an account from the database.
|
||||
* @param {WalletID} id
|
||||
* @param {Function} callback - Returns [Error, {@link Wallet}].
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getAccountIndex = function getAccountIndex(wid, name, callback) {
|
||||
return this.db.get('i/' + wid + '/' + name, function(err, index) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback();
|
||||
|
||||
if (!index)
|
||||
return callback(null, -1);
|
||||
|
||||
return callback(null, index.readUInt32LE(0, true));
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.getAccount = function getAccount(wid, id, callback) {
|
||||
var self = this;
|
||||
var aid = wid + '/' + id;
|
||||
var account;
|
||||
|
||||
if (id == null)
|
||||
return callback();
|
||||
|
||||
if (typeof id === 'string') {
|
||||
return this.getAccountIndex(wid, id, function(err, index) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (index === -1)
|
||||
return callback();
|
||||
|
||||
return self.getAccount(wid, index, callback);
|
||||
});
|
||||
}
|
||||
|
||||
this.db.get('a/' + aid, function(err, data) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
if (!data)
|
||||
return callback();
|
||||
|
||||
try {
|
||||
data = bcoin.account.parseRaw(data);
|
||||
data.db = self;
|
||||
account = new bcoin.account(data);
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
|
||||
account.open(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
return callback(null, account);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove wallet from the database. Destroy wallet if passed in.
|
||||
* @param {WalletID} id
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype.remove = function remove(id, callback) {
|
||||
this.db.del('w/' + id, function(err) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Save a wallet to the database.
|
||||
* @param {Wallet} wallet
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype.saveAccount = function saveAccount(account, callback) {
|
||||
var index = new Buffer(4);
|
||||
var batch = this.db.batch();
|
||||
index.writeUInt32LE(account.accountIndex, 0, true);
|
||||
batch.put('a/' + account.wid + '/' + account.accountIndex, account.toRaw());
|
||||
batch.put('i/' + account.wid + '/' + account.name, index);
|
||||
batch.write(callback);
|
||||
};
|
||||
|
||||
WalletDB.prototype.createAccount = function createAccount(options, callback) {
|
||||
var self = this;
|
||||
var account;
|
||||
|
||||
this.hasAccount(options.wid, options.accountIndex, function(err, exists) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (exists)
|
||||
return callback(new Error('account already exists.'));
|
||||
|
||||
options = utils.merge({}, options);
|
||||
|
||||
if (self.network.witness)
|
||||
options.witness = options.witness !== false;
|
||||
|
||||
options.network = self.network;
|
||||
options.db = self;
|
||||
account = new bcoin.account(options);
|
||||
|
||||
account.open(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
return callback(null, account);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.hasAccount = function hasAccount(wid, account, callback) {
|
||||
if (!wid || account == null)
|
||||
return callback(null, false);
|
||||
|
||||
this.db.has('a/' + wid + '/' + account, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new wallet, save to database, setup watcher.
|
||||
* @param {Object} options - See {@link Wallet}.
|
||||
@ -502,9 +376,6 @@ WalletDB.prototype.create = function create(options, callback) {
|
||||
|
||||
options = utils.merge({}, options);
|
||||
|
||||
if (self.network.witness)
|
||||
options.witness = options.witness !== false;
|
||||
|
||||
options.network = self.network;
|
||||
options.db = self;
|
||||
wallet = new bcoin.wallet(options);
|
||||
@ -551,6 +422,160 @@ WalletDB.prototype.ensure = function ensure(options, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an account from the database.
|
||||
* @param {WalletID} id
|
||||
* @param {String|Number} name - Account name/index.
|
||||
* @param {Function} callback - Returns [Error, {@link Wallet}].
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getAccount = function getAccount(id, name, callback) {
|
||||
var self = this;
|
||||
var account;
|
||||
|
||||
return this.getAccountIndex(id, name, function(err, index) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (index === -1)
|
||||
return callback();
|
||||
|
||||
self.db.get('a/' + id + '/' + index, function(err, data) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
if (!data)
|
||||
return callback();
|
||||
|
||||
try {
|
||||
data = bcoin.account.parseRaw(data);
|
||||
data.db = self;
|
||||
account = new bcoin.account(data);
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
|
||||
account.open(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
return callback(null, account);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Lookup the corresponding account name's index.
|
||||
* @param {WalletID} id
|
||||
* @param {String|Number} name - Account name/index.
|
||||
* @param {Function} callback - Returns [Error, Number].
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getAccountIndex = function getAccountIndex(id, name, callback) {
|
||||
if (name == null)
|
||||
return callback(null, -1);
|
||||
|
||||
if (typeof name === 'number')
|
||||
return callback(null, name);
|
||||
|
||||
return this.db.get('i/' + id + '/' + name, function(err, index) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback();
|
||||
|
||||
if (!index)
|
||||
return callback(null, -1);
|
||||
|
||||
return callback(null, index.readUInt32LE(0, true));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Save an account to the database.
|
||||
* @param {Account} account
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype.saveAccount = function saveAccount(account, callback) {
|
||||
var index, batch;
|
||||
|
||||
if (!isAlpha(account.name))
|
||||
return callback(new Error('Account names must be alphanumeric.'));
|
||||
|
||||
batch = this.db.batch();
|
||||
|
||||
index = new Buffer(4);
|
||||
index.writeUInt32LE(account.accountIndex, 0, true);
|
||||
|
||||
batch.put('a/' + account.id + '/' + account.accountIndex, account.toRaw());
|
||||
batch.put('i/' + account.id + '/' + account.name, index);
|
||||
|
||||
batch.write(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an account.
|
||||
* @param {Object} options - See {@link Account} options.
|
||||
* @param {Function} callback - Returns [Error, {@link Account}].
|
||||
*/
|
||||
|
||||
WalletDB.prototype.createAccount = function createAccount(options, callback) {
|
||||
var self = this;
|
||||
var account;
|
||||
|
||||
this.hasAccount(options.id, options.accountIndex, function(err, exists) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (exists)
|
||||
return callback(new Error('Account already exists.'));
|
||||
|
||||
options = utils.merge({}, options);
|
||||
|
||||
if (self.network.witness)
|
||||
options.witness = options.witness !== false;
|
||||
|
||||
options.network = self.network;
|
||||
options.db = self;
|
||||
account = new bcoin.account(options);
|
||||
|
||||
account.open(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
return callback(null, account);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Test for the existence of an account.
|
||||
* @param {WalletID} id
|
||||
* @param {String|Number} account
|
||||
* @param {Function} callback - Returns [Error, Boolean].
|
||||
*/
|
||||
|
||||
WalletDB.prototype.hasAccount = function hasAccount(id, account, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!id)
|
||||
return callback(null, false);
|
||||
|
||||
this.getAccountIndex(id, account, function(err, index) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (index === -1)
|
||||
return callback(null, false);
|
||||
|
||||
self.db.has('a/' + id + '/' + index, callback);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Save an address to the path map.
|
||||
* The path map exists in the form of:
|
||||
@ -958,6 +983,10 @@ function serializePaths(out) {
|
||||
return p.render();
|
||||
}
|
||||
|
||||
function isAlpha(key) {
|
||||
return /^[a-zA-Z0-9]+$/.test(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user