diff --git a/lib/bcoin/keyring.js b/lib/bcoin/keyring.js index 20013f49..863e5570 100644 --- a/lib/bcoin/keyring.js +++ b/lib/bcoin/keyring.js @@ -73,49 +73,73 @@ function KeyRing(options) { KeyRing.prototype.fromOptions = function fromOptions(options) { var i; + assert(options.key); + if (options.network) this.network = bcoin.network.get(options.network); - if (options.type) + if (options.type) { + assert(options.type === 'pubkeyhash' || options.type === 'multisig'); this.type = options.type; + } - if (options.m) + if (options.m != null) { + assert(utils.isNumber(options.m)); this.m = options.m; + } - if (options.n) + if (options.n != null) { + assert(utils.isNumber(options.n)); this.n = options.n; + } - if (options.witness != null) + if (options.witness != null) { + assert(typeof options.witness === 'boolean'); this.witness = options.witness; + } - if (options.id) + if (options.id) { + assert(utils.isAlpha(options.id)); this.id = options.id; + } - if (options.name) + if (options.name) { + assert(utils.isAlpha(options.name)); this.name = options.name; + } - if (options.account != null) + if (options.account != null) { + assert(utils.isNumber(options.account)); this.account = options.account; + } - if (options.change != null) + if (options.change != null) { + assert(utils.isNumber(options.change)); this.change = options.change; + } - if (options.index != null) + if (options.index != null) { + assert(utils.isNumber(options.index)); this.index = options.index; + } this.key = options.key; + if (this.key.getPublicKey) + this.key = this.key.getPublicKey(); + + assert(Buffer.isBuffer(this.key)); + if (this.n > 1) this.type = 'multisig'; - assert(this.type === 'pubkeyhash' || this.type === 'multisig'); - if (this.m < 1 || this.m > this.n) throw new Error('m ranges between 1 and n'); this.addKey(this.key); if (options.keys) { + assert(Array.isArray(options.keys)); for (i = 0; i < options.keys.length; i++) this.addKey(options.keys[i]); } @@ -139,6 +163,8 @@ KeyRing.fromOptions = function fromOptions(options) { */ KeyRing.prototype.addKey = function addKey(key) { + assert(Buffer.isBuffer(key)); + if (utils.indexOf(this.keys, key) !== -1) return; @@ -154,6 +180,8 @@ KeyRing.prototype.addKey = function addKey(key) { */ KeyRing.prototype.removeKey = function removeKey(key) { + assert(Buffer.isBuffer(key)); + if (this.keys.length === this.n) throw new Error('Cannot remove key.'); @@ -639,12 +667,12 @@ KeyRing.prototype.fromJSON = function fromJSON(json) { assert(json); assert(typeof json.network === 'string'); - assert(typeof json.type === 'string'); + assert(json.type === 'pubkeyhash' || json.type === 'multisig'); assert(utils.isNumber(json.m)); assert(utils.isNumber(json.n)); assert(typeof json.witness === 'boolean'); - assert(!json.id || typeof json.id === 'string'); - assert(!json.name || typeof json.name === 'string'); + assert(!json.id || utils.isAlpha(json.id)); + assert(!json.name || utils.isAlpha(json.name)); assert(utils.isNumber(json.account)); assert(utils.isNumber(json.change)); assert(utils.isNumber(json.index)); diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index c6483cbc..75e63637 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -2621,3 +2621,17 @@ utils.mkdir = function mkdir(path, dirname) { */ utils._paths = {}; + +/** + * Test whether a string is alphanumeric + * enough to use as a leveldb key. + * @param {String} key + * @returns {Boolean} + */ + +utils.isAlpha = function isAlpha(key) { + if (typeof key !== 'string') + return false; + // We allow /-~ (exclusive), 0-} (inclusive) + return /^[\u0030-\u007d]+$/.test(key); +}; diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 4dadede7..822efd95 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -83,9 +83,24 @@ Wallet.prototype.fromOptions = function fromOptions(options) { master = MasterKey.fromKey(master); this.master = master; - this.initialized = options.initialized || false; - this.accountDepth = options.accountDepth || 0; - this.id = options.id || this.getID(); + + if (options.initialized != null) { + assert(typeof options.initialized === 'boolean'); + this.initialized = options.initialized; + } + + if (options.accountDepth != null) { + assert(utils.isNumber(options.accountDepth)); + this.accountDepth = options.accountDepth; + } + + if (options.id) { + assert(utils.isAlpha(options.id), 'Wallet ID must be alphanumeric.'); + this.id = options.id; + } + + if (!this.id) + this.id = this.getID(); return this; }; @@ -1452,11 +1467,16 @@ Wallet.prototype.toJSON = function toJSON() { */ Wallet.prototype.fromJSON = function fromJSON(json) { + assert(utils.isAlpha(json.id), 'Wallet ID must be alphanumeric.'); + assert(typeof json.initialized === 'boolean'); + assert(utils.isNumber(json.accountDepth)); + this.network = bcoin.network.get(json.network); this.id = json.id; this.initialized = json.initialized; this.accountDepth = json.accountDepth; this.master = MasterKey.fromJSON(json.master); + return this; }; @@ -1599,45 +1619,59 @@ Account.prototype.fromOptions = function fromOptions(options) { var i; assert(options, 'Options are required.'); - assert(options.id, 'Wallet ID is required.'); - assert(options.accountKey, 'Account key is required.'); + assert(utils.isAlpha(options.id), 'Wallet ID must be alphanumeric.'); + assert(utils.isAlpha(options.name), 'Account name must be alphanumeric.'); + assert(bcoin.hd.isHD(options.accountKey), 'Account key is required.'); assert(utils.isNumber(options.accountIndex), 'Account index is required.'); this.id = options.id; this.name = options.name; - if (options.witness != null) + if (options.witness != null) { + assert(typeof options.witness === 'boolean'); this.witness = options.witness; + } this.accountKey = options.accountKey; - if (options.accountIndex != null) + if (options.accountIndex != null) { + assert(utils.isNumber(options.accountIndex)); this.accountIndex = options.accountIndex; + } - if (options.receiveDepth != null) + if (options.receiveDepth != null) { + assert(utils.isNumber(options.receiveDepth)); this.receiveDepth = options.receiveDepth; + } - if (options.changeDepth != null) + if (options.changeDepth != null) { + assert(utils.isNumber(options.changeDepth)); this.changeDepth = options.changeDepth; + } - if (options.type) + if (options.type) { + assert(options.type === 'pubkeyhash' || options.type === 'multisig'); this.type = options.type; + } - if (options.m) + if (options.m != null) { + assert(utils.isNumber(options.m)); this.m = options.m; + } - if (options.n) + if (options.n != null) { + assert(utils.isNumber(options.n)); this.n = options.n; + } - if (options.initialized != null) + if (options.initialized != null) { + assert(typeof options.initialized === 'boolean'); this.initialized = options.initialized; + } if (this.n > 1) this.type = 'multisig'; - assert(this.type === 'pubkeyhash' || this.type === 'multisig', - '`type` must be multisig or pubkeyhash.'); - if (this.m < 1 || this.m > this.n) throw new Error('m ranges between 1 and n'); @@ -1647,6 +1681,7 @@ Account.prototype.fromOptions = function fromOptions(options) { this.pushKey(this.accountKey); if (options.keys) { + assert(Array.isArray(options.keys)); for (i = 0; i < options.keys.length; i++) this.pushKey(options.keys[i]); } @@ -2258,6 +2293,17 @@ Account.prototype.fromJSON = function fromJSON(json) { var i; assert.equal(json.network, this.network.type); + assert(utils.isAlpha(json.id), 'Wallet ID must be alphanumeric.'); + assert(utils.isAlpha(json.name), 'Account name must be alphanumeric.'); + assert(typeof json.initialized === 'boolean'); + assert(json.type === 'pubkeyhash' || json.type === 'multisig'); + assert(utils.isNumber(json.m)); + assert(utils.isNumber(json.n)); + assert(typeof json.witness === 'boolean'); + assert(utils.isNumber(json.accountIndex)); + assert(utils.isNumber(json.receiveDepth)); + assert(utils.isNumber(json.changeDepth)); + assert(Array.isArray(json.keys)); this.id = json.id; this.name = json.name; @@ -2405,10 +2451,27 @@ function MasterKey(options) { */ MasterKey.prototype.fromOptions = function fromOptions(options) { - this.encrypted = !!options.encrypted; - this.iv = options.iv; - this.ciphertext = options.ciphertext; - this.key = options.key || null; + assert(options); + + if (options.encrypted != null) { + assert(typeof options.encrypted === 'boolean'); + this.encrypted = options.encrypted; + } + + if (options.iv) { + assert(Buffer.isBuffer(options.iv)); + this.iv = options.iv; + } + + if (options.ciphertext) { + assert(Buffer.isBuffer(options.ciphertext)); + this.ciphertext = options.ciphertext; + } + + if (options.key) { + assert(Buffer.isBuffer(options.key)); + this.key = options.key; + } assert(this.encrypted ? !this.key : this.key); diff --git a/lib/bcoin/walletdb.js b/lib/bcoin/walletdb.js index 54961a5b..4b67f413 100644 --- a/lib/bcoin/walletdb.js +++ b/lib/bcoin/walletdb.js @@ -468,7 +468,7 @@ WalletDB.prototype.get = function get(id, callback) { */ WalletDB.prototype.save = function save(wallet, callback) { - if (!isAlpha(wallet.id)) + if (!utils.isAlpha(wallet.id)) return callback(new Error('Wallet IDs must be alphanumeric.')); this.db.put('w/' + wallet.id, wallet.toRaw(), callback); @@ -664,7 +664,7 @@ WalletDB.prototype.getAccountIndex = function getAccountIndex(id, name, callback WalletDB.prototype.saveAccount = function saveAccount(account, callback) { var index, batch; - if (!isAlpha(account.name)) + if (!utils.isAlpha(account.name)) return callback(new Error('Account names must be alphanumeric.')); batch = this.db.batch(); @@ -1464,11 +1464,6 @@ function serializePaths(out) { return p.render(); } -function isAlpha(key) { - // We allow /-~ (exclusive), 0-} (inclusive) - return /^[\u0030-\u007d]+$/.test(key); -} - function ReadLock(parent) { if (!(this instanceof ReadLock)) return new ReadLock(parent);