serialization. refactor.
This commit is contained in:
parent
663e62639d
commit
ab9301ce7e
@ -860,7 +860,9 @@ HDPrivateKey.generate = function generate(options, network) {
|
||||
*/
|
||||
|
||||
HDPrivateKey.parseBase58 = function parseBase58(xkey) {
|
||||
return HDPrivateKey.parseRaw(utils.fromBase58(xkey));
|
||||
var data = HDPrivateKey.parseRaw(utils.fromBase58(xkey));
|
||||
data.xprivkey = xkey;
|
||||
return data;
|
||||
};
|
||||
|
||||
HDPrivateKey.parseRaw = function parseRaw(raw) {
|
||||
@ -887,7 +889,6 @@ HDPrivateKey.parseRaw = function parseRaw(raw) {
|
||||
assert(i < network.types.length, 'Network not found.');
|
||||
|
||||
data.network = type;
|
||||
data.xprivkey = xkey;
|
||||
|
||||
return data;
|
||||
};
|
||||
@ -935,6 +936,10 @@ HDPrivateKey.fromBase58 = function fromBase58(xkey) {
|
||||
return new HDPrivateKey(HDPrivateKey.parseBase58(xkey));
|
||||
};
|
||||
|
||||
HDPrivateKey.fromRaw = function fromRaw(raw) {
|
||||
return new HDPrivateKey(HDPrivateKey.parseRaw(raw));
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert key to a more json-friendly object.
|
||||
* @param {String?} passphrase - Address passphrase
|
||||
@ -979,8 +984,8 @@ HDPrivateKey.prototype.toJSON = function toJSON(passphrase) {
|
||||
HDPrivateKey.parseJSON = function parseJSON(json, passphrase) {
|
||||
var data = {};
|
||||
|
||||
assert.equal(json.v, 1);
|
||||
assert.equal(json.name, 'hdkey');
|
||||
// assert.equal(json.v, 1);
|
||||
// assert.equal(json.name, 'hdkey');
|
||||
|
||||
if (json.encrypted && !passphrase)
|
||||
throw new Error('Cannot decrypt address');
|
||||
@ -1319,7 +1324,9 @@ HDPublicKey.isExtended = function isExtended(data) {
|
||||
*/
|
||||
|
||||
HDPublicKey.parseBase58 = function parseBase58(xkey) {
|
||||
return HDPublicKey.parseRaw(utils.fromBase58(xkey));
|
||||
var data = HDPublicKey.parseRaw(utils.fromBase58(xkey));
|
||||
data.xpubkey = xkey;
|
||||
return data;
|
||||
};
|
||||
|
||||
HDPublicKey.parseRaw = function parseRaw(raw) {
|
||||
@ -1345,7 +1352,6 @@ HDPublicKey.parseRaw = function parseRaw(raw) {
|
||||
assert(i < network.types.length, 'Network not found.');
|
||||
|
||||
data.network = type;
|
||||
data.xpubkey = xkey;
|
||||
|
||||
return data;
|
||||
};
|
||||
@ -1392,6 +1398,10 @@ HDPublicKey.fromBase58 = function fromBase58(xkey) {
|
||||
return new HDPublicKey(HDPublicKey.parseBase58(xkey));
|
||||
};
|
||||
|
||||
HDPublicKey.fromRaw = function fromRaw(data) {
|
||||
return new HDPublicKey(HDPublicKey.parseRaw(data));
|
||||
};
|
||||
|
||||
/**
|
||||
* Test an object to see if it is a HDPublicKey.
|
||||
* @param {Object} obj
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
var bcoin = require('./env');
|
||||
var utils = bcoin.utils;
|
||||
var assert = utils.assert;
|
||||
var BufferReader = require('./reader');
|
||||
var BufferWriter = require('./writer');
|
||||
|
||||
/**
|
||||
* Represents a key ring which amounts to an address. Used for {@link Wallet}.
|
||||
@ -31,29 +33,20 @@ function KeyRing(options) {
|
||||
if (!(this instanceof KeyRing))
|
||||
return new KeyRing(options);
|
||||
|
||||
if (options instanceof KeyRing)
|
||||
return options;
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
this.addressMap = null;
|
||||
|
||||
this.network = bcoin.network.get(options.network);
|
||||
this.key = options.key;
|
||||
this.path = options.path;
|
||||
this.change = !!options.change;
|
||||
this.index = options.index;
|
||||
|
||||
this.type = options.type || 'pubkeyhash';
|
||||
this.keys = [];
|
||||
this.m = options.m || 1;
|
||||
this.n = options.n || 1;
|
||||
this.witness = options.witness || false;
|
||||
this.path = options.path;
|
||||
this.key = options.key;
|
||||
this.keys = [];
|
||||
|
||||
if (this.n > 1)
|
||||
this.type = 'multisig';
|
||||
|
||||
this.addressMap = null;
|
||||
|
||||
assert(this.type === 'pubkeyhash' || this.type === 'multisig');
|
||||
|
||||
if (this.m < 1 || this.m > this.n)
|
||||
@ -67,27 +60,6 @@ function KeyRing(options) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an object to see if it is an KeyRing.
|
||||
* @param {Object} obj
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
KeyRing.isKeyRing = function isKeyRing(obj) {
|
||||
return obj
|
||||
&& Array.isArray(obj.keys)
|
||||
&& typeof obj._getAddressMap === 'function';
|
||||
};
|
||||
|
||||
/**
|
||||
* Return address ID (pubkeyhash address of pubkey).
|
||||
* @returns {Base58Address}
|
||||
*/
|
||||
|
||||
KeyRing.prototype.getID = function getID() {
|
||||
return this.getKeyAddress();
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a key to shared keys.
|
||||
* @param {Buffer} key
|
||||
@ -97,9 +69,7 @@ KeyRing.prototype.addKey = function addKey(key) {
|
||||
if (utils.indexOf(this.keys, key) !== -1)
|
||||
return;
|
||||
|
||||
this.keys.push(key);
|
||||
|
||||
this.keys = utils.sortKeys(this.keys);
|
||||
utils.binaryInsert(this.keys, key, utils.cmp);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -108,14 +78,7 @@ KeyRing.prototype.addKey = function addKey(key) {
|
||||
*/
|
||||
|
||||
KeyRing.prototype.removeKey = function removeKey(key) {
|
||||
var index = utils.indexOf(this.keys, key);
|
||||
|
||||
if (index === -1)
|
||||
return;
|
||||
|
||||
this.keys.splice(index, 1);
|
||||
|
||||
this.keys = utils.sortKeys(this.keys);
|
||||
utils.binaryRemove(this.keys, key, utils.cmp);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -369,7 +332,12 @@ KeyRing.prototype.getAddress = function getAddress() {
|
||||
return this.getKeyAddress();
|
||||
};
|
||||
|
||||
KeyRing.prototype._getAddressMap = function _getAddressMap() {
|
||||
/**
|
||||
* Create the address map for testing txs.
|
||||
* @returns {AddressMap}
|
||||
*/
|
||||
|
||||
KeyRing.prototype.getAddressMap = function getAddressMap() {
|
||||
if (!this.addressMap) {
|
||||
this.addressMap = {};
|
||||
|
||||
@ -393,7 +361,7 @@ KeyRing.prototype._getAddressMap = function _getAddressMap() {
|
||||
*/
|
||||
|
||||
KeyRing.prototype.ownInput = function ownInput(tx, index) {
|
||||
var addressMap = this._getAddressMap();
|
||||
var addressMap = this.getAddressMap();
|
||||
|
||||
if (tx instanceof bcoin.input)
|
||||
return tx.test(addressMap);
|
||||
@ -409,7 +377,7 @@ KeyRing.prototype.ownInput = function ownInput(tx, index) {
|
||||
*/
|
||||
|
||||
KeyRing.prototype.ownOutput = function ownOutput(tx, index) {
|
||||
var addressMap = this._getAddressMap();
|
||||
var addressMap = this.getAddressMap();
|
||||
|
||||
if (tx instanceof bcoin.output)
|
||||
return tx.test(addressMap);
|
||||
@ -552,17 +520,15 @@ KeyRing.prototype.toJSON = function toJSON() {
|
||||
return {
|
||||
v: 1,
|
||||
name: 'address',
|
||||
address: this.getAddress(),
|
||||
network: this.network.type,
|
||||
change: this.change,
|
||||
index: this.index,
|
||||
type: this.type,
|
||||
m: this.m,
|
||||
n: this.n,
|
||||
witness: this.witness,
|
||||
path: this.path,
|
||||
key: utils.toBase58(this.key),
|
||||
type: this.type,
|
||||
witness: this.witness,
|
||||
keys: this.keys.map(utils.toBase58),
|
||||
m: this.m,
|
||||
n: this.n
|
||||
address: this.getAddress()
|
||||
};
|
||||
};
|
||||
|
||||
@ -578,18 +544,87 @@ KeyRing.fromJSON = function fromJSON(json) {
|
||||
assert.equal(json.name, 'address');
|
||||
return new KeyRing({
|
||||
nework: json.network,
|
||||
change: json.change,
|
||||
index: json.index,
|
||||
type: json.type,
|
||||
m: json.m,
|
||||
n: json.n,
|
||||
witness: json.witness,
|
||||
path: json.path,
|
||||
key: utils.fromBase58(json.key),
|
||||
type: json.type,
|
||||
witness: json.witness,
|
||||
keys: json.keys.map(utils.fromBase58),
|
||||
m: json.m,
|
||||
n: json.n
|
||||
keys: json.keys.map(utils.fromBase58)
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize the keyring.
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
KeyRing.prototype.toRaw = function toRaw(writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
var i;
|
||||
|
||||
p.writeU32(this.network.magic);
|
||||
p.writeU8(this.type === 'pubkeyhash' ? 0 : 1);
|
||||
p.writeU8(this.m);
|
||||
p.writeU8(this.n);
|
||||
p.writeU8(this.witness ? 1 : 0);
|
||||
p.writeVarString(this.path, 'ascii');
|
||||
p.writeVarBytes(this.key);
|
||||
p.writeU8(this.keys.length);
|
||||
|
||||
for (i = 0; i < this.keys.length; i++)
|
||||
p.writeVarBytes(this.keys[i]);
|
||||
|
||||
if (!writer)
|
||||
p = p.render();
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a keyring from serialized data.
|
||||
* @returns {KeyRing}
|
||||
*/
|
||||
|
||||
KeyRing.fromRaw = function fromRaw(data) {
|
||||
var p = new BufferReader(data);
|
||||
var network = bcoin.network.fromMagic(p.readU32());
|
||||
var type = p.readU8() === 0 ? 'pubkeyhash' : 'multisig';
|
||||
var m = p.readU8();
|
||||
var n = p.readU8();
|
||||
var witness = p.readU8() === 1;
|
||||
var path = p.readVarString('ascii');
|
||||
var key = p.readVarBytes();
|
||||
var keys = new Array(p.readU8());
|
||||
var i;
|
||||
|
||||
for (i = 0; i < keys.length; i++)
|
||||
keys[i] = p.readVarBytes();
|
||||
|
||||
return new KeyRing({
|
||||
nework: network,
|
||||
type: type,
|
||||
m: m,
|
||||
n: n,
|
||||
witness: witness,
|
||||
path: path,
|
||||
key: key,
|
||||
keys: keys
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Test an object to see if it is an KeyRing.
|
||||
* @param {Object} obj
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
KeyRing.isKeyRing = function isKeyRing(obj) {
|
||||
return obj
|
||||
&& Array.isArray(obj.keys)
|
||||
&& typeof obj.getAddressMap === 'function';
|
||||
};
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
@ -143,6 +143,25 @@ Network.get = function get(options) {
|
||||
assert(false, 'Unknown network.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a network by its magic number.
|
||||
* @returns {Network}
|
||||
*/
|
||||
|
||||
Network.fromMagic = function fromMagic(magic) {
|
||||
var i, type;
|
||||
|
||||
for (i = 0; i < network.types.length; i++) {
|
||||
type = network.types[i];
|
||||
if (magic === network[type].magic)
|
||||
break;
|
||||
}
|
||||
|
||||
assert(i < network.types.length, 'Network not found.');
|
||||
|
||||
return Network.get(type);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the network to a string.
|
||||
* @returns {String}
|
||||
|
||||
@ -62,9 +62,6 @@ function TXDB(db, options) {
|
||||
this.filter = this.options.useFilter
|
||||
? new bcoin.bloom.rolling(800000, 0.01)
|
||||
: null;
|
||||
|
||||
if (this.options.mapAddress)
|
||||
this.options.indexAddress = true;
|
||||
}
|
||||
|
||||
utils.inherits(TXDB, EventEmitter);
|
||||
@ -75,26 +72,38 @@ TXDB.prototype._lock = function _lock(func, args, force) {
|
||||
|
||||
TXDB.prototype._loadFilter = function loadFilter(callback) {
|
||||
var self = this;
|
||||
var i;
|
||||
var iter;
|
||||
|
||||
if (!this.filter)
|
||||
return callback();
|
||||
|
||||
this.db.iterate({
|
||||
iter = this.db.iterator({
|
||||
gte: 'W',
|
||||
lte: 'W~',
|
||||
transform: function(key) {
|
||||
return key.split('/')[1];
|
||||
}
|
||||
}, function(err, keys) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
for (i = 0; i < keys.length; i++)
|
||||
self.filter.add(keys[i], 'hex');
|
||||
|
||||
return callback();
|
||||
keys: true,
|
||||
values: false,
|
||||
fillCache: false,
|
||||
keyAsBuffer: false
|
||||
});
|
||||
|
||||
(function next() {
|
||||
iter.next(function(err, key, value) {
|
||||
if (err) {
|
||||
return iter.end(function() {
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
if (key === undefined)
|
||||
return iter.end(callback);
|
||||
|
||||
key = key.split('/')[1];
|
||||
|
||||
self.filter.add(key, 'hex');
|
||||
|
||||
next();
|
||||
});
|
||||
})();
|
||||
};
|
||||
|
||||
TXDB.prototype._testFilter = function _testFilter(addresses) {
|
||||
@ -120,9 +129,6 @@ TXDB.prototype._testFilter = function _testFilter(addresses) {
|
||||
TXDB.prototype.getMap = function getMap(tx, callback) {
|
||||
var input, output, addresses, table, map;
|
||||
|
||||
if (!this.options.indexAddress)
|
||||
return callback();
|
||||
|
||||
input = tx.getInputHashes();
|
||||
output = tx.getOutputHashes();
|
||||
addresses = utils.uniq(input.concat(output));
|
||||
@ -134,6 +140,9 @@ TXDB.prototype.getMap = function getMap(tx, callback) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (table.count === 0)
|
||||
return callback();
|
||||
|
||||
map = {
|
||||
table: table,
|
||||
input: [],
|
||||
@ -158,14 +167,6 @@ TXDB.prototype.getMap = function getMap(tx, callback) {
|
||||
return callback(null, map);
|
||||
}
|
||||
|
||||
if (!this.options.mapAddress) {
|
||||
table = addresses.reduce(function(out, address) {
|
||||
out[address] = [address];
|
||||
return out;
|
||||
}, {});
|
||||
return cb(null, table);
|
||||
}
|
||||
|
||||
return this.mapAddresses(addresses, cb);
|
||||
};
|
||||
|
||||
@ -177,7 +178,7 @@ TXDB.prototype.getMap = function getMap(tx, callback) {
|
||||
|
||||
TXDB.prototype.mapAddresses = function mapAddresses(address, callback) {
|
||||
var self = this;
|
||||
var table = {};
|
||||
var table = { count: 0 };
|
||||
|
||||
if (Array.isArray(address)) {
|
||||
return utils.forEachSerial(address, function(address, next) {
|
||||
@ -187,6 +188,7 @@ TXDB.prototype.mapAddresses = function mapAddresses(address, callback) {
|
||||
|
||||
assert(res[address]);
|
||||
table[address] = res[address];
|
||||
table.count += res.count;
|
||||
|
||||
next();
|
||||
});
|
||||
@ -198,13 +200,12 @@ TXDB.prototype.mapAddresses = function mapAddresses(address, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
this.db.fetch('W/' + address, function(json) {
|
||||
return JSON.parse(json.toString('utf8'));
|
||||
}, function(err, data) {
|
||||
this.wdb.getAddress(address, function(err, paths) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
table[address] = data ? data.wallets : [];
|
||||
table[address] = paths ? Object.keys(paths) : [];
|
||||
table.count += table[address].length;
|
||||
|
||||
return callback(null, table);
|
||||
});
|
||||
@ -310,11 +311,6 @@ TXDB.prototype.add = function add(tx, callback, force) {
|
||||
if (!map)
|
||||
return callback(null, false);
|
||||
|
||||
if (self.options.mapAddress) {
|
||||
if (map.all.length === 0)
|
||||
return callback(null, false);
|
||||
}
|
||||
|
||||
return self._add(tx, map, callback, force);
|
||||
});
|
||||
};
|
||||
@ -352,30 +348,26 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
|
||||
batch.put('t/' + hash, tx.toExtended());
|
||||
|
||||
if (self.options.indexExtra) {
|
||||
if (tx.ts === 0) {
|
||||
assert(tx.ps > 0);
|
||||
batch.put('p/' + hash, DUMMY);
|
||||
batch.put('m/' + pad32(tx.ps) + '/' + hash, DUMMY);
|
||||
} else {
|
||||
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('T/' + id + '/' + hash, DUMMY);
|
||||
if (tx.ts === 0) {
|
||||
batch.put('P/' + id + '/' + hash, DUMMY);
|
||||
batch.put('M/' + id + '/' + pad32(tx.ps) + '/' + hash, DUMMY);
|
||||
} else {
|
||||
batch.put('H/' + id + '/' + pad32(tx.height) + '/' + hash, DUMMY);
|
||||
batch.put('M/' + id + '/' + pad32(tx.ts) + '/' + hash, DUMMY);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (tx.ts === 0) {
|
||||
assert(tx.ps > 0);
|
||||
batch.put('p/' + hash, DUMMY);
|
||||
batch.put('m/' + pad32(tx.ps) + '/' + hash, DUMMY);
|
||||
} else {
|
||||
batch.put('h/' + pad32(tx.height) + '/' + hash, DUMMY);
|
||||
batch.put('m/' + pad32(tx.ts) + '/' + hash, DUMMY);
|
||||
}
|
||||
|
||||
map.all.forEach(function(id) {
|
||||
batch.put('T/' + id + '/' + hash, DUMMY);
|
||||
if (tx.ts === 0) {
|
||||
batch.put('P/' + id + '/' + hash, DUMMY);
|
||||
batch.put('M/' + id + '/' + pad32(tx.ps) + '/' + hash, DUMMY);
|
||||
} else {
|
||||
batch.put('H/' + id + '/' + pad32(tx.height) + '/' + hash, DUMMY);
|
||||
batch.put('M/' + id + '/' + pad32(tx.ts) + '/' + hash, DUMMY);
|
||||
}
|
||||
});
|
||||
|
||||
// Consume unspent money or add orphans
|
||||
utils.forEachSerial(tx.inputs, function(input, next, i) {
|
||||
var key, address;
|
||||
@ -386,10 +378,8 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
address = input.getHash();
|
||||
|
||||
// Only add orphans if this input is ours.
|
||||
if (self.options.mapAddress) {
|
||||
if (!address || !map.table[address].length)
|
||||
return next();
|
||||
}
|
||||
if (!address || !map.table[address].length)
|
||||
return next();
|
||||
|
||||
self.getCoin(input.prevout.hash, input.prevout.index, function(err, coin) {
|
||||
if (err)
|
||||
@ -409,7 +399,7 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
|
||||
updated = true;
|
||||
|
||||
if (self.options.indexAddress && address) {
|
||||
if (address) {
|
||||
map.table[address].forEach(function(id) {
|
||||
batch.del('C/' + id + '/' + key);
|
||||
});
|
||||
@ -481,10 +471,8 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
var key, coin;
|
||||
|
||||
// Do not add unspents for outputs that aren't ours.
|
||||
if (self.options.mapAddress) {
|
||||
if (!address || !map.table[address].length)
|
||||
return next();
|
||||
}
|
||||
if (!address || !map.table[address].length)
|
||||
return next();
|
||||
|
||||
key = hash + '/' + i;
|
||||
|
||||
@ -548,7 +536,7 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
|
||||
return next(err);
|
||||
|
||||
if (!orphans) {
|
||||
if (self.options.indexAddress && address) {
|
||||
if (address) {
|
||||
map.table[address].forEach(function(id) {
|
||||
batch.put('C/' + id + '/' + key, DUMMY);
|
||||
});
|
||||
@ -719,30 +707,24 @@ TXDB.prototype._confirm = function _confirm(tx, map, callback, force) {
|
||||
|
||||
batch.put('t/' + hash, tx.toExtended());
|
||||
|
||||
if (self.options.indexExtra) {
|
||||
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);
|
||||
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('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);
|
||||
});
|
||||
}
|
||||
}
|
||||
map.all.forEach(function(id) {
|
||||
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);
|
||||
});
|
||||
|
||||
utils.forEachSerial(tx.outputs, function(output, next, i) {
|
||||
var address = output.getHash();
|
||||
|
||||
// Only update coins if this output is ours.
|
||||
if (self.options.mapAddress) {
|
||||
if (!address || !map.table[address].length)
|
||||
return next();
|
||||
}
|
||||
if (!address || !map.table[address].length)
|
||||
return next();
|
||||
|
||||
self.getCoin(hash, i, function(err, coin) {
|
||||
if (err)
|
||||
@ -813,11 +795,6 @@ TXDB.prototype.remove = function remove(hash, callback, force) {
|
||||
if (!map)
|
||||
return callback(null, false);
|
||||
|
||||
if (self.options.mapAddress) {
|
||||
if (map.all.length === 0)
|
||||
return callback(null, false);
|
||||
}
|
||||
|
||||
return self._remove(tx, map, callback, force);
|
||||
});
|
||||
});
|
||||
@ -847,11 +824,6 @@ TXDB.prototype.lazyRemove = function lazyRemove(tx, callback, force) {
|
||||
if (!map)
|
||||
return callback(null, false);
|
||||
|
||||
if (self.options.mapAddress) {
|
||||
if (map.all.length === 0)
|
||||
return callback(null, false);
|
||||
}
|
||||
|
||||
return self._remove(tx, map, callback, force);
|
||||
});
|
||||
};
|
||||
@ -879,29 +851,25 @@ TXDB.prototype._remove = function remove(tx, map, callback, force) {
|
||||
|
||||
batch.del('t/' + hash);
|
||||
|
||||
if (self.options.indexExtra) {
|
||||
if (tx.ts === 0) {
|
||||
batch.del('p/' + hash);
|
||||
batch.del('m/' + pad32(tx.ps) + '/' + hash);
|
||||
} else {
|
||||
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('T/' + id + '/' + hash);
|
||||
if (tx.ts === 0) {
|
||||
batch.del('P/' + id + '/' + hash);
|
||||
batch.del('M/' + id + '/' + pad32(tx.ps) + '/' + hash);
|
||||
} else {
|
||||
batch.del('H/' + id + '/' + pad32(tx.height) + '/' + hash);
|
||||
batch.del('M/' + id + '/' + pad32(tx.ts) + '/' + hash);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (tx.ts === 0) {
|
||||
batch.del('p/' + hash);
|
||||
batch.del('m/' + pad32(tx.ps) + '/' + hash);
|
||||
} else {
|
||||
batch.del('h/' + pad32(tx.height) + '/' + hash);
|
||||
batch.del('m/' + pad32(tx.ts) + '/' + hash);
|
||||
}
|
||||
|
||||
map.all.forEach(function(id) {
|
||||
batch.del('T/' + id + '/' + hash);
|
||||
if (tx.ts === 0) {
|
||||
batch.del('P/' + id + '/' + hash);
|
||||
batch.del('M/' + id + '/' + pad32(tx.ps) + '/' + hash);
|
||||
} else {
|
||||
batch.del('H/' + id + '/' + pad32(tx.height) + '/' + hash);
|
||||
batch.del('M/' + id + '/' + pad32(tx.ts) + '/' + hash);
|
||||
}
|
||||
});
|
||||
|
||||
this.fillHistory(tx, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
@ -916,12 +884,10 @@ TXDB.prototype._remove = function remove(tx, map, callback, force) {
|
||||
if (!input.coin)
|
||||
return;
|
||||
|
||||
if (self.options.mapAddress) {
|
||||
if (!address || !map.table[address].length)
|
||||
return;
|
||||
}
|
||||
if (!address || !map.table[address].length)
|
||||
return;
|
||||
|
||||
if (self.options.indexAddress && address) {
|
||||
if (address) {
|
||||
map.table[address].forEach(function(id) {
|
||||
batch.put('C/' + id + '/' + key, DUMMY);
|
||||
});
|
||||
@ -936,15 +902,13 @@ TXDB.prototype._remove = function remove(tx, map, callback, force) {
|
||||
var key = hash + '/' + i;
|
||||
var address = output.getHash();
|
||||
|
||||
if (self.options.mapAddress) {
|
||||
if (!address || !map.table[address].length)
|
||||
return;
|
||||
}
|
||||
if (!address || !map.table[address].length)
|
||||
return;
|
||||
|
||||
if (output.script.isUnspendable())
|
||||
return;
|
||||
|
||||
if (self.options.indexAddress && address) {
|
||||
if (address) {
|
||||
map.table[address].forEach(function(id) {
|
||||
batch.del('C/' + id + '/' + key);
|
||||
});
|
||||
@ -1000,11 +964,6 @@ TXDB.prototype.unconfirm = function unconfirm(hash, callback, force) {
|
||||
if (!map)
|
||||
return callback(null, false);
|
||||
|
||||
if (self.options.mapAddress) {
|
||||
if (map.all.length === 0)
|
||||
return callback(null, false);
|
||||
}
|
||||
|
||||
return self._unconfirm(tx, map, callback, force);
|
||||
});
|
||||
});
|
||||
@ -1043,21 +1002,17 @@ TXDB.prototype._unconfirm = function unconfirm(tx, map, callback, force) {
|
||||
|
||||
batch.put('t/' + hash, tx.toExtended());
|
||||
|
||||
if (self.options.indexExtra) {
|
||||
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);
|
||||
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('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);
|
||||
});
|
||||
}
|
||||
}
|
||||
map.all.forEach(function(id) {
|
||||
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);
|
||||
});
|
||||
|
||||
utils.forEachSerial(tx.outputs, function(output, next, i) {
|
||||
self.getCoin(hash, i, function(err, coin) {
|
||||
@ -1730,56 +1685,6 @@ TXDB.prototype.getBalance = function getBalance(address, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get hashes of all transactions in the database.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {Function} callback - Returns [Error, {@link Hash}[]].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getHistoryHashesByAddress = function getHistoryHashesByAddress(address, callback) {
|
||||
return this.getHistoryHashes(address, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all transactions.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {Function} callback - Returns [Error, {@link TX}[]].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getHistoryByAddress = function getHistoryByAddress(address, callback) {
|
||||
return this.getHistory(address, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get coins.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {Function} callback - Returns [Error, {@link Coin}[]].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getCoinsByAddress = function getCoins(address, callback) {
|
||||
return this.getCoins(address, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get unconfirmed transactions.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {Function} callback - Returns [Error, {@link TX}[]].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getUnconfirmedByAddress = function getUnconfirmedByAddress(address, callback) {
|
||||
return this.getUnconfirmed(address, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate balance.
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {Function} callback - Returns [Error, {@link Balance}].
|
||||
*/
|
||||
|
||||
TXDB.prototype.getBalanceByAddress = function getBalanceByAddress(address, callback) {
|
||||
return this.getBalance(address, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {WalletID|WalletID[]} address - By address (can be null).
|
||||
* @param {Number} now - Current time.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@
|
||||
/*
|
||||
* Database Layout:
|
||||
* (inherits all from txdb)
|
||||
* W/[address]/[id] -> dummy (map address to id)
|
||||
* W/[address] -> id & path data
|
||||
* w/[id] -> wallet
|
||||
*/
|
||||
|
||||
@ -17,6 +17,8 @@ var EventEmitter = require('events').EventEmitter;
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
var DUMMY = new Buffer([0]);
|
||||
var BufferReader = require('./reader');
|
||||
var BufferWriter = require('./writer');
|
||||
|
||||
/**
|
||||
* WalletDB
|
||||
@ -40,7 +42,7 @@ function WalletDB(options) {
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.providers = [];
|
||||
this.watchers = [];
|
||||
this.options = options;
|
||||
this.loaded = false;
|
||||
this.network = bcoin.network.get(options.network);
|
||||
@ -110,9 +112,6 @@ WalletDB.prototype._init = function _init() {
|
||||
|
||||
this.tx = new bcoin.txdb(this, {
|
||||
network: this.network,
|
||||
indexExtra: true,
|
||||
indexAddress: true,
|
||||
mapAddress: true,
|
||||
verify: this.options.verify,
|
||||
useFilter: true
|
||||
});
|
||||
@ -221,60 +220,61 @@ WalletDB.prototype.destroy = function destroy(callback) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Derive an address.
|
||||
* Helper function to get a wallet.
|
||||
* @private
|
||||
* @param {WalletID} id
|
||||
* @param {Boolean} change
|
||||
* @param {Function} errback
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype.rpc = function rpc(id, callback, method) {
|
||||
WalletDB.prototype.fetchWallet = function fetchWallet(id, errback, callback) {
|
||||
var self = this;
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
this.get(id, function(err, _, wallet) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
return errback(err);
|
||||
|
||||
if (!wallet)
|
||||
return callback(new Error('No wallet.'));
|
||||
return errback(new Error('No wallet.'));
|
||||
|
||||
method(wallet);
|
||||
callback(wallet);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.syncOutputDepth = function syncOutputDepth(id, tx, callback) {
|
||||
this.rpc(id, callback, function(wallet) {
|
||||
this.fetchWallet(id, callback, function(wallet) {
|
||||
wallet.syncOutputDepth(tx, callback);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.createAddress = function createAddress(id, change, callback) {
|
||||
this.rpc(id, callback, function(wallet) {
|
||||
this.fetchWallet(id, callback, function(wallet) {
|
||||
wallet.createAddress(change, callback);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.getReceiveAddress = function getReceiveAddress(id, callback) {
|
||||
this.rpc(id, callback, function(wallet) {
|
||||
this.fetchWallet(id, callback, function(wallet) {
|
||||
callback(null, wallet.receiveAddress);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.getChangeAddress = function getChangeAddress(id, callback) {
|
||||
this.rpc(id, callback, function(wallet) {
|
||||
this.fetchWallet(id, callback, function(wallet) {
|
||||
callback(null, wallet.changeAddress);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.fill = function fill(id, tx, options, callback) {
|
||||
this.rpc(id, callback, function(wallet) {
|
||||
this.fetchWallet(id, callback, function(wallet) {
|
||||
wallet.fill(tx, options, callback);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.scriptInputs = function scriptInputs(id, tx, callback) {
|
||||
this.rpc(id, callback, function(wallet) {
|
||||
this.fetchWallet(id, callback, function(wallet) {
|
||||
wallet.scriptInputs(tx, callback);
|
||||
});
|
||||
};
|
||||
@ -288,29 +288,41 @@ WalletDB.prototype.sign = function sign(id, tx, options, callback) {
|
||||
if (typeof options === 'string' || Buffer.isBuffer(options))
|
||||
options = { passphrase: options };
|
||||
|
||||
this.rpc(id, callback, function(wallet) {
|
||||
this.fetchWallet(id, callback, function(wallet) {
|
||||
wallet.sign(tx, options, callback);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.createTX = function createTX(id, options, outputs, callback) {
|
||||
this.rpc(id, callback, function(wallet) {
|
||||
this.fetchWallet(id, callback, function(wallet) {
|
||||
wallet.createTX(options, outputs, callback);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.addKey = function addKey(id, key, callback) {
|
||||
this.rpc(id, callback, function(wallet) {
|
||||
this.fetchWallet(id, callback, function(wallet) {
|
||||
wallet.addKey(key, callback);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.removeKey = function removeKey(id, key, callback) {
|
||||
this.rpc(id, callback, function(wallet) {
|
||||
this.fetchWallet(id, callback, function(wallet) {
|
||||
wallet.removeKey(key, callback);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.getInfo = function getInfo(id, callback) {
|
||||
this.fetchWallet(id, callback, function(wallet) {
|
||||
callback(null, wallet);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.getRedeem = function getRedeem(id, hash, callback) {
|
||||
this.fetchWallet(id, callback, function(wallet) {
|
||||
wallet.getRedeem(hash, callback);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Save a "naked" (non-instantiated) wallet. Will
|
||||
* also index the address table.
|
||||
@ -320,8 +332,7 @@ WalletDB.prototype.removeKey = function removeKey(id, key, callback) {
|
||||
*/
|
||||
|
||||
WalletDB.prototype.saveJSON = function saveJSON(id, json, callback) {
|
||||
var data = new Buffer(JSON.stringify(json), 'utf8');
|
||||
this.db.put('w/' + id, data, callback);
|
||||
this.db.put('w/' + id, json, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -358,6 +369,9 @@ WalletDB.prototype.removeJSON = function removeJSON(id, callback) {
|
||||
WalletDB.prototype.getJSON = function getJSON(id, callback) {
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
if (!id)
|
||||
return callback();
|
||||
|
||||
this.db.get('w/' + id, function(err, json) {
|
||||
if (err && err.type === 'NotFoundError')
|
||||
return callback();
|
||||
@ -366,7 +380,7 @@ WalletDB.prototype.getJSON = function getJSON(id, callback) {
|
||||
return callback(err);
|
||||
|
||||
try {
|
||||
json = JSON.parse(json.toString('utf8'));
|
||||
json = bcoin.wallet.parseRaw(json);
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
@ -376,7 +390,7 @@ WalletDB.prototype.getJSON = function getJSON(id, callback) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a wallet from the database, instantiate, decrypt, and setup provider.
|
||||
* Get a wallet from the database, instantiate, decrypt, and setup watcher.
|
||||
* @param {WalletID} id
|
||||
* @param {Function} callback - Returns [Error, {@link Wallet}].
|
||||
*/
|
||||
@ -386,19 +400,18 @@ WalletDB.prototype.get = function get(id, callback) {
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
return this.getJSON(id, function(err, options) {
|
||||
return this.getJSON(id, function(err, json) {
|
||||
var wallet;
|
||||
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (!options)
|
||||
if (!json)
|
||||
return callback();
|
||||
|
||||
try {
|
||||
options = bcoin.wallet.parseJSON(options);
|
||||
options.db = self;
|
||||
wallet = new bcoin.wallet(options);
|
||||
json.db = self;
|
||||
wallet = new bcoin.wallet(json);
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
@ -425,7 +438,7 @@ WalletDB.prototype.save = function save(wallet, callback) {
|
||||
self.save(wallet, next);
|
||||
}, callback);
|
||||
}
|
||||
this.saveJSON(wallet.id, wallet.toJSON(), callback);
|
||||
this.saveJSON(wallet.id, wallet.toRaw(), callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -445,27 +458,24 @@ WalletDB.prototype.remove = function remove(id, callback) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new wallet, save to database, setup provider.
|
||||
* Create a new wallet, save to database, setup watcher.
|
||||
* @param {Object} options - See {@link Wallet}.
|
||||
* @param {Function} callback - Returns [Error, {@link Wallet}].
|
||||
*/
|
||||
|
||||
WalletDB.prototype.create = function create(options, callback) {
|
||||
var self = this;
|
||||
var wallet;
|
||||
|
||||
function create(err, json) {
|
||||
var wallet;
|
||||
this.has(options.id, function(err, exists) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (json) {
|
||||
return callback(
|
||||
new Error('`' + options.id + '` already exists.'),
|
||||
null,
|
||||
null,
|
||||
json);
|
||||
}
|
||||
if (exists)
|
||||
return callback(new Error('Wallet already exists.'));
|
||||
|
||||
if (self.network.witness)
|
||||
options.witness = options.witness !== false;
|
||||
@ -480,12 +490,20 @@ WalletDB.prototype.create = function create(options, callback) {
|
||||
|
||||
return callback(null, new bcoin.cwallet(wallet.id, self), wallet);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (!options.id)
|
||||
return create();
|
||||
/**
|
||||
* Test for the existence of a wallet.
|
||||
* @param {WalletID?} id
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
return this.getJSON(options.id, create);
|
||||
WalletDB.prototype.has = function has(id, callback) {
|
||||
if (!id)
|
||||
return callback(null, false);
|
||||
|
||||
this.db.hash('w/' + id, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -497,41 +515,38 @@ WalletDB.prototype.create = function create(options, callback) {
|
||||
|
||||
WalletDB.prototype.ensure = function ensure(options, callback) {
|
||||
var self = this;
|
||||
return this.create(options, function(err, cwallet, wallet, json) {
|
||||
if (err && !json)
|
||||
return this.get(options.id, function(err, cwallet, wallet) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (cwallet)
|
||||
return callback(null, cwallet);
|
||||
return callback(null, cwallet, wallet);
|
||||
|
||||
assert(json);
|
||||
|
||||
try {
|
||||
options = bcoin.wallet.parseJSON(json);
|
||||
options.db = self;
|
||||
wallet = new bcoin.wallet(options);
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
|
||||
wallet.init(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
return callback(null, new bcoin.cwallet(wallet.id, self), wallet);
|
||||
});
|
||||
self.create(options, callback);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.saveAddress = function saveAddress(id, address, callback) {
|
||||
/**
|
||||
* Save an address to the path map.
|
||||
* The path map exists in the form of:
|
||||
* `W/[address-hash] -> {walletid1=path1, walletid2=path2, ...}`
|
||||
* @param {WalletID} id
|
||||
* @param {KeyRing[]} addresses
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype.saveAddress = function saveAddress(id, addresses, callback) {
|
||||
var self = this;
|
||||
var hashes = [];
|
||||
var batch = this.db.batch();
|
||||
var i, address;
|
||||
|
||||
if (!Array.isArray(address))
|
||||
address = [address];
|
||||
if (!Array.isArray(addresses))
|
||||
addresses = [addresses];
|
||||
|
||||
for (i = 0; i < addresses.length; i++) {
|
||||
address = addresses[i];
|
||||
|
||||
address.forEach(function(address) {
|
||||
hashes.push([address.getKeyHash('hex'), address.path]);
|
||||
|
||||
if (address.type === 'multisig')
|
||||
@ -539,33 +554,26 @@ WalletDB.prototype.saveAddress = function saveAddress(id, address, callback) {
|
||||
|
||||
if (address.witness)
|
||||
hashes.push([address.getProgramHash('hex'), address.path]);
|
||||
});
|
||||
}
|
||||
|
||||
utils.forEach(hashes, function(hash, next) {
|
||||
if (self.tx.filter)
|
||||
self.tx.filter.add(hash[0], 'hex');
|
||||
|
||||
self.db.fetch('W/' + hash[0], function(json) {
|
||||
return JSON.parse(json.toString('utf8'));
|
||||
}, function(err, json) {
|
||||
self.db.fetch('W/' + hash[0], parsePaths, function(err, paths) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (!json) {
|
||||
json = {
|
||||
wallets: [],
|
||||
path: hash[1]
|
||||
};
|
||||
}
|
||||
if (!paths)
|
||||
paths = {};
|
||||
|
||||
if (json.wallets.indexOf(id) !== -1)
|
||||
if (paths[id])
|
||||
return next();
|
||||
|
||||
json.wallets.push(id);
|
||||
paths[id] = hash[1];
|
||||
|
||||
json = new Buffer(JSON.stringify(json), 'utf8');
|
||||
batch.put('W/' + hash[0], serializePaths(paths));
|
||||
|
||||
batch.put('W/' + hash[0], json);
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
@ -576,39 +584,52 @@ WalletDB.prototype.saveAddress = function saveAddress(id, address, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether an address hash exists in the
|
||||
* path map and is relevant to the wallet id.
|
||||
* @param {WalletID} id
|
||||
* @param {Hash} address
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype.hasAddress = function hasAddress(id, address, callback) {
|
||||
this.getAddress(id, address, function(err, address) {
|
||||
this.getAddress(address, function(err, paths) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
return callback(null, !!address);
|
||||
if (!paths || !paths[id])
|
||||
return callback(null, false);
|
||||
|
||||
return callback(null, true);
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.getAddress = function getAddress(id, address, callback) {
|
||||
var self = this;
|
||||
this.db.fetch('W/' + address, function(json) {
|
||||
return JSON.parse(json.toString('utf8'));
|
||||
}, function(err, address) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
/**
|
||||
* Get path data for the specified address hash.
|
||||
* @param {Hash} address
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
if (!address || address.wallets.indexOf(id) === -1)
|
||||
return callback();
|
||||
|
||||
return callback(null, address);
|
||||
});
|
||||
WalletDB.prototype.getAddress = function getAddress(address, callback) {
|
||||
this.db.fetch('W/' + address, parsePaths, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the corresponding path for an address hash.
|
||||
* @param {WalletID} id
|
||||
* @param {Hash} address
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getPath = function getPath(id, address, callback) {
|
||||
this.getAddress(id, address, function(err, address) {
|
||||
this.getAddress(address, function(err, paths) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (!address)
|
||||
if (!paths || !paths[id])
|
||||
return callback();
|
||||
|
||||
return callback(null, address.path);
|
||||
return callback(null, paths[id]);
|
||||
});
|
||||
};
|
||||
|
||||
@ -637,35 +658,35 @@ WalletDB.prototype.getCoin = function getCoin(hash, index, callback) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @see {@link TXDB#getHistoryByAddress}.
|
||||
* @see {@link TXDB#getHistory}.
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getHistory = function getHistory(id, callback) {
|
||||
return this.tx.getHistoryByAddress(id, callback);
|
||||
return this.tx.getHistory(id, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* @see {@link TXDB#getCoinsByAddress}.
|
||||
* @see {@link TXDB#getCoins}.
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getCoins = function getCoins(id, callback) {
|
||||
return this.tx.getCoinsByAddress(id, callback);
|
||||
return this.tx.getCoins(id, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* @see {@link TXDB#getUnconfirmedByAddress}.
|
||||
* @see {@link TXDB#getUnconfirmed}.
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getUnconfirmed = function getUnconfirmed(id, callback) {
|
||||
return this.tx.getUnconfirmedByAddress(id, callback);
|
||||
return this.tx.getUnconfirmed(id, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* @see {@link TXDB#getBalanceByAddress}.
|
||||
* @see {@link TXDB#getBalance}.
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getBalance = function getBalance(id, callback) {
|
||||
return this.tx.getBalanceByAddress(id, callback);
|
||||
return this.tx.getBalance(id, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -708,6 +729,15 @@ WalletDB.prototype.fillCoins = function fillCoins(tx, callback) {
|
||||
return this.tx.fillCoins(tx, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Zap all walletdb transactions.
|
||||
* @see {@link TXDB#zap}.
|
||||
*/
|
||||
|
||||
WalletDB.prototype.zap = function zap(id, now, age, callback) {
|
||||
return this.tx.zap(id, now, age, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify the database that a block has been
|
||||
* removed (reorg). Unconfirms transactions by height.
|
||||
@ -748,73 +778,109 @@ WalletDB.prototype.removeBlock = function removeBlock(block, callback) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Zap all walletdb transactions.
|
||||
* @see {@link TXDB#zap}.
|
||||
* Register an event emitter with the walletdb.
|
||||
* @param {WalletID} id
|
||||
* @param {EventEmitter} watcher
|
||||
*/
|
||||
|
||||
WalletDB.prototype.zap = function zap(now, age, callback) {
|
||||
return this.tx.zap(now, age, callback);
|
||||
WalletDB.prototype.register = function register(id, watcher) {
|
||||
if (!this.watchers[id])
|
||||
this.watchers[id] = [];
|
||||
|
||||
if (this.watchers[id].indexOf(watcher) === -1)
|
||||
this.watchers[id].push(watcher);
|
||||
};
|
||||
|
||||
/**
|
||||
* Zap transactions for wallet.
|
||||
* @see {@link TXDB#zap}.
|
||||
* Unregister an event emitter with the walletdb.
|
||||
* @param {WalletID} id
|
||||
* @param {EventEmitter} watcher
|
||||
*/
|
||||
|
||||
WalletDB.prototype.zapWallet = function zapWallet(id, now, age, callback) {
|
||||
return this.tx.zap(id, now, age, callback);
|
||||
};
|
||||
|
||||
WalletDB.prototype.register = function register(id, provider) {
|
||||
if (!this.providers[id])
|
||||
this.providers[id] = [];
|
||||
|
||||
if (this.providers[id].indexOf(provider) === -1)
|
||||
this.providers[id].push(provider);
|
||||
};
|
||||
|
||||
WalletDB.prototype.unregister = function unregister(id, provider) {
|
||||
var providers = this.providers[id];
|
||||
WalletDB.prototype.unregister = function unregister(id, watcher) {
|
||||
var watchers = this.watchers[id];
|
||||
var i;
|
||||
|
||||
if (!providers)
|
||||
if (!watchers)
|
||||
return;
|
||||
|
||||
i = providers.indexOf(provider);
|
||||
i = watchers.indexOf(watcher);
|
||||
if (i !== -1)
|
||||
providers.splice(i, 1);
|
||||
watchers.splice(i, 1);
|
||||
|
||||
if (providers.length === 0)
|
||||
delete this.providers[id];
|
||||
if (watchers.length === 0)
|
||||
delete this.watchers[id];
|
||||
};
|
||||
|
||||
/**
|
||||
* Fire an event for all registered event emitters.
|
||||
* @param {WalletID} id
|
||||
* @param {...Object} args
|
||||
*/
|
||||
|
||||
WalletDB.prototype.fire = function fire(id) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
var providers = this.providers[id];
|
||||
var watchers = this.watchers[id];
|
||||
var i;
|
||||
|
||||
if (!providers)
|
||||
if (!watchers)
|
||||
return;
|
||||
|
||||
for (i = 0; i < providers.length; i++)
|
||||
providers[i].emit.apply(providers[i], args);
|
||||
for (i = 0; i < watchers.length; i++)
|
||||
watchers[i].emit.apply(watchers[i], args);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test for a listener on a registered event emitter.
|
||||
* @param {WalletID} id
|
||||
* @param {String} event
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.hasListener = function hasListener(id, event) {
|
||||
var providers = this.providers[id];
|
||||
var watchers = this.watchers[id];
|
||||
var i;
|
||||
|
||||
if (!providers)
|
||||
if (!watchers)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < providers.length; i++) {
|
||||
if (providers[i].listeners(event).length !== 0)
|
||||
for (i = 0; i < watchers.length; i++) {
|
||||
if (watchers[i].listeners(event).length !== 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function parsePaths(data) {
|
||||
var p = new BufferReader(data);
|
||||
var out = {};
|
||||
|
||||
while (p.left())
|
||||
out[p.readVarString('utf8')] = p.readVarString('ascii');
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function serializePaths(out) {
|
||||
var p = new BufferWriter();
|
||||
var keys = Object.keys(out);
|
||||
var i, id, path;
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
id = keys[i];
|
||||
path = out[id];
|
||||
p.writeVarString(id, 'utf8');
|
||||
p.writeVarString(path, 'ascii');
|
||||
}
|
||||
|
||||
return p.render();
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user