wallet: add wid->id index.

This commit is contained in:
Christopher Jeffrey 2017-12-30 03:37:55 -08:00
parent c9d2037af6
commit 5b448b5bce
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
6 changed files with 143 additions and 99 deletions

View File

@ -48,12 +48,6 @@
* @global
*/
/**
* Wallet ID
* @typedef {String} WalletID
* @global
*/
/**
* Base58 string.
* @typedef {String} Base58String

View File

@ -842,8 +842,7 @@ class Account {
getSize() {
let size = 0;
size += encoding.sizeVarString(this.name, 'ascii');
size += 96;
size += 92;
size += this.keys.length * 74;
return size;
}
@ -865,8 +864,6 @@ class Account {
if (this.witness)
flags |= 2;
bw.writeU32(this.accountIndex);
bw.writeVarString(this.name, 'ascii');
bw.writeU8(flags);
bw.writeU8(this.type);
bw.writeU8(this.m);
@ -893,10 +890,6 @@ class Account {
fromRaw(data) {
const br = bio.read(data);
this.accountIndex = br.readU32();
this.name = br.readVarString('ascii');
const flags = br.readU8();
this.initialized = (flags & 1) !== 0;

View File

@ -18,6 +18,7 @@ const bdb = require('bdb');
* P[wid][addr-hash] -> path data
* r[wid][index][hash] -> path account index
* w[wid] -> wallet
* W[wid] -> wallet id
* l[id] -> wid
* a[wid][index] -> account
* i[wid][name] -> account index
@ -38,6 +39,7 @@ exports.wdb = {
P: bdb.key('P', ['uint32', 'hash']),
r: bdb.key('r', ['uint32', 'uint32', 'hash']),
w: bdb.key('w', ['uint32']),
W: bdb.key('W', ['uint32']),
l: bdb.key('l', ['ascii']),
a: bdb.key('a', ['uint32', 'uint32']),
i: bdb.key('i', ['uint32', 'ascii']),

View File

@ -158,9 +158,7 @@ class Wallet extends EventEmitter {
* @returns {Promise}
*/
async init(options) {
const passphrase = options.passphrase;
async init(options, passphrase) {
if (passphrase)
await this.master.encrypt(passphrase);
@ -741,7 +739,6 @@ class Wallet extends EventEmitter {
/**
* Lookup the corresponding account index's name.
* @param {WalletID} wid
* @param {Number} index - Account index.
* @returns {Promise} - Returns String.
*/
@ -2269,8 +2266,7 @@ class Wallet extends EventEmitter {
getSize() {
let size = 0;
size += 45;
size += encoding.sizeVarString(this.id, 'ascii');
size += 41;
size += this.master.getSize();
return size;
}
@ -2289,8 +2285,6 @@ class Wallet extends EventEmitter {
if (this.watchOnly)
flags |= 1;
bw.writeU32(this.wid);
bw.writeVarString(this.id, 'ascii');
bw.writeU8(flags);
bw.writeU32(this.accountDepth);
bw.writeBytes(this.token);
@ -2309,9 +2303,6 @@ class Wallet extends EventEmitter {
fromRaw(data) {
const br = bio.read(data);
this.wid = br.readU32();
this.id = br.readVarString('ascii');
const flags = br.readU8();
this.watchOnly = (flags & 1) !== 0;

View File

@ -607,7 +607,7 @@ class WalletDB extends EventEmitter {
const raw = await this.db.get(layout.D.build());
if (!raw)
return 1;
return 0;
return raw.readUInt32LE(0, true);
}
@ -676,39 +676,64 @@ class WalletDB extends EventEmitter {
this.wallets.delete(wallet.wid);
}
/**
* Map wallet id to wid.
* @param {String|Number} id
* @returns {Promise} - Returns {Number}.
*/
async ensureWID(id) {
if (typeof id === 'number') {
if (!await this.db.has(layout.W.build(id)))
return -1;
return id;
}
return this.getWID(id);
}
/**
* Map wallet id to wid.
* @param {String} id
* @returns {Promise} - Returns {WalletID}.
* @returns {Promise} - Returns {Number}.
*/
async getWID(id) {
if (!id)
return null;
if (typeof id === 'number')
return id;
const data = await this.db.get(layout.l.build(id));
if (!data)
return null;
return -1;
assert(data.length === 4);
return data.readUInt32LE(0, true);
}
/**
* Map wallet wid to id.
* @param {Number} wid
* @returns {Promise} - Returns {String}.
*/
async getID(wid) {
const data = await this.db.get(layout.W.build(wid));
if (!data)
return null;
return toString(data);
}
/**
* Get a wallet from the database, setup watcher.
* @param {WalletID} wid
* @param {Number|String} id
* @returns {Promise} - Returns {@link Wallet}.
*/
async get(id) {
const wid = await this.getWID(id);
const wid = await this.ensureWID(id);
if (!wid)
if (wid === -1)
return null;
const unlock = await this.readLock.lock(wid);
@ -723,7 +748,7 @@ class WalletDB extends EventEmitter {
/**
* Get a wallet from the database without a lock.
* @private
* @param {WalletID} wid
* @param {Number} wid
* @returns {Promise} - Returns {@link Wallet}.
*/
@ -733,13 +758,19 @@ class WalletDB extends EventEmitter {
if (cache)
return cache;
const data = await this.db.get(layout.w.build(wid));
const id = await this.getID(wid);
if (!data)
if (!id)
return null;
const data = await this.db.get(layout.w.build(wid));
assert(data);
const wallet = Wallet.fromRaw(this, data);
wallet.wid = wid;
wallet.id = id;
await wallet.open();
this.register(wallet);
@ -757,6 +788,7 @@ class WalletDB extends EventEmitter {
const id = wallet.id;
b.put(layout.w.build(wid), wallet.toRaw());
b.put(layout.W.build(wid), fromString(id));
b.put(layout.l.build(id), fromU32(wid));
}
@ -801,16 +833,16 @@ class WalletDB extends EventEmitter {
if (await this.has(id))
throw new Error('WDB: ID not available.');
const old = wallet.id;
const b = this.db.batch();
b.del(layout.l.build(old));
// Update wid->id index.
b.put(layout.W.build(wallet.wid), fromString(id));
wallet.id = id;
// Delete old id->wid index.
b.del(layout.l.build(wallet.id));
this.save(b, wallet);
wallet.id = old;
// Add new id->wid index.
b.put(layout.l.build(id), fromU32(wallet.wid));
await b.write();
@ -824,24 +856,31 @@ class WalletDB extends EventEmitter {
*/
renameAccount(b, account, name) {
const wid = account.wid;
const index = account.accountIndex;
// Remove old wid/name->account index.
b.del(layout.i.build(account.wid, account.name));
b.del(layout.i.build(wid, account.name));
// Name->Index lookups
b.put(layout.i.build(wid, name), fromU32(index));
// Index->Name lookups
b.put(layout.n.build(wid, index), fromString(name));
account.name = name;
this.saveAccount(b, account);
}
/**
* Remove a wallet.
* @param {WalletID} wid
* @param {Number|String} id
* @returns {Promise}
*/
async remove(id) {
const wid = await this.getWID(id);
const wid = await this.ensureWID(id);
if (!wid)
if (wid === -1)
return false;
// Grab all locks.
@ -861,24 +900,23 @@ class WalletDB extends EventEmitter {
/**
* Remove a wallet (without a lock).
* @private
* @param {WalletID} wid
* @param {Number} wid
* @returns {Promise}
*/
async _remove(wid) {
if (wid === 1)
throw new Error('Cannot remove primary wallet.');
const id = await this.getID(wid);
const data = await this.db.get(layout.w.build(wid));
if (!data)
if (!id)
return false;
const {id} = Wallet.fromRaw(this, data);
if (id === 'primary')
throw new Error('Cannot remove primary wallet.');
const b = this.db.batch();
b.del(layout.w.build(wid));
b.del(layout.W.build(wid));
b.del(layout.l.build(id));
const piter = this.db.iterator({
@ -969,13 +1007,13 @@ class WalletDB extends EventEmitter {
/**
* Get a wallet with token auth first.
* @param {WalletID} wid
* @param {Number|String} id
* @param {Buffer} token
* @returns {Promise} - Returns {@link Wallet}.
*/
async auth(wid, token) {
const wallet = await this.get(wid);
async auth(id, token) {
const wallet = await this.get(id);
if (!wallet)
return null;
@ -1014,14 +1052,16 @@ class WalletDB extends EventEmitter {
*/
async _create(options) {
if (await this.has(options.id))
throw new Error('WDB: Wallet already exists.');
if (options.id) {
if (await this.has(options.id))
throw new Error('WDB: Wallet already exists.');
}
const wallet = Wallet.fromOptions(this, options);
wallet.wid = this.depth;
await wallet.init(options);
await wallet.init(options, options.passphrase);
this.depth += 1;
@ -1034,13 +1074,13 @@ class WalletDB extends EventEmitter {
/**
* Test for the existence of a wallet.
* @param {WalletID} id
* @param {Number|String} id
* @returns {Promise}
*/
async has(id) {
const wid = await this.getWID(id);
return wid != null;
const wid = await this.ensureWID(id);
return wid !== -1;
}
/**
@ -1050,10 +1090,12 @@ class WalletDB extends EventEmitter {
*/
async ensure(options) {
const wallet = await this.get(options.id);
if (options.id) {
const wallet = await this.get(options.id);
if (wallet)
return wallet;
if (wallet)
return wallet;
}
return this.create(options);
}
@ -1061,23 +1103,31 @@ class WalletDB extends EventEmitter {
/**
* Get an account from the database by wid.
* @private
* @param {WalletID} wid
* @param {Number} wid
* @param {Number} index - Account index.
* @returns {Promise} - Returns {@link Wallet}.
*/
async getAccount(wid, index) {
const data = await this.db.get(layout.a.build(wid, index));
const name = await this.getAccountName(wid, index);
if (!data)
if (!name)
return null;
return Account.fromRaw(this, data);
const data = await this.db.get(layout.a.build(wid, index));
assert(data);
const account = Account.fromRaw(this, data);
account.accountIndex = index;
account.name = name;
return account;
}
/**
* List account names and indexes from the db.
* @param {WalletID} wid
* @param {Number} wid
* @returns {Promise} - Returns Array.
*/
@ -1085,13 +1135,13 @@ class WalletDB extends EventEmitter {
return this.db.values({
gte: layout.n.min(wid),
lte: layout.n.max(wid),
parse: data => data.toString('ascii')
parse: toString
});
}
/**
* Lookup the corresponding account name's index.
* @param {WalletID} wid
* @param {Number} wid
* @param {String} name - Account name/index.
* @returns {Promise} - Returns Number.
*/
@ -1107,7 +1157,7 @@ class WalletDB extends EventEmitter {
/**
* Lookup the corresponding account index's name.
* @param {WalletID} wid
* @param {Number} wid
* @param {Number} index
* @returns {Promise} - Returns Number.
*/
@ -1118,7 +1168,7 @@ class WalletDB extends EventEmitter {
if (!name)
return null;
return name.toString('ascii');
return toString(name);
}
/**
@ -1144,7 +1194,7 @@ class WalletDB extends EventEmitter {
/**
* Test for the existence of an account.
* @param {WalletID} wid
* @param {Number} wid
* @param {String|Number} acct
* @returns {Promise} - Returns Boolean.
*/
@ -1190,7 +1240,7 @@ class WalletDB extends EventEmitter {
/**
* Retrieve path by hash.
* @param {WalletID} wid
* @param {Number} wid
* @param {Hash} hash
* @returns {Promise}
*/
@ -1209,7 +1259,7 @@ class WalletDB extends EventEmitter {
/**
* Retrieve path by hash.
* @param {WalletID} wid
* @param {Number} wid
* @param {Hash} hash
* @returns {Promise}
*/
@ -1228,7 +1278,7 @@ class WalletDB extends EventEmitter {
/**
* Test whether a wallet contains a path.
* @param {WalletID} wid
* @param {Number} wid
* @param {Hash} hash
* @returns {Promise}
*/
@ -1268,7 +1318,7 @@ class WalletDB extends EventEmitter {
/**
* Get all address hashes.
* @param {WalletID} wid
* @param {Number} wid
* @returns {Promise}
*/
@ -1282,7 +1332,7 @@ class WalletDB extends EventEmitter {
/**
* Get all account address hashes.
* @param {WalletID} wid
* @param {Number} wid
* @param {Number} account
* @returns {Promise}
*/
@ -1297,7 +1347,7 @@ class WalletDB extends EventEmitter {
/**
* Get all paths for a wallet.
* @param {WalletID} wid
* @param {Number} wid
* @returns {Promise}
*/
@ -1329,16 +1379,16 @@ class WalletDB extends EventEmitter {
*/
async getWallets() {
return this.db.keys({
gte: layout.l.min(),
lte: layout.l.max(),
parse: key => layout.l.parse(key)
return this.db.values({
gte: layout.W.min(),
lte: layout.W.max(),
parse: toString
});
}
/**
* Encrypt all imported keys for a wallet.
* @param {WalletID} wid
* @param {Number} wid
* @param {Buffer} key
* @returns {Promise}
*/
@ -1371,7 +1421,7 @@ class WalletDB extends EventEmitter {
/**
* Decrypt all imported keys for a wallet.
* @param {WalletID} wid
* @param {Number} wid
* @param {Buffer} key
* @returns {Promise}
*/
@ -1423,7 +1473,7 @@ class WalletDB extends EventEmitter {
/**
* Resend all pending transactions for a specific wallet.
* @private
* @param {WalletID} wid
* @param {Number} wid
* @returns {Promise}
*/
@ -2273,7 +2323,16 @@ function fromU32(num) {
}
function fromString(str) {
return Buffer.from(str, 'ascii');
const buf = Buffer.alloc(1 + str.length);
buf[0] = str.length;
buf.write(str, 1, str.length, 'ascii');
return buf;
}
function toString(buf) {
assert(buf.length > 0);
assert(buf[0] === buf.length - 1);
return buf.toString('ascii', 1, buf.length);
}
/*

View File

@ -459,8 +459,6 @@ async function updateWallet(wid) {
// Concatenate wallet with key.
const bw = bio.write();
bw.writeU32(wid);
bw.writeVarString(id, 'ascii');
bw.writeU8(flags);
bw.writeU32(accountDepth);
bw.writeBytes(token);
@ -468,6 +466,7 @@ async function updateWallet(wid) {
bw.writeBytes(key);
parent.put(layout.w.build(wid), bw.render());
parent.put(layout.W.build(wid), fromString(id));
console.log('Updating accounts for %d...', wid);
@ -534,8 +533,6 @@ async function updateAccount(wid, acct) {
if (witness)
flags |= 2;
bw.writeU32(accountIndex);
bw.writeVarString(name, 'ascii');
bw.writeU8(flags);
bw.writeU8(type);
bw.writeU8(m);
@ -562,6 +559,7 @@ async function updateAccount(wid, acct) {
}
parent.put(layout.a.build(wid, acct), bw.render());
parent.put(layout.n.build(wid, acct), fromString(name));
console.log('Updated account: %d/%d.', wid, acct);
}
@ -896,6 +894,13 @@ function parsei(key) { // i[wid][name]
return [key.readUInt32BE(1, true), key.toString('ascii', 5)];
}
function fromString(str) {
const buf = Buffer.alloc(1 + str.length);
buf[0] = str.length;
buf.write(str, 1, str.length, 'ascii');
return buf;
}
/*
* Execute
*/