wallet: add wid->id index.
This commit is contained in:
parent
c9d2037af6
commit
5b448b5bce
@ -48,12 +48,6 @@
|
||||
* @global
|
||||
*/
|
||||
|
||||
/**
|
||||
* Wallet ID
|
||||
* @typedef {String} WalletID
|
||||
* @global
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base58 string.
|
||||
* @typedef {String} Base58String
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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']),
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user