refactor. allow tx querying by timestamp.

This commit is contained in:
Christopher Jeffrey 2016-03-04 03:49:43 -08:00
parent e962686e93
commit f67902a9cf
7 changed files with 362 additions and 270 deletions

View File

@ -68,11 +68,7 @@ Address.prototype.getID = function getID() {
Address.prototype.addKey = function addKey(key) {
key = utils.ensureBuffer(key);
var has = this.keys.some(function(k) {
return utils.isEqual(k, key);
});
if (has)
if (utils.indexOf(this.keys, key) !== -1)
return;
this.keys.push(key);
@ -81,15 +77,13 @@ Address.prototype.addKey = function addKey(key) {
};
Address.prototype.removeKey = function removeKey(key) {
var index;
key = utils.ensureBuffer(key);
var index = this.keys.map(function(k, i) {
return utils.isEqual(k, key) ? i : null;
}).filter(function(i) {
return i !== null;
})[0];
index = utils.indexOf(this.keys, key);
if (index == null)
if (index === -1)
return;
this.keys.splice(index, 1);
@ -175,7 +169,8 @@ Address.prototype.getProgramAddress = function getProgramAddress() {
if (this._programAddress)
return this._programAddress;
this._programAddress = Address.compileHash(this.getProgramHash(), 'scripthash');
this._programAddress =
Address.compileHash(this.getProgramHash(), 'scripthash');
return this._programAddress;
};
@ -215,10 +210,13 @@ Address.prototype.getScriptAddress = function getScriptAddress() {
if (this._scriptAddress)
return this._scriptAddress;
if (this.witness)
this._scriptAddress = Address.compileHash(this.getScriptHash256(), 'witnessscripthash');
else
this._scriptAddress = Address.compileHash(this.getScriptHash160(), 'scripthash');
if (this.witness) {
this._scriptAddress =
Address.compileHash(this.getScriptHash256(), 'witnessscripthash');
} else {
this._scriptAddress =
Address.compileHash(this.getScriptHash160(), 'scripthash');
}
return this._scriptAddress;
};
@ -273,57 +271,26 @@ Address.prototype._getAddressMap = function _getAddressMap() {
return this.addressMap;
};
Address.prototype.ownOutput = function ownOutput(tx, index) {
var addressMap = this._getAddressMap();
var outputs = tx.outputs;
if ((tx instanceof bcoin.output) || (tx instanceof bcoin.coin)) {
outputs = [tx];
tx = null;
}
outputs = outputs.filter(function(output, i) {
if (index != null && index !== i)
return false;
return output.test(addressMap);
}, this);
if (outputs.length === 0)
return false;
return outputs;
};
Address.prototype.ownInput = function ownInput(tx, index) {
var addressMap = this._getAddressMap();
var inputs = tx.inputs;
if (tx instanceof bcoin.input) {
inputs = [tx];
tx = null;
}
if (tx instanceof bcoin.input)
return tx.test(addressMap);
inputs = inputs.filter(function(input, i) {
if (index != null && index !== i)
return false;
return tx.testInputs(addressMap, index);
};
if (input.output)
return input.output.test(addressMap);
Address.prototype.ownOutput = function ownOutput(tx, index) {
var addressMap = this._getAddressMap();
return input.test(addressMap);
}, this);
if (tx instanceof bcoin.output)
return tx.test(addressMap);
if (inputs.length === 0)
return false;
return inputs;
return tx.testOutputs(addressMap, index);
};
Address.prototype.scriptInputs = function scriptInputs(tx, index) {
var self = this;
var publicKey = this.getPublicKey();
var redeem = this.getScript();
if (index && typeof index === 'object')
index = tx.inputs.indexOf(index);
@ -347,13 +314,11 @@ Address.prototype.scriptInputs = function scriptInputs(tx, index) {
Address.prototype.signInputs = function signInputs(tx, type, index) {
var self = this;
var key = this.key;
var total = 0;
if (index && typeof index === 'object')
index = tx.inputs.indexOf(index);
if (!key.privateKey)
if (!this.key.privateKey)
return 0;
return tx.inputs.reduce(function(total, input, i) {
@ -375,13 +340,11 @@ Address.prototype.signInputs = function signInputs(tx, type, index) {
Address.prototype.sign = function sign(tx, type, index) {
var self = this;
var redeem = this.getScript();
var key = this.key;
if (index && typeof index === 'object')
index = tx.inputs.indexOf(index);
if (!key.privateKey)
if (!this.key.privateKey)
return 0;
// Add signature script to each input

View File

@ -201,9 +201,9 @@ HTTPServer.prototype._init = function _init() {
// Update wallet / sync address depth
this.put('/wallet/:id', function(req, res, next, send) {
var id = req.params.id;
var receive = req.body.receiveDepth;
var change = req.body.changeDepth;
self.node.walletdb.syncDepth(id, receive, change, function(err) {
var receive = req.body.receiveDepth >>> 0;
var change = req.body.changeDepth >>> 0;
self.node.walletdb.setDepth(id, receive, change, function(err) {
if (err)
return next(err);

View File

@ -117,6 +117,10 @@ MerkleBlock.prototype._verify = function _verify() {
return true;
};
MerkleBlock.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
return -1;
};
MerkleBlock.prototype.inspect = function inspect() {
var copy = bcoin.merkleblock(this);
copy.__proto__ = null;

View File

@ -1282,7 +1282,7 @@ Pool.prototype.searchWallet = function(wallet, callback) {
if (!this.options.spv)
return callback();
wallet.getLast(function(err, ts, height) {
wallet.getLastTime(function(err, ts, height) {
if (err)
return callback(err);

View File

@ -76,7 +76,9 @@ TXPool.prototype._lock = function _lock(func, args, force) {
TXPool.prototype.getMap = function getMap(tx, callback) {
var self = this;
var addresses = tx.getAddresses();
var input = tx.getInputAddresses();
var output = tx.getOutputAddresses();
var addresses = utils.uniqs(input.concat(output));
var map;
function cb(err, map) {
@ -87,12 +89,12 @@ TXPool.prototype.getMap = function getMap(tx, callback) {
map.output = [];
map.all = [];
tx.getInputAddresses().forEach(function(address) {
input.forEach(function(address) {
assert(map[address]);
map.input = map.input.concat(map[address]);
});
tx.getOutputAddresses().forEach(function(address) {
output.forEach(function(address) {
assert(map[address]);
map.output = map.output.concat(map[address]);
});
@ -122,7 +124,6 @@ TXPool.prototype.mapAddresses = function mapAddresses(address, callback) {
var iter;
if (Array.isArray(address)) {
address = utils.uniqs(address);
return utils.forEachSerial(address, function(address, next) {
self.mapAddresses(address, function(err, res) {
if (err)
@ -166,7 +167,6 @@ TXPool.prototype.mapAddresses = function mapAddresses(address, callback) {
return iter.end(function(err) {
if (err)
return callback(err);
map[address] = utils.uniqs(map[address]);
return callback(null, map);
});
}
@ -261,6 +261,13 @@ TXPool.prototype.add = function add(tx, callback) {
});
};
function pad32(num) {
num = num + '';
while (num.length < 10)
num = '0' + num;
return num;
}
// This big scary function is what a persistent tx pool
// looks like. It's a semi mempool in that it can handle
// receiving txs out of order.
@ -269,10 +276,10 @@ TXPool.prototype._add = function add(tx, map, callback, force) {
var prefix = this.prefix + '/';
var hash = tx.hash('hex');
var updated = false;
var own = false;
var uniq = {};
var batch;
assert(tx.ts > 0 || tx.ps > 0);
var unlock = this._lock(add, [tx, map, callback], force);
if (!unlock)
return;
@ -290,38 +297,29 @@ TXPool.prototype._add = function add(tx, map, callback, force) {
batch = self.db.batch();
if (existing) {
own = true;
// Tricky - update the tx and coin in storage,
// and remove pending flag to mark as confirmed.
if (existing.ts === 0 && tx.ts !== 0) {
assert(tx.height >= 0);
assert(existing.ps > 0);
batch.put(prefix + 't/t/' + hash, tx.toExtended());
batch.put(prefix + 't/h/' + tx.height + '/' + hash, DUMMY);
batch.del(prefix + 'p/t/' + hash);
batch.put(prefix + 't/h/h/' + pad32(tx.height) + '/' + hash, DUMMY);
batch.del(prefix + 't/s/s/' + pad32(existing.ps) + '/' + hash);
batch.put(prefix + 't/s/s/' + pad32(tx.ts) + '/' + hash, DUMMY);
batch.del(prefix + 't/p/t/' + hash);
tx.inputs.forEach(function(input) {
var address = input.getAddress();
if (input.isCoinbase())
return;
if (address && !uniq[address]) {
uniq[address] = true;
map[address].forEach(function(id) {
batch.del(prefix + 'p/a/' + id + '/' + hash);
});
}
map.all.forEach(function(id) {
batch.put(
prefix + 't/h/a/' + id + '/' + pad32(tx.height) + '/' + hash, DUMMY);
batch.del(
prefix + 't/s/a/' + id + '/' + pad32(existing.ps) + '/' + hash);
batch.put(
prefix + 't/s/a/' + id + '/' + pad32(tx.ts) + '/' + hash, DUMMY);
batch.del(prefix + 't/p/a/' + id + '/' + hash);
});
utils.forEachSerial(tx.outputs, function(output, next, i) {
var address = output.getAddress();
if (address && !uniq[address]) {
uniq[address] = true;
map[address].forEach(function(id) {
batch.del(prefix + 'p/a/' + id + '/' + hash);
});
}
self.getCoin(hash, i, function(err, coin) {
if (err)
return next(err);
@ -355,6 +353,31 @@ TXPool.prototype._add = function add(tx, map, callback, force) {
return done(null, false);
}
batch.put(prefix + 't/t/' + hash, tx.toExtended());
if (tx.ts === 0) {
assert(tx.ps > 0);
batch.put(prefix + 't/p/t/' + hash, DUMMY);
batch.put(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash, DUMMY);
} else {
batch.put(prefix + 't/h/h/' + pad32(tx.height) + '/' + hash, DUMMY);
batch.put(prefix + 't/s/s/' + pad32(tx.ts) + '/' + hash, DUMMY);
}
map.all.forEach(function(id) {
batch.put(prefix + 't/a/' + id + '/' + hash, DUMMY);
if (tx.ts === 0) {
batch.put(prefix + 't/p/a/' + id + '/' + hash, DUMMY);
batch.put(
prefix + 't/s/a/' + id + '/' + pad32(tx.ps) + '/' + hash, DUMMY);
} else {
batch.put(
prefix + 't/h/a/' + id + '/' + pad32(tx.height) + '/' + hash, DUMMY);
batch.put(
prefix + 't/s/a/' + id + '/' + pad32(tx.ts) + '/' + hash, DUMMY);
}
});
// Consume unspent money or add orphans
utils.forEachSerial(tx.inputs, function(input, next, i) {
var key;
@ -372,15 +395,6 @@ TXPool.prototype._add = function add(tx, map, callback, force) {
address = input.getAddress();
if (address && !uniq[address]) {
uniq[address] = true;
map[address].forEach(function(id) {
batch.put(prefix + 't/a/' + id + '/' + hash, DUMMY);
if (tx.ts === 0)
batch.put(prefix + 'p/a/' + id + '/' + hash, DUMMY);
});
}
if (coin) {
// Add TX to inputs and spend money
input.output = coin;
@ -393,7 +407,6 @@ TXPool.prototype._add = function add(tx, map, callback, force) {
return done(null, false);
updated = true;
own = true;
if (address) {
map[address].forEach(function(id) {
@ -416,8 +429,6 @@ TXPool.prototype._add = function add(tx, map, callback, force) {
if (!address || !map[address].length)
return next();
own = true;
self.getTX(input.prevout.hash, function(err, result) {
if (err)
return done(err);
@ -452,7 +463,6 @@ TXPool.prototype._add = function add(tx, map, callback, force) {
key = hash + '/' + i;
coin = bcoin.coin(tx, i);
own = true;
self._getOrphans(key, function(err, orphans) {
var some = false;
@ -504,15 +514,6 @@ TXPool.prototype._add = function add(tx, map, callback, force) {
return next(err);
if (!orphans) {
if (address && !uniq[address]) {
uniq[address] = true;
map[address].forEach(function(id) {
batch.put(prefix + 't/a/' + id + '/' + hash, DUMMY);
if (tx.ts === 0)
batch.put(prefix + 'p/a/' + id + '/' + hash, DUMMY);
});
}
if (address) {
map[address].forEach(function(id) {
batch.put(
@ -533,15 +534,6 @@ TXPool.prototype._add = function add(tx, map, callback, force) {
if (err)
return done(err);
if (!own)
return done(null, false);
batch.put(prefix + 't/t/' + hash, tx.toExtended());
if (tx.ts === 0)
batch.put(prefix + 'p/t/' + hash, DUMMY);
else
batch.put(prefix + 't/h/' + tx.height + '/' + hash, DUMMY);
batch.write(function(err) {
if (err)
return done(err);
@ -620,13 +612,27 @@ TXPool.prototype._remove = function remove(tx, map, callback) {
var prefix = this.prefix + '/';
var hash = tx.hash('hex');
var batch = this.db.batch();
var uniq = {};
batch.del(prefix + 't/t/' + hash);
if (tx.ts === 0)
batch.del(prefix + 'p/t/' + hash);
else
batch.del(prefix + 't/h/' + tx.height + '/' + hash);
if (tx.ts === 0) {
batch.del(prefix + 't/p/t/' + hash);
batch.del(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash);
} else {
batch.del(prefix + 't/h/h/' + pad32(tx.height) + '/' + hash);
batch.del(prefix + 't/s/s/' + pad32(tx.ts) + '/' + hash);
}
map.all.forEach(function(id) {
batch.del(prefix + 't/a/' + id + '/' + hash);
if (tx.ts === 0) {
batch.del(prefix + 't/p/a/' + id + '/' + hash);
batch.del(prefix + 't/s/a/' + id + '/' + pad32(tx.ps) + '/' + hash);
} else {
batch.del(prefix + 't/h/a/' + id + '/' + pad32(tx.height) + '/' + hash);
batch.del(prefix + 't/s/a/' + id + '/' + pad32(tx.ts) + '/' + hash);
}
});
this.fillTX(tx, function(err) {
if (err)
@ -638,15 +644,6 @@ TXPool.prototype._remove = function remove(tx, map, callback) {
if (input.isCoinbase())
return;
if (address && !uniq[address]) {
uniq[address] = true;
map[address].forEach(function(id) {
batch.del(prefix + 't/a/' + id + '/' + hash);
if (tx.ts === 0)
batch.del(prefix + 'p/a/' + id + '/' + hash);
});
}
if (!input.output)
return;
@ -670,15 +667,6 @@ TXPool.prototype._remove = function remove(tx, map, callback) {
tx.outputs.forEach(function(output, i) {
var address = output.getAddress();
if (address && !uniq[address]) {
uniq[address] = true;
map[address].forEach(function(id) {
batch.del(prefix + 't/a/' + id + '/' + hash);
if (tx.ts === 0)
batch.del(prefix + 'p/a/' + id + '/' + hash);
});
}
if (address) {
map[address].forEach(function(id) {
batch.del(prefix + 'u/a/' + id + '/' + hash + '/' + i);
@ -735,10 +723,13 @@ TXPool.prototype.unconfirm = function unconfirm(hash, callback) {
TXPool.prototype._unconfirm = function unconfirm(tx, map, callback) {
var self = this;
var prefix = this.prefix + '/';
var uniq = {};
var hash = tx.hash('hex');
var batch = this.db.batch();
var height = tx.height;
var ts = tx.ts;
if (height !== -1)
return callback(null, false);
tx.height = -1;
tx.ps = utils.now();
@ -747,35 +738,19 @@ TXPool.prototype._unconfirm = function unconfirm(tx, map, callback) {
tx.block = null;
batch.put(prefix + 't/t/' + hash, tx.toExtended());
batch.put(prefix + 'p/t/' + hash, DUMMY);
batch.del(prefix + 't/h/' + height + '/' + hash);
batch.put(prefix + 't/p/t/' + hash, DUMMY);
batch.del(prefix + 't/h/h/' + pad32(height) + '/' + hash);
batch.del(prefix + 't/s/s/' + pad32(ts) + '/' + hash);
batch.put(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash, DUMMY);
tx.inputs.forEach(function(input) {
var address;
if (input.isCoinbase())
return;
address = input.getAddress();
if (address && !uniq[address]) {
uniq[address] = true;
map[address].forEach(function(id) {
batch.put(prefix + 'p/a/' + id + '/' + hash, DUMMY);
});
}
map.all.forEach(function(id) {
batch.del(prefix + 't/h/a/' + id + '/' + pad32(height) + '/' + hash);
batch.del(prefix + 't/s/a/' + id + '/' + pad32(ts) + '/' + hash);
batch.put(prefix + 't/s/a/' + id + '/' + pad32(tx.ps) + '/' + hash, DUMMY);
batch.put(prefix + 't/p/a/' + id + '/' + hash, DUMMY);
});
utils.forEachSerial(tx.outputs, function(output, next, i) {
var address = output.getAddress();
if (address && !uniq[address]) {
uniq[address] = true;
map[address].forEach(function(id) {
batch.put(prefix + 'p/a/' + id + '/' + hash, DUMMY);
});
}
self.getCoin(hash, i, function(err, coin) {
if (err)
return next(err);
@ -897,8 +872,8 @@ TXPool.prototype.getPendingHashes = function getPendingHashes(address, callback)
}
iter = this.db.db.iterator({
gte: address ? prefix + 'p/a/' + address : prefix + 'p/t',
lte: address ? prefix + 'p/a/' + address + '~' : prefix + 'p/t~',
gte: address ? prefix + 't/p/a/' + address : prefix + 't/p/t',
lte: address ? prefix + 't/p/a/' + address + '~' : prefix + 't/p/t~',
keys: true,
values: false,
fillCache: false,
@ -994,41 +969,27 @@ TXPool.prototype.getCoinIDs = function getCoinIDs(address, callback) {
})();
};
TXPool.prototype.getHeightHashes = function getHeightHashes(height, callback) {
TXPool.prototype.getHeightRangeHashes = function getHeightRangeHashes(address, options, callback) {
var prefix = this.prefix + '/';
var self = this;
var txs = [];
var total = 0;
var iter;
callback = utils.ensure(callback);
if (Array.isArray(height)) {
return utils.forEachSerial(height, function(height, next) {
self.getHeightHashes(height, function(err, tx) {
if (err)
return next(err);
txs = txs.concat(tx);
next();
});
}, function(err) {
if (err)
return callback(err);
txs = utils.uniqs(txs);
return callback(null, txs);
});
}
iter = this.db.db.iterator({
gte: prefix + 't/h/' + height,
lte: prefix + 't/h/' + height + '~',
gte: address
? prefix + 't/h/a/' + address + '/' + pad32(options.start) + '/'
: prefix + 't/h/h/' + pad32(options.start) + '/',
lte: address
? prefix + 't/h/a/' + address + '/' + pad32(options.end) + '/~'
: prefix + 't/h/h/' + pad32(options.end) + '/~',
keys: true,
values: false,
fillCache: false,
keyAsBuffer: false
keyAsBuffer: false,
reverse: options.reverse
});
(function next() {
@ -1049,11 +1010,105 @@ TXPool.prototype.getHeightHashes = function getHeightHashes(height, callback) {
txs.push(key.split('/')[4]);
if (++total === options.limit)
return callback(null, txs);
next();
});
})();
};
TXPool.prototype.getHeightHashes = function getHeightHashes(height, callback) {
return this.getHeightRangeHashes({ start: height, end: height }, callback);
};
TXPool.prototype.getTimeRangeHashes = function getTimeRangeHashes(address, options, callback) {
var prefix = this.prefix + '/';
var self = this;
var txs = [];
var total = 0;
var iter;
callback = utils.ensure(callback);
iter = this.db.db.iterator({
gte: address
? prefix + 't/s/a/' + address + '/' + pad32(options.start) + '/'
: prefix + 't/s/s/' + pad32(options.start) + '/',
lte: address
? prefix + 't/s/a/' + address + '/' + pad32(options.end) + '/~'
: prefix + 't/s/s/' + pad32(options.end) + '/~',
keys: true,
values: false,
fillCache: false,
keyAsBuffer: false,
reverse: options.reverse
});
(function next() {
iter.next(function(err, key, value) {
if (err) {
return iter.end(function() {
callback(err);
});
}
if (key === undefined) {
return iter.end(function(err) {
if (err)
return callback(err);
return callback(null, txs);
});
}
txs.push(key.split('/')[4]);
if (++total === options.limit)
return callback(null, txs);
next();
});
})();
};
TXPool.prototype.getTimeRange = function getLast(address, options, callback) {
var self = this;
var txs = [];
return this.getTimeRangeHashes(address, options, function(err, hashes) {
if (err)
return callback(err);
utils.forEachSerial(hashes, function(hash, next) {
self.getTX(hash, function(err, tx) {
if (err)
return callback(err);
if (!tx)
return next();
txs.push(tx);
next();
});
}, function(err) {
if (err)
return callback(err);
return callback(null, txs);
});
});
};
TXPool.prototype.getLast = function getLast(address, limit, callback) {
return this.getTimeRange(address, {
start: 0,
end: 0xffffffff,
reverse: true,
limit: limit
}, callback);
};
TXPool.prototype.getAllByAddress = function getAllByAddress(address, callback) {
var self = this;
var txs = [];
@ -1083,7 +1138,7 @@ TXPool.prototype.getAllByAddress = function getAllByAddress(address, callback) {
});
};
TXPool.prototype.getLast = function getLast(address, callback) {
TXPool.prototype.getLastTime = function getLastTime(address, callback) {
return this.getAllByAddress(address, function(err, txs) {
var lastTs, lastHeight;

View File

@ -152,12 +152,14 @@ Wallet.prototype._init = function _init() {
});
this.provider.on('unconfirmed', function(tx) {
self.syncOutputDepth(tx);
self.emit('unconfirmed', tx);
});
};
Wallet.prototype.destroy = function destroy() {
if (!this.provider)
return;
this.provider.destroy();
this.provider = null;
};
@ -216,9 +218,6 @@ Wallet.prototype.removeKey = function removeKey(key) {
else if (bcoin.hd.publicKey.isExtended(key))
key = bcoin.hd.publicKey(key);
if (key instanceof bcoin.keypair)
key = key.hd;
if (key instanceof bcoin.hd.privateKey)
key = key.hdPublicKey;
@ -428,7 +427,7 @@ Wallet.prototype.parsePath = function parsePath(path) {
Wallet.prototype.setReceiveDepth = function setReceiveDepth(depth) {
var i;
if (depth <= this.receiveDepth)
if (!(depth > this.receiveDepth))
return false;
for (i = this.receiveDepth; i < depth; i++)
@ -445,7 +444,7 @@ Wallet.prototype.setReceiveDepth = function setReceiveDepth(depth) {
Wallet.prototype.setChangeDepth = function setChangeDepth(depth) {
var i;
if (depth <= this.changeDepth)
if (!(depth > this.changeDepth))
return false;
for (i = this.changeDepth; i < depth; i++)
@ -674,33 +673,30 @@ Wallet.prototype.syncOutputDepth = function syncOutputDepth(tx) {
var depth = this.getOutputDepth(tx);
var res = false;
if (depth.changeDepth >= this.changeDepth) {
this.setChangeDepth(depth.changeDepth + 1);
if (this.setChangeDepth(depth.changeDepth + 1))
res = true;
}
if (depth.receiveDepth >= this.receiveDepth) {
this.setReceiveDepth(depth.receiveDepth + 1);
if (this.setReceiveDepth(depth.receiveDepth + 1))
res = true;
}
return res;
};
Wallet.prototype.scan = function scan(txByAddress, callback) {
var self = this;
var res = false;
return this._scan({}, txByAddress, function(err, depth) {
if (err)
return callback(err);
if (depth.changeDepth >= self.changeDepth)
self.setChangeDepth(depth.changeDepth + 1);
if (self.setChangeDepth(depth.changeDepth + 1))
res = true;
if (depth.receiveDepth >= self.receiveDepth)
self.setReceiveDepth(depth.receiveDepth + 1);
if (self.setReceiveDepth(depth.receiveDepth + 1))
res = true;
return callback(null, self);
return callback(null, res);
});
};
@ -822,11 +818,25 @@ Wallet.prototype.getBalance = function getBalance(callback) {
return this.provider.getBalance(callback);
};
Wallet.prototype.getLast = function getLast(callback) {
Wallet.prototype.getLastTime = function getLastTime(callback) {
if (!this.provider)
return callback(new Error('No wallet provider available.'));
return this.provider.getLast(callback);
return this.provider.getLastTime(callback);
};
Wallet.prototype.getLast = function getLast(limit, callback) {
if (!this.provider)
return callback(new Error('No wallet provider available.'));
return this.provider.getLast(limit, callback);
};
Wallet.prototype.getTimeRange = function getTimeRange(options, callback) {
if (!this.provider)
return callback(new Error('No wallet provider available.'));
return this.provider.getTimeRange(options, callback);
};
Wallet.prototype.getPrivateKey = function getPrivateKey(enc) {
@ -957,9 +967,6 @@ Wallet.prototype.toJSON = function toJSON() {
receiveDepth: this.receiveDepth,
changeDepth: this.changeDepth,
master: this.master.toJSON(this.options.passphrase),
// Store account key to:
// a. save ourselves derivations.
// b. allow deriving of addresses without decrypting the master key.
accountKey: this.accountKey.xpubkey,
addressMap: this.addressMap,
keys: this.keys.map(function(key) {
@ -993,10 +1000,15 @@ Wallet._fromJSON = function _fromJSON(json, passphrase) {
};
};
Wallet.fromJSON = function fromJSON(json, passphrase) {
return new Wallet(Wallet._fromJSON(json, passphrase));
};
// For updating the address table quickly
// without decrypting the master key.
Wallet.sync = function sync(json, options) {
Wallet._syncDepth = function sync(json, options) {
var master, wallet;
var res = false;
assert.equal(json.v, 3);
assert.equal(json.name, 'wallet');
@ -1008,17 +1020,26 @@ Wallet.sync = function sync(json, options) {
json.master = json.accountKey;
wallet = new Wallet(json);
if (options.txs != null) {
options.txs.forEach(function(tx) {
wallet.syncOutputDepth(tx);
});
if (!wallet._initialized)
return;
if (options.tx != null) {
if (wallet.syncOutputDepth(options.tx))
res = true;
}
if (options.receiveDepth != null)
wallet.setReceiveDepth(options.receiveDepth);
if (options.receiveDepth != null) {
if (wallet.setReceiveDepth(options.receiveDepth))
res = true;
}
if (options.changeDepth != null)
wallet.setChangeDepth(options.changeDepth);
if (options.changeDepth != null) {
if (wallet.setChangeDepth(options.changeDepth))
res = true;
}
if (!res)
return;
wallet = wallet.toJSON();
wallet.master = master;
@ -1026,8 +1047,27 @@ Wallet.sync = function sync(json, options) {
return wallet;
};
Wallet.fromJSON = function fromJSON(json, passphrase) {
return new Wallet(Wallet._fromJSON(json, passphrase));
Wallet.syncOutputDepth = function syncOutputDepth(json, tx) {
return Wallet._syncDepth(json, { tx: tx });
};
Wallet.setReceiveDepth = function setReceiveDepth(json, receiveDepth) {
return Wallet._syncDepth(json, {
receiveDepth: receiveDepth || 0
});
};
Wallet.setChangeDepth = function setChangeDepth(json, changeDepth) {
return Wallet._syncDepth(json, {
changeDepth: changeDepth || 0
});
};
Wallet.setDepth = function setDepth(json, receiveDepth, changeDepth) {
return Wallet._syncDepth(json, {
receiveDepth: receiveDepth || 0,
changeDepth: changeDepth || 0
});
};
/**

View File

@ -166,22 +166,7 @@ WalletDB.prototype._init = function _init() {
return;
utils.forEachSerial(map.output, function(id, next) {
self.getJSON(id, function(err, json) {
if (err) {
self.emit('error', err);
return next();
}
// Allocate new addresses if necessary.
json = bcoin.wallet.sync(json, { txs: [tx] });
self.saveJSON(id, json, function(err) {
if (err)
return next(err);
next();
});
});
self.syncOutputDepth(id, tx, next);
}, function(err) {
if (err)
self.emit('error', err);
@ -189,29 +174,53 @@ WalletDB.prototype._init = function _init() {
});
};
WalletDB.prototype.syncDepth = function syncDepth(id, changeDepth, receiveDepth, callback) {
WalletDB.prototype.syncOutputDepth = function syncOutputDepth(id, tx, callback) {
var self = this;
callback = utils.ensure(callback);
if (!receiveDepth)
receiveDepth = 0;
if (!changeDepth)
changeDepth = 0;
self.getJSON(id, function(err, json) {
this.getJSON(id, function(err, json) {
if (err)
return callback(err);
// Allocate new addresses if necessary.
json = bcoin.wallet.sync(json, {
receiveDepth: receiveDepth,
changeDepth: changeDepth
});
json = bcoin.wallet.syncOutputDepth(json, tx);
if (!json)
return callback();
self.saveJSON(id, json, function(err) {
if (err)
return callback(err);
self.emit('sync depth', id, receiveDepth, changeDepth);
self.emit('sync output depth', id, tx);
callback();
});
});
};
WalletDB.prototype.setDepth = function setDepth(id, receive, change, callback) {
var self = this;
callback = utils.ensure(callback);
this.getJSON(id, function(err, json) {
if (err)
return callback(err);
// Allocate new addresses if necessary.
json = bcoin.wallet.setDepth(json, receive, change);
if (!json)
return callback();
self.saveJSON(id, json, function(err) {
if (err)
return callback(err);
self.emit('set depth', id, receive, change);
callback();
});
});
@ -477,6 +486,7 @@ WalletDB.prototype.update = function update(wallet, address) {
if (err)
self.emit('error', err);
// XXX might have to encrypt key - slow
self._saveDB(wallet.id, wallet.toJSON(), function(err) {
if (err)
self.emit('error', err);
@ -520,10 +530,22 @@ WalletDB.prototype.getBalance = function getBalance(id, callback) {
return this.tx.getBalanceByAddress(id, callback);
};
WalletDB.prototype.getLast = function getLast(id, callback) {
WalletDB.prototype.getLastTime = function getLastTime(id, callback) {
var self = this;
id = id.id || id;
return this.tx.getLast(id, callback);
return this.tx.getLastTime(id, callback);
};
WalletDB.prototype.getLast = function getLast(id, limit, callback) {
var self = this;
id = id.id || id;
return this.tx.getLast(id, limit, callback);
};
WalletDB.prototype.getTimeRange = function getTimeRange(id, options, callback) {
var self = this;
id = id.id || id;
return this.tx.getTimeRange(id, options, callback);
};
WalletDB.prototype.fillTX = function fillTX(tx, callback) {
@ -650,8 +672,16 @@ Provider.prototype.getBalance = function getBalance(callback) {
return this.db.getBalance(this.id, callback);
};
Provider.prototype.getLast = function getLast(callback) {
return this.db.getLast(this.id, callback);
Provider.prototype.getLastTime = function getLastTime(callback) {
return this.db.getLastTime(this.id, callback);
};
Provider.prototype.getLast = function getLast(limit, callback) {
return this.db.getLast(this.id, limit, callback);
};
Provider.prototype.getTimeRange = function getTimeRange(options, callback) {
return this.db.getTimeRange(this.id, options, callback);
};
Provider.prototype.getTX = function getTX(hash, callback) {