refactor database layout.
This commit is contained in:
parent
0f4a591665
commit
703c9aec3c
21
lib/bcoin.js
21
lib/bcoin.js
@ -8,13 +8,30 @@
|
||||
*/
|
||||
|
||||
var Environment = require('./bcoin/env');
|
||||
var networks = {};
|
||||
|
||||
/**
|
||||
* Create a new Environment.
|
||||
* Create a new Environment. Note that this will
|
||||
* be cached by network. Calling `bcoin('main')`
|
||||
* twice will return the same environment.
|
||||
* @param {Object} options - See {@link Environment}.
|
||||
* @returns {Environment}
|
||||
*/
|
||||
|
||||
module.exports = function BCoin(options) {
|
||||
return new Environment(options);
|
||||
var network = 'main';
|
||||
|
||||
if (options) {
|
||||
if (options.network)
|
||||
network = options.network;
|
||||
else if (typeof options === 'string')
|
||||
network = options;
|
||||
}
|
||||
|
||||
if (!networks[network])
|
||||
networks[network] = new Environment(options);
|
||||
|
||||
return networks[network];
|
||||
};
|
||||
|
||||
module.exports.env = Environment;
|
||||
|
||||
@ -7,6 +7,21 @@
|
||||
|
||||
module.exports = function(bcoin) {
|
||||
|
||||
/*
|
||||
* Database Layout:
|
||||
* R -> tip hash
|
||||
* e/[hash] -> entry
|
||||
* h/[hash] -> height
|
||||
* H/[height] -> hash
|
||||
* n/[hash] -> next hash
|
||||
* b/[hash] -> block
|
||||
* t/[hash] -> extended tx
|
||||
* c/[hash]/[index] -> coin
|
||||
* u/[hash] -> undo coins
|
||||
* T/[address]/[hash] -> dummy (tx by address)
|
||||
* C/[address]/[hash] -> dummy (coin by address)
|
||||
*/
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var network = bcoin.protocol.network;
|
||||
var utils = require('./utils');
|
||||
@ -110,7 +125,7 @@ ChainDB.prototype._init = function _init() {
|
||||
self.emit('open');
|
||||
}
|
||||
|
||||
self.db.get('c/b/' + network.genesis.hash, function(err, exists) {
|
||||
self.db.get('h/' + network.genesis.hash, function(err, exists) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return self.emit('error', err);
|
||||
|
||||
@ -225,7 +240,7 @@ ChainDB.prototype.getHeight = function getHeight(hash, callback) {
|
||||
if (this.cacheHash.has(hash))
|
||||
return callback(null, this.cacheHash.get(hash).height);
|
||||
|
||||
this.db.get('c/b/' + hash, function(err, height) {
|
||||
this.db.get('h/' + hash, function(err, height) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
@ -255,7 +270,7 @@ ChainDB.prototype.getHash = function getHash(height, callback) {
|
||||
if (this.cacheHeight.has(height))
|
||||
return callback(null, this.cacheHeight.get(height).hash);
|
||||
|
||||
this.db.get('c/h/' + pad32(height), function(err, hash) {
|
||||
this.db.get('H/' + pad32(height), function(err, hash) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
@ -389,7 +404,7 @@ ChainDB.prototype._getEntry = function _getEntry(hash, callback) {
|
||||
if (self.cacheHash.has(hash))
|
||||
return callback(null, self.cacheHash.get(hash));
|
||||
|
||||
return self.db.get('c/c/' + hash, function(err, data) {
|
||||
return self.db.get('e/' + hash, function(err, data) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
@ -455,8 +470,8 @@ ChainDB.prototype.save = function save(entry, block, connect, callback) {
|
||||
height = new Buffer(4);
|
||||
utils.writeU32(height, entry.height, 0);
|
||||
|
||||
batch.put('c/b/' + entry.hash, height);
|
||||
batch.put('c/c/' + entry.hash, entry.toRaw());
|
||||
batch.put('h/' + entry.hash, height);
|
||||
batch.put('e/' + entry.hash, entry.toRaw());
|
||||
|
||||
this.cacheHash.set(entry.hash, entry);
|
||||
|
||||
@ -470,9 +485,9 @@ ChainDB.prototype.save = function save(entry, block, connect, callback) {
|
||||
|
||||
this.cacheHeight.set(entry.height, entry);
|
||||
|
||||
batch.put('c/n/' + entry.prevBlock, hash);
|
||||
batch.put('c/h/' + pad32(entry.height), hash);
|
||||
batch.put('c/t', hash);
|
||||
batch.put('n/' + entry.prevBlock, hash);
|
||||
batch.put('H/' + pad32(entry.height), hash);
|
||||
batch.put('R', hash);
|
||||
|
||||
this.emit('add entry', entry);
|
||||
|
||||
@ -490,7 +505,7 @@ ChainDB.prototype.save = function save(entry, block, connect, callback) {
|
||||
|
||||
ChainDB.prototype.getTip = function getTip(callback) {
|
||||
var self = this;
|
||||
return this.db.get('c/t', function(err, hash) {
|
||||
return this.db.get('R', function(err, hash) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
@ -512,9 +527,9 @@ ChainDB.prototype.connect = function connect(entry, block, callback) {
|
||||
var batch = this.db.batch();
|
||||
var hash = new Buffer(entry.hash, 'hex');
|
||||
|
||||
batch.put('c/n/' + entry.prevBlock, hash);
|
||||
batch.put('c/h/' + pad32(entry.height), hash);
|
||||
batch.put('c/t', hash);
|
||||
batch.put('n/' + entry.prevBlock, hash);
|
||||
batch.put('H/' + pad32(entry.height), hash);
|
||||
batch.put('R', hash);
|
||||
|
||||
this.cacheHash.set(entry.hash, entry);
|
||||
this.cacheHeight.set(entry.height, entry);
|
||||
@ -552,9 +567,9 @@ ChainDB.prototype.disconnect = function disconnect(block, callback) {
|
||||
|
||||
batch = self.db.batch();
|
||||
|
||||
batch.del('c/n/' + entry.prevBlock);
|
||||
batch.del('c/h/' + pad32(entry.height));
|
||||
batch.put('c/t', new Buffer(entry.prevBlock, 'hex'));
|
||||
batch.del('n/' + entry.prevBlock);
|
||||
batch.del('H/' + pad32(entry.height));
|
||||
batch.put('R', new Buffer(entry.prevBlock, 'hex'));
|
||||
|
||||
self.cacheHeight.remove(entry.height);
|
||||
|
||||
@ -586,7 +601,7 @@ ChainDB.prototype._ensureEntry = function _ensureEntry(block, callback) {
|
||||
*/
|
||||
|
||||
ChainDB.prototype.getNextHash = function getNextHash(hash, callback) {
|
||||
return this.db.get('c/n/' + hash, function(err, nextHash) {
|
||||
return this.db.get('n/' + hash, function(err, nextHash) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
@ -665,14 +680,14 @@ ChainDB.prototype.reset = function reset(block, callback) {
|
||||
batch = self.db.batch();
|
||||
|
||||
if (tip.hash === entry.hash) {
|
||||
batch.put('c/t', new Buffer(tip.hash, 'hex'));
|
||||
batch.put('R', new Buffer(tip.hash, 'hex'));
|
||||
return batch.write(callback);
|
||||
}
|
||||
|
||||
batch.del('c/h/' + pad32(tip.height));
|
||||
batch.del('c/b/' + tip.hash);
|
||||
batch.del('c/c/' + tip.hash);
|
||||
batch.del('c/n/' + tip.prevBlock);
|
||||
batch.del('H/' + pad32(tip.height));
|
||||
batch.del('h/' + tip.hash);
|
||||
batch.del('e/' + tip.hash);
|
||||
batch.del('n/' + tip.prevBlock);
|
||||
|
||||
self.emit('remove entry', tip);
|
||||
|
||||
@ -725,7 +740,7 @@ ChainDB.prototype.saveBlock = function saveBlock(block, batch, connect, callback
|
||||
if (this.options.spv)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
batch.put('b/b/' + block.hash('hex'), block.render());
|
||||
batch.put('b/' + block.hash('hex'), block.render());
|
||||
|
||||
if (!connect)
|
||||
return utils.nextTick(callback);
|
||||
@ -755,7 +770,7 @@ ChainDB.prototype.removeBlock = function removeBlock(hash, batch, callback) {
|
||||
if (!block)
|
||||
return callback();
|
||||
|
||||
batch.del('b/b/' + block.hash('hex'));
|
||||
batch.del('b/' + block.hash('hex'));
|
||||
|
||||
self.disconnectBlock(block, batch, callback);
|
||||
});
|
||||
@ -794,12 +809,12 @@ ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) {
|
||||
hash = tx.hash('hex');
|
||||
|
||||
if (self.options.indexTX) {
|
||||
batch.put('t/t/' + hash, tx.toExtended());
|
||||
batch.put('t/' + hash, tx.toExtended());
|
||||
if (self.options.indexAddress) {
|
||||
addresses = tx.getAddresses();
|
||||
for (j = 0; j < addresses.length; j++) {
|
||||
address = addresses[j];
|
||||
batch.put('t/a/' + address + '/' + hash, DUMMY);
|
||||
batch.put('T/' + address + '/' + hash, DUMMY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -816,10 +831,10 @@ ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) {
|
||||
if (self.options.indexAddress) {
|
||||
address = input.getAddress();
|
||||
if (address)
|
||||
batch.del('u/a/' + address + '/' + key);
|
||||
batch.del('C/' + address + '/' + key);
|
||||
}
|
||||
|
||||
batch.del('u/t/' + key);
|
||||
batch.del('c/' + key);
|
||||
|
||||
Framer.coin(input.coin, false, undo);
|
||||
|
||||
@ -834,17 +849,17 @@ ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) {
|
||||
if (self.options.indexAddress) {
|
||||
address = output.getAddress();
|
||||
if (address)
|
||||
batch.put('u/a/' + address + '/' + key, DUMMY);
|
||||
batch.put('C/' + address + '/' + key, DUMMY);
|
||||
}
|
||||
|
||||
batch.put('u/t/' + key, coin.toRaw());
|
||||
batch.put('c/' + key, coin.toRaw());
|
||||
|
||||
self.coinCache.set(key, coin);
|
||||
}
|
||||
}
|
||||
|
||||
if (undo.written > 0)
|
||||
batch.put('b/u/' + block.hash('hex'), undo.render());
|
||||
batch.put('u/' + block.hash('hex'), undo.render());
|
||||
|
||||
self.emit('add block', block);
|
||||
|
||||
@ -882,12 +897,12 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callb
|
||||
hash = tx.hash('hex');
|
||||
|
||||
if (self.options.indexTX) {
|
||||
batch.del('t/t/' + hash);
|
||||
batch.del('t/' + hash);
|
||||
if (self.options.indexAddress) {
|
||||
addresses = tx.getAddresses();
|
||||
for (j = 0; j < addresses.length; j++) {
|
||||
address = addresses[j];
|
||||
batch.del('t/a/' + address + '/' + hash);
|
||||
batch.del('T/' + address + '/' + hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -904,10 +919,10 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callb
|
||||
if (self.options.indexAddress) {
|
||||
address = input.getAddress();
|
||||
if (address)
|
||||
batch.put('u/a/' + address + '/' + key, DUMMY);
|
||||
batch.put('C/' + address + '/' + key, DUMMY);
|
||||
}
|
||||
|
||||
batch.put('u/t/' + key, input.coin.toRaw());
|
||||
batch.put('c/' + key, input.coin.toRaw());
|
||||
|
||||
self.coinCache.set(key, input.coin);
|
||||
}
|
||||
@ -919,16 +934,16 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callb
|
||||
if (self.options.indexAddress) {
|
||||
address = output.getAddress();
|
||||
if (address)
|
||||
batch.del('u/a/' + address + '/' + key);
|
||||
batch.del('C/' + address + '/' + key);
|
||||
}
|
||||
|
||||
batch.del('u/t/' + key);
|
||||
batch.del('c/' + key);
|
||||
|
||||
self.coinCache.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
batch.del('b/u/' + block.hash('hex'));
|
||||
batch.del('u/' + block.hash('hex'));
|
||||
|
||||
self.emit('remove block', block);
|
||||
|
||||
@ -1038,8 +1053,8 @@ ChainDB.prototype.getCoinsByAddress = function getCoinsByAddress(addresses, call
|
||||
|
||||
utils.forEachSerial(addresses, function(address, done) {
|
||||
var iter = self.db.iterator({
|
||||
gte: 'u/a/' + address + '/',
|
||||
lte: 'u/a/' + address + '/~',
|
||||
gte: 'C/' + address + '/',
|
||||
lte: 'C/' + address + '/~',
|
||||
keys: true,
|
||||
values: true,
|
||||
fillCache: true,
|
||||
@ -1061,8 +1076,8 @@ ChainDB.prototype.getCoinsByAddress = function getCoinsByAddress(addresses, call
|
||||
return iter.end(done);
|
||||
|
||||
parts = key.split('/');
|
||||
hash = parts[3];
|
||||
index = +parts[4];
|
||||
hash = parts[2];
|
||||
index = +parts[3];
|
||||
|
||||
ids.push([hash, index]);
|
||||
|
||||
@ -1110,7 +1125,7 @@ ChainDB.prototype.getCoin = function getCoin(hash, index, callback) {
|
||||
if (coin)
|
||||
return utils.asyncify(callback)(null, coin);
|
||||
|
||||
this.db.get('u/t/' + key, function(err, data) {
|
||||
this.db.get('c/' + key, function(err, data) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
@ -1150,8 +1165,8 @@ ChainDB.prototype.getTXByAddress = function getTXByAddress(addresses, callback)
|
||||
|
||||
utils.forEachSerial(addresses, function(address, done) {
|
||||
var iter = self.db.iterator({
|
||||
gte: 't/a/' + address + '/',
|
||||
lte: 't/a/' + address + '/~',
|
||||
gte: 'T/' + address + '/',
|
||||
lte: 'T/' + address + '/~',
|
||||
keys: true,
|
||||
values: true,
|
||||
fillCache: true,
|
||||
@ -1172,7 +1187,7 @@ ChainDB.prototype.getTXByAddress = function getTXByAddress(addresses, callback)
|
||||
if (key === undefined)
|
||||
return iter.end(done);
|
||||
|
||||
hash = key.split('/')[3];
|
||||
hash = key.split('/')[2];
|
||||
|
||||
if (addresses.length > 1) {
|
||||
if (have[hash])
|
||||
@ -1217,7 +1232,7 @@ ChainDB.prototype.getTXByAddress = function getTXByAddress(addresses, callback)
|
||||
|
||||
ChainDB.prototype.getTX = function getTX(hash, callback) {
|
||||
var self = this;
|
||||
var key = 't/t/' + hash;
|
||||
var key = 't/' + hash;
|
||||
var tx;
|
||||
|
||||
this.db.get(key, function(err, data) {
|
||||
@ -1419,7 +1434,7 @@ ChainDB.prototype.fillHistoryBlock = function fillHistoryBlock(block, callback)
|
||||
ChainDB.prototype.getUndoCoins = function getUndoCoins(hash, callback) {
|
||||
var coins, p, coin, i, tx;
|
||||
|
||||
return this.db.get('b/u/' + hash, function(err, data) {
|
||||
return this.db.get('u/' + hash, function(err, data) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
@ -1493,7 +1508,7 @@ ChainDB.prototype.getBlock = function getBlock(hash, callback) {
|
||||
if (!hash)
|
||||
return callback();
|
||||
|
||||
key = 'b/b/' + hash;
|
||||
key = 'b/' + hash;
|
||||
|
||||
self.db.get(key, function(err, data) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
@ -1547,8 +1562,8 @@ ChainDB.prototype.isSpentTX = function isSpentTX(hash, callback) {
|
||||
hash = hash.hash('hex');
|
||||
|
||||
iter = this.db.iterator({
|
||||
gte: 'u/t/' + hash,
|
||||
lte: 'u/t/' + hash + '~',
|
||||
gte: 'c/' + hash,
|
||||
lte: 'c/' + hash + '~',
|
||||
keys: true,
|
||||
values: false,
|
||||
fillCache: false,
|
||||
@ -1598,8 +1613,8 @@ ChainDB.prototype._pruneBlock = function _pruneBlock(block, batch, callback) {
|
||||
hash = hash.toString('hex');
|
||||
|
||||
batch.del(key);
|
||||
batch.del('b/b/' + hash);
|
||||
batch.del('b/u/' + hash);
|
||||
batch.del('b/' + hash);
|
||||
batch.del('u/' + hash);
|
||||
|
||||
return callback();
|
||||
});
|
||||
|
||||
@ -7,6 +7,13 @@
|
||||
|
||||
module.exports = function(bcoin) {
|
||||
|
||||
/*
|
||||
* Database Layout:
|
||||
* (inherits all from txdb)
|
||||
* o/[hash] -> orphan/waiting hashes
|
||||
* O/[hash] -> orphan tx
|
||||
*/
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var bn = require('bn.js');
|
||||
var constants = bcoin.protocol.constants;
|
||||
@ -127,7 +134,7 @@ Mempool.prototype._init = function _init() {
|
||||
self.db = bcoin.ldb(options);
|
||||
|
||||
// Use the txdb object for its get methods.
|
||||
self.tx = new bcoin.txdb('m', self.db);
|
||||
self.tx = new bcoin.txdb(self.db);
|
||||
|
||||
self.db.open(function(err) {
|
||||
if (err) {
|
||||
@ -322,7 +329,7 @@ Mempool.prototype.purgeOrphans = function purgeOrphans(callback) {
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
utils.forEachSerial(['m/D', 'm/d'], function(type, callback) {
|
||||
utils.forEachSerial(['O', 'o'], function(type, callback) {
|
||||
var iter = self.db.iterator({
|
||||
gte: type,
|
||||
lte: type + '~',
|
||||
@ -871,7 +878,7 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx, callback, force) {
|
||||
|
||||
p.writeHash(hash);
|
||||
|
||||
batch.put('m/d/' + prev, p.render());
|
||||
batch.put('o/' + prev, p.render());
|
||||
|
||||
next();
|
||||
});
|
||||
@ -881,7 +888,7 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx, callback, force) {
|
||||
|
||||
self.orphans++;
|
||||
|
||||
batch.put('m/D/' + hash, tx.toExtended(true));
|
||||
batch.put('O/' + hash, tx.toExtended(true));
|
||||
|
||||
if (self.orphans > constants.mempool.MAX_ORPHAN_TX) {
|
||||
return self.purgeOrphans(function(err) {
|
||||
@ -924,7 +931,7 @@ Mempool.prototype.getWaiting = function getWaiting(hash, callback) {
|
||||
var hashes = [];
|
||||
var p;
|
||||
|
||||
this.db.get('m/d/' + hash, function(err, buf) {
|
||||
this.db.get('o/' + hash, function(err, buf) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
@ -953,7 +960,7 @@ Mempool.prototype.getWaiting = function getWaiting(hash, callback) {
|
||||
Mempool.prototype.getOrphan = function getOrphan(orphanHash, callback) {
|
||||
var self = this;
|
||||
|
||||
this.db.get('m/D/' + orphanHash, function(err, orphan) {
|
||||
this.db.get('O/' + orphanHash, function(err, orphan) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return next(err);
|
||||
|
||||
@ -1015,12 +1022,12 @@ Mempool.prototype.resolveOrphans = function resolveOrphans(tx, callback, force)
|
||||
|
||||
if (orphan.hasCoins()) {
|
||||
self.orphans--;
|
||||
batch.del('m/D/' + orphanHash);
|
||||
batch.del('O/' + orphanHash);
|
||||
resolved.push(orphan);
|
||||
return next();
|
||||
}
|
||||
|
||||
batch.put('m/D/' + orphanHash, orphan.toExtended(true));
|
||||
batch.put('O/' + orphanHash, orphan.toExtended(true));
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
@ -1034,7 +1041,7 @@ Mempool.prototype.resolveOrphans = function resolveOrphans(tx, callback, force)
|
||||
return callback(null, resolved);
|
||||
}
|
||||
|
||||
batch.del('m/d/' + hash);
|
||||
batch.del('o/' + hash);
|
||||
|
||||
return batch.write(done);
|
||||
});
|
||||
@ -1069,7 +1076,7 @@ Mempool.prototype.removeOrphan = function removeOrphan(tx, callback) {
|
||||
hash = tx.hash('hex');
|
||||
prevout = tx.getPrevout();
|
||||
|
||||
batch.del('m/D/' + hash);
|
||||
batch.del('O/' + hash);
|
||||
|
||||
utils.forEach(prevout, function(prev, next) {
|
||||
var i, p;
|
||||
@ -1085,7 +1092,7 @@ Mempool.prototype.removeOrphan = function removeOrphan(tx, callback) {
|
||||
hashes.splice(i, 1);
|
||||
|
||||
if (hashes.length === 0) {
|
||||
batch.del('m/d/' + prev);
|
||||
batch.del('o/' + prev);
|
||||
return next();
|
||||
}
|
||||
|
||||
@ -1094,7 +1101,7 @@ Mempool.prototype.removeOrphan = function removeOrphan(tx, callback) {
|
||||
for (i = 0; i < hashes.length; i++)
|
||||
p.writeHash(hashes[i]);
|
||||
|
||||
batch.put('m/d/' + prev, p.render());
|
||||
batch.put('o/' + prev, p.render());
|
||||
|
||||
next();
|
||||
});
|
||||
@ -1353,7 +1360,6 @@ Mempool.prototype.getConfidence = function getConfidence(hash, callback) {
|
||||
|
||||
Mempool.prototype._addUnchecked = function addUnchecked(tx, callback, force) {
|
||||
var self = this;
|
||||
var prefix = 'm/';
|
||||
var hash = tx.hash('hex');
|
||||
var i, addresses, address, input, output, key, coin, batch;
|
||||
|
||||
@ -1365,13 +1371,13 @@ Mempool.prototype._addUnchecked = function addUnchecked(tx, callback, force) {
|
||||
|
||||
batch = this.db.batch();
|
||||
|
||||
batch.put(prefix + 't/t/' + hash, tx.toExtended());
|
||||
batch.put(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash, DUMMY);
|
||||
batch.put('t/' + hash, tx.toExtended());
|
||||
batch.put('m/' + pad32(tx.ps) + '/' + hash, DUMMY);
|
||||
|
||||
addresses = tx.getAddresses();
|
||||
|
||||
for (i = 0; i < addresses.length; i++)
|
||||
batch.put(prefix + 't/a/' + addresses[i] + '/' + hash, DUMMY);
|
||||
batch.put('T/' + addresses[i] + '/' + hash, DUMMY);
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
@ -1384,11 +1390,11 @@ Mempool.prototype._addUnchecked = function addUnchecked(tx, callback, force) {
|
||||
|
||||
address = input.getAddress();
|
||||
|
||||
batch.del(prefix + 'u/t/' + key);
|
||||
batch.put(prefix + 's/t/' + key, tx.hash());
|
||||
batch.del('c/' + key);
|
||||
batch.put('s/' + key, tx.hash());
|
||||
|
||||
if (address)
|
||||
batch.del(prefix + 'u/a/' + address + '/' + key);
|
||||
batch.del('C/' + address + '/' + key);
|
||||
}
|
||||
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
@ -1397,10 +1403,10 @@ Mempool.prototype._addUnchecked = function addUnchecked(tx, callback, force) {
|
||||
address = output.getAddress();
|
||||
coin = bcoin.coin(tx, i).toRaw();
|
||||
|
||||
batch.put(prefix + 'u/t/' + key, coin);
|
||||
batch.put('c/' + key, coin);
|
||||
|
||||
if (address)
|
||||
batch.put(prefix + 'u/a/' + address + '/' + key, DUMMY);
|
||||
batch.put('C/' + address + '/' + key, DUMMY);
|
||||
}
|
||||
|
||||
return batch.write(callback);
|
||||
@ -1416,7 +1422,6 @@ Mempool.prototype._addUnchecked = function addUnchecked(tx, callback, force) {
|
||||
|
||||
Mempool.prototype._removeUnchecked = function removeUnchecked(hash, callback, force) {
|
||||
var self = this;
|
||||
var prefix = 'm/';
|
||||
var batch, i, addresses, output;
|
||||
|
||||
var unlock = this.writeLock.lock(removeUnchecked, [hash, callback], force);
|
||||
@ -1437,13 +1442,13 @@ Mempool.prototype._removeUnchecked = function removeUnchecked(hash, callback, fo
|
||||
|
||||
batch = self.db.batch();
|
||||
|
||||
batch.del(prefix + 't/t/' + hash);
|
||||
batch.del(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash);
|
||||
batch.del('t/' + hash);
|
||||
batch.del('m/' + pad32(tx.ps) + '/' + hash);
|
||||
|
||||
addresses = tx.getAddresses();
|
||||
|
||||
for (i = 0; i < addresses.length; i++)
|
||||
batch.del(prefix + 't/a/' + addresses[i] + '/' + hash);
|
||||
batch.del('T/' + addresses[i] + '/' + hash);
|
||||
|
||||
utils.forEachSerial(tx.inputs, function(input, next) {
|
||||
var key = input.prevout.hash + '/' + input.prevout.index;
|
||||
@ -1457,20 +1462,20 @@ Mempool.prototype._removeUnchecked = function removeUnchecked(hash, callback, fo
|
||||
|
||||
address = input.getAddress();
|
||||
|
||||
batch.del(prefix + 's/t/' + key);
|
||||
batch.del('s/' + key);
|
||||
|
||||
self.hasTX(input.prevout.hash, function(err, result) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (result) {
|
||||
batch.put(prefix + 'u/t/' + key, input.coin.toRaw());
|
||||
batch.put('c/' + key, input.coin.toRaw());
|
||||
if (address)
|
||||
batch.put(prefix + 'u/a/' + address + '/' + key, DUMMY);
|
||||
batch.put('C/' + address + '/' + key, DUMMY);
|
||||
} else {
|
||||
batch.del(prefix + 'u/t/' + key);
|
||||
batch.del('c/' + key);
|
||||
if (address)
|
||||
batch.del(prefix + 'u/a/' + address + '/' + key);
|
||||
batch.del('C/' + address + '/' + key);
|
||||
}
|
||||
|
||||
next();
|
||||
@ -1484,10 +1489,10 @@ Mempool.prototype._removeUnchecked = function removeUnchecked(hash, callback, fo
|
||||
key = hash + '/' + i;
|
||||
address = output.getAddress();
|
||||
|
||||
batch.del(prefix + 'u/t/' + key);
|
||||
batch.del('c/' + key);
|
||||
|
||||
if (address)
|
||||
batch.del(prefix + 'u/a/' + address + '/' + key);
|
||||
batch.del('C/' + address + '/' + key);
|
||||
}
|
||||
|
||||
return batch.write(callback);
|
||||
|
||||
@ -7,6 +7,22 @@
|
||||
|
||||
module.exports = function(bcoin) {
|
||||
|
||||
/*
|
||||
* Database Layout:
|
||||
* t/[hash] -> extended tx
|
||||
* c/[hash]/[index] -> coin
|
||||
* s/[hash]/[index] -> spent by hash
|
||||
* o/[hash]/[index] -> orphan inputs
|
||||
* 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)
|
||||
*/
|
||||
|
||||
var bn = require('bn.js');
|
||||
var utils = require('./utils');
|
||||
var assert = bcoin.utils.assert;
|
||||
@ -20,8 +36,6 @@ var BufferWriter = require('./writer');
|
||||
* TXDB
|
||||
* @exports TXDB
|
||||
* @constructor
|
||||
* @param {String} prefix - The prefix for
|
||||
* the database (`txdb/` by default).
|
||||
* @param {LowlevelUp} db
|
||||
* @param {Object?} options
|
||||
* @param {Boolean?} options.mapAddress - Map addresses to IDs.
|
||||
@ -29,12 +43,11 @@ var BufferWriter = require('./writer');
|
||||
* @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).
|
||||
* @property {String} prefix
|
||||
*/
|
||||
|
||||
function TXDB(prefix, db, options) {
|
||||
function TXDB(db, options) {
|
||||
if (!(this instanceof TXDB))
|
||||
return new TXDB(prefix, db, options);
|
||||
return new TXDB(db, options);
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
@ -42,7 +55,6 @@ function TXDB(prefix, db, options) {
|
||||
options = {};
|
||||
|
||||
this.db = db;
|
||||
this.prefix = prefix || 'txdb';
|
||||
this.options = options;
|
||||
this.busy = false;
|
||||
this.jobs = [];
|
||||
@ -121,7 +133,6 @@ TXDB.prototype.getMap = function getMap(tx, callback) {
|
||||
|
||||
TXDB.prototype.mapAddresses = function mapAddresses(address, callback) {
|
||||
var self = this;
|
||||
var prefix = this.prefix + '/';
|
||||
var table = {};
|
||||
var iter;
|
||||
|
||||
@ -145,8 +156,8 @@ TXDB.prototype.mapAddresses = function mapAddresses(address, callback) {
|
||||
}
|
||||
|
||||
iter = this.db.iterator({
|
||||
gte: prefix + 'a/' + address,
|
||||
lte: prefix + 'a/' + address + '~',
|
||||
gte: 'W/' + address,
|
||||
lte: 'W/' + address + '~',
|
||||
keys: true,
|
||||
values: false,
|
||||
fillCache: false,
|
||||
@ -173,7 +184,7 @@ TXDB.prototype.mapAddresses = function mapAddresses(address, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
key = key.split('/')[3];
|
||||
key = key.split('/')[2];
|
||||
table[address].push(key);
|
||||
|
||||
next();
|
||||
@ -192,10 +203,9 @@ TXDB.prototype.mapAddresses = function mapAddresses(address, callback) {
|
||||
*/
|
||||
|
||||
TXDB.prototype._addOrphan = function _addOrphan(key, hash, index, callback) {
|
||||
var prefix = this.prefix + '/';
|
||||
var p;
|
||||
|
||||
this.db.get(prefix + 'o/' + key, function(err, buf) {
|
||||
this.db.get('o/' + key, function(err, buf) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
@ -220,10 +230,9 @@ TXDB.prototype._addOrphan = function _addOrphan(key, hash, index, callback) {
|
||||
|
||||
TXDB.prototype._getOrphans = function _getOrphans(key, callback) {
|
||||
var self = this;
|
||||
var prefix = this.prefix + '/';
|
||||
var p, orphans;
|
||||
|
||||
this.db.get(prefix + 'o/' + key, function(err, buf) {
|
||||
this.db.get('o/' + key, function(err, buf) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
@ -297,7 +306,6 @@ TXDB.prototype.add = function add(tx, callback, force) {
|
||||
// receiving txs out of order.
|
||||
TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
var self = this;
|
||||
var prefix = this.prefix + '/';
|
||||
var hash = tx.hash('hex');
|
||||
var updated = false;
|
||||
var batch;
|
||||
@ -324,30 +332,27 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
|
||||
batch = self.db.batch();
|
||||
|
||||
batch.put(prefix + 't/t/' + hash, tx.toExtended());
|
||||
batch.put('t/' + hash, tx.toExtended());
|
||||
|
||||
if (self.options.indexExtra) {
|
||||
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);
|
||||
batch.put('p/' + hash, DUMMY);
|
||||
batch.put('m/' + 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);
|
||||
batch.put('h/' + pad32(tx.height) + '/' + hash, DUMMY);
|
||||
batch.put('m/' + pad32(tx.ts) + '/' + hash, DUMMY);
|
||||
}
|
||||
|
||||
if (self.options.indexAddress) {
|
||||
map.all.forEach(function(id) {
|
||||
batch.put(prefix + 't/a/' + id + '/' + hash, DUMMY);
|
||||
batch.put('T/' + 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);
|
||||
batch.put('P/' + id + '/' + hash, DUMMY);
|
||||
batch.put('M/' + 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);
|
||||
batch.put('H/' + id + '/' + pad32(tx.height) + '/' + hash, DUMMY);
|
||||
batch.put('M/' + id + '/' + pad32(tx.ts) + '/' + hash, DUMMY);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -388,12 +393,12 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
|
||||
if (self.options.indexAddress && address) {
|
||||
map.table[address].forEach(function(id) {
|
||||
batch.del(prefix + 'u/a/' + id + '/' + key);
|
||||
batch.del('C/' + id + '/' + key);
|
||||
});
|
||||
}
|
||||
|
||||
batch.del(prefix + 'u/t/' + key);
|
||||
batch.put(prefix + 's/t/' + key, tx.hash());
|
||||
batch.del('c/' + key);
|
||||
batch.put('s/' + key, tx.hash());
|
||||
|
||||
return next();
|
||||
}
|
||||
@ -442,7 +447,7 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
batch.put(prefix + 'o/' + key, orphans);
|
||||
batch.put('o/' + key, orphans);
|
||||
|
||||
return next();
|
||||
});
|
||||
@ -513,7 +518,7 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
if (!some)
|
||||
orphans = null;
|
||||
|
||||
self.db.del(prefix + 'o/' + key, finish);
|
||||
self.db.del('o/' + key, finish);
|
||||
});
|
||||
|
||||
function finish(err) {
|
||||
@ -523,11 +528,11 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
if (!orphans) {
|
||||
if (self.options.indexAddress && address) {
|
||||
map.table[address].forEach(function(id) {
|
||||
batch.put(prefix + 'u/a/' + id + '/' + key, DUMMY);
|
||||
batch.put('C/' + id + '/' + key, DUMMY);
|
||||
});
|
||||
}
|
||||
|
||||
batch.put(prefix + 'u/t/' + key, coin.toRaw());
|
||||
batch.put('c/' + key, coin.toRaw());
|
||||
updated = true;
|
||||
}
|
||||
|
||||
@ -639,8 +644,7 @@ TXDB.prototype.isDoubleSpend = function isDoubleSpend(tx, callback) {
|
||||
|
||||
TXDB.prototype.isSpent = function isSpent(hash, index, callback) {
|
||||
var self = this;
|
||||
var prefix = this.prefix + '/';
|
||||
var key = prefix + 's/t/' + hash + '/' + index;
|
||||
var key = 's/' + hash + '/' + index;
|
||||
|
||||
return this.db.get(key, function(err, hash) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
@ -665,7 +669,6 @@ TXDB.prototype.isSpent = function isSpent(hash, index, callback) {
|
||||
|
||||
TXDB.prototype._confirm = function _confirm(tx, map, callback, force) {
|
||||
var self = this;
|
||||
var prefix = this.prefix + '/';
|
||||
var hash = tx.hash('hex');
|
||||
var batch;
|
||||
|
||||
@ -698,20 +701,20 @@ TXDB.prototype._confirm = function _confirm(tx, map, callback, force) {
|
||||
assert(tx.height >= 0);
|
||||
assert(existing.ps > 0);
|
||||
|
||||
batch.put(prefix + 't/t/' + hash, tx.toExtended());
|
||||
batch.put('t/' + hash, tx.toExtended());
|
||||
|
||||
if (self.options.indexExtra) {
|
||||
batch.del(prefix + 't/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('p/' + hash);
|
||||
batch.put('h/' + pad32(tx.height) + '/' + hash, DUMMY);
|
||||
batch.del('m/' + pad32(existing.ps) + '/' + hash);
|
||||
batch.put('m/' + pad32(tx.ts) + '/' + hash, DUMMY);
|
||||
|
||||
if (self.options.indexAddress) {
|
||||
map.all.forEach(function(id) {
|
||||
batch.del(prefix + 't/p/a/' + id + '/' + hash);
|
||||
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('P/' + id + '/' + hash);
|
||||
batch.put('H/' + id + '/' + pad32(tx.height) + '/' + hash, DUMMY);
|
||||
batch.del('M/' + id + '/' + pad32(existing.ps) + '/' + hash);
|
||||
batch.put('M/' + id + '/' + pad32(tx.ts) + '/' + hash, DUMMY);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -734,7 +737,7 @@ TXDB.prototype._confirm = function _confirm(tx, map, callback, force) {
|
||||
|
||||
coin.height = tx.height;
|
||||
|
||||
batch.put(prefix + 'u/t/' + hash + '/' + i, coin.toRaw());
|
||||
batch.put('c/' + hash + '/' + i, coin.toRaw());
|
||||
|
||||
next();
|
||||
});
|
||||
@ -836,7 +839,6 @@ TXDB.prototype.lazyRemove = function lazyRemove(tx, callback, force) {
|
||||
|
||||
TXDB.prototype._remove = function remove(tx, map, callback, force) {
|
||||
var self = this;
|
||||
var prefix = this.prefix + '/';
|
||||
var hash = tx.hash('hex');
|
||||
var batch;
|
||||
|
||||
@ -848,26 +850,26 @@ TXDB.prototype._remove = function remove(tx, map, callback, force) {
|
||||
|
||||
batch = this.db.batch();
|
||||
|
||||
batch.del(prefix + 't/t/' + hash);
|
||||
batch.del('t/' + hash);
|
||||
|
||||
if (self.options.indexExtra) {
|
||||
if (tx.ts === 0) {
|
||||
batch.del(prefix + 't/p/t/' + hash);
|
||||
batch.del(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash);
|
||||
batch.del('p/' + hash);
|
||||
batch.del('m/' + 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);
|
||||
batch.del('h/' + pad32(tx.height) + '/' + hash);
|
||||
batch.del('m/' + pad32(tx.ts) + '/' + hash);
|
||||
}
|
||||
|
||||
if (self.options.indexAddress) {
|
||||
map.all.forEach(function(id) {
|
||||
batch.del(prefix + 't/a/' + id + '/' + hash);
|
||||
batch.del('T/' + 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);
|
||||
batch.del('P/' + id + '/' + hash);
|
||||
batch.del('M/' + 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);
|
||||
batch.del('H/' + id + '/' + pad32(tx.height) + '/' + hash);
|
||||
batch.del('M/' + id + '/' + pad32(tx.ts) + '/' + hash);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -894,13 +896,13 @@ TXDB.prototype._remove = function remove(tx, map, callback, force) {
|
||||
|
||||
if (self.options.indexAddress && address) {
|
||||
map.table[address].forEach(function(id) {
|
||||
batch.put(prefix + 'u/a/' + id + '/' + key, DUMMY);
|
||||
batch.put('C/' + id + '/' + key, DUMMY);
|
||||
});
|
||||
}
|
||||
|
||||
batch.put(prefix + 'u/t/' + key, input.coin.toRaw());
|
||||
batch.del(prefix + 's/t/' + key);
|
||||
batch.del(prefix + 'o/' + key);
|
||||
batch.put('c/' + key, input.coin.toRaw());
|
||||
batch.del('s/' + key);
|
||||
batch.del('o/' + key);
|
||||
});
|
||||
|
||||
tx.outputs.forEach(function(output, i) {
|
||||
@ -914,11 +916,11 @@ TXDB.prototype._remove = function remove(tx, map, callback, force) {
|
||||
|
||||
if (self.options.indexAddress && address) {
|
||||
map.table[address].forEach(function(id) {
|
||||
batch.del(prefix + 'u/a/' + id + '/' + key);
|
||||
batch.del('C/' + id + '/' + key);
|
||||
});
|
||||
}
|
||||
|
||||
batch.del(prefix + 'u/t/' + key);
|
||||
batch.del('c/' + key);
|
||||
});
|
||||
|
||||
batch.write(function(err) {
|
||||
@ -984,7 +986,6 @@ TXDB.prototype.unconfirm = function unconfirm(hash, callback, force) {
|
||||
|
||||
TXDB.prototype._unconfirm = function unconfirm(tx, map, callback, force) {
|
||||
var self = this;
|
||||
var prefix = this.prefix + '/';
|
||||
var hash, batch, height, ts;
|
||||
|
||||
var unlock = this._lock(unconfirm, [tx, map, callback], force);
|
||||
@ -1007,20 +1008,20 @@ TXDB.prototype._unconfirm = function unconfirm(tx, map, callback, force) {
|
||||
tx.index = -1;
|
||||
tx.block = null;
|
||||
|
||||
batch.put(prefix + 't/t/' + hash, tx.toExtended());
|
||||
batch.put('t/' + hash, tx.toExtended());
|
||||
|
||||
if (self.options.indexExtra) {
|
||||
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);
|
||||
batch.put('p/' + hash, DUMMY);
|
||||
batch.del('h/' + pad32(height) + '/' + hash);
|
||||
batch.del('m/' + pad32(ts) + '/' + hash);
|
||||
batch.put('m/' + pad32(tx.ps) + '/' + hash, DUMMY);
|
||||
|
||||
if (self.options.indexAddress) {
|
||||
map.all.forEach(function(id) {
|
||||
batch.put(prefix + 't/p/a/' + id + '/' + hash, DUMMY);
|
||||
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('P/' + id + '/' + hash, DUMMY);
|
||||
batch.del('H/' + id + '/' + pad32(height) + '/' + hash);
|
||||
batch.del('M/' + id + '/' + pad32(ts) + '/' + hash);
|
||||
batch.put('M/' + id + '/' + pad32(tx.ps) + '/' + hash, DUMMY);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1035,7 +1036,7 @@ TXDB.prototype._unconfirm = function unconfirm(tx, map, callback, force) {
|
||||
|
||||
coin.height = tx.height;
|
||||
|
||||
batch.put(prefix + 'u/t/' + hash + '/' + i, coin.toRaw());
|
||||
batch.put('c/' + hash + '/' + i, coin.toRaw());
|
||||
|
||||
next();
|
||||
});
|
||||
@ -1062,7 +1063,6 @@ TXDB.prototype._unconfirm = function unconfirm(tx, map, callback, force) {
|
||||
|
||||
TXDB.prototype.getHistoryHashes = function getHistoryHashes(address, callback) {
|
||||
var self = this;
|
||||
var prefix = this.prefix + '/';
|
||||
var txs = [];
|
||||
var iter;
|
||||
|
||||
@ -1094,8 +1094,8 @@ TXDB.prototype.getHistoryHashes = function getHistoryHashes(address, callback) {
|
||||
}
|
||||
|
||||
iter = this.db.iterator({
|
||||
gte: address ? prefix + 't/a/' + address : prefix + 't/t',
|
||||
lte: address ? prefix + 't/a/' + address + '~' : prefix + 't/t~',
|
||||
gte: address ? 'T/' + address : 't',
|
||||
lte: address ? 'T/' + address + '~' : 't~',
|
||||
keys: true,
|
||||
values: false,
|
||||
fillCache: false,
|
||||
@ -1119,9 +1119,9 @@ TXDB.prototype.getHistoryHashes = function getHistoryHashes(address, callback) {
|
||||
}
|
||||
|
||||
if (address)
|
||||
txs.push(key.split('/')[4]);
|
||||
txs.push(key.split('/')[2]);
|
||||
else
|
||||
txs.push(key.split('/')[3]);
|
||||
txs.push(key.split('/')[1]);
|
||||
|
||||
next();
|
||||
});
|
||||
@ -1136,7 +1136,6 @@ TXDB.prototype.getHistoryHashes = function getHistoryHashes(address, callback) {
|
||||
|
||||
TXDB.prototype.getUnconfirmedHashes = function getUnconfirmedHashes(address, callback) {
|
||||
var self = this;
|
||||
var prefix = this.prefix + '/';
|
||||
var txs = [];
|
||||
var iter;
|
||||
|
||||
@ -1169,8 +1168,8 @@ TXDB.prototype.getUnconfirmedHashes = function getUnconfirmedHashes(address, cal
|
||||
}
|
||||
|
||||
iter = this.db.iterator({
|
||||
gte: address ? prefix + 't/p/a/' + address : prefix + 't/p/t',
|
||||
lte: address ? prefix + 't/p/a/' + address + '~' : prefix + 't/p/t~',
|
||||
gte: address ? 'P/' + address : 'p',
|
||||
lte: address ? 'P/' + address + '~' : 'p~',
|
||||
keys: true,
|
||||
values: false,
|
||||
fillCache: false,
|
||||
@ -1194,9 +1193,9 @@ TXDB.prototype.getUnconfirmedHashes = function getUnconfirmedHashes(address, cal
|
||||
}
|
||||
|
||||
if (address)
|
||||
txs.push(key.split('/')[5]);
|
||||
txs.push(key.split('/')[2]);
|
||||
else
|
||||
txs.push(key.split('/')[4]);
|
||||
txs.push(key.split('/')[1]);
|
||||
|
||||
next();
|
||||
});
|
||||
@ -1211,7 +1210,6 @@ TXDB.prototype.getUnconfirmedHashes = function getUnconfirmedHashes(address, cal
|
||||
|
||||
TXDB.prototype.getCoinHashes = function getCoinHashes(address, callback) {
|
||||
var self = this;
|
||||
var prefix = this.prefix + '/';
|
||||
var coins = [];
|
||||
var iter;
|
||||
|
||||
@ -1242,11 +1240,11 @@ TXDB.prototype.getCoinHashes = function getCoinHashes(address, callback) {
|
||||
|
||||
iter = this.db.iterator({
|
||||
gte: address
|
||||
? prefix + 'u/a/' + address
|
||||
: prefix + 'u/t',
|
||||
? 'C/' + address
|
||||
: 'c',
|
||||
lte: address
|
||||
? prefix + 'u/a/' + address + '~'
|
||||
: prefix + 'u/t~',
|
||||
? 'C/' + address + '~'
|
||||
: 'c~',
|
||||
keys: true,
|
||||
values: false,
|
||||
fillCache: false,
|
||||
@ -1272,9 +1270,9 @@ TXDB.prototype.getCoinHashes = function getCoinHashes(address, callback) {
|
||||
key = key.split('/');
|
||||
|
||||
if (address)
|
||||
coins.push([key[4], +key[5]]);
|
||||
coins.push([key[2], +key[3]]);
|
||||
else
|
||||
coins.push([key[3], +key[4]]);
|
||||
coins.push([key[1], +key[2]]);
|
||||
|
||||
next();
|
||||
});
|
||||
@ -1293,7 +1291,6 @@ TXDB.prototype.getCoinHashes = function getCoinHashes(address, callback) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.getHeightRangeHashes = function getHeightRangeHashes(address, options, callback) {
|
||||
var prefix = this.prefix + '/';
|
||||
var txs = [];
|
||||
var iter;
|
||||
|
||||
@ -1310,11 +1307,11 @@ TXDB.prototype.getHeightRangeHashes = function getHeightRangeHashes(address, opt
|
||||
|
||||
iter = this.db.iterator({
|
||||
gte: address
|
||||
? prefix + 't/h/a/' + address + '/' + pad32(options.start) + '/'
|
||||
: prefix + 't/h/h/' + pad32(options.start) + '/',
|
||||
? 'H/' + address + '/' + pad32(options.start) + '/'
|
||||
: 'h/' + pad32(options.start) + '/',
|
||||
lte: address
|
||||
? prefix + 't/h/a/' + address + '/' + pad32(options.end) + '/~'
|
||||
: prefix + 't/h/h/' + pad32(options.end) + '/~',
|
||||
? 'H/' + address + '/' + pad32(options.end) + '/~'
|
||||
: 'h/' + pad32(options.end) + '/~',
|
||||
keys: true,
|
||||
values: false,
|
||||
fillCache: false,
|
||||
@ -1340,9 +1337,9 @@ TXDB.prototype.getHeightRangeHashes = function getHeightRangeHashes(address, opt
|
||||
}
|
||||
|
||||
if (address)
|
||||
txs.push(key.split('/')[6]);
|
||||
txs.push(key.split('/')[3]);
|
||||
else
|
||||
txs.push(key.split('/')[5]);
|
||||
txs.push(key.split('/')[2]);
|
||||
|
||||
next();
|
||||
});
|
||||
@ -1371,7 +1368,6 @@ TXDB.prototype.getHeightHashes = function getHeightHashes(height, callback) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.getRangeHashes = function getRangeHashes(address, options, callback) {
|
||||
var prefix = this.prefix + '/';
|
||||
var txs = [];
|
||||
var iter;
|
||||
|
||||
@ -1384,11 +1380,11 @@ TXDB.prototype.getRangeHashes = function getRangeHashes(address, options, callba
|
||||
|
||||
iter = this.db.iterator({
|
||||
gte: address
|
||||
? prefix + 't/s/a/' + address + '/' + pad32(options.start) + '/'
|
||||
: prefix + 't/s/s/' + pad32(options.start) + '/',
|
||||
? 'M/' + address + '/' + pad32(options.start) + '/'
|
||||
: 'm/' + pad32(options.start) + '/',
|
||||
lte: address
|
||||
? prefix + 't/s/a/' + address + '/' + pad32(options.end) + '/~'
|
||||
: prefix + 't/s/s/' + pad32(options.end) + '/~',
|
||||
? 'M/' + address + '/' + pad32(options.end) + '/~'
|
||||
: 'm/' + pad32(options.end) + '/~',
|
||||
keys: true,
|
||||
values: false,
|
||||
fillCache: false,
|
||||
@ -1414,9 +1410,9 @@ TXDB.prototype.getRangeHashes = function getRangeHashes(address, options, callba
|
||||
}
|
||||
|
||||
if (address)
|
||||
txs.push(key.split('/')[6]);
|
||||
txs.push(key.split('/')[3]);
|
||||
else
|
||||
txs.push(key.split('/')[5]);
|
||||
txs.push(key.split('/')[2]);
|
||||
|
||||
next();
|
||||
});
|
||||
@ -1740,8 +1736,7 @@ TXDB.prototype.fillCoins = function fillCoins(tx, callback) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.getTX = function getTX(hash, callback) {
|
||||
var prefix = this.prefix + '/';
|
||||
var key = prefix + 't/t/' + hash;
|
||||
var key = 't/' + hash;
|
||||
|
||||
this.db.get(key, function(err, tx) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
@ -1783,8 +1778,7 @@ TXDB.prototype.hasTX = function hasTX(hash, callback) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.getCoin = function getCoin(hash, index, callback) {
|
||||
var prefix = this.prefix + '/';
|
||||
var key = prefix + 'u/t/' + hash + '/' + index;
|
||||
var key = 'c/' + hash + '/' + index;
|
||||
|
||||
this.db.get(key, function(err, coin) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
|
||||
@ -7,6 +7,13 @@
|
||||
|
||||
module.exports = function(bcoin) {
|
||||
|
||||
/*
|
||||
* Database Layout:
|
||||
* (inherits all from txdb)
|
||||
* W/[address]/[id] -> dummy (map address to id)
|
||||
* w/[id] -> wallet
|
||||
*/
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
@ -108,7 +115,7 @@ WalletDB.prototype._init = function _init() {
|
||||
self.loaded = true;
|
||||
});
|
||||
|
||||
this.tx = new bcoin.txdb('w', this.db, {
|
||||
this.tx = new bcoin.txdb(this.db, {
|
||||
indexExtra: true,
|
||||
indexAddress: true,
|
||||
mapAddress: true,
|
||||
@ -366,7 +373,7 @@ WalletDB.prototype.saveJSON = function saveJSON(id, json, callback) {
|
||||
if (json) {
|
||||
batch = self.db.batch();
|
||||
Object.keys(json.addressMap).forEach(function(address) {
|
||||
batch.put('w/a/' + address + '/' + json.id, DUMMY);
|
||||
batch.put('W/' + address + '/' + json.id, DUMMY);
|
||||
});
|
||||
return batch.write(function(err) {
|
||||
if (err)
|
||||
@ -402,7 +409,7 @@ WalletDB.prototype.removeJSON = function removeJSON(id, callback) {
|
||||
if (json) {
|
||||
batch = self.db.batch();
|
||||
Object.keys(json.addressMap).forEach(function(address) {
|
||||
batch.del('w/a/' + address + '/' + json.id);
|
||||
batch.del('W/' + address + '/' + json.id);
|
||||
});
|
||||
return batch.write(function(err) {
|
||||
if (err)
|
||||
@ -423,7 +430,7 @@ WalletDB.prototype.removeJSON = function removeJSON(id, callback) {
|
||||
*/
|
||||
|
||||
WalletDB.prototype._getDB = function _getDB(id, callback) {
|
||||
var key = 'w/w/' + id;
|
||||
var key = 'w/' + id;
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
@ -453,7 +460,7 @@ WalletDB.prototype._getDB = function _getDB(id, callback) {
|
||||
*/
|
||||
|
||||
WalletDB.prototype._saveDB = function _saveDB(id, json, callback) {
|
||||
var key = 'w/w/' + id;
|
||||
var key = 'w/' + id;
|
||||
var data;
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
@ -477,7 +484,7 @@ WalletDB.prototype._saveDB = function _saveDB(id, json, callback) {
|
||||
|
||||
WalletDB.prototype._removeDB = function _removeDB(id, callback) {
|
||||
var self = this;
|
||||
var key = 'w/w/' + id;
|
||||
var key = 'w/' + id;
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
@ -691,18 +698,18 @@ WalletDB.prototype.update = function update(wallet, address) {
|
||||
batch = this.db.batch();
|
||||
|
||||
batch.put(
|
||||
'w/a/' + address.getKeyAddress() + '/' + wallet.id,
|
||||
'W/' + address.getKeyAddress() + '/' + wallet.id,
|
||||
DUMMY);
|
||||
|
||||
if (address.type === 'multisig') {
|
||||
batch.put(
|
||||
'w/a/' + address.getScriptAddress() + '/' + wallet.id,
|
||||
'W/' + address.getScriptAddress() + '/' + wallet.id,
|
||||
DUMMY);
|
||||
}
|
||||
|
||||
if (address.witness) {
|
||||
batch.put(
|
||||
'w/a/' + address.getProgramAddress() + '/' + wallet.id,
|
||||
'W/' + address.getProgramAddress() + '/' + wallet.id,
|
||||
DUMMY);
|
||||
}
|
||||
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
var bn = require('bn.js');
|
||||
var bcoin = require('../')({ db: 'memory' });
|
||||
var bcoin = require('../')();
|
||||
var constants = bcoin.protocol.constants;
|
||||
var utils = bcoin.utils;
|
||||
var assert = require('assert');
|
||||
var opcodes = constants.opcodes;
|
||||
|
||||
describe('Mempool', function() {
|
||||
var chain = new bcoin.chain();
|
||||
var mempool = new bcoin.mempool({ chain: chain });
|
||||
var chain = new bcoin.chain({ db: 'memory' });
|
||||
var mempool = new bcoin.mempool({ chain: chain, db: 'memory' });
|
||||
mempool.on('error', function() {});
|
||||
|
||||
it('should open mempool', function(cb) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
var bn = require('bn.js');
|
||||
var bcoin = require('../')({ db: process.env.BCOIN_TEST_DB || 'memory' });
|
||||
var bcoin = require('../').env();
|
||||
var constants = bcoin.protocol.constants;
|
||||
var utils = bcoin.utils;
|
||||
var assert = require('assert');
|
||||
@ -24,7 +24,7 @@ var dummyInput = {
|
||||
};
|
||||
|
||||
describe('Wallet', function() {
|
||||
var wdb = new bcoin.walletdb({ verify: true });
|
||||
var wdb = new bcoin.walletdb({ db: 'memory', verify: true });
|
||||
|
||||
it('should open walletdb', function(cb) {
|
||||
wdb.open(cb);
|
||||
@ -554,12 +554,4 @@ describe('Wallet', function() {
|
||||
it('should verify 2-of-3 witnessscripthash tx with bullshit nesting', function(cb) {
|
||||
multisig(true, true, cb);
|
||||
});
|
||||
|
||||
it('should have gratuitous dump', function(cb) {
|
||||
bcoin.walletdb().dump(function(err, records) {
|
||||
assert.ifError(err);
|
||||
// console.log(records);
|
||||
setTimeout(cb, 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user