refactor. allow tx querying by timestamp.
This commit is contained in:
parent
e962686e93
commit
f67902a9cf
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user