diff --git a/CHANGELOG.md b/CHANGELOG.md index adcbd6db3..d2b21d082 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,8 @@ to these calls. - HTTP Changes: - Wallet and account create methods now accept `lookahead` values up to `2^32 - 1`. + - Create account is now: `POST /wallet/:id/account/:account` instead of `PUT /wallet/:id/account/:account`. + - Introduce new API to modify account: `PUT /wallet/:id/account/:account`. (old create) - New RPC methods: - `createbatch` and `sendbatch` create batch transactions with any number diff --git a/bin/hsw-cli b/bin/hsw-cli index 163fb9763..3dcd5c7d4 100755 --- a/bin/hsw-cli +++ b/bin/hsw-cli @@ -21,6 +21,7 @@ const HELP = ` Commands: $ abandon [hash]: Abandon a transaction. $ account create [account-name]: Create account. + $ account modify [account-name]: Set account options. $ account get [account-name]: Get account details. $ account list: List account names. $ address [account-name]: Derive new address. @@ -233,6 +234,18 @@ class CLI { this.log(account); } + async modifyAccount() { + const name = this.config.str([0, 'name']); + + const options = { + lookahead: this.config.uint('lookahead') + }; + + const account = await this.wallet.modifyAccount(name, options); + + this.log(account); + } + async createAddress() { const account = this.config.str([0, 'account']); const addr = await this.wallet.createAddress(account); @@ -579,6 +592,11 @@ class CLI { await this.createAccount(); break; } + if (this.argv[0] === 'modify') { + this.argv.shift(); + await this.modifyAccount(); + break; + } if (this.argv[0] === 'get') this.argv.shift(); await this.getAccount(); diff --git a/lib/client/wallet.js b/lib/client/wallet.js index 0a9bc7b84..f6b7d6b9f 100644 --- a/lib/client/wallet.js +++ b/lib/client/wallet.js @@ -682,6 +682,21 @@ class WalletClient extends Client { */ createAccount(id, name, options) { + if (name == null) + throw new Error('Account name is required.'); + + return this.post(`/wallet/${id}/account/${name}`, options); + } + + /** + * Modify account. + * @param {String} id + * @param {String} name + * @param {Object} options + * @returns {Promise} + */ + + modifyAccount(id, name, options) { return this.put(`/wallet/${id}/account/${name}`, options); } @@ -1363,12 +1378,20 @@ class Wallet extends EventEmitter { */ createAccount(name, options) { - if (name == null) - throw new Error('Account name is required.'); - return this.client.createAccount(this.id, name, options); } + /** + * Modify account. + * @param {String} name + * @param {Object} options + * @returns {Promise} + */ + + modifyAccount(name, options) { + return this.client.modifyAccount(this.id, name, options); + } + /** * Create address. * @param {Object} options diff --git a/lib/wallet/account.js b/lib/wallet/account.js index be18c4d26..d60c7903b 100644 --- a/lib/wallet/account.js +++ b/lib/wallet/account.js @@ -563,6 +563,8 @@ class Account extends bio.Struct { */ async setLookahead(b, lookahead) { + assert((lookahead >>> 0) === lookahead, 'Lookahead must be a number.'); + if (lookahead === this.lookahead) return; diff --git a/lib/wallet/http.js b/lib/wallet/http.js index 1e192fb10..9175ce7dc 100644 --- a/lib/wallet/http.js +++ b/lib/wallet/http.js @@ -337,7 +337,7 @@ class HTTP extends Server { }); // Create account - this.put('/wallet/:id/account/:account', async (req, res) => { + this.post('/wallet/:id/account/:account', async (req, res) => { const valid = Validator.fromRequest(req); const passphrase = valid.str('passphrase'); @@ -362,6 +362,22 @@ class HTTP extends Server { res.json(200, account.getJSON(balance)); }); + // Modify account + this.put('/wallet/:id/account/:account', async (req, res) => { + const valid = Validator.fromRequest(req); + const passphrase = valid.str('passphrase'); + const acct = valid.str('account'); + + const options = { + lookahead: valid.u32('lookahead') + }; + + const account = await req.wallet.modifyAccount(acct, options, passphrase); + const balance = await req.wallet.getBalance(account.accountIndex); + + res.json(200, account.getJSON(balance)); + }); + // Change passphrase this.post('/wallet/:id/passphrase', async (req, res) => { const valid = Validator.fromRequest(req); diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index a036feeec..b7f4b1f0a 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -659,6 +659,50 @@ class Wallet extends EventEmitter { return account; } + /** + * Modify an account. Requires passphrase if master key is encrypted. + * @param {String|Number} acct + * @param {Object} options + * @param {String} [passphrase] + * @returns {Promise} + */ + + async modifyAccount(acct, options, passphrase) { + const unlock = await this.writeLock.lock(); + try { + return await this._modifyAccount(acct, options, passphrase); + } finally { + unlock(); + } + } + + /** + * Create an account without a lock. + * @param {String|Number} acct + * @param {Object} options + * @param {String} [passphrase] + * @returns {Promise} + */ + + async _modifyAccount(acct, options, passphrase) { + if (!await this.hasAccount(acct)) + throw new Error(`Account ${acct} does not exist.`); + + await this.unlock(passphrase); + + const account = await this.getAccount(acct); + assert(account); + + const b = this.db.batch(); + + if (options.lookahead != null) + await account.setLookahead(b, options.lookahead); + + await b.write(); + + return account; + } + /** * Ensure an account. Requires passphrase if master key is encrypted. * @param {Object} options - See {@link Account} options. @@ -714,7 +758,7 @@ class Wallet extends EventEmitter { /** * Retrieve an account from the database. * @param {Number|String} acct - * @returns {Promise} - Returns {@link Account}. + * @returns {Promise} */ async getAccount(acct) { @@ -4328,41 +4372,6 @@ class Wallet extends EventEmitter { return paths; } - /** - * Increase lookahead for account. - * @param {(Number|String)?} account - * @param {Number} lookahead - * @returns {Promise} - */ - - async setLookahead(acct, lookahead) { - const unlock = await this.writeLock.lock(); - try { - return this._setLookahead(acct, lookahead); - } finally { - unlock(); - } - } - - /** - * Increase lookahead for account (without a lock). - * @private - * @param {(Number|String)?} account - * @param {Number} lookahead - * @returns {Promise} - */ - - async _setLookahead(acct, lookahead) { - const account = await this.getAccount(acct); - - if (!account) - throw new Error('Account not found.'); - - const b = this.db.batch(); - await account.setLookahead(b, lookahead); - await b.write(); - } - /** * Sync address depths based on a transaction's outputs. * This is used for deriving new addresses when diff --git a/test/wallet-http-test.js b/test/wallet-http-test.js index 9d542c47b..b2bd77632 100644 --- a/test/wallet-http-test.js +++ b/test/wallet-http-test.js @@ -124,6 +124,19 @@ describe('Wallet HTTP', function() { assert.strictEqual(getNewAccount.lookahead, 1001); }); + it('should modify account lookahead to 1000', async () => { + const wname = 'lookahead2'; + await wclient.createWallet(wname); + + const defAccount = await wclient.getAccount(wname, 'default'); + assert.strictEqual(defAccount.lookahead, 200); + + const modified = await wclient.modifyAccount(wname, 'default', { + lookahead: 1000 + }); + assert.strictEqual(modified.lookahead, 1000); + }); + it('should get key by address from watch-only', async () => { const phrase = 'abandon abandon abandon abandon abandon abandon ' + 'abandon abandon abandon abandon abandon about';