wallet: more rewriting.
This commit is contained in:
parent
2544e5310a
commit
61a77d90e9
@ -1238,7 +1238,7 @@ ChainDB.prototype.getCoinsByAddress = co(function* getCoinsByAddress(addresses)
|
||||
if (!hash)
|
||||
continue;
|
||||
|
||||
keys = yield this.db.iterate({
|
||||
keys = yield this.db.keys({
|
||||
gte: layout.C(hash, constants.ZERO_HASH, 0),
|
||||
lte: layout.C(hash, constants.MAX_HASH, 0xffffffff),
|
||||
parse: layout.Cc
|
||||
@ -1263,11 +1263,9 @@ ChainDB.prototype.getCoinsByAddress = co(function* getCoinsByAddress(addresses)
|
||||
|
||||
ChainDB.prototype.getEntries = function getEntries() {
|
||||
var self = this;
|
||||
return this.db.iterate({
|
||||
return this.db.values({
|
||||
gte: layout.e(constants.ZERO_HASH),
|
||||
lte: layout.e(constants.MAX_HASH),
|
||||
keys: false,
|
||||
values: true,
|
||||
parse: function(key, value) {
|
||||
return bcoin.chainentry.fromRaw(self.chain, value);
|
||||
}
|
||||
@ -1294,7 +1292,7 @@ ChainDB.prototype.getHashesByAddress = co(function* getHashesByAddress(addresses
|
||||
if (!hash)
|
||||
continue;
|
||||
|
||||
yield this.db.iterate({
|
||||
yield this.db.keys({
|
||||
gte: layout.T(hash, constants.ZERO_HASH),
|
||||
lte: layout.T(hash, constants.MAX_HASH),
|
||||
parse: function(key) {
|
||||
|
||||
@ -301,14 +301,17 @@ LowlevelUp.prototype.has = co(function* has(key) {
|
||||
* @returns {Promise} - Returns Array.
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.iterate = co(function* iterate(options) {
|
||||
LowlevelUp.prototype.range = co(function* range(options) {
|
||||
var items = [];
|
||||
var parse = options.parse;
|
||||
var iter, item, data;
|
||||
var iter, item;
|
||||
|
||||
assert(typeof parse === 'function', 'Parse must be a function.');
|
||||
|
||||
iter = this.iterator(options);
|
||||
iter = this.iterator({
|
||||
gte: options.gte,
|
||||
lte: options.lte,
|
||||
keys: true,
|
||||
values: true
|
||||
});
|
||||
|
||||
for (;;) {
|
||||
item = yield iter.next();
|
||||
@ -316,20 +319,130 @@ LowlevelUp.prototype.iterate = co(function* iterate(options) {
|
||||
if (!item)
|
||||
break;
|
||||
|
||||
try {
|
||||
data = parse(item.key, item.value);
|
||||
} catch (e) {
|
||||
yield iter.end();
|
||||
throw e;
|
||||
if (parse) {
|
||||
try {
|
||||
item = parse(item.key, item.value);
|
||||
} catch (e) {
|
||||
yield iter.end();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (data)
|
||||
items.push(data);
|
||||
if (item)
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
});
|
||||
|
||||
/**
|
||||
* Collect all keys from iterator options.
|
||||
* @param {Object} options - Iterator options.
|
||||
* @returns {Promise} - Returns Array.
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.keys = co(function* keys(options) {
|
||||
var keys = [];
|
||||
var parse = options.parse;
|
||||
var iter, item, key;
|
||||
|
||||
iter = this.iterator({
|
||||
gte: options.gte,
|
||||
lte: options.lte,
|
||||
keys: true,
|
||||
values: false
|
||||
});
|
||||
|
||||
for (;;) {
|
||||
item = yield iter.next();
|
||||
|
||||
if (!item)
|
||||
break;
|
||||
|
||||
key = item.key;
|
||||
|
||||
if (parse) {
|
||||
try {
|
||||
key = parse(key);
|
||||
} catch (e) {
|
||||
yield iter.end();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (key)
|
||||
keys.push(key);
|
||||
}
|
||||
|
||||
return keys;
|
||||
});
|
||||
|
||||
/**
|
||||
* Collect all keys from iterator options.
|
||||
* @param {Object} options - Iterator options.
|
||||
* @returns {Promise} - Returns Array.
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.values = co(function* values(options) {
|
||||
var values = [];
|
||||
var parse = options.parse;
|
||||
var iter, item, value;
|
||||
|
||||
iter = this.iterator({
|
||||
gte: options.gte,
|
||||
lte: options.lte,
|
||||
keys: false,
|
||||
values: true
|
||||
});
|
||||
|
||||
for (;;) {
|
||||
item = yield iter.next();
|
||||
|
||||
if (!item)
|
||||
break;
|
||||
|
||||
value = item.value;
|
||||
|
||||
if (parse) {
|
||||
try {
|
||||
value = parse(value);
|
||||
} catch (e) {
|
||||
yield iter.end();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (value)
|
||||
values.push(value);
|
||||
}
|
||||
|
||||
return values;
|
||||
});
|
||||
|
||||
/**
|
||||
* Dump database (for debugging).
|
||||
* @returns {Promise} - Returns Object.
|
||||
*/
|
||||
|
||||
LowlevelUp.prototype.dump = co(function* dump() {
|
||||
var records = {};
|
||||
var i, items, item, key, value;
|
||||
|
||||
items = yield this.range({
|
||||
gte: new Buffer([0x00]),
|
||||
lte: new Buffer([0xff])
|
||||
});
|
||||
|
||||
for (i = 0; i < items.length; i++) {
|
||||
item = items[i];
|
||||
key = item.key.toString('hex');
|
||||
value = item.value.toString('hex');
|
||||
records[key] = value;
|
||||
}
|
||||
|
||||
return records;
|
||||
});
|
||||
|
||||
/**
|
||||
* Write and assert a version number for the database.
|
||||
* @param {Number} version
|
||||
|
||||
@ -16,6 +16,8 @@ var networks = bcoin.networks;
|
||||
var BufferReader = require('../utils/reader');
|
||||
var BufferWriter = require('../utils/writer');
|
||||
var scriptTypes = constants.scriptTypes;
|
||||
var Address = require('./address');
|
||||
var ec = require('../crypto/ec');
|
||||
|
||||
/**
|
||||
* Represents a key ring which amounts to an address.
|
||||
@ -61,6 +63,9 @@ function KeyRing(options, network) {
|
||||
|
||||
KeyRing.prototype.fromOptions = function fromOptions(options, network) {
|
||||
var key = toKey(options);
|
||||
var script = options.script;
|
||||
var compressed = options.compressed;
|
||||
var network = options.network;
|
||||
|
||||
if (Buffer.isBuffer(key))
|
||||
return this.fromKey(key, network);
|
||||
@ -73,18 +78,15 @@ KeyRing.prototype.fromOptions = function fromOptions(options, network) {
|
||||
if (options.privateKey)
|
||||
key = toKey(options.privateKey);
|
||||
|
||||
if (options.network)
|
||||
this.network = bcoin.network.get(options.network);
|
||||
|
||||
if (options.witness != null) {
|
||||
assert(typeof options.witness === 'boolean');
|
||||
this.witness = options.witness;
|
||||
}
|
||||
|
||||
if (options.script)
|
||||
return this.fromScript(key, options.script, this.network);
|
||||
if (script)
|
||||
return this.fromScript(key, script, compressed, network);
|
||||
|
||||
this.fromKey(key, this.network);
|
||||
this.fromKey(key, compressed, network);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -100,44 +102,51 @@ KeyRing.fromOptions = function fromOptions(options) {
|
||||
/**
|
||||
* Inject data from private key.
|
||||
* @private
|
||||
* @param {Buffer} privateKey
|
||||
* @param {Buffer} key
|
||||
* @param {Boolean?} compressed
|
||||
* @param {(NetworkType|Network}) network
|
||||
*/
|
||||
|
||||
KeyRing.prototype.fromPrivate = function fromPrivate(privateKey, network) {
|
||||
assert(Buffer.isBuffer(privateKey), 'Private key must be a buffer.');
|
||||
assert(bcoin.ec.privateKeyVerify(privateKey), 'Not a valid private key.');
|
||||
KeyRing.prototype.fromPrivate = function fromPrivate(key, compressed, network) {
|
||||
assert(Buffer.isBuffer(key), 'Private key must be a buffer.');
|
||||
assert(ec.privateKeyVerify(key), 'Not a valid private key.');
|
||||
|
||||
if (typeof compressed !== 'boolean') {
|
||||
network = compressed;
|
||||
compressed = null;
|
||||
}
|
||||
|
||||
this.network = bcoin.network.get(network);
|
||||
this.privateKey = privateKey;
|
||||
this.publicKey = bcoin.ec.publicKeyCreate(this.privateKey, true);
|
||||
this.privateKey = key;
|
||||
this.publicKey = ec.publicKeyCreate(key, compressed !== false);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate keyring from a private key.
|
||||
* @param {Buffer} privateKey
|
||||
* @param {Buffer} key
|
||||
* @param {Boolean?} compressed
|
||||
* @param {(NetworkType|Network}) network
|
||||
* @returns {KeyRing}
|
||||
*/
|
||||
|
||||
KeyRing.fromPrivate = function fromPrivate(privateKey, network) {
|
||||
return new KeyRing().fromPrivate(privateKey, network);
|
||||
KeyRing.fromPrivate = function fromPrivate(key, compressed, network) {
|
||||
return new KeyRing().fromPrivate(key, compressed, network);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject data from public key.
|
||||
* @private
|
||||
* @param {Buffer} privateKey
|
||||
* @param {Buffer} key
|
||||
* @param {(NetworkType|Network}) network
|
||||
*/
|
||||
|
||||
KeyRing.prototype.fromPublic = function fromPublic(publicKey, network) {
|
||||
assert(Buffer.isBuffer(publicKey), 'Public key must be a buffer.');
|
||||
assert(bcoin.ec.publicKeyVerify(publicKey), 'Not a valid public key.');
|
||||
KeyRing.prototype.fromPublic = function fromPublic(key, network) {
|
||||
assert(Buffer.isBuffer(key), 'Public key must be a buffer.');
|
||||
assert(ec.publicKeyVerify(key), 'Not a valid public key.');
|
||||
this.network = bcoin.network.get(network);
|
||||
this.publicKey = publicKey;
|
||||
this.publicKey = key;
|
||||
return this;
|
||||
};
|
||||
|
||||
@ -147,12 +156,17 @@ KeyRing.prototype.fromPublic = function fromPublic(publicKey, network) {
|
||||
* @returns {KeyRing}
|
||||
*/
|
||||
|
||||
KeyRing.generate = function(network) {
|
||||
var key = new KeyRing();
|
||||
key.network = bcoin.network.get(network);
|
||||
key.privateKey = bcoin.ec.generatePrivateKey();
|
||||
key.publicKey = bcoin.ec.publicKeyCreate(key.privateKey, true);
|
||||
return key;
|
||||
KeyRing.generate = function(compressed, network) {
|
||||
var key;
|
||||
|
||||
if (typeof compressed !== 'boolean') {
|
||||
network = compressed;
|
||||
compressed = null;
|
||||
}
|
||||
|
||||
key = ec.generatePrivateKey();
|
||||
|
||||
return KeyRing.fromKey(key, compressed, network);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -162,8 +176,8 @@ KeyRing.generate = function(network) {
|
||||
* @returns {KeyRing}
|
||||
*/
|
||||
|
||||
KeyRing.fromPublic = function fromPublic(publicKey, network) {
|
||||
return new KeyRing().fromPublic(publicKey, network);
|
||||
KeyRing.fromPublic = function fromPublic(key, network) {
|
||||
return new KeyRing().fromPublic(key, network);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -173,14 +187,18 @@ KeyRing.fromPublic = function fromPublic(publicKey, network) {
|
||||
* @param {(NetworkType|Network}) network
|
||||
*/
|
||||
|
||||
KeyRing.prototype.fromKey = function fromKey(key, network) {
|
||||
KeyRing.prototype.fromKey = function fromKey(key, compressed, network) {
|
||||
assert(Buffer.isBuffer(key), 'Key must be a buffer.');
|
||||
assert(key.length === 32 || key.length === 33, 'Not a key.');
|
||||
|
||||
if (key.length === 33)
|
||||
return this.fromPublic(key, network);
|
||||
if (typeof compressed !== 'boolean') {
|
||||
network = compressed;
|
||||
compressed = null;
|
||||
}
|
||||
|
||||
return this.fromPrivate(key, network);
|
||||
if (key.length === 32)
|
||||
return this.fromPrivate(key, compressed !== false, network);
|
||||
|
||||
return this.fromPublic(key, network);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -190,8 +208,8 @@ KeyRing.prototype.fromKey = function fromKey(key, network) {
|
||||
* @returns {KeyRing}
|
||||
*/
|
||||
|
||||
KeyRing.fromKey = function fromKey(key, network) {
|
||||
return new KeyRing().fromKey(key, network);
|
||||
KeyRing.fromKey = function fromKey(key, compressed, network) {
|
||||
return new KeyRing().fromKey(key, compressed, network);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -202,10 +220,17 @@ KeyRing.fromKey = function fromKey(key, network) {
|
||||
* @param {(NetworkType|Network}) network
|
||||
*/
|
||||
|
||||
KeyRing.prototype.fromScript = function fromScript(key, script, network) {
|
||||
KeyRing.prototype.fromScript = function fromScript(key, script, compressed, network) {
|
||||
assert(script instanceof bcoin.script, 'Non-script passed into KeyRing.');
|
||||
this.fromKey(key, network);
|
||||
|
||||
if (typeof compressed !== 'boolean') {
|
||||
network = compressed;
|
||||
compressed = null;
|
||||
}
|
||||
|
||||
this.fromKey(key, compressed, network);
|
||||
this.script = script;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@ -217,8 +242,8 @@ KeyRing.prototype.fromScript = function fromScript(key, script, network) {
|
||||
* @returns {KeyRing}
|
||||
*/
|
||||
|
||||
KeyRing.fromScript = function fromScript(key, script, network) {
|
||||
return new KeyRing().fromScript(key, script, network);
|
||||
KeyRing.fromScript = function fromScript(key, script, compressed, network) {
|
||||
return new KeyRing().fromScript(key, script, compressed, network);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -235,7 +260,8 @@ KeyRing.prototype.toSecret = function toSecret() {
|
||||
p.writeU8(this.network.keyPrefix.privkey);
|
||||
p.writeBytes(this.privateKey);
|
||||
|
||||
p.writeU8(1);
|
||||
if (this.publicKey.length === 33)
|
||||
p.writeU8(1);
|
||||
|
||||
p.writeChecksum();
|
||||
|
||||
@ -274,9 +300,7 @@ KeyRing.prototype.fromSecret = function fromSecret(data) {
|
||||
|
||||
p.verifyChecksum();
|
||||
|
||||
assert(compressed === false, 'Cannot handle uncompressed.');
|
||||
|
||||
return this.fromPrivate(key, type);
|
||||
return this.fromPrivate(key, compressed, type);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -528,7 +552,7 @@ KeyRing.prototype.getKeyAddress = function getKeyAddress(enc) {
|
||||
*/
|
||||
|
||||
KeyRing.prototype.compile = function compile(hash, type, version) {
|
||||
return bcoin.address.fromHash(hash, type, version, this.network);
|
||||
return Address.fromHash(hash, type, version, this.network);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -651,7 +675,7 @@ KeyRing.prototype.getRedeem = function(hash) {
|
||||
|
||||
KeyRing.prototype.sign = function sign(msg) {
|
||||
assert(this.privateKey, 'Cannot sign without private key.');
|
||||
return bcoin.ec.sign(msg, this.privateKey);
|
||||
return ec.sign(msg, this.privateKey);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -662,7 +686,7 @@ KeyRing.prototype.sign = function sign(msg) {
|
||||
*/
|
||||
|
||||
KeyRing.prototype.verify = function verify(msg, sig) {
|
||||
return bcoin.ec.verify(msg, sig, this.publicKey);
|
||||
return ec.verify(msg, sig, this.publicKey);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -680,6 +704,22 @@ KeyRing.prototype.getType = function getType() {
|
||||
return scriptTypes.PUBKEYHASH;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get address type.
|
||||
* @returns {ScriptType}
|
||||
*/
|
||||
|
||||
KeyRing.prototype.getAddressType = function getAddressType() {
|
||||
if (this.witness) {
|
||||
if (this.script)
|
||||
return scriptTypes.WITNESSSCRIPTHASH;
|
||||
return scriptTypes.WITNESSPUBKEYHASH;
|
||||
}
|
||||
if (this.script)
|
||||
return scriptTypes.SCRIPTHASH;
|
||||
return scriptTypes.PUBKEYHASH;
|
||||
};
|
||||
|
||||
/*
|
||||
* Getters
|
||||
*/
|
||||
@ -807,10 +847,12 @@ KeyRing.prototype.toRaw = function toRaw(writer) {
|
||||
|
||||
p.writeU8(this.witness ? 1 : 0);
|
||||
|
||||
if (this.privateKey)
|
||||
if (this.privateKey) {
|
||||
p.writeVarBytes(this.privateKey);
|
||||
else
|
||||
p.writeU8(this.publicKey.length === 33);
|
||||
} else {
|
||||
p.writeVarBytes(this.publicKey);
|
||||
}
|
||||
|
||||
if (this.script)
|
||||
p.writeVarBytes(this.script.toRaw());
|
||||
@ -831,7 +873,7 @@ KeyRing.prototype.toRaw = function toRaw(writer) {
|
||||
|
||||
KeyRing.prototype.fromRaw = function fromRaw(data, network) {
|
||||
var p = new BufferReader(data);
|
||||
var key, script;
|
||||
var compressed, key, script;
|
||||
|
||||
this.network = bcoin.network.get(network);
|
||||
this.witness = p.readU8() === 1;
|
||||
@ -839,10 +881,12 @@ KeyRing.prototype.fromRaw = function fromRaw(data, network) {
|
||||
key = p.readVarBytes();
|
||||
|
||||
if (key.length === 32) {
|
||||
compressed = p.readU8() === 1;
|
||||
this.privateKey = key;
|
||||
this.publicKey = bcoin.ec.publicKeyCreate(key, true);
|
||||
this.publicKey = ec.publicKeyCreate(key, compressed);
|
||||
} else {
|
||||
this.publicKey = key;
|
||||
assert(ec.publicKeyVerify(key), 'Invalid public key.');
|
||||
}
|
||||
|
||||
script = p.readVarBytes();
|
||||
|
||||
@ -295,6 +295,21 @@ LRU.prototype.keys = function keys() {
|
||||
return keys;
|
||||
};
|
||||
|
||||
/**
|
||||
* Collect all values in the cache, sorted by LRU.
|
||||
* @returns {String[]}
|
||||
*/
|
||||
|
||||
LRU.prototype.values = function values() {
|
||||
var values = [];
|
||||
var item;
|
||||
|
||||
for (item = this.head; item; item = item.next)
|
||||
values.push(item.value);
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the LRU cache to an array of items.
|
||||
* @returns {Object[]}
|
||||
@ -336,12 +351,16 @@ function NullCache(size) {}
|
||||
NullCache.prototype.set = function set(key, value) {};
|
||||
NullCache.prototype.remove = function remove(key) {};
|
||||
NullCache.prototype.get = function get(key) {};
|
||||
NullCache.prototype.has = function has(key) {};
|
||||
NullCache.prototype.has = function has(key) { return false; };
|
||||
NullCache.prototype.reset = function reset() {};
|
||||
NullCache.prototype.keys = function keys(key) { return []; };
|
||||
NullCache.prototype.values = function values(key) { return []; };
|
||||
NullCache.prototype.toArray = function toArray(key) { return []; };
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
LRU.nil = NullCache;
|
||||
|
||||
module.exports = LRU;
|
||||
|
||||
@ -349,12 +349,7 @@ Account.prototype._checkKeys = co(function* _checkKeys() {
|
||||
ring = this.deriveReceive(0);
|
||||
hash = ring.getScriptHash('hex');
|
||||
|
||||
paths = yield this.db.getAddressPaths(hash);
|
||||
|
||||
if (!paths)
|
||||
return false;
|
||||
|
||||
return paths[this.wid] != null;
|
||||
return yield this.db.hasAddress(this.wid, hash);
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@ -17,6 +17,12 @@ layout.walletdb = {
|
||||
pp: function(key) {
|
||||
return key.slice(1);
|
||||
},
|
||||
P: function(wid, hash) {
|
||||
return 'p' + pad32(wid) + hash;
|
||||
},
|
||||
Pp: function(key) {
|
||||
return key.slice(11);
|
||||
},
|
||||
w: function(wid) {
|
||||
return 'w' + pad32(wid);
|
||||
},
|
||||
|
||||
@ -30,8 +30,10 @@ function Path() {
|
||||
if (!(this instanceof Path))
|
||||
return new Path();
|
||||
|
||||
// Passed in by caller.
|
||||
this.wid = null;
|
||||
this.name = null; // Passed in by caller.
|
||||
this.name = null;
|
||||
|
||||
this.account = 0;
|
||||
this.change = -1;
|
||||
this.index = -1;
|
||||
@ -97,10 +99,7 @@ Path.prototype.clone = function clone() {
|
||||
Path.prototype.fromRaw = function fromRaw(data) {
|
||||
var p = new BufferReader(data);
|
||||
|
||||
this.wid = p.readU32();
|
||||
this.account = p.readU32();
|
||||
this.change = -1;
|
||||
this.index = -1;
|
||||
this.keyType = p.readU8();
|
||||
|
||||
switch (this.keyType) {
|
||||
@ -144,9 +143,7 @@ Path.fromRaw = function fromRaw(data) {
|
||||
Path.prototype.toRaw = function toRaw(writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
|
||||
p.writeU32(this.wid);
|
||||
p.writeU32(this.account);
|
||||
|
||||
p.writeU8(this.keyType);
|
||||
|
||||
switch (this.keyType) {
|
||||
@ -199,7 +196,7 @@ Path.prototype.fromHD = function fromHD(account, ring, change, index) {
|
||||
this.keyType = Path.types.HD;
|
||||
|
||||
this.version = ring.witness ? 0 : -1;
|
||||
this.type = ring.getType();
|
||||
this.type = ring.getAddressType();
|
||||
|
||||
this.id = account.id;
|
||||
this.hash = ring.getHash('hex');
|
||||
@ -234,7 +231,7 @@ Path.prototype.fromKey = function fromKey(account, ring) {
|
||||
this.keyType = Path.types.KEY;
|
||||
this.data = ring.toRaw();
|
||||
this.version = ring.witness ? 0 : -1;
|
||||
this.type = ring.getType();
|
||||
this.type = ring.getAddressType();
|
||||
this.id = account.id;
|
||||
this.hash = ring.getHash('hex');
|
||||
return this;
|
||||
@ -312,8 +309,9 @@ Path.prototype.toAddress = function toAddress(network) {
|
||||
Path.prototype.toJSON = function toJSON() {
|
||||
return {
|
||||
name: this.name,
|
||||
account: this.account,
|
||||
change: this.change === 1,
|
||||
path: this.toPath()
|
||||
derivation: this.toPath()
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -341,12 +341,40 @@ TXDB.prototype.has = function has(key) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.iterate = function iterate(options) {
|
||||
TXDB.prototype.range = function range(options) {
|
||||
if (options.gte)
|
||||
options.gte = this.prefix(options.gte);
|
||||
if (options.lte)
|
||||
options.lte = this.prefix(options.lte);
|
||||
return this.db.iterate(options);
|
||||
return this.db.range(options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterate.
|
||||
* @param {Object} options
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.keys = function keys(options) {
|
||||
if (options.gte)
|
||||
options.gte = this.prefix(options.gte);
|
||||
if (options.lte)
|
||||
options.lte = this.prefix(options.lte);
|
||||
return this.db.keys(options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterate.
|
||||
* @param {Object} options
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.values = function values(options) {
|
||||
if (options.gte)
|
||||
options.gte = this.prefix(options.gte);
|
||||
if (options.lte)
|
||||
options.lte = this.prefix(options.lte);
|
||||
return this.db.values(options);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1307,7 +1335,7 @@ TXDB.prototype.getLocked = function getLocked() {
|
||||
*/
|
||||
|
||||
TXDB.prototype.getHistoryHashes = function getHistoryHashes(account) {
|
||||
return this.iterate({
|
||||
return this.keys({
|
||||
gte: account != null
|
||||
? layout.T(account, constants.NULL_HASH)
|
||||
: layout.t(constants.NULL_HASH),
|
||||
@ -1332,7 +1360,7 @@ TXDB.prototype.getHistoryHashes = function getHistoryHashes(account) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.getUnconfirmedHashes = function getUnconfirmedHashes(account) {
|
||||
return this.iterate({
|
||||
return this.keys({
|
||||
gte: account != null
|
||||
? layout.P(account, constants.NULL_HASH)
|
||||
: layout.p(constants.NULL_HASH),
|
||||
@ -1357,7 +1385,7 @@ TXDB.prototype.getUnconfirmedHashes = function getUnconfirmedHashes(account) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.getOutpoints = function getOutpoints(account) {
|
||||
return this.iterate({
|
||||
return this.keys({
|
||||
gte: account != null
|
||||
? layout.C(account, constants.NULL_HASH, 0)
|
||||
: layout.c(constants.NULL_HASH, 0),
|
||||
@ -1397,7 +1425,7 @@ TXDB.prototype.getHeightRangeHashes = function getHeightRangeHashes(account, opt
|
||||
start = options.start || 0;
|
||||
end = options.end || 0xffffffff;
|
||||
|
||||
return this.iterate({
|
||||
return this.keys({
|
||||
gte: account != null
|
||||
? layout.H(account, start, constants.NULL_HASH)
|
||||
: layout.h(start, constants.NULL_HASH),
|
||||
@ -1449,7 +1477,7 @@ TXDB.prototype.getRangeHashes = function getRangeHashes(account, options) {
|
||||
start = options.start || 0;
|
||||
end = options.end || 0xffffffff;
|
||||
|
||||
return this.iterate({
|
||||
return this.keys({
|
||||
gte: account != null
|
||||
? layout.M(account, start, constants.NULL_HASH)
|
||||
: layout.m(start, constants.NULL_HASH),
|
||||
@ -1532,14 +1560,10 @@ TXDB.prototype.getHistory = function getHistory(account) {
|
||||
return this.getAccountHistory(account);
|
||||
|
||||
// Fast case
|
||||
return this.iterate({
|
||||
return this.values({
|
||||
gte: layout.t(constants.NULL_HASH),
|
||||
lte: layout.t(constants.HIGH_HASH),
|
||||
keys: false,
|
||||
values: true,
|
||||
parse: function(key, value) {
|
||||
return bcoin.tx.fromExtended(value);
|
||||
}
|
||||
parse: bcoin.tx.fromExtended
|
||||
});
|
||||
};
|
||||
|
||||
@ -1607,10 +1631,9 @@ TXDB.prototype.getCoins = function getCoins(account) {
|
||||
return this.getAccountCoins(account);
|
||||
|
||||
// Fast case
|
||||
return this.iterate({
|
||||
gte: layout.c(constants.NULL_HASH, 0),
|
||||
return this.range({
|
||||
gte: layout.c(constants.NULL_HASH, 0x00000000),
|
||||
lte: layout.c(constants.HIGH_HASH, 0xffffffff),
|
||||
values: true,
|
||||
parse: function(key, value) {
|
||||
var parts = layout.cc(key);
|
||||
var hash = parts[0];
|
||||
@ -1663,10 +1686,9 @@ TXDB.prototype.fillHistory = function fillHistory(tx) {
|
||||
|
||||
hash = tx.hash('hex');
|
||||
|
||||
return this.iterate({
|
||||
gte: layout.d(hash, 0),
|
||||
return this.range({
|
||||
gte: layout.d(hash, 0x00000000),
|
||||
lte: layout.d(hash, 0xffffffff),
|
||||
values: true,
|
||||
parse: function(key, value) {
|
||||
var index = layout.dd(key)[1];
|
||||
var coin = bcoin.coin.fromRaw(value);
|
||||
@ -1897,10 +1919,9 @@ TXDB.prototype.getBalance = co(function* getBalance(account) {
|
||||
// Fast case
|
||||
balance = new Balance(this.wallet);
|
||||
|
||||
yield this.iterate({
|
||||
gte: layout.c(constants.NULL_HASH, 0),
|
||||
yield this.range({
|
||||
gte: layout.c(constants.NULL_HASH, 0x00000000),
|
||||
lte: layout.c(constants.HIGH_HASH, 0xffffffff),
|
||||
values: true,
|
||||
parse: function(key, data) {
|
||||
var parts = layout.cc(key);
|
||||
var hash = parts[0];
|
||||
|
||||
@ -403,6 +403,56 @@ Wallet.prototype._retoken = co(function* retoken(passphrase) {
|
||||
return this.token;
|
||||
});
|
||||
|
||||
/**
|
||||
* Rename the wallet.
|
||||
* @param {String} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
Wallet.prototype.rename = co(function* rename(id) {
|
||||
var unlock = yield this.writeLock.lock();
|
||||
try {
|
||||
return yield this.db.rename(this, id);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Rename account.
|
||||
* @param {(String|Number)?} account
|
||||
* @param {String} name
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
Wallet.prototype.renameAccount = co(function* renameAccount(account, name) {
|
||||
var unlock = yield this.writeLock.lock();
|
||||
try {
|
||||
return yield this._renameAccount(account, name);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Rename account without a lock.
|
||||
* @private
|
||||
* @param {(String|Number)?} account
|
||||
* @param {String} name
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
Wallet.prototype._renameAccount = co(function* _renameAccount(account, name) {
|
||||
assert(utils.isName(name), 'Bad account name.');
|
||||
|
||||
account = yield this.getAccount(account);
|
||||
|
||||
if (!account)
|
||||
throw new Error('Account not found.');
|
||||
|
||||
yield this.db.renameAccount(account, name);
|
||||
});
|
||||
|
||||
/**
|
||||
* Lock the wallet, destroy decrypted key.
|
||||
*/
|
||||
@ -587,7 +637,7 @@ Wallet.prototype.getAccounts = function getAccounts() {
|
||||
*/
|
||||
|
||||
Wallet.prototype.getAddressHashes = function getAddressHashes() {
|
||||
return this.db.getAddressHashes(this.wid);
|
||||
return this.db.getWalletHashes(this.wid);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -759,7 +809,7 @@ Wallet.prototype.getPath = co(function* getPath(address) {
|
||||
if (!hash)
|
||||
return;
|
||||
|
||||
path = yield this.db.getAddressPath(this.wid, hash);
|
||||
path = yield this.db.getPath(this.wid, hash);
|
||||
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
@ -23,7 +23,8 @@ var MAX_POINT = String.fromCharCode(0xdbff, 0xdfff); // U+10FFFF
|
||||
|
||||
/*
|
||||
* Database Layout:
|
||||
* p[addr-hash] -> path data
|
||||
* p[addr-hash] -> wallet ids
|
||||
* P[wid][addr-hash] -> path data
|
||||
* w[wid] -> wallet
|
||||
* l[id] -> wid
|
||||
* a[wid][index] -> account
|
||||
@ -44,6 +45,16 @@ var layout = {
|
||||
pp: function(key) {
|
||||
return key.toString('hex', 1);
|
||||
},
|
||||
P: function(wid, hash) {
|
||||
var key = new Buffer(1 + 4 + (hash.length / 2));
|
||||
key[0] = 0x50;
|
||||
key.writeUInt32BE(wid, 1, true);
|
||||
key.write(hash, 5, 'hex');
|
||||
return key;
|
||||
},
|
||||
Pp: function(key) {
|
||||
return key.toString('hex', 5);
|
||||
},
|
||||
w: function(wid) {
|
||||
var key = new Buffer(5);
|
||||
key[0] = 0x77;
|
||||
@ -140,9 +151,11 @@ function WalletDB(options) {
|
||||
this.writeLock = new bcoin.locker();
|
||||
this.txLock = new bcoin.locker();
|
||||
|
||||
this.walletCache = new bcoin.lru(10000);
|
||||
this.widCache = new bcoin.lru(10000);
|
||||
this.indexCache = new bcoin.lru(10000);
|
||||
this.accountCache = new bcoin.lru(10000);
|
||||
this.pathCache = new bcoin.lru(100000);
|
||||
this.pathMapCache = new bcoin.lru(100000);
|
||||
|
||||
// Try to optimize for up to 1m addresses.
|
||||
// We use a regular bloom filter here
|
||||
@ -276,9 +289,12 @@ WalletDB.prototype.getDepth = co(function* getDepth() {
|
||||
*/
|
||||
|
||||
WalletDB.prototype.start = function start(wid) {
|
||||
var batch;
|
||||
assert(utils.isNumber(wid), 'Bad ID for batch.');
|
||||
assert(!this.batches[wid], 'Batch already started.');
|
||||
this.batches[wid] = this.db.batch();
|
||||
batch = this.db.batch();
|
||||
this.batches[wid] = batch;
|
||||
return batch;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -327,21 +343,27 @@ WalletDB.prototype.commit = function commit(wid) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.loadFilter = function loadFilter() {
|
||||
var self = this;
|
||||
WalletDB.prototype.loadFilter = co(function* loadFilter() {
|
||||
var iter, item, hash;
|
||||
|
||||
if (!this.filter)
|
||||
return Promise.resolve(null);
|
||||
return;
|
||||
|
||||
return this.db.iterate({
|
||||
iter = this.db.iterator({
|
||||
gte: layout.p(constants.NULL_HASH),
|
||||
lte: layout.p(constants.HIGH_HASH),
|
||||
parse: function(key) {
|
||||
var hash = layout.pp(key);
|
||||
self.filter.add(hash, 'hex');
|
||||
}
|
||||
lte: layout.p(constants.HIGH_HASH)
|
||||
});
|
||||
};
|
||||
|
||||
for (;;) {
|
||||
item = yield iter.next();
|
||||
|
||||
if (!item)
|
||||
break;
|
||||
|
||||
hash = layout.pp(item.key);
|
||||
this.filter.add(hash, 'hex');
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test the bloom filter against an array of address hashes.
|
||||
@ -369,18 +391,9 @@ WalletDB.prototype.testFilter = function testFilter(hashes) {
|
||||
* @returns {Promise} - Returns Object.
|
||||
*/
|
||||
|
||||
WalletDB.prototype.dump = co(function* dump() {
|
||||
var records = {};
|
||||
yield this.db.iterate({
|
||||
gte: new Buffer([0x00]),
|
||||
lte: new Buffer([0xff]),
|
||||
values: true,
|
||||
parse: function(key, value) {
|
||||
records[key.toString('hex')] = value.toString('hex');
|
||||
}
|
||||
});
|
||||
return records;
|
||||
});
|
||||
WalletDB.prototype.dump = function dump() {
|
||||
return this.db.dump();
|
||||
};
|
||||
|
||||
/**
|
||||
* Register an object with the walletdb.
|
||||
@ -418,7 +431,7 @@ WalletDB.prototype.getWalletID = co(function* getWalletID(id) {
|
||||
if (typeof id === 'number')
|
||||
return id;
|
||||
|
||||
wid = this.walletCache.get(id);
|
||||
wid = this.widCache.get(id);
|
||||
|
||||
if (wid)
|
||||
return wid;
|
||||
@ -430,7 +443,7 @@ WalletDB.prototype.getWalletID = co(function* getWalletID(id) {
|
||||
|
||||
wid = data.readUInt32LE(0, true);
|
||||
|
||||
this.walletCache.set(id, wid);
|
||||
this.widCache.set(id, wid);
|
||||
|
||||
return wid;
|
||||
});
|
||||
@ -441,11 +454,10 @@ WalletDB.prototype.getWalletID = co(function* getWalletID(id) {
|
||||
* @returns {Promise} - Returns {@link Wallet}.
|
||||
*/
|
||||
|
||||
WalletDB.prototype.get = co(function* get(wid) {
|
||||
WalletDB.prototype.get = co(function* get(id) {
|
||||
var wid = yield this.getWalletID(id);
|
||||
var unlock;
|
||||
|
||||
wid = yield this.getWalletID(wid);
|
||||
|
||||
if (!wid)
|
||||
return;
|
||||
|
||||
@ -466,11 +478,8 @@ WalletDB.prototype.get = co(function* get(wid) {
|
||||
*/
|
||||
|
||||
WalletDB.prototype._get = co(function* get(wid) {
|
||||
var data, wallet;
|
||||
|
||||
// By the time the lock is released,
|
||||
// the wallet may be watched.
|
||||
wallet = this.wallets[wid];
|
||||
var wallet = this.wallets[wid];
|
||||
var data;
|
||||
|
||||
if (wallet)
|
||||
return wallet;
|
||||
@ -495,14 +504,123 @@ WalletDB.prototype._get = co(function* get(wid) {
|
||||
*/
|
||||
|
||||
WalletDB.prototype.save = function save(wallet) {
|
||||
var batch = this.batch(wallet.wid);
|
||||
var wid = new Buffer(4);
|
||||
this.walletCache.set(wallet.id, wallet.wid);
|
||||
batch.put(layout.w(wallet.wid), wallet.toRaw());
|
||||
wid.writeUInt32LE(wallet.wid, 0, true);
|
||||
batch.put(layout.l(wallet.id), wid);
|
||||
var wid = wallet.wid;
|
||||
var id = wallet.id;
|
||||
var batch = this.batch(wid);
|
||||
var buf = new Buffer(4);
|
||||
|
||||
this.widCache.set(id, wid);
|
||||
|
||||
batch.put(layout.w(wid), wallet.toRaw());
|
||||
|
||||
buf.writeUInt32LE(wid, 0, true);
|
||||
batch.put(layout.l(id), buf);
|
||||
};
|
||||
|
||||
/**
|
||||
* Rename a wallet.
|
||||
* @param {Wallet} wallet
|
||||
* @param {String} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.rename = co(function* rename(wallet, id) {
|
||||
var unlock = yield this.writeLock.lock();
|
||||
try {
|
||||
return yield this._rename(wallet, id);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Rename a wallet without a lock.
|
||||
* @private
|
||||
* @param {Wallet} wallet
|
||||
* @param {String} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype._rename = co(function* _rename(wallet, id) {
|
||||
var old = wallet.id;
|
||||
var i, paths, path, batch;
|
||||
|
||||
assert(utils.isName(id), 'Bad wallet ID.');
|
||||
|
||||
if (yield this.has(id))
|
||||
throw new Error('ID not available.');
|
||||
|
||||
this.widCache.remove(old);
|
||||
|
||||
paths = this.pathCache.values();
|
||||
|
||||
// TODO: Optimize this bullshit.
|
||||
for (i = 0; i < paths.length; i++) {
|
||||
path = paths[i];
|
||||
|
||||
if (path.wid !== wallet.wid)
|
||||
continue;
|
||||
|
||||
path.id = id;
|
||||
}
|
||||
|
||||
wallet.id = id;
|
||||
|
||||
batch = this.start(wallet.wid);
|
||||
batch.del(layout.l(old));
|
||||
|
||||
this.save(wallet);
|
||||
|
||||
yield this.commit(wallet.wid);
|
||||
});
|
||||
|
||||
/**
|
||||
* Rename an account.
|
||||
* @param {Account} account
|
||||
* @param {String} name
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.renameAccount = co(function* renameAccount(account, name) {
|
||||
var old = account.name;
|
||||
var key = account.wid + '/' + old;
|
||||
var i, paths, path, batch;
|
||||
|
||||
assert(utils.isName(name), 'Bad account name.');
|
||||
|
||||
if (account.accountIndex === 0)
|
||||
throw new Error('Cannot rename primary account.');
|
||||
|
||||
if (yield this.hasAccount(account.wid, name))
|
||||
throw new Error('Account name not available.');
|
||||
|
||||
this.indexCache.remove(key);
|
||||
|
||||
paths = this.pathCache.values();
|
||||
|
||||
// TODO: Optimize this bullshit.
|
||||
for (i = 0; i < paths.length; i++) {
|
||||
path = paths[i];
|
||||
|
||||
if (path.wid !== account.wid)
|
||||
continue;
|
||||
|
||||
if (path.account !== account.accountIndex)
|
||||
continue;
|
||||
|
||||
path.name = name;
|
||||
}
|
||||
|
||||
account.name = name;
|
||||
|
||||
batch = this.start(account.wid);
|
||||
batch.del(layout.i(account.wid, old));
|
||||
|
||||
this.saveAccount(account);
|
||||
|
||||
yield this.commit(account.wid);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test an api key against a wallet's api key.
|
||||
* @param {WalletID} wid
|
||||
@ -657,19 +775,20 @@ WalletDB.prototype._getAccount = co(function* getAccount(wid, index) {
|
||||
|
||||
WalletDB.prototype.getAccounts = co(function* getAccounts(wid) {
|
||||
var map = [];
|
||||
var i, accounts;
|
||||
var i, items, item, name, index, accounts;
|
||||
|
||||
yield this.db.iterate({
|
||||
items = yield this.db.range({
|
||||
gte: layout.i(wid, ''),
|
||||
lte: layout.i(wid, MAX_POINT),
|
||||
values: true,
|
||||
parse: function(key, value) {
|
||||
var name = layout.ii(key)[1];
|
||||
var index = value.readUInt32LE(0, true);
|
||||
map[index] = name;
|
||||
}
|
||||
lte: layout.i(wid, MAX_POINT)
|
||||
});
|
||||
|
||||
for (i = 0; i < items.length; i++) {
|
||||
item = items[i];
|
||||
name = layout.ii(item.key)[1];
|
||||
index = item.value.readUInt32LE(0, true);
|
||||
map[index] = name;
|
||||
}
|
||||
|
||||
// Get it out of hash table mode.
|
||||
accounts = [];
|
||||
|
||||
@ -689,7 +808,7 @@ WalletDB.prototype.getAccounts = co(function* getAccounts(wid) {
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getAccountIndex = co(function* getAccountIndex(wid, name) {
|
||||
var index;
|
||||
var key, index;
|
||||
|
||||
if (!wid)
|
||||
return -1;
|
||||
@ -700,12 +819,22 @@ WalletDB.prototype.getAccountIndex = co(function* getAccountIndex(wid, name) {
|
||||
if (typeof name === 'number')
|
||||
return name;
|
||||
|
||||
key = wid + '/' + name;
|
||||
index = this.indexCache.get(key);
|
||||
|
||||
if (index != null)
|
||||
return index;
|
||||
|
||||
index = yield this.db.get(layout.i(wid, name));
|
||||
|
||||
if (!index)
|
||||
return -1;
|
||||
|
||||
return index.readUInt32LE(0, true);
|
||||
index = index.readUInt32LE(0, true);
|
||||
|
||||
this.indexCache.set(key, index);
|
||||
|
||||
return index;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -731,14 +860,17 @@ WalletDB.prototype.getAccountName = co(function* getAccountName(wid, index) {
|
||||
*/
|
||||
|
||||
WalletDB.prototype.saveAccount = function saveAccount(account) {
|
||||
var batch = this.batch(account.wid);
|
||||
var index = new Buffer(4);
|
||||
var key = account.wid + '/' + account.accountIndex;
|
||||
var wid = account.wid;
|
||||
var index = account.accountIndex;
|
||||
var name = account.name;
|
||||
var batch = this.batch(wid);
|
||||
var key = wid + '/' + index;
|
||||
var buf = new Buffer(4);
|
||||
|
||||
index.writeUInt32LE(account.accountIndex, 0, true);
|
||||
buf.writeUInt32LE(index, 0, true);
|
||||
|
||||
batch.put(layout.a(account.wid, account.accountIndex), account.toRaw());
|
||||
batch.put(layout.i(account.wid, account.name), index);
|
||||
batch.put(layout.a(wid, index), account.toRaw());
|
||||
batch.put(layout.i(wid, name), buf);
|
||||
|
||||
this.accountCache.set(key, account);
|
||||
};
|
||||
@ -794,10 +926,34 @@ WalletDB.prototype.hasAccount = co(function* hasAccount(wid, account) {
|
||||
return yield this.db.has(layout.a(wid, index));
|
||||
});
|
||||
|
||||
/**
|
||||
* Lookup the corresponding account name's index.
|
||||
* @param {WalletID} wid
|
||||
* @param {String|Number} name - Account name/index.
|
||||
* @returns {Promise} - Returns Number.
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getWalletsByHash = co(function* getWalletsByHash(hash) {
|
||||
var wallets = this.pathMapCache.get(hash);
|
||||
var data;
|
||||
|
||||
if (wallets)
|
||||
return wallets;
|
||||
|
||||
data = yield this.db.get(layout.p(hash));
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
wallets = parseWallets(data);
|
||||
|
||||
this.pathMapCache.get(hash, wallets);
|
||||
|
||||
return wallets;
|
||||
});
|
||||
|
||||
/**
|
||||
* Save addresses to the path map.
|
||||
* The path map exists in the form of:
|
||||
* `p/[address-hash] -> {walletid1=path1, walletid2=path2, ...}`
|
||||
* @param {WalletID} wid
|
||||
* @param {KeyRing[]} rings
|
||||
* @returns {Promise}
|
||||
@ -810,7 +966,7 @@ WalletDB.prototype.saveAddress = co(function* saveAddress(wid, rings) {
|
||||
ring = rings[i];
|
||||
path = ring.path;
|
||||
|
||||
yield this.writeAddress(wid, ring.getAddress(), path);
|
||||
yield this.writePath(wid, path);
|
||||
|
||||
if (!ring.witness)
|
||||
continue;
|
||||
@ -820,45 +976,17 @@ WalletDB.prototype.saveAddress = co(function* saveAddress(wid, rings) {
|
||||
path.version = -1;
|
||||
path.type = Script.types.SCRIPTHASH;
|
||||
|
||||
yield this.writeAddress(wid, ring.getProgramAddress(), path);
|
||||
yield this.writePath(wid, path);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Save a single address to the path map.
|
||||
* @param {WalletID} wid
|
||||
* @param {KeyRing} rings
|
||||
* @param {Path} path
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.writeAddress = co(function* writeAddress(wid, address, path) {
|
||||
var hash = address.getHash('hex');
|
||||
var batch = this.batch(wid);
|
||||
var paths;
|
||||
|
||||
if (this.filter)
|
||||
this.filter.add(hash, 'hex');
|
||||
|
||||
this.emit('save address', address, path);
|
||||
|
||||
paths = yield this.getAddressPaths(hash);
|
||||
|
||||
if (!paths)
|
||||
paths = {};
|
||||
|
||||
if (paths[wid])
|
||||
return;
|
||||
|
||||
paths[wid] = path;
|
||||
|
||||
this.pathCache.set(hash, paths);
|
||||
|
||||
batch.put(layout.p(hash), serializePaths(paths));
|
||||
});
|
||||
|
||||
/**
|
||||
* Save paths to the path map.
|
||||
*
|
||||
* The path map exists in the form of:
|
||||
* - `p[address-hash] -> wids`
|
||||
* - `P[wid][address-hash] -> path`
|
||||
*
|
||||
* @param {WalletID} wid
|
||||
* @param {Path[]} paths
|
||||
* @returns {Promise}
|
||||
@ -883,26 +1011,29 @@ WalletDB.prototype.savePath = co(function* savePath(wid, paths) {
|
||||
WalletDB.prototype.writePath = co(function* writePath(wid, path) {
|
||||
var hash = path.hash;
|
||||
var batch = this.batch(wid);
|
||||
var paths;
|
||||
var key = wid + hash;
|
||||
var wallets;
|
||||
|
||||
if (this.filter)
|
||||
this.filter.add(hash, 'hex');
|
||||
|
||||
this.emit('save address', path.toAddress(), path);
|
||||
|
||||
paths = yield this.getAddressPaths(hash);
|
||||
wallets = yield this.getWalletsByHash(hash);
|
||||
|
||||
if (!paths)
|
||||
paths = {};
|
||||
if (!wallets)
|
||||
wallets = [];
|
||||
|
||||
if (paths[wid])
|
||||
if (wallets.indexOf(wid) !== -1)
|
||||
return;
|
||||
|
||||
paths[wid] = path;
|
||||
wallets.push(wid);
|
||||
|
||||
this.pathCache.set(hash, paths);
|
||||
this.pathMapCache.set(hash, wallets);
|
||||
this.pathCache.set(key, path);
|
||||
|
||||
batch.put(layout.p(hash), serializePaths(paths));
|
||||
batch.put(layout.p(hash), serializeWallets(wallets));
|
||||
batch.put(layout.P(wid, hash), path.toRaw());
|
||||
});
|
||||
|
||||
/**
|
||||
@ -911,47 +1042,57 @@ WalletDB.prototype.writePath = co(function* writePath(wid, path) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getAddressPaths = co(function* getAddressPaths(hash) {
|
||||
var paths, data;
|
||||
WalletDB.prototype.getPaths = co(function* getPaths(hash) {
|
||||
var wallets = yield this.getWalletsByHash(hash);
|
||||
var i, wid, path, paths;
|
||||
|
||||
if (!hash)
|
||||
if (!wallets)
|
||||
return;
|
||||
|
||||
paths = this.pathCache.get(hash);
|
||||
paths = [];
|
||||
|
||||
if (paths)
|
||||
return paths;
|
||||
|
||||
data = yield this.db.get(layout.p(hash));
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
paths = parsePaths(data, hash);
|
||||
|
||||
yield this.fillPathNames(paths);
|
||||
|
||||
this.pathCache.set(hash, paths);
|
||||
for (i = 0; i < wallets.length; i++) {
|
||||
wid = wallets[i];
|
||||
path = yield this.getPath(wid, hash);
|
||||
if (path)
|
||||
paths.push(path);
|
||||
}
|
||||
|
||||
return paths;
|
||||
});
|
||||
|
||||
/**
|
||||
* Assign account names to an array of paths.
|
||||
* @param {Path[]} paths
|
||||
* Retrieve path by hash.
|
||||
* @param {WalletID} wid
|
||||
* @param {Hash} hash
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.fillPathNames = co(function* fillPathNames(paths) {
|
||||
var i, path;
|
||||
WalletDB.prototype.getPath = co(function* getPath(wid, hash) {
|
||||
var key, path, data;
|
||||
|
||||
for (i = 0; i < paths.length; i++) {
|
||||
path = paths[i];
|
||||
if (path.name)
|
||||
continue;
|
||||
// These should be mostly cached.
|
||||
path.name = yield this.db.getAccountName(path.wid, path.account);
|
||||
}
|
||||
if (!hash)
|
||||
return;
|
||||
|
||||
key = wid + hash;
|
||||
path = this.pathCache.get(key);
|
||||
|
||||
if (path)
|
||||
return path;
|
||||
|
||||
data = yield this.db.get(layout.P(wid, hash));
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
path = Path.fromRaw(data);
|
||||
path.wid = wid;
|
||||
path.hash = hash;
|
||||
path.name = yield this.getAccountName(wid, path.account);
|
||||
|
||||
this.pathCache.set(key, path);
|
||||
|
||||
return path;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -963,33 +1104,34 @@ WalletDB.prototype.fillPathNames = co(function* fillPathNames(paths) {
|
||||
*/
|
||||
|
||||
WalletDB.prototype.hasAddress = co(function* hasAddress(wid, hash) {
|
||||
var paths = yield this.getAddressPaths(hash);
|
||||
|
||||
if (!paths || !paths[wid])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
var path = yield this.getPath(wid, hash);
|
||||
return path != null;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get all address hashes.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getHashes = function getHashes() {
|
||||
return this.db.keys({
|
||||
gte: layout.p(constants.NULL_HASH),
|
||||
lte: layout.p(constants.HIGH_HASH),
|
||||
parse: layout.pp
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all address hashes.
|
||||
* @param {WalletID} wid
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getAddressHashes = function getAddressHashes(wid) {
|
||||
return this.db.iterate({
|
||||
gte: layout.p(constants.NULL_HASH),
|
||||
lte: layout.p(constants.HIGH_HASH),
|
||||
values: true,
|
||||
parse: function(key, value) {
|
||||
var paths = parsePaths(value);
|
||||
|
||||
if (wid && !paths[wid])
|
||||
return;
|
||||
|
||||
return layout.pp(key);
|
||||
}
|
||||
WalletDB.prototype.getWalletHashes = function getWalletHashes(wid) {
|
||||
return this.db.keys({
|
||||
gte: layout.P(wid, constants.NULL_HASH),
|
||||
lte: layout.P(wid, constants.HIGH_HASH),
|
||||
parse: layout.Pp
|
||||
});
|
||||
};
|
||||
|
||||
@ -1000,25 +1142,26 @@ WalletDB.prototype.getAddressHashes = function getAddressHashes(wid) {
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getWalletPaths = co(function* getWalletPaths(wid) {
|
||||
var paths = yield this.db.iterate({
|
||||
gte: layout.p(constants.NULL_HASH),
|
||||
lte: layout.p(constants.HIGH_HASH),
|
||||
values: true,
|
||||
parse: function(key, value) {
|
||||
var hash = layout.pp(key);
|
||||
var paths = parsePaths(value, hash);
|
||||
var path = paths[wid];
|
||||
var i, item, items, hash, path;
|
||||
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
return path;
|
||||
}
|
||||
items = yield this.db.range({
|
||||
gte: layout.P(wid, constants.NULL_HASH),
|
||||
lte: layout.P(wid, constants.HIGH_HASH)
|
||||
});
|
||||
|
||||
yield this.fillPathNames(paths);
|
||||
for (i = 0; i < items.length; i++) {
|
||||
item = items[i];
|
||||
hash = layout.Pp(item.key);
|
||||
path = Path.fromRaw(item.value);
|
||||
|
||||
return paths;
|
||||
path.hash = hash;
|
||||
path.wid = wid;
|
||||
path.name = yield this.getAccountName(wid, path.account);
|
||||
|
||||
items[i] = path;
|
||||
}
|
||||
|
||||
return items;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1027,12 +1170,10 @@ WalletDB.prototype.getWalletPaths = co(function* getWalletPaths(wid) {
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getWallets = function getWallets() {
|
||||
return this.db.iterate({
|
||||
return this.db.keys({
|
||||
gte: layout.l(''),
|
||||
lte: layout.l(MAX_POINT),
|
||||
parse: function(key) {
|
||||
return layout.ll(key);
|
||||
}
|
||||
parse: layout.ll
|
||||
});
|
||||
};
|
||||
|
||||
@ -1067,7 +1208,7 @@ WalletDB.prototype._rescan = co(function* rescan(chaindb, height) {
|
||||
if (height == null)
|
||||
height = this.height;
|
||||
|
||||
hashes = yield this.getAddressHashes();
|
||||
hashes = yield this.getHashes();
|
||||
|
||||
this.logger.info('Scanning for %d addresses.', hashes.length);
|
||||
|
||||
@ -1082,33 +1223,46 @@ WalletDB.prototype._rescan = co(function* rescan(chaindb, height) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getPendingKeys = function getPendingKeys() {
|
||||
WalletDB.prototype.getPendingKeys = co(function* getPendingKeys() {
|
||||
var layout = require('./txdb').layout;
|
||||
var dummy = new Buffer(0);
|
||||
var uniq = {};
|
||||
var keys = [];
|
||||
var result = [];
|
||||
var i, iter, item, key, wid, hash;
|
||||
|
||||
return this.db.iterate({
|
||||
iter = yield this.db.iterator({
|
||||
gte: layout.prefix(0x00000000, dummy),
|
||||
lte: layout.prefix(0xffffffff, dummy),
|
||||
keys: true,
|
||||
parse: function(key) {
|
||||
var wid, hash;
|
||||
|
||||
if (key[5] !== 0x70)
|
||||
return;
|
||||
|
||||
wid = layout.pre(key);
|
||||
hash = layout.pp(key);
|
||||
|
||||
if (uniq[hash])
|
||||
return;
|
||||
|
||||
uniq[hash] = true;
|
||||
|
||||
return layout.prefix(wid, layout.t(hash));
|
||||
}
|
||||
lte: layout.prefix(0xffffffff, dummy)
|
||||
});
|
||||
};
|
||||
|
||||
for (;;) {
|
||||
item = yield iter.next();
|
||||
|
||||
if (!item)
|
||||
break;
|
||||
|
||||
if (item.key[5] === 0x70)
|
||||
keys.push(item.key);
|
||||
}
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
key = keys[i];
|
||||
|
||||
wid = layout.pre(key);
|
||||
hash = layout.pp(key);
|
||||
|
||||
if (uniq[hash])
|
||||
continue;
|
||||
|
||||
uniq[hash] = true;
|
||||
|
||||
key = layout.prefix(wid, layout.t(hash));
|
||||
result.push(key);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
/**
|
||||
* Resend all pending transactions.
|
||||
@ -1189,22 +1343,14 @@ WalletDB.prototype.getTable = co(function* getTable(hashes) {
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
paths = yield this.getAddressPaths(hash);
|
||||
paths = yield this.getPaths(hash);
|
||||
|
||||
if (!paths) {
|
||||
assert(!table[hash]);
|
||||
table[hash] = [];
|
||||
continue;
|
||||
}
|
||||
|
||||
keys = Object.keys(paths);
|
||||
values = [];
|
||||
|
||||
for (j = 0; j < keys.length; j++)
|
||||
values.push(paths[keys[j]]);
|
||||
|
||||
assert(!table[hash]);
|
||||
table[hash] = values;
|
||||
table[hash] = paths;
|
||||
match = true;
|
||||
}
|
||||
|
||||
@ -1522,28 +1668,6 @@ WalletDB.prototype._addTX = co(function* addTX(tx, force) {
|
||||
return wallets;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the corresponding path for an address hash.
|
||||
* @param {WalletID} wid
|
||||
* @param {Hash} hash
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.getAddressPath = co(function* getAddressPath(wid, hash) {
|
||||
var paths = yield this.getAddressPaths(hash);
|
||||
var path;
|
||||
|
||||
if (!paths)
|
||||
return;
|
||||
|
||||
path = paths[wid];
|
||||
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
return path;
|
||||
});
|
||||
|
||||
/**
|
||||
* Path Info
|
||||
* @constructor
|
||||
@ -1924,35 +2048,6 @@ WalletBlock.prototype.toJSON = function toJSON() {
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function parsePaths(data, hash) {
|
||||
var p = new BufferReader(data);
|
||||
var out = {};
|
||||
var path;
|
||||
|
||||
while (p.left()) {
|
||||
path = Path.fromRaw(p);
|
||||
out[path.wid] = path;
|
||||
if (hash)
|
||||
path.hash = hash;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function serializePaths(out) {
|
||||
var p = new BufferWriter();
|
||||
var keys = Object.keys(out);
|
||||
var i, wid, path;
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
wid = keys[i];
|
||||
path = out[wid];
|
||||
path.toRaw(p);
|
||||
}
|
||||
|
||||
return p.render();
|
||||
}
|
||||
|
||||
function parseWallets(data) {
|
||||
var p = new BufferReader(data);
|
||||
var wallets = [];
|
||||
|
||||
@ -953,6 +953,14 @@ describe('Wallet', function() {
|
||||
assert.equal(details[0].toJSON().outputs[0].path.name, 'foo');
|
||||
}));
|
||||
|
||||
it('should rename wallet', cob(function *() {
|
||||
var w = wallet;
|
||||
yield wallet.rename('test');
|
||||
var txs = yield w.getRange('foo', { start: 0xdeadbeef - 1000 });
|
||||
var details = yield w.toDetails(txs);
|
||||
assert.equal(details[0].toJSON().id, 'test');
|
||||
}));
|
||||
|
||||
it('should cleanup', cob(function *() {
|
||||
var records = yield walletdb.dump();
|
||||
constants.tx.COINBASE_MATURITY = 100;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user