wallet: classify.

This commit is contained in:
Christopher Jeffrey 2017-11-16 20:11:17 -08:00
parent 9240d2f827
commit f313ca166d
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
13 changed files with 9296 additions and 9345 deletions

View File

@ -908,7 +908,7 @@ class ChainDB {
return this.db.values({ return this.db.values({
gte: layout.e(encoding.ZERO_HASH), gte: layout.e(encoding.ZERO_HASH),
lte: layout.e(encoding.MAX_HASH), lte: layout.e(encoding.MAX_HASH),
parse: value => ChainEntry.fromRaw(value) parse: data => ChainEntry.fromRaw(data)
}); });
} }

View File

@ -17,32 +17,22 @@ const HD = require('../hd/hd');
const {encoding} = bio; const {encoding} = bio;
/** /**
* Account
* Represents a BIP44 Account belonging to a {@link Wallet}. * Represents a BIP44 Account belonging to a {@link Wallet}.
* Note that this object does not enforce locks. Any method * Note that this object does not enforce locks. Any method
* that does a write is internal API only and will lead * that does a write is internal API only and will lead
* to race conditions if used elsewhere. * to race conditions if used elsewhere.
* @alias module:wallet.Account * @alias module:wallet.Account
* @constructor
* @param {Object} options
* @param {HDPublicKey} options.accountKey
* @param {Boolean?} options.witness - Whether to use witness programs.
* @param {Number} options.accountIndex - The BIP44 account index.
* @param {Number?} options.receiveDepth - The index of the _next_ receiving
* address.
* @param {Number?} options.changeDepth - The index of the _next_ change
* address.
* @param {String?} options.type - Type of wallet (pubkeyhash, multisig)
* (default=pubkeyhash).
* @param {Number?} options.m - `m` value for multisig.
* @param {Number?} options.n - `n` value for multisig.
* @param {String?} options.wid - Wallet ID
* @param {String?} options.name - Account name
*/ */
function Account(wdb, options) { class Account {
if (!(this instanceof Account)) /**
return new Account(wdb, options); * Create an account.
* @constructor
* @param {Object} options
*/
constructor(wdb, options) {
assert(wdb, 'Database is required.'); assert(wdb, 'Database is required.');
this.wdb = wdb; this.wdb = wdb;
@ -69,34 +59,13 @@ function Account(wdb, options) {
this.fromOptions(options); this.fromOptions(options);
} }
/**
* Account types.
* @enum {Number}
* @default
*/
Account.types = {
PUBKEYHASH: 0,
MULTISIG: 1
};
/**
* Account types by value.
* @const {RevMap}
*/
Account.typesByVal = {
0: 'pubkeyhash',
1: 'multisig'
};
/** /**
* Inject properties from options object. * Inject properties from options object.
* @private * @private
* @param {Object} options * @param {Object} options
*/ */
Account.prototype.fromOptions = function fromOptions(options) { fromOptions(options) {
assert(options, 'Options are required.'); assert(options, 'Options are required.');
assert((options.wid >>> 0) === options.wid); assert((options.wid >>> 0) === options.wid);
assert(common.isName(options.id), 'Bad Wallet ID.'); assert(common.isName(options.id), 'Bad Wallet ID.');
@ -193,7 +162,7 @@ Account.prototype.fromOptions = function fromOptions(options) {
} }
return this; return this;
}; }
/** /**
* Instantiate account from options. * Instantiate account from options.
@ -202,16 +171,9 @@ Account.prototype.fromOptions = function fromOptions(options) {
* @returns {Account} * @returns {Account}
*/ */
Account.fromOptions = function fromOptions(wdb, options) { static fromOptions(wdb, options) {
return new Account(wdb).fromOptions(options); return new this(wdb).fromOptions(options);
}; }
/*
* Default address lookahead.
* @const {Number}
*/
Account.MAX_LOOKAHEAD = 40;
/** /**
* Attempt to intialize the account (generating * Attempt to intialize the account (generating
@ -221,7 +183,7 @@ Account.MAX_LOOKAHEAD = 40;
* @returns {Promise} * @returns {Promise}
*/ */
Account.prototype.init = async function init(b) { async init(b) {
// Waiting for more keys. // Waiting for more keys.
if (this.keys.length !== this.n - 1) { if (this.keys.length !== this.n - 1) {
assert(!this.initialized); assert(!this.initialized);
@ -236,7 +198,7 @@ Account.prototype.init = async function init(b) {
this.initialized = true; this.initialized = true;
await this.initDepth(b); await this.initDepth(b);
}; }
/** /**
* Add a public account key to the account (multisig). * Add a public account key to the account (multisig).
@ -246,7 +208,7 @@ Account.prototype.init = async function init(b) {
* @throws Error on non-hdkey/non-accountkey. * @throws Error on non-hdkey/non-accountkey.
*/ */
Account.prototype.pushKey = function pushKey(key) { pushKey(key) {
if (typeof key === 'string') if (typeof key === 'string')
key = HD.PublicKey.fromBase58(key, this.network); key = HD.PublicKey.fromBase58(key, this.network);
@ -273,7 +235,7 @@ Account.prototype.pushKey = function pushKey(key) {
} }
return true; return true;
}; }
/** /**
* Remove a public account key to the account (multisig). * Remove a public account key to the account (multisig).
@ -283,7 +245,7 @@ Account.prototype.pushKey = function pushKey(key) {
* @throws Error on non-hdkey/non-accountkey. * @throws Error on non-hdkey/non-accountkey.
*/ */
Account.prototype.spliceKey = function spliceKey(key) { spliceKey(key) {
if (typeof key === 'string') if (typeof key === 'string')
key = HD.PublicKey.fromBase58(key, this.network); key = HD.PublicKey.fromBase58(key, this.network);
@ -300,7 +262,7 @@ Account.prototype.spliceKey = function spliceKey(key) {
throw new Error('Cannot remove key.'); throw new Error('Cannot remove key.');
return binary.remove(this.keys, key, cmp); return binary.remove(this.keys, key, cmp);
}; }
/** /**
* Add a public account key to the account (multisig). * Add a public account key to the account (multisig).
@ -309,7 +271,7 @@ Account.prototype.spliceKey = function spliceKey(key) {
* @returns {Promise} * @returns {Promise}
*/ */
Account.prototype.addSharedKey = async function addSharedKey(b, key) { async addSharedKey(b, key) {
const result = this.pushKey(key); const result = this.pushKey(key);
if (await this.hasDuplicate()) { if (await this.hasDuplicate()) {
@ -321,7 +283,7 @@ Account.prototype.addSharedKey = async function addSharedKey(b, key) {
await this.init(b); await this.init(b);
return result; return result;
}; }
/** /**
* Ensure accounts are not sharing keys. * Ensure accounts are not sharing keys.
@ -329,7 +291,7 @@ Account.prototype.addSharedKey = async function addSharedKey(b, key) {
* @returns {Promise} * @returns {Promise}
*/ */
Account.prototype.hasDuplicate = async function hasDuplicate() { async hasDuplicate() {
if (this.keys.length !== this.n - 1) if (this.keys.length !== this.n - 1)
return false; return false;
@ -337,7 +299,7 @@ Account.prototype.hasDuplicate = async function hasDuplicate() {
const hash = ring.getScriptHash('hex'); const hash = ring.getScriptHash('hex');
return this.wdb.hasPath(this.wid, hash); return this.wdb.hasPath(this.wid, hash);
}; }
/** /**
* Remove a public account key from the account (multisig). * Remove a public account key from the account (multisig).
@ -346,7 +308,7 @@ Account.prototype.hasDuplicate = async function hasDuplicate() {
* @returns {Promise} * @returns {Promise}
*/ */
Account.prototype.removeSharedKey = function removeSharedKey(b, key) { removeSharedKey(b, key) {
const result = this.spliceKey(key); const result = this.spliceKey(key);
if (!result) if (!result)
@ -355,34 +317,34 @@ Account.prototype.removeSharedKey = function removeSharedKey(b, key) {
this.save(b); this.save(b);
return true; return true;
}; }
/** /**
* Create a new receiving address (increments receiveDepth). * Create a new receiving address (increments receiveDepth).
* @returns {WalletKey} * @returns {WalletKey}
*/ */
Account.prototype.createReceive = function createReceive() { createReceive() {
return this.createKey(0); return this.createKey(0);
}; }
/** /**
* Create a new change address (increments receiveDepth). * Create a new change address (increments receiveDepth).
* @returns {WalletKey} * @returns {WalletKey}
*/ */
Account.prototype.createChange = function createChange() { createChange() {
return this.createKey(1); return this.createKey(1);
}; }
/** /**
* Create a new change address (increments receiveDepth). * Create a new change address (increments receiveDepth).
* @returns {WalletKey} * @returns {WalletKey}
*/ */
Account.prototype.createNested = function createNested() { createNested() {
return this.createKey(2); return this.createKey(2);
}; }
/** /**
* Create a new address (increments depth). * Create a new address (increments depth).
@ -390,7 +352,7 @@ Account.prototype.createNested = function createNested() {
* @returns {Promise} - Returns {@link WalletKey}. * @returns {Promise} - Returns {@link WalletKey}.
*/ */
Account.prototype.createKey = async function createKey(b, branch) { async createKey(b, branch) {
let key, lookahead; let key, lookahead;
switch (branch) { switch (branch) {
@ -422,7 +384,7 @@ Account.prototype.createKey = async function createKey(b, branch) {
this.save(); this.save();
return key; return key;
}; }
/** /**
* Derive a receiving address at `index`. Do not increment depth. * Derive a receiving address at `index`. Do not increment depth.
@ -430,9 +392,9 @@ Account.prototype.createKey = async function createKey(b, branch) {
* @returns {WalletKey} * @returns {WalletKey}
*/ */
Account.prototype.deriveReceive = function deriveReceive(index, master) { deriveReceive(index, master) {
return this.deriveKey(0, index, master); return this.deriveKey(0, index, master);
}; }
/** /**
* Derive a change address at `index`. Do not increment depth. * Derive a change address at `index`. Do not increment depth.
@ -440,9 +402,9 @@ Account.prototype.deriveReceive = function deriveReceive(index, master) {
* @returns {WalletKey} * @returns {WalletKey}
*/ */
Account.prototype.deriveChange = function deriveChange(index, master) { deriveChange(index, master) {
return this.deriveKey(1, index, master); return this.deriveKey(1, index, master);
}; }
/** /**
* Derive a nested address at `index`. Do not increment depth. * Derive a nested address at `index`. Do not increment depth.
@ -450,12 +412,12 @@ Account.prototype.deriveChange = function deriveChange(index, master) {
* @returns {WalletKey} * @returns {WalletKey}
*/ */
Account.prototype.deriveNested = function deriveNested(index, master) { deriveNested(index, master) {
if (!this.witness) if (!this.witness)
throw new Error('Cannot derive nested on non-witness account.'); throw new Error('Cannot derive nested on non-witness account.');
return this.deriveKey(2, index, master); return this.deriveKey(2, index, master);
}; }
/** /**
* Derive an address from `path` object. * Derive an address from `path` object.
@ -464,7 +426,7 @@ Account.prototype.deriveNested = function deriveNested(index, master) {
* @returns {WalletKey} * @returns {WalletKey}
*/ */
Account.prototype.derivePath = function derivePath(path, master) { derivePath(path, master) {
switch (path.keyType) { switch (path.keyType) {
case Path.types.HD: { case Path.types.HD: {
return this.deriveKey(path.branch, path.index, master); return this.deriveKey(path.branch, path.index, master);
@ -489,7 +451,7 @@ Account.prototype.derivePath = function derivePath(path, master) {
throw new Error('Bad key type.'); throw new Error('Bad key type.');
} }
} }
}; }
/** /**
* Derive an address at `index`. Do not increment depth. * Derive an address at `index`. Do not increment depth.
@ -498,7 +460,7 @@ Account.prototype.derivePath = function derivePath(path, master) {
* @returns {WalletKey} * @returns {WalletKey}
*/ */
Account.prototype.deriveKey = function deriveKey(branch, index, master) { deriveKey(branch, index, master) {
assert(typeof branch === 'number'); assert(typeof branch === 'number');
const keys = []; const keys = [];
@ -531,7 +493,7 @@ Account.prototype.deriveKey = function deriveKey(branch, index, master) {
} }
return ring; return ring;
}; }
/** /**
* Save the account to the database. Necessary * Save the account to the database. Necessary
@ -539,9 +501,9 @@ Account.prototype.deriveKey = function deriveKey(branch, index, master) {
* @returns {Promise} * @returns {Promise}
*/ */
Account.prototype.save = function save(b) { save(b) {
return this.wdb.saveAccount(b, this); return this.wdb.saveAccount(b, this);
}; }
/** /**
* Save addresses to path map. * Save addresses to path map.
@ -549,9 +511,9 @@ Account.prototype.save = function save(b) {
* @returns {Promise} * @returns {Promise}
*/ */
Account.prototype.saveKey = function saveKey(b, ring) { saveKey(b, ring) {
return this.wdb.saveKey(b, this.wid, ring); return this.wdb.saveKey(b, this.wid, ring);
}; }
/** /**
* Save paths to path map. * Save paths to path map.
@ -559,16 +521,16 @@ Account.prototype.saveKey = function saveKey(b, ring) {
* @returns {Promise} * @returns {Promise}
*/ */
Account.prototype.savePath = function savePath(b, path) { savePath(b, path) {
return this.wdb.savePath(b, this.wid, path); return this.wdb.savePath(b, this.wid, path);
}; }
/** /**
* Initialize address depths (including lookahead). * Initialize address depths (including lookahead).
* @returns {Promise} * @returns {Promise}
*/ */
Account.prototype.initDepth = async function initDepth(b) { async initDepth(b) {
// Receive Address // Receive Address
this.receiveDepth = 1; this.receiveDepth = 1;
@ -596,7 +558,7 @@ Account.prototype.initDepth = async function initDepth(b) {
} }
this.save(b); this.save(b);
}; }
/** /**
* Allocate new lookahead addresses if necessary. * Allocate new lookahead addresses if necessary.
@ -606,7 +568,7 @@ Account.prototype.initDepth = async function initDepth(b) {
* @returns {Promise} - Returns {@link WalletKey}. * @returns {Promise} - Returns {@link WalletKey}.
*/ */
Account.prototype.syncDepth = async function syncDepth(b, receive, change, nested) { async syncDepth(b, receive, change, nested) {
let derived = false; let derived = false;
let result = null; let result = null;
@ -662,7 +624,7 @@ Account.prototype.syncDepth = async function syncDepth(b, receive, change, neste
this.save(b); this.save(b);
return result; return result;
}; }
/** /**
* Allocate new lookahead addresses. * Allocate new lookahead addresses.
@ -670,7 +632,7 @@ Account.prototype.syncDepth = async function syncDepth(b, receive, change, neste
* @returns {Promise} * @returns {Promise}
*/ */
Account.prototype.setLookahead = async function setLookahead(b, lookahead) { async setLookahead(b, lookahead) {
if (lookahead === this.lookahead) if (lookahead === this.lookahead)
return; return;
@ -722,38 +684,38 @@ Account.prototype.setLookahead = async function setLookahead(b, lookahead) {
this.lookahead = lookahead; this.lookahead = lookahead;
this.save(b); this.save(b);
}; }
/** /**
* Get current receive key. * Get current receive key.
* @returns {WalletKey} * @returns {WalletKey}
*/ */
Account.prototype.receiveKey = function receiveKey() { receiveKey() {
if (!this.initialized) if (!this.initialized)
return null; return null;
return this.deriveReceive(this.receiveDepth - 1); return this.deriveReceive(this.receiveDepth - 1);
}; }
/** /**
* Get current change key. * Get current change key.
* @returns {WalletKey} * @returns {WalletKey}
*/ */
Account.prototype.changeKey = function changeKey() { changeKey() {
if (!this.initialized) if (!this.initialized)
return null; return null;
return this.deriveChange(this.changeDepth - 1); return this.deriveChange(this.changeDepth - 1);
}; }
/** /**
* Get current nested key. * Get current nested key.
* @returns {WalletKey} * @returns {WalletKey}
*/ */
Account.prototype.nestedKey = function nestedKey() { nestedKey() {
if (!this.initialized) if (!this.initialized)
return null; return null;
@ -761,56 +723,56 @@ Account.prototype.nestedKey = function nestedKey() {
return null; return null;
return this.deriveNested(this.nestedDepth - 1); return this.deriveNested(this.nestedDepth - 1);
}; }
/** /**
* Get current receive address. * Get current receive address.
* @returns {Address} * @returns {Address}
*/ */
Account.prototype.receiveAddress = function receiveAddress() { receiveAddress() {
const key = this.receiveKey(); const key = this.receiveKey();
if (!key) if (!key)
return null; return null;
return key.getAddress(); return key.getAddress();
}; }
/** /**
* Get current change address. * Get current change address.
* @returns {Address} * @returns {Address}
*/ */
Account.prototype.changeAddress = function changeAddress() { changeAddress() {
const key = this.changeKey(); const key = this.changeKey();
if (!key) if (!key)
return null; return null;
return key.getAddress(); return key.getAddress();
}; }
/** /**
* Get current nested address. * Get current nested address.
* @returns {Address} * @returns {Address}
*/ */
Account.prototype.nestedAddress = function nestedAddress() { nestedAddress() {
const key = this.nestedKey(); const key = this.nestedKey();
if (!key) if (!key)
return null; return null;
return key.getAddress(); return key.getAddress();
}; }
/** /**
* Convert the account to a more inspection-friendly object. * Convert the account to a more inspection-friendly object.
* @returns {Object} * @returns {Object}
*/ */
Account.prototype.inspect = function inspect() { inspect() {
const receive = this.receiveAddress(); const receive = this.receiveAddress();
const change = this.changeAddress(); const change = this.changeAddress();
const nested = this.nestedAddress(); const nested = this.nestedAddress();
@ -837,7 +799,7 @@ Account.prototype.inspect = function inspect() {
accountKey: this.accountKey.toBase58(this.network), accountKey: this.accountKey.toBase58(this.network),
keys: this.keys.map(key => key.toBase58(this.network)) keys: this.keys.map(key => key.toBase58(this.network))
}; };
}; }
/** /**
* Convert the account to an object suitable for * Convert the account to an object suitable for
@ -845,7 +807,7 @@ Account.prototype.inspect = function inspect() {
* @returns {Object} * @returns {Object}
*/ */
Account.prototype.toJSON = function toJSON(balance) { toJSON(balance) {
const receive = this.receiveAddress(); const receive = this.receiveAddress();
const change = this.changeAddress(); const change = this.changeAddress();
const nested = this.nestedAddress(); const nested = this.nestedAddress();
@ -870,27 +832,27 @@ Account.prototype.toJSON = function toJSON(balance) {
keys: this.keys.map(key => key.toBase58(this.network)), keys: this.keys.map(key => key.toBase58(this.network)),
balance: balance ? balance.toJSON(true) : null balance: balance ? balance.toJSON(true) : null
}; };
}; }
/** /**
* Calculate serialization size. * Calculate serialization size.
* @returns {Number} * @returns {Number}
*/ */
Account.prototype.getSize = function getSize() { getSize() {
let size = 0; let size = 0;
size += encoding.sizeVarString(this.name, 'ascii'); size += encoding.sizeVarString(this.name, 'ascii');
size += 105; size += 105;
size += this.keys.length * 82; size += this.keys.length * 82;
return size; return size;
}; }
/** /**
* Serialize the account. * Serialize the account.
* @returns {Buffer} * @returns {Buffer}
*/ */
Account.prototype.toRaw = function toRaw() { toRaw() {
const size = this.getSize(); const size = this.getSize();
const bw = bio.write(size); const bw = bio.write(size);
@ -912,7 +874,7 @@ Account.prototype.toRaw = function toRaw() {
bw.writeBytes(key.toRaw(this.network)); bw.writeBytes(key.toRaw(this.network));
return bw.render(); return bw.render();
}; }
/** /**
* Inject properties from serialized data. * Inject properties from serialized data.
@ -921,7 +883,7 @@ Account.prototype.toRaw = function toRaw() {
* @returns {Object} * @returns {Object}
*/ */
Account.prototype.fromRaw = function fromRaw(data) { fromRaw(data) {
const br = bio.read(data); const br = bio.read(data);
this.name = br.readVarString('ascii'); this.name = br.readVarString('ascii');
@ -947,7 +909,7 @@ Account.prototype.fromRaw = function fromRaw(data) {
} }
return this; return this;
}; }
/** /**
* Instantiate a account from serialized data. * Instantiate a account from serialized data.
@ -956,9 +918,9 @@ Account.prototype.fromRaw = function fromRaw(data) {
* @returns {Account} * @returns {Account}
*/ */
Account.fromRaw = function fromRaw(wdb, data) { static fromRaw(wdb, data) {
return new Account(wdb).fromRaw(data); return new this(wdb).fromRaw(data);
}; }
/** /**
* Test an object to see if it is a Account. * Test an object to see if it is a Account.
@ -966,10 +928,39 @@ Account.fromRaw = function fromRaw(wdb, data) {
* @returns {Boolean} * @returns {Boolean}
*/ */
Account.isAccount = function isAccount(obj) { static isAccount(obj) {
return obj instanceof Account; return obj instanceof Account;
}
}
/**
* Account types.
* @enum {Number}
* @default
*/
Account.types = {
PUBKEYHASH: 0,
MULTISIG: 1
}; };
/**
* Account types by value.
* @const {RevMap}
*/
Account.typesByVal = [
'pubkeyhash',
'multisig'
];
/*
* Default address lookahead.
* @const {Number}
*/
Account.MAX_LOOKAHEAD = 40;
/* /*
* Helpers * Helpers
*/ */

View File

@ -26,14 +26,16 @@ const HDPrivateKey = require('../hd/private');
const HDPublicKey = require('../hd/public'); const HDPublicKey = require('../hd/public');
const common = require('./common'); const common = require('./common');
class HTTP extends Server {
/** /**
* HTTP * HTTP
* @alias module:wallet.HTTP * @alias module:wallet.HTTP
*/
class HTTP extends Server {
/**
* Create an http server.
* @constructor * @constructor
* @param {Object} options * @param {Object} options
* @see HTTPBase
* @emits HTTP#socket
*/ */
constructor(options) { constructor(options) {

View File

@ -22,17 +22,20 @@ const {encoding} = bio;
const {Mnemonic} = HD; const {Mnemonic} = HD;
/** /**
* Master Key
* Master BIP32 key which can exist * Master BIP32 key which can exist
* in a timed out encrypted state. * in a timed out encrypted state.
* @alias module:wallet.MasterKey * @alias module:wallet.MasterKey
*/
class MasterKey {
/**
* Create a master key.
* @constructor * @constructor
* @param {Object} options * @param {Object} options
*/ */
function MasterKey(options) { constructor(options) {
if (!(this instanceof MasterKey))
return new MasterKey(options);
this.encrypted = false; this.encrypted = false;
this.iv = null; this.iv = null;
this.ciphertext = null; this.ciphertext = null;
@ -54,43 +57,13 @@ function MasterKey(options) {
this.fromOptions(options); this.fromOptions(options);
} }
/**
* Key derivation salt.
* @const {Buffer}
* @default
*/
MasterKey.SALT = Buffer.from('bcoin', 'ascii');
/**
* Key derivation algorithms.
* @enum {Number}
* @default
*/
MasterKey.alg = {
PBKDF2: 0,
SCRYPT: 1
};
/**
* Key derivation algorithms by value.
* @enum {String}
* @default
*/
MasterKey.algByVal = {
0: 'PBKDF2',
1: 'SCRYPT'
};
/** /**
* Inject properties from options object. * Inject properties from options object.
* @private * @private
* @param {Object} options * @param {Object} options
*/ */
MasterKey.prototype.fromOptions = function fromOptions(options) { fromOptions(options) {
assert(options); assert(options);
if (options.network != null) if (options.network != null)
@ -155,16 +128,16 @@ MasterKey.prototype.fromOptions = function fromOptions(options) {
assert(this.encrypted ? !this.key : this.key); assert(this.encrypted ? !this.key : this.key);
return this; return this;
}; }
/** /**
* Instantiate master key from options. * Instantiate master key from options.
* @returns {MasterKey} * @returns {MasterKey}
*/ */
MasterKey.fromOptions = function fromOptions(options) { static fromOptions(options) {
return new MasterKey().fromOptions(options); return new this().fromOptions(options);
}; }
/** /**
* Decrypt the key and set a timeout to destroy decrypted data. * Decrypt the key and set a timeout to destroy decrypted data.
@ -173,14 +146,14 @@ MasterKey.fromOptions = function fromOptions(options) {
* @returns {Promise} - Returns {@link HDPrivateKey}. * @returns {Promise} - Returns {@link HDPrivateKey}.
*/ */
MasterKey.prototype.unlock = async function unlock(passphrase, timeout) { async unlock(passphrase, timeout) {
const _unlock = await this.locker.lock(); const _unlock = await this.locker.lock();
try { try {
return await this._unlock(passphrase, timeout); return await this._unlock(passphrase, timeout);
} finally { } finally {
_unlock(); _unlock();
} }
}; }
/** /**
* Decrypt the key without a lock. * Decrypt the key without a lock.
@ -190,7 +163,7 @@ MasterKey.prototype.unlock = async function unlock(passphrase, timeout) {
* @returns {Promise} - Returns {@link HDPrivateKey}. * @returns {Promise} - Returns {@link HDPrivateKey}.
*/ */
MasterKey.prototype._unlock = async function _unlock(passphrase, timeout) { async _unlock(passphrase, timeout) {
if (this.key) { if (this.key) {
if (this.encrypted) { if (this.encrypted) {
assert(this.timer != null); assert(this.timer != null);
@ -214,7 +187,7 @@ MasterKey.prototype._unlock = async function _unlock(passphrase, timeout) {
this.aesKey = key; this.aesKey = key;
return this.key; return this.key;
}; }
/** /**
* Start the destroy timer. * Start the destroy timer.
@ -222,7 +195,7 @@ MasterKey.prototype._unlock = async function _unlock(passphrase, timeout) {
* @param {Number} [timeout=60000] timeout in ms. * @param {Number} [timeout=60000] timeout in ms.
*/ */
MasterKey.prototype.start = function start(timeout) { start(timeout) {
if (!timeout) if (!timeout)
timeout = 60; timeout = 60;
@ -233,20 +206,20 @@ MasterKey.prototype.start = function start(timeout) {
this.until = util.now() + timeout; this.until = util.now() + timeout;
this.timer = setTimeout(this._onTimeout, timeout * 1000); this.timer = setTimeout(this._onTimeout, timeout * 1000);
}; }
/** /**
* Stop the destroy timer. * Stop the destroy timer.
* @private * @private
*/ */
MasterKey.prototype.stop = function stop() { stop() {
if (this.timer != null) { if (this.timer != null) {
clearTimeout(this.timer); clearTimeout(this.timer);
this.timer = null; this.timer = null;
this.until = 0; this.until = 0;
} }
}; }
/** /**
* Derive an aes key based on params. * Derive an aes key based on params.
@ -254,7 +227,7 @@ MasterKey.prototype.stop = function stop() {
* @returns {Promise} * @returns {Promise}
*/ */
MasterKey.prototype.derive = async function derive(passwd) { async derive(passwd) {
const salt = MasterKey.SALT; const salt = MasterKey.SALT;
const N = this.N; const N = this.N;
const r = this.r; const r = this.r;
@ -271,7 +244,7 @@ MasterKey.prototype.derive = async function derive(passwd) {
default: default:
throw new Error(`Unknown algorithm: ${this.alg}.`); throw new Error(`Unknown algorithm: ${this.alg}.`);
} }
}; }
/** /**
* Encrypt data with in-memory aes key. * Encrypt data with in-memory aes key.
@ -280,7 +253,7 @@ MasterKey.prototype.derive = async function derive(passwd) {
* @returns {Buffer} * @returns {Buffer}
*/ */
MasterKey.prototype.encipher = function encipher(data, iv) { encipher(data, iv) {
if (!this.aesKey) if (!this.aesKey)
return null; return null;
@ -288,7 +261,7 @@ MasterKey.prototype.encipher = function encipher(data, iv) {
iv = Buffer.from(iv, 'hex'); iv = Buffer.from(iv, 'hex');
return aes.encipher(data, this.aesKey, iv.slice(0, 16)); return aes.encipher(data, this.aesKey, iv.slice(0, 16));
}; }
/** /**
* Decrypt data with in-memory aes key. * Decrypt data with in-memory aes key.
@ -297,7 +270,7 @@ MasterKey.prototype.encipher = function encipher(data, iv) {
* @returns {Buffer} * @returns {Buffer}
*/ */
MasterKey.prototype.decipher = function decipher(data, iv) { decipher(data, iv) {
if (!this.aesKey) if (!this.aesKey)
return null; return null;
@ -305,7 +278,7 @@ MasterKey.prototype.decipher = function decipher(data, iv) {
iv = Buffer.from(iv, 'hex'); iv = Buffer.from(iv, 'hex');
return aes.decipher(data, this.aesKey, iv.slice(0, 16)); return aes.decipher(data, this.aesKey, iv.slice(0, 16));
}; }
/** /**
* Destroy the key by zeroing the * Destroy the key by zeroing the
@ -314,14 +287,14 @@ MasterKey.prototype.decipher = function decipher(data, iv) {
* @returns {Promise} * @returns {Promise}
*/ */
MasterKey.prototype.lock = async function lock() { async lock() {
const unlock = await this.locker.lock(); const unlock = await this.locker.lock();
try { try {
return await this._lock(); return await this._lock();
} finally { } finally {
unlock(); unlock();
} }
}; }
/** /**
* Destroy the key by zeroing the * Destroy the key by zeroing the
@ -329,7 +302,7 @@ MasterKey.prototype.lock = async function lock() {
* the timer if there is one. * the timer if there is one.
*/ */
MasterKey.prototype._lock = function _lock() { _lock() {
if (!this.encrypted) { if (!this.encrypted) {
assert(this.timer == null); assert(this.timer == null);
assert(this.key); assert(this.key);
@ -347,16 +320,16 @@ MasterKey.prototype._lock = function _lock() {
cleanse(this.aesKey); cleanse(this.aesKey);
this.aesKey = null; this.aesKey = null;
} }
}; }
/** /**
* Destroy the key permanently. * Destroy the key permanently.
*/ */
MasterKey.prototype.destroy = async function destroy() { async destroy() {
await this.lock(); await this.lock();
this.locker.destroy(); this.locker.destroy();
}; }
/** /**
* Decrypt the key permanently. * Decrypt the key permanently.
@ -364,14 +337,14 @@ MasterKey.prototype.destroy = async function destroy() {
* @returns {Promise} * @returns {Promise}
*/ */
MasterKey.prototype.decrypt = async function decrypt(passphrase, clean) { async decrypt(passphrase, clean) {
const unlock = await this.locker.lock(); const unlock = await this.locker.lock();
try { try {
return await this._decrypt(passphrase, clean); return await this._decrypt(passphrase, clean);
} finally { } finally {
unlock(); unlock();
} }
}; }
/** /**
* Decrypt the key permanently without a lock. * Decrypt the key permanently without a lock.
@ -380,7 +353,7 @@ MasterKey.prototype.decrypt = async function decrypt(passphrase, clean) {
* @returns {Promise} * @returns {Promise}
*/ */
MasterKey.prototype._decrypt = async function _decrypt(passphrase, clean) { async _decrypt(passphrase, clean) {
if (!this.encrypted) if (!this.encrypted)
throw new Error('Master key is not encrypted.'); throw new Error('Master key is not encrypted.');
@ -403,7 +376,7 @@ MasterKey.prototype._decrypt = async function _decrypt(passphrase, clean) {
} }
return key; return key;
}; }
/** /**
* Encrypt the key permanently. * Encrypt the key permanently.
@ -411,14 +384,14 @@ MasterKey.prototype._decrypt = async function _decrypt(passphrase, clean) {
* @returns {Promise} * @returns {Promise}
*/ */
MasterKey.prototype.encrypt = async function encrypt(passphrase, clean) { async encrypt(passphrase, clean) {
const unlock = await this.locker.lock(); const unlock = await this.locker.lock();
try { try {
return await this._encrypt(passphrase, clean); return await this._encrypt(passphrase, clean);
} finally { } finally {
unlock(); unlock();
} }
}; }
/** /**
* Encrypt the key permanently without a lock. * Encrypt the key permanently without a lock.
@ -427,7 +400,7 @@ MasterKey.prototype.encrypt = async function encrypt(passphrase, clean) {
* @returns {Promise} * @returns {Promise}
*/ */
MasterKey.prototype._encrypt = async function _encrypt(passphrase, clean) { async _encrypt(passphrase, clean) {
if (this.encrypted) if (this.encrypted)
throw new Error('Master key is already encrypted.'); throw new Error('Master key is already encrypted.');
@ -454,14 +427,14 @@ MasterKey.prototype._encrypt = async function _encrypt(passphrase, clean) {
} }
return key; return key;
}; }
/** /**
* Calculate key serialization size. * Calculate key serialization size.
* @returns {Number} * @returns {Number}
*/ */
MasterKey.prototype.keySize = function keySize() { keySize() {
let size = 0; let size = 0;
size += this.key.getSize(); size += this.key.getSize();
@ -471,14 +444,14 @@ MasterKey.prototype.keySize = function keySize() {
size += this.mnemonic.getSize(); size += this.mnemonic.getSize();
return size; return size;
}; }
/** /**
* Serialize key and menmonic to a single buffer. * Serialize key and menmonic to a single buffer.
* @returns {Buffer} * @returns {Buffer}
*/ */
MasterKey.prototype.writeKey = function writeKey() { writeKey() {
const bw = bio.write(this.keySize()); const bw = bio.write(this.keySize());
this.key.toWriter(bw, this.network); this.key.toWriter(bw, this.network);
@ -491,14 +464,14 @@ MasterKey.prototype.writeKey = function writeKey() {
} }
return bw.render(); return bw.render();
}; }
/** /**
* Inject properties from serialized key. * Inject properties from serialized key.
* @param {Buffer} data * @param {Buffer} data
*/ */
MasterKey.prototype.readKey = function readKey(data) { readKey(data) {
const br = bio.read(data); const br = bio.read(data);
this.key = HD.PrivateKey.fromReader(br, this.network); this.key = HD.PrivateKey.fromReader(br, this.network);
@ -507,14 +480,14 @@ MasterKey.prototype.readKey = function readKey(data) {
this.mnemonic = Mnemonic.fromReader(br); this.mnemonic = Mnemonic.fromReader(br);
return this; return this;
}; }
/** /**
* Calculate serialization size. * Calculate serialization size.
* @returns {Number} * @returns {Number}
*/ */
MasterKey.prototype.getSize = function getSize() { getSize() {
let size = 0; let size = 0;
if (this.encrypted) { if (this.encrypted) {
@ -529,7 +502,7 @@ MasterKey.prototype.getSize = function getSize() {
size += encoding.sizeVarlen(this.keySize()); size += encoding.sizeVarlen(this.keySize());
return size; return size;
}; }
/** /**
* Serialize the key in the form of: * Serialize the key in the form of:
@ -537,7 +510,7 @@ MasterKey.prototype.getSize = function getSize() {
* @returns {Buffer} * @returns {Buffer}
*/ */
MasterKey.prototype.toRaw = function toRaw() { toRaw() {
const bw = bio.write(this.getSize()); const bw = bio.write(this.getSize());
if (this.encrypted) { if (this.encrypted) {
@ -569,7 +542,7 @@ MasterKey.prototype.toRaw = function toRaw() {
} }
return bw.render(); return bw.render();
}; }
/** /**
* Inject properties from serialized data. * Inject properties from serialized data.
@ -577,7 +550,7 @@ MasterKey.prototype.toRaw = function toRaw() {
* @param {Buffer} raw * @param {Buffer} raw
*/ */
MasterKey.prototype.fromRaw = function fromRaw(raw, network) { fromRaw(raw, network) {
const br = bio.read(raw); const br = bio.read(raw);
this.network = Network.get(network); this.network = Network.get(network);
@ -607,16 +580,16 @@ MasterKey.prototype.fromRaw = function fromRaw(raw, network) {
this.mnemonic = Mnemonic.fromReader(br); this.mnemonic = Mnemonic.fromReader(br);
return this; return this;
}; }
/** /**
* Instantiate master key from serialized data. * Instantiate master key from serialized data.
* @returns {MasterKey} * @returns {MasterKey}
*/ */
MasterKey.fromRaw = function fromRaw(raw, network) { static fromRaw(raw, network) {
return new MasterKey().fromRaw(raw, network); return new this().fromRaw(raw, network);
}; }
/** /**
* Inject properties from an HDPrivateKey. * Inject properties from an HDPrivateKey.
@ -625,7 +598,7 @@ MasterKey.fromRaw = function fromRaw(raw, network) {
* @param {Mnemonic?} mnemonic * @param {Mnemonic?} mnemonic
*/ */
MasterKey.prototype.fromKey = function fromKey(key, mnemonic, network) { fromKey(key, mnemonic, network) {
this.encrypted = false; this.encrypted = false;
this.iv = null; this.iv = null;
this.ciphertext = null; this.ciphertext = null;
@ -633,7 +606,7 @@ MasterKey.prototype.fromKey = function fromKey(key, mnemonic, network) {
this.mnemonic = mnemonic || null; this.mnemonic = mnemonic || null;
this.network = Network.get(network); this.network = Network.get(network);
return this; return this;
}; }
/** /**
* Instantiate master key from an HDPrivateKey. * Instantiate master key from an HDPrivateKey.
@ -642,9 +615,9 @@ MasterKey.prototype.fromKey = function fromKey(key, mnemonic, network) {
* @returns {MasterKey} * @returns {MasterKey}
*/ */
MasterKey.fromKey = function fromKey(key, mnemonic, network) { static fromKey(key, mnemonic, network) {
return new MasterKey().fromKey(key, mnemonic, network); return new this().fromKey(key, mnemonic, network);
}; }
/** /**
* Convert master key to a jsonifiable object. * Convert master key to a jsonifiable object.
@ -653,7 +626,7 @@ MasterKey.fromKey = function fromKey(key, mnemonic, network) {
* @returns {Object} * @returns {Object}
*/ */
MasterKey.prototype.toJSON = function toJSON(unsafe) { toJSON(unsafe) {
if (this.encrypted) { if (this.encrypted) {
return { return {
encrypted: true, encrypted: true,
@ -672,14 +645,14 @@ MasterKey.prototype.toJSON = function toJSON(unsafe) {
key: unsafe ? this.key.toJSON(this.network) : undefined, key: unsafe ? this.key.toJSON(this.network) : undefined,
mnemonic: unsafe && this.mnemonic ? this.mnemonic.toJSON() : undefined mnemonic: unsafe && this.mnemonic ? this.mnemonic.toJSON() : undefined
}; };
}; }
/** /**
* Inspect the key. * Inspect the key.
* @returns {Object} * @returns {Object}
*/ */
MasterKey.prototype.inspect = function inspect() { inspect() {
const json = this.toJSON(true); const json = this.toJSON(true);
if (this.key) if (this.key)
@ -689,7 +662,7 @@ MasterKey.prototype.inspect = function inspect() {
json.mnemonic = this.mnemonic.toJSON(); json.mnemonic = this.mnemonic.toJSON();
return json; return json;
}; }
/** /**
* Test whether an object is a MasterKey. * Test whether an object is a MasterKey.
@ -697,10 +670,41 @@ MasterKey.prototype.inspect = function inspect() {
* @returns {Boolean} * @returns {Boolean}
*/ */
MasterKey.isMasterKey = function isMasterKey(obj) { static isMasterKey(obj) {
return obj instanceof MasterKey; return obj instanceof MasterKey;
}
}
/**
* Key derivation salt.
* @const {Buffer}
* @default
*/
MasterKey.SALT = Buffer.from('bcoin', 'ascii');
/**
* Key derivation algorithms.
* @enum {Number}
* @default
*/
MasterKey.alg = {
PBKDF2: 0,
SCRYPT: 1
}; };
/**
* Key derivation algorithms by value.
* @enum {String}
* @default
*/
MasterKey.algByVal = [
'PBKDF2',
'SCRYPT'
];
/* /*
* Expose * Expose
*/ */

View File

@ -13,43 +13,43 @@ const EventEmitter = require('events');
* Null Client * Null Client
* Sort of a fake local client for separation of concerns. * Sort of a fake local client for separation of concerns.
* @alias module:node.NullClient * @alias module:node.NullClient
*/
class NullClient extends EventEmitter {
/**
* Create a client.
* @constructor * @constructor
*/ */
function NullClient(wdb) { constructor(wdb) {
if (!(this instanceof NullClient)) super();
return new NullClient(wdb);
EventEmitter.call(this);
this.wdb = wdb; this.wdb = wdb;
this.network = wdb.network; this.network = wdb.network;
this.opened = false; this.opened = false;
} }
Object.setPrototypeOf(NullClient.prototype, EventEmitter.prototype);
/** /**
* Open the client. * Open the client.
* @returns {Promise} * @returns {Promise}
*/ */
NullClient.prototype.open = async function open(options) { async open(options) {
assert(!this.opened, 'NullClient is already open.'); assert(!this.opened, 'NullClient is already open.');
this.opened = true; this.opened = true;
setImmediate(() => this.emit('connect')); setImmediate(() => this.emit('connect'));
}; }
/** /**
* Close the client. * Close the client.
* @returns {Promise} * @returns {Promise}
*/ */
NullClient.prototype.close = async function close() { async close() {
assert(this.opened, 'NullClient is not open.'); assert(this.opened, 'NullClient is not open.');
this.opened = false; this.opened = false;
setImmediate(() => this.emit('disconnect')); setImmediate(() => this.emit('disconnect'));
}; }
/** /**
* Add a listener. * Add a listener.
@ -57,9 +57,9 @@ NullClient.prototype.close = async function close() {
* @param {Function} handler * @param {Function} handler
*/ */
NullClient.prototype.bind = function bind(type, handler) { bind(type, handler) {
return this.on(type, handler); return this.on(type, handler);
}; }
/** /**
* Add a listener. * Add a listener.
@ -67,19 +67,19 @@ NullClient.prototype.bind = function bind(type, handler) {
* @param {Function} handler * @param {Function} handler
*/ */
NullClient.prototype.hook = function hook(type, handler) { hook(type, handler) {
return this.on(type, handler); return this.on(type, handler);
}; }
/** /**
* Get chain tip. * Get chain tip.
* @returns {Promise} * @returns {Promise}
*/ */
NullClient.prototype.getTip = async function getTip() { async getTip() {
const {hash, height, time} = this.network.genesis; const {hash, height, time} = this.network.genesis;
return { hash, height, time }; return { hash, height, time };
}; }
/** /**
* Get chain entry. * Get chain entry.
@ -87,9 +87,9 @@ NullClient.prototype.getTip = async function getTip() {
* @returns {Promise} * @returns {Promise}
*/ */
NullClient.prototype.getEntry = async function getEntry(hash) { async getEntry(hash) {
return { hash, height: 0, time: 0 }; return { hash, height: 0, time: 0 };
}; }
/** /**
* Send a transaction. Do not wait for promise. * Send a transaction. Do not wait for promise.
@ -97,9 +97,9 @@ NullClient.prototype.getEntry = async function getEntry(hash) {
* @returns {Promise} * @returns {Promise}
*/ */
NullClient.prototype.send = async function send(tx) { async send(tx) {
this.wdb.emit('send', tx); this.wdb.emit('send', tx);
}; }
/** /**
* Set bloom filter. * Set bloom filter.
@ -107,9 +107,9 @@ NullClient.prototype.send = async function send(tx) {
* @returns {Promise} * @returns {Promise}
*/ */
NullClient.prototype.setFilter = async function setFilter(filter) { async setFilter(filter) {
this.wdb.emit('set filter', filter); this.wdb.emit('set filter', filter);
}; }
/** /**
* Add data to filter. * Add data to filter.
@ -117,18 +117,18 @@ NullClient.prototype.setFilter = async function setFilter(filter) {
* @returns {Promise} * @returns {Promise}
*/ */
NullClient.prototype.addFilter = async function addFilter(data) { async addFilter(data) {
this.wdb.emit('add filter', data); this.wdb.emit('add filter', data);
}; }
/** /**
* Reset filter. * Reset filter.
* @returns {Promise} * @returns {Promise}
*/ */
NullClient.prototype.resetFilter = async function resetFilter() { async resetFilter() {
this.wdb.emit('reset filter'); this.wdb.emit('reset filter');
}; }
/** /**
* Esimate smart fee. * Esimate smart fee.
@ -136,9 +136,9 @@ NullClient.prototype.resetFilter = async function resetFilter() {
* @returns {Promise} * @returns {Promise}
*/ */
NullClient.prototype.estimateFee = async function estimateFee(blocks) { async estimateFee(blocks) {
return this.network.feeRate; return this.network.feeRate;
}; }
/** /**
* Get hash range. * Get hash range.
@ -147,9 +147,9 @@ NullClient.prototype.estimateFee = async function estimateFee(blocks) {
* @returns {Promise} * @returns {Promise}
*/ */
NullClient.prototype.getHashes = async function getHashes(start = -1, end = -1) { async getHashes(start = -1, end = -1) {
return [this.network.genesis.hash]; return [this.network.genesis.hash];
}; }
/** /**
* Rescan for any missed transactions. * Rescan for any missed transactions.
@ -159,9 +159,10 @@ NullClient.prototype.getHashes = async function getHashes(start = -1, end = -1)
* @returns {Promise} * @returns {Promise}
*/ */
NullClient.prototype.rescan = async function rescan(start) { async rescan(start) {
; ;
}; }
}
/* /*
* Expose * Expose

View File

@ -14,19 +14,20 @@ const {encoding} = bio;
/** /**
* Path * Path
* @alias module:wallet.Path * @alias module:wallet.Path
* @constructor
* @property {WalletID} wid
* @property {String} name - Account name. * @property {String} name - Account name.
* @property {Number} account - Account index. * @property {Number} account - Account index.
* @property {Number} branch - Branch index. * @property {Number} branch - Branch index.
* @property {Number} index - Address index. * @property {Number} index - Address index.
* @property {Address|null} address
*/ */
function Path(options) { class Path {
if (!(this instanceof Path)) /**
return new Path(options); * Create a path.
* @constructor
* @param {Object?} options
*/
constructor(options) {
this.keyType = Path.types.HD; this.keyType = Path.types.HD;
this.name = null; // Passed in by caller. this.name = null; // Passed in by caller.
@ -46,18 +47,6 @@ function Path(options) {
this.fromOptions(options); this.fromOptions(options);
} }
/**
* Path types.
* @enum {Number}
* @default
*/
Path.types = {
HD: 0,
KEY: 1,
ADDRESS: 2
};
/** /**
* Instantiate path from options object. * Instantiate path from options object.
* @private * @private
@ -65,7 +54,7 @@ Path.types = {
* @returns {Path} * @returns {Path}
*/ */
Path.prototype.fromOptions = function fromOptions(options) { fromOptions(options) {
this.keyType = options.keyType; this.keyType = options.keyType;
this.name = options.name; this.name = options.name;
@ -81,7 +70,7 @@ Path.prototype.fromOptions = function fromOptions(options) {
this.hash = options.hash; this.hash = options.hash;
return this; return this;
}; }
/** /**
* Instantiate path from options object. * Instantiate path from options object.
@ -89,17 +78,17 @@ Path.prototype.fromOptions = function fromOptions(options) {
* @returns {Path} * @returns {Path}
*/ */
Path.fromOptions = function fromOptions(options) { static fromOptions(options) {
return new Path().fromOptions(options); return new this().fromOptions(options);
}; }
/** /**
* Clone the path object. * Clone the path object.
* @returns {Path} * @returns {Path}
*/ */
Path.prototype.clone = function clone() { clone() {
const path = new Path(); const path = new this.constructor();
path.keyType = this.keyType; path.keyType = this.keyType;
@ -116,7 +105,7 @@ Path.prototype.clone = function clone() {
path.hash = this.hash; path.hash = this.hash;
return path; return path;
}; }
/** /**
* Inject properties from serialized data. * Inject properties from serialized data.
@ -124,7 +113,7 @@ Path.prototype.clone = function clone() {
* @param {Buffer} data * @param {Buffer} data
*/ */
Path.prototype.fromRaw = function fromRaw(data) { fromRaw(data) {
const br = bio.read(data); const br = bio.read(data);
this.account = br.readU32(); this.account = br.readU32();
@ -154,7 +143,7 @@ Path.prototype.fromRaw = function fromRaw(data) {
this.type = 4; this.type = 4;
return this; return this;
}; }
/** /**
* Instantiate path from serialized data. * Instantiate path from serialized data.
@ -162,16 +151,16 @@ Path.prototype.fromRaw = function fromRaw(data) {
* @returns {Path} * @returns {Path}
*/ */
Path.fromRaw = function fromRaw(data) { static fromRaw(data) {
return new Path().fromRaw(data); return new this().fromRaw(data);
}; }
/** /**
* Calculate serialization size. * Calculate serialization size.
* @returns {Number} * @returns {Number}
*/ */
Path.prototype.getSize = function getSize() { getSize() {
let size = 0; let size = 0;
size += 5; size += 5;
@ -189,14 +178,14 @@ Path.prototype.getSize = function getSize() {
size += 2; size += 2;
return size; return size;
}; }
/** /**
* Serialize path. * Serialize path.
* @returns {Buffer} * @returns {Buffer}
*/ */
Path.prototype.toRaw = function toRaw() { toRaw() {
const size = this.getSize(); const size = this.getSize();
const bw = bio.write(size); const bw = bio.write(size);
@ -229,7 +218,7 @@ Path.prototype.toRaw = function toRaw() {
bw.writeU8(this.type); bw.writeU8(this.type);
return bw.render(); return bw.render();
}; }
/** /**
* Inject properties from address. * Inject properties from address.
@ -238,7 +227,7 @@ Path.prototype.toRaw = function toRaw() {
* @param {Address} address * @param {Address} address
*/ */
Path.prototype.fromAddress = function fromAddress(account, address) { fromAddress(account, address) {
this.keyType = Path.types.ADDRESS; this.keyType = Path.types.ADDRESS;
this.name = account.name; this.name = account.name;
this.account = account.accountIndex; this.account = account.accountIndex;
@ -246,7 +235,7 @@ Path.prototype.fromAddress = function fromAddress(account, address) {
this.type = address.type; this.type = address.type;
this.hash = address.getHash('hex'); this.hash = address.getHash('hex');
return this; return this;
}; }
/** /**
* Instantiate path from address. * Instantiate path from address.
@ -255,54 +244,79 @@ Path.prototype.fromAddress = function fromAddress(account, address) {
* @returns {Path} * @returns {Path}
*/ */
Path.fromAddress = function fromAddress(account, address) { static fromAddress(account, address) {
return new Path().fromAddress(account, address); return new this().fromAddress(account, address);
}; }
/** /**
* Convert path object to string derivation path. * Convert path object to string derivation path.
* @returns {String} * @returns {String}
*/ */
Path.prototype.toPath = function toPath() { toPath() {
if (this.keyType !== Path.types.HD) if (this.keyType !== Path.types.HD)
return null; return null;
return `m/${this.account}'/${this.branch}/${this.index}`; return `m/${this.account}'/${this.branch}/${this.index}`;
}; }
/** /**
* Convert path object to an address (currently unused). * Convert path object to an address (currently unused).
* @returns {Address} * @returns {Address}
*/ */
Path.prototype.toAddress = function toAddress() { toAddress() {
return Address.fromHash(this.hash, this.type, this.version); return Address.fromHash(this.hash, this.type, this.version);
}; }
/** /**
* Convert path to a json-friendly object. * Convert path to a json-friendly object.
* @returns {Object} * @returns {Object}
*/ */
Path.prototype.toJSON = function toJSON() { toJSON() {
return { return {
name: this.name, name: this.name,
account: this.account, account: this.account,
change: this.branch === 1, change: this.branch === 1,
derivation: this.toPath() derivation: this.toPath()
}; };
}; }
/** /**
* Inspect the path. * Inspect the path.
* @returns {String} * @returns {String}
*/ */
Path.prototype.inspect = function inspect() { inspect() {
return `<Path: ${this.name}:${this.toPath()}>`; return `<Path: ${this.name}:${this.toPath()}>`;
}
}
/**
* Path types.
* @enum {Number}
* @default
*/
Path.types = {
HD: 0,
KEY: 1,
ADDRESS: 2
}; };
/**
* Path types.
* @enum {Number}
* @default
*/
Path.typesByVal = [
'HD',
'KEY',
'ADDRESS'
];
/** /**
* Expose * Expose
*/ */

View File

@ -20,13 +20,18 @@ const plugin = exports;
/** /**
* Plugin * Plugin
* @extends EventEmitter
*/
class Plugin extends EventEmitter {
/**
* Create a plugin.
* @constructor * @constructor
* @param {Node} node * @param {Node} node
*/ */
function Plugin(node) { constructor(node) {
if (!(this instanceof Plugin)) super();
return new Plugin(node);
const config = node.config; const config = node.config;
@ -68,22 +73,21 @@ function Plugin(node) {
this.init(); this.init();
} }
Object.setPrototypeOf(Plugin.prototype, EventEmitter.prototype); init() {
Plugin.prototype.init = function init() {
this.wdb.on('error', err => this.emit('error', err)); this.wdb.on('error', err => this.emit('error', err));
this.http.on('error', err => this.emit('error', err)); this.http.on('error', err => this.emit('error', err));
}; }
Plugin.prototype.open = async function open() { async open() {
await this.wdb.open(); await this.wdb.open();
this.rpc.wallet = this.wdb.primary; this.rpc.wallet = this.wdb.primary;
}; }
Plugin.prototype.close = async function close() { async close() {
this.rpc.wallet = this.wdb.primary; this.rpc.wallet = null;
await this.wdb.close(); await this.wdb.close();
}; }
}
/** /**
* Plugin name. * Plugin name.

View File

@ -18,13 +18,15 @@ const {encoding} = bio;
/** /**
* Chain State * Chain State
*/
class ChainState {
/**
* Create a chain state.
* @constructor * @constructor
*/ */
function ChainState() { constructor() {
if (!(this instanceof ChainState))
return new ChainState();
this.startHeight = 0; this.startHeight = 0;
this.startHash = encoding.NULL_HASH; this.startHash = encoding.NULL_HASH;
this.height = 0; this.height = 0;
@ -36,14 +38,14 @@ function ChainState() {
* @returns {ChainState} * @returns {ChainState}
*/ */
ChainState.prototype.clone = function clone() { clone() {
const state = new ChainState(); const state = new ChainState();
state.startHeight = this.startHeight; state.startHeight = this.startHeight;
state.startHash = this.startHash; state.startHash = this.startHash;
state.height = this.height; state.height = this.height;
state.marked = this.marked; state.marked = this.marked;
return state; return state;
}; }
/** /**
* Inject properties from serialized data. * Inject properties from serialized data.
@ -51,7 +53,7 @@ ChainState.prototype.clone = function clone() {
* @param {Buffer} data * @param {Buffer} data
*/ */
ChainState.prototype.fromRaw = function fromRaw(data) { fromRaw(data) {
const br = bio.read(data); const br = bio.read(data);
this.startHeight = br.readU32(); this.startHeight = br.readU32();
@ -60,7 +62,7 @@ ChainState.prototype.fromRaw = function fromRaw(data) {
this.marked = br.readU8() === 1; this.marked = br.readU8() === 1;
return this; return this;
}; }
/** /**
* Instantiate chain state from serialized data. * Instantiate chain state from serialized data.
@ -69,16 +71,16 @@ ChainState.prototype.fromRaw = function fromRaw(data) {
* @returns {ChainState} * @returns {ChainState}
*/ */
ChainState.fromRaw = function fromRaw(data) { static fromRaw(data) {
return new ChainState().fromRaw(data); return new this().fromRaw(data);
}; }
/** /**
* Serialize the chain state. * Serialize the chain state.
* @returns {Buffer} * @returns {Buffer}
*/ */
ChainState.prototype.toRaw = function toRaw() { toRaw() {
const bw = bio.write(41); const bw = bio.write(41);
bw.writeU32(this.startHeight); bw.writeU32(this.startHeight);
@ -87,20 +89,23 @@ ChainState.prototype.toRaw = function toRaw() {
bw.writeU8(this.marked ? 1 : 0); bw.writeU8(this.marked ? 1 : 0);
return bw.render(); return bw.render();
}; }
}
/** /**
* Block Meta * Block Meta
*/
class BlockMeta {
/**
* Create block meta.
* @constructor * @constructor
* @param {Hash} hash * @param {Hash} hash
* @param {Number} height * @param {Number} height
* @param {Number} time * @param {Number} time
*/ */
function BlockMeta(hash, height, time) { constructor(hash, height, time) {
if (!(this instanceof BlockMeta))
return new BlockMeta(hash, height, time);
this.hash = hash || encoding.NULL_HASH; this.hash = hash || encoding.NULL_HASH;
this.height = height != null ? height : -1; this.height = height != null ? height : -1;
this.time = time || 0; this.time = time || 0;
@ -111,18 +116,18 @@ function BlockMeta(hash, height, time) {
* @returns {BlockMeta} * @returns {BlockMeta}
*/ */
BlockMeta.prototype.clone = function clone() { clone() {
return new BlockMeta(this.hash, this.height, this.time); return new this.constructor(this.hash, this.height, this.time);
}; }
/** /**
* Get block meta hash as a buffer. * Get block meta hash as a buffer.
* @returns {Buffer} * @returns {Buffer}
*/ */
BlockMeta.prototype.toHash = function toHash() { toHash() {
return Buffer.from(this.hash, 'hex'); return Buffer.from(this.hash, 'hex');
}; }
/** /**
* Instantiate block meta from chain entry. * Instantiate block meta from chain entry.
@ -130,12 +135,12 @@ BlockMeta.prototype.toHash = function toHash() {
* @param {ChainEntry} entry * @param {ChainEntry} entry
*/ */
BlockMeta.prototype.fromEntry = function fromEntry(entry) { fromEntry(entry) {
this.hash = entry.hash; this.hash = entry.hash;
this.height = entry.height; this.height = entry.height;
this.time = entry.time; this.time = entry.time;
return this; return this;
}; }
/** /**
* Instantiate block meta from json object. * Instantiate block meta from json object.
@ -143,12 +148,12 @@ BlockMeta.prototype.fromEntry = function fromEntry(entry) {
* @param {Object} json * @param {Object} json
*/ */
BlockMeta.prototype.fromJSON = function fromJSON(json) { fromJSON(json) {
this.hash = encoding.revHex(json.hash); this.hash = encoding.revHex(json.hash);
this.height = json.height; this.height = json.height;
this.time = json.time; this.time = json.time;
return this; return this;
}; }
/** /**
* Instantiate block meta from serialized tip data. * Instantiate block meta from serialized tip data.
@ -156,13 +161,13 @@ BlockMeta.prototype.fromJSON = function fromJSON(json) {
* @param {Buffer} data * @param {Buffer} data
*/ */
BlockMeta.prototype.fromRaw = function fromRaw(data) { fromRaw(data) {
const br = bio.read(data); const br = bio.read(data);
this.hash = br.readHash('hex'); this.hash = br.readHash('hex');
this.height = br.readU32(); this.height = br.readU32();
this.time = br.readU32(); this.time = br.readU32();
return this; return this;
}; }
/** /**
* Instantiate block meta from chain entry. * Instantiate block meta from chain entry.
@ -170,9 +175,9 @@ BlockMeta.prototype.fromRaw = function fromRaw(data) {
* @returns {BlockMeta} * @returns {BlockMeta}
*/ */
BlockMeta.fromEntry = function fromEntry(entry) { static fromEntry(entry) {
return new BlockMeta().fromEntry(entry); return new this().fromEntry(entry);
}; }
/** /**
* Instantiate block meta from json object. * Instantiate block meta from json object.
@ -180,9 +185,9 @@ BlockMeta.fromEntry = function fromEntry(entry) {
* @returns {BlockMeta} * @returns {BlockMeta}
*/ */
BlockMeta.fromJSON = function fromJSON(json) { static fromJSON(json) {
return new BlockMeta().fromJSON(json); return new this().fromJSON(json);
}; }
/** /**
* Instantiate block meta from serialized data. * Instantiate block meta from serialized data.
@ -191,47 +196,50 @@ BlockMeta.fromJSON = function fromJSON(json) {
* @returns {BlockMeta} * @returns {BlockMeta}
*/ */
BlockMeta.fromRaw = function fromRaw(data) { static fromRaw(data) {
return new BlockMeta().fromRaw(data); return new this().fromRaw(data);
}; }
/** /**
* Serialize the block meta. * Serialize the block meta.
* @returns {Buffer} * @returns {Buffer}
*/ */
BlockMeta.prototype.toRaw = function toRaw() { toRaw() {
const bw = bio.write(42); const bw = bio.write(42);
bw.writeHash(this.hash); bw.writeHash(this.hash);
bw.writeU32(this.height); bw.writeU32(this.height);
bw.writeU32(this.time); bw.writeU32(this.time);
return bw.render(); return bw.render();
}; }
/** /**
* Convert the block meta to a more json-friendly object. * Convert the block meta to a more json-friendly object.
* @returns {Object} * @returns {Object}
*/ */
BlockMeta.prototype.toJSON = function toJSON() { toJSON() {
return { return {
hash: encoding.revHex(this.hash), hash: encoding.revHex(this.hash),
height: this.height, height: this.height,
time: this.time time: this.time
}; };
}; }
}
/** /**
* TX Record * TX Record
*/
class TXRecord {
/**
* Create tx record.
* @constructor * @constructor
* @param {TX} tx * @param {TX} tx
* @param {BlockMeta?} block * @param {BlockMeta?} block
*/ */
function TXRecord(tx, block) { constructor(tx, block) {
if (!(this instanceof TXRecord))
return new TXRecord(tx, block);
this.tx = null; this.tx = null;
this.hash = null; this.hash = null;
this.mtime = util.now(); this.mtime = util.now();
@ -252,7 +260,7 @@ function TXRecord(tx, block) {
* @returns {TXRecord} * @returns {TXRecord}
*/ */
TXRecord.prototype.fromTX = function fromTX(tx, block) { fromTX(tx, block) {
this.tx = tx; this.tx = tx;
this.hash = tx.hash('hex'); this.hash = tx.hash('hex');
@ -260,7 +268,7 @@ TXRecord.prototype.fromTX = function fromTX(tx, block) {
this.setBlock(block); this.setBlock(block);
return this; return this;
}; }
/** /**
* Instantiate tx record from tx and block. * Instantiate tx record from tx and block.
@ -269,42 +277,42 @@ TXRecord.prototype.fromTX = function fromTX(tx, block) {
* @returns {TXRecord} * @returns {TXRecord}
*/ */
TXRecord.fromTX = function fromTX(tx, block) { static fromTX(tx, block) {
return new TXRecord().fromTX(tx, block); return new this().fromTX(tx, block);
}; }
/** /**
* Set block data (confirm). * Set block data (confirm).
* @param {BlockMeta} block * @param {BlockMeta} block
*/ */
TXRecord.prototype.setBlock = function setBlock(block) { setBlock(block) {
this.height = block.height; this.height = block.height;
this.block = block.hash; this.block = block.hash;
this.time = block.time; this.time = block.time;
}; }
/** /**
* Unset block (unconfirm). * Unset block (unconfirm).
*/ */
TXRecord.prototype.unsetBlock = function unsetBlock() { unsetBlock() {
this.height = -1; this.height = -1;
this.block = null; this.block = null;
this.time = 0; this.time = 0;
}; }
/** /**
* Convert tx record to a block meta. * Convert tx record to a block meta.
* @returns {BlockMeta} * @returns {BlockMeta}
*/ */
TXRecord.prototype.getBlock = function getBlock() { getBlock() {
if (this.height === -1) if (this.height === -1)
return null; return null;
return new BlockMeta(this.block, this.height, this.time); return new BlockMeta(this.block, this.height, this.time);
}; }
/** /**
* Calculate current number of transaction confirmations. * Calculate current number of transaction confirmations.
@ -312,7 +320,7 @@ TXRecord.prototype.getBlock = function getBlock() {
* @returns {Number} confirmations * @returns {Number} confirmations
*/ */
TXRecord.prototype.getDepth = function getDepth(height) { getDepth(height) {
assert(typeof height === 'number', 'Must pass in height.'); assert(typeof height === 'number', 'Must pass in height.');
if (this.height === -1) if (this.height === -1)
@ -322,14 +330,14 @@ TXRecord.prototype.getDepth = function getDepth(height) {
return 0; return 0;
return height - this.height + 1; return height - this.height + 1;
}; }
/** /**
* Get serialization size. * Get serialization size.
* @returns {Number} * @returns {Number}
*/ */
TXRecord.prototype.getSize = function getSize() { getSize() {
let size = 0; let size = 0;
size += this.tx.getSize(); size += this.tx.getSize();
@ -344,14 +352,14 @@ TXRecord.prototype.getSize = function getSize() {
} }
return size; return size;
}; }
/** /**
* Serialize a transaction to "extended format". * Serialize a transaction to "extended format".
* @returns {Buffer} * @returns {Buffer}
*/ */
TXRecord.prototype.toRaw = function toRaw() { toRaw() {
const size = this.getSize(); const size = this.getSize();
const bw = bio.write(size); const bw = bio.write(size);
@ -375,7 +383,7 @@ TXRecord.prototype.toRaw = function toRaw() {
} }
return bw.render(); return bw.render();
}; }
/** /**
* Inject properties from "extended" format. * Inject properties from "extended" format.
@ -383,7 +391,7 @@ TXRecord.prototype.toRaw = function toRaw() {
* @param {Buffer} data * @param {Buffer} data
*/ */
TXRecord.prototype.fromRaw = function fromRaw(data) { fromRaw(data) {
const br = bio.read(data); const br = bio.read(data);
this.tx = new TX(); this.tx = new TX();
@ -402,7 +410,7 @@ TXRecord.prototype.fromRaw = function fromRaw(data) {
} }
return this; return this;
}; }
/** /**
* Instantiate a transaction from a buffer * Instantiate a transaction from a buffer
@ -411,70 +419,77 @@ TXRecord.prototype.fromRaw = function fromRaw(data) {
* @returns {TX} * @returns {TX}
*/ */
TXRecord.fromRaw = function fromRaw(data) { static fromRaw(data) {
return new TXRecord().fromRaw(data); return new this().fromRaw(data);
}; }
}
/** /**
* Map Record * Map Record
*/
class MapRecord {
/**
* Create map record.
* @constructor * @constructor
*/ */
function MapRecord() { constructor() {
this.wids = new Set(); this.wids = new Set();
} }
MapRecord.prototype.add = function add(wid) { add(wid) {
if (this.wids.has(wid)) if (this.wids.has(wid))
return false; return false;
this.wids.add(wid); this.wids.add(wid);
return true; return true;
}; }
MapRecord.prototype.remove = function remove(wid) { remove(wid) {
return this.wids.delete(wid); return this.wids.delete(wid);
}; }
MapRecord.prototype.toWriter = function toWriter(bw) { toWriter(bw) {
bw.writeU32(this.wids.size); bw.writeU32(this.wids.size);
for (const wid of this.wids) for (const wid of this.wids)
bw.writeU32(wid); bw.writeU32(wid);
return bw; return bw;
}; }
MapRecord.prototype.getSize = function getSize() { getSize() {
return 4 + this.wids.size * 4; return 4 + this.wids.size * 4;
}; }
MapRecord.prototype.toRaw = function toRaw() { toRaw() {
const size = this.getSize(); const size = this.getSize();
return this.toWriter(bio.write(size)).render(); return this.toWriter(bio.write(size)).render();
}; }
MapRecord.prototype.fromReader = function fromReader(br) { fromReader(br) {
const count = br.readU32(); const count = br.readU32();
for (let i = 0; i < count; i++) for (let i = 0; i < count; i++)
this.wids.add(br.readU32()); this.wids.add(br.readU32());
return this; return this;
}; }
MapRecord.prototype.fromRaw = function fromRaw(data) { fromRaw(data) {
return this.fromReader(bio.read(data)); return this.fromReader(bio.read(data));
}; }
MapRecord.fromReader = function fromReader(br) { static fromReader(br) {
return new MapRecord().fromReader(br); return new this().fromReader(br);
}; }
MapRecord.fromRaw = function fromRaw(data) { static fromRaw(data) {
return new MapRecord().fromRaw(data); return new this().fromRaw(data);
}; }
}
/* /*
* Expose * Expose

View File

@ -16,14 +16,17 @@ const RPC = require('./rpc');
/** /**
* Wallet Node * Wallet Node
* @extends Node * @extends Node
* @constructor
*/ */
function WalletNode(options) { class WalletNode extends Node {
if (!(this instanceof WalletNode)) /**
return new WalletNode(options); * Create a wallet node.
* @constructor
* @param {Object?} options
*/
Node.call(this, 'bcoin', 'wallet.conf', 'wallet.log', options); constructor(options) {
super('bcoin', 'wallet.conf', 'wallet.log', options);
this.opened = false; this.opened = false;
@ -72,19 +75,17 @@ function WalletNode(options) {
this.init(); this.init();
} }
Object.setPrototypeOf(WalletNode.prototype, Node.prototype);
/** /**
* Initialize the node. * Initialize the node.
* @private * @private
*/ */
WalletNode.prototype.init = function init() { init() {
this.wdb.on('error', err => this.error(err)); this.wdb.on('error', err => this.error(err));
this.http.on('error', err => this.error(err)); this.http.on('error', err => this.error(err));
this.loadPlugins(); this.loadPlugins();
}; }
/** /**
* Open the node and all its child objects, * Open the node and all its child objects,
@ -93,7 +94,7 @@ WalletNode.prototype.init = function init() {
* @returns {Promise} * @returns {Promise}
*/ */
WalletNode.prototype.open = async function open() { async open() {
assert(!this.opened, 'WalletNode is already open.'); assert(!this.opened, 'WalletNode is already open.');
this.opened = true; this.opened = true;
@ -108,7 +109,7 @@ WalletNode.prototype.open = async function open() {
await this.handleOpen(); await this.handleOpen();
this.logger.info('Wallet node is loaded.'); this.logger.info('Wallet node is loaded.');
}; }
/** /**
* Close the node, wait for the database to close. * Close the node, wait for the database to close.
@ -116,7 +117,7 @@ WalletNode.prototype.open = async function open() {
* @returns {Promise} * @returns {Promise}
*/ */
WalletNode.prototype.close = async function close() { async close() {
assert(this.opened, 'WalletNode is not open.'); assert(this.opened, 'WalletNode is not open.');
this.opened = false; this.opened = false;
@ -129,7 +130,8 @@ WalletNode.prototype.close = async function close() {
await this.wdb.close(); await this.wdb.close();
await this.handleClose(); await this.handleClose();
}; }
}
/* /*
* Expose * Expose

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -36,79 +36,6 @@ class WalletKey extends KeyRing {
this.index = -1; this.index = -1;
} }
/**
* Instantiate key ring from options.
* @param {Object} options
* @returns {WalletKey}
*/
static fromOptions(options) {
return new this().fromOptions(options);
}
/**
* Instantiate wallet key from a private key.
* @param {Buffer} key
* @param {Boolean?} compressed
* @returns {WalletKey}
*/
static fromPrivate(key, compressed) {
return new this().fromPrivate(key, compressed);
}
/**
* Generate a wallet key.
* @param {Boolean?} compressed
* @returns {WalletKey}
*/
static generate(compressed) {
return new this().generate(compressed);
}
/**
* Instantiate wallet key from a public key.
* @param {Buffer} publicKey
* @returns {WalletKey}
*/
static fromPublic(key) {
return new this().fromPublic(key);
}
/**
* Instantiate wallet key from a public key.
* @param {Buffer} publicKey
* @returns {WalletKey}
*/
static fromKey(key, compressed) {
return new this().fromKey(key, compressed);
}
/**
* Instantiate wallet key from script.
* @param {Buffer} key
* @param {Script} script
* @returns {WalletKey}
*/
static fromScript(key, script, compressed) {
return new this().fromScript(key, script, compressed);
}
/**
* Instantiate a wallet key from a serialized CBitcoinSecret.
* @param {Base58String} secret
* @param {Network?} network
* @returns {WalletKey}
*/
static fromSecret(data, network) {
return new this().fromSecret(data, network);
}
/** /**
* Convert an WalletKey to a more json-friendly object. * Convert an WalletKey to a more json-friendly object.
* @returns {Object} * @returns {Object}
@ -130,26 +57,6 @@ class WalletKey extends KeyRing {
}; };
} }
/**
* Instantiate an WalletKey from a jsonified transaction object.
* @param {Object} json - The jsonified transaction object.
* @returns {WalletKey}
*/
static fromJSON(json) {
return new this().fromJSON(json);
}
/**
* Instantiate a wallet key from serialized data.
* @param {Buffer} data
* @returns {WalletKey}
*/
static fromRaw(data) {
return new this().fromRaw(data);
}
/** /**
* Inject properties from hd key. * Inject properties from hd key.
* @private * @private