diff --git a/lib/wallet/account.js b/lib/wallet/account.js index caeb9dac..5be77405 100644 --- a/lib/wallet/account.js +++ b/lib/wallet/account.js @@ -211,7 +211,7 @@ Account.fromOptions = function fromOptions(db, options) { * @const {Number} */ -Account.MAX_LOOKAHEAD = 5; +Account.MAX_LOOKAHEAD = 10; /** * Attempt to intialize the account (generating @@ -794,7 +794,6 @@ Account.prototype.toRaw = function toRaw(writer) { p.writeU32(this.network.magic); p.writeVarString(this.name, 'ascii'); p.writeU8(this.initialized ? 1 : 0); - p.writeU8(this.watchOnly ? 1 : 0); p.writeU8(this.type); p.writeU8(this.m); p.writeU8(this.n); @@ -831,7 +830,6 @@ Account.prototype.fromRaw = function fromRaw(data) { this.network = Network.fromMagic(p.readU32()); this.name = p.readVarString('ascii'); this.initialized = p.readU8() === 1; - this.watchOnly = p.readU8() === 1; this.type = p.readU8(); this.m = p.readU8(); this.n = p.readU8(); diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index 2cf91b21..e409ec1b 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -71,6 +71,7 @@ function Wallet(db, options) { this.id = null; this.master = null; this.initialized = false; + this.watchOnly = false; this.accountDepth = 0; this.token = constants.ZERO_HASH; this.tokenDepth = 0; @@ -111,6 +112,11 @@ Wallet.prototype.fromOptions = function fromOptions(options) { this.initialized = options.initialized; } + if (options.watchOnly != null) { + assert(typeof options.watchOnly === 'boolean'); + this.watchOnly = options.watchOnly; + } + if (options.accountDepth != null) { assert(utils.isNumber(options.accountDepth)); this.accountDepth = options.accountDepth; @@ -633,7 +639,6 @@ Wallet.prototype._createAccount = co(function* createAccount(options) { var passphrase = options.passphrase; var timeout = options.timeout; var name = options.name; - var watchOnly = options.watchOnly === true; var key, master, account; if (typeof options.account === 'string') @@ -644,7 +649,7 @@ Wallet.prototype._createAccount = co(function* createAccount(options) { master = yield this.unlock(passphrase, timeout); - if (watchOnly && options.accountKey) { + if (this.watchOnly && options.accountKey) { key = options.accountKey; if (!HD.isHD(key)) key = HD.from(key, this.network); @@ -664,7 +669,7 @@ Wallet.prototype._createAccount = co(function* createAccount(options) { keys: options.keys, m: options.m, n: options.n, - watchOnly: watchOnly + watchOnly: this.watchOnly }; this.start(); @@ -742,6 +747,7 @@ Wallet.prototype.getAccount = co(function* getAccount(account) { account.wid = this.wid; account.id = this.id; + account.watchOnly = this.watchOnly; return account; }); @@ -976,6 +982,14 @@ Wallet.prototype._importKey = co(function* importKey(account, ring, passphrase) if (account == null) account = 0; + if (!this.watchOnly) { + if (!ring.privateKey) + throw new Error('Cannot import pubkey into non watch-only wallet.'); + } else { + if (ring.privateKey) + throw new Error('Cannot import privkey into watch-only wallet.'); + } + exists = yield this.getPath(ring.getHash('hex')); if (exists) @@ -989,9 +1003,6 @@ Wallet.prototype._importKey = co(function* importKey(account, ring, passphrase) if (account.type !== Account.types.PUBKEYHASH) throw new Error('Cannot import into non-pkh account.'); - if (!ring.privateKey && !account.watchOnly) - throw new Error('Cannot import pubkey into non-watchonly account.'); - yield this.unlock(passphrase); ring = WalletKey.fromRing(account, ring); @@ -1053,6 +1064,9 @@ Wallet.prototype._importAddress = co(function* importAddress(account, address) { if (account == null) account = 0; + if (!this.watchOnly) + throw new Error('Cannot import address into non watch-only wallet.'); + exists = yield this.getPath(address); if (exists) @@ -1066,9 +1080,6 @@ Wallet.prototype._importAddress = co(function* importAddress(account, address) { if (account.type !== Account.types.PUBKEYHASH) throw new Error('Cannot import into non-pkh account.'); - if (!account.watchOnly) - throw new Error('Cannot import address into non-watchonly account.'); - path = Path.fromAddress(account, address); this.start(); @@ -1132,6 +1143,9 @@ Wallet.prototype._fund = co(function* fund(tx, options) { if (!this.initialized) throw new Error('Wallet is not initialized.'); + if (this.watchOnly) + throw new Error('Cannot fund from watch-only wallet.'); + if (options.account != null) { account = yield this.getAccount(options.account); if (!account) @@ -1595,6 +1609,9 @@ Wallet.prototype.sign = co(function* sign(tx, options) { if (typeof options === 'string' || Buffer.isBuffer(options)) options = { passphrase: options }; + if (this.watchOnly) + throw new Error('Cannot sign from a watch-only wallet.'); + yield this.unlock(options.passphrase, options.timeout); rings = yield this.deriveInputs(tx); @@ -2073,6 +2090,7 @@ Wallet.prototype.toJSON = function toJSON() { wid: this.wid, id: this.id, initialized: this.initialized, + watchOnly: this.watchOnly, accountDepth: this.accountDepth, token: this.token.toString('hex'), tokenDepth: this.tokenDepth, @@ -2090,6 +2108,7 @@ Wallet.prototype.toJSON = function toJSON() { Wallet.prototype.fromJSON = function fromJSON(json) { assert(utils.isNumber(json.wid)); assert(typeof json.initialized === 'boolean'); + assert(typeof json.watchOnly === 'boolean'); assert(utils.isName(json.id), 'Bad wallet ID.'); assert(utils.isNumber(json.accountDepth)); assert(typeof json.token === 'string'); @@ -2100,6 +2119,7 @@ Wallet.prototype.fromJSON = function fromJSON(json) { this.wid = json.wid; this.id = json.id; this.initialized = json.initialized; + this.watchOnly = json.watchOnly; this.accountDepth = json.accountDepth; this.token = new Buffer(json.token, 'hex'); this.master = MasterKey.fromJSON(json.master); @@ -2119,6 +2139,7 @@ Wallet.prototype.toRaw = function toRaw(writer) { p.writeU32(this.wid); p.writeVarString(this.id, 'ascii'); p.writeU8(this.initialized ? 1 : 0); + p.writeU8(this.watchOnly ? 1 : 0); p.writeU32(this.accountDepth); p.writeBytes(this.token); p.writeU32(this.tokenDepth); @@ -2142,6 +2163,7 @@ Wallet.prototype.fromRaw = function fromRaw(data) { this.wid = p.readU32(); this.id = p.readVarString('ascii'); this.initialized = p.readU8() === 1; + this.watchOnly = p.readU8() === 1; this.accountDepth = p.readU32(); this.token = p.readBytes(32); this.tokenDepth = p.readU32(); diff --git a/test/wallet-test.js b/test/wallet-test.js index 850e0338..fb38943d 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -946,12 +946,10 @@ describe('Wallet', function() { it('should import pubkey', cob(function *() { var priv = bcoin.keyring.generate(); var key = new bcoin.keyring(priv.publicKey); - var w = yield walletdb.create(); + var w = yield walletdb.create({ watchOnly: true }); var options, k, t1, t2, tx; - yield w.createAccount({ name: 'watchonly', watchOnly: true }); - - yield w.importKey('watchonly', key); + yield w.importKey('default', key); k = yield w.getPath(key.getHash('hex')); @@ -963,12 +961,10 @@ describe('Wallet', function() { it('should import address', cob(function *() { var key = bcoin.keyring.generate(); - var w = yield walletdb.create(); + var w = yield walletdb.create({ watchOnly: true }); var options, k, t1, t2, tx; - yield w.createAccount({ name: 'watchonly', watchOnly: true }); - - yield w.importAddress('watchonly', key.getAddress()); + yield w.importAddress('default', key.getAddress()); k = yield w.getPath(key.getHash('hex'));