From 800ea31e9c44c6e32d102493541e1f0f1e20b09c Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 16 Jul 2020 17:43:14 +0200 Subject: [PATCH] add new /wallet endpoint and deprecate /multiaddr et /unspent --- accounts/multiaddr-rest-api.js | 1 + accounts/unspent-rest-api.js | 1 + accounts/wallet-rest-api..js | 136 +++++++++++++++++++++++++++++++++ lib/wallet/wallet-service.js | 88 ++++++++++++++++++++- 4 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 accounts/wallet-rest-api..js diff --git a/accounts/multiaddr-rest-api.js b/accounts/multiaddr-rest-api.js index 3399e23..3bc7aaf 100644 --- a/accounts/multiaddr-rest-api.js +++ b/accounts/multiaddr-rest-api.js @@ -17,6 +17,7 @@ const debugApi = !!(process.argv.indexOf('api-debug') > -1) /** * Multiaddr API endpoints + * @deprecated */ class MultiaddrRestApi { diff --git a/accounts/unspent-rest-api.js b/accounts/unspent-rest-api.js index 617fb44..baf1779 100644 --- a/accounts/unspent-rest-api.js +++ b/accounts/unspent-rest-api.js @@ -17,6 +17,7 @@ const debugApi = !!(process.argv.indexOf('api-debug') > -1) /** * Unspent API endpoints + * @deprecated */ class UnspentRestApi { diff --git a/accounts/wallet-rest-api..js b/accounts/wallet-rest-api..js new file mode 100644 index 0000000..96ab5e1 --- /dev/null +++ b/accounts/wallet-rest-api..js @@ -0,0 +1,136 @@ +/*! + * accounts/wallet-rest-api.js + * Copyright © 2019 – Katana Cryptographic Ltd. All Rights Reserved. + */ +'use strict' + +const bodyParser = require('body-parser') +const Logger = require('../lib/logger') +const errors = require('../lib/errors') +const walletService = require('../lib/wallet/wallet-service') +const authMgr = require('../lib/auth/authorizations-manager') +const HttpServer = require('../lib/http-server/http-server') +const apiHelper = require('./api-helper') + +const debugApi = !!(process.argv.indexOf('api-debug') > -1) + + +/** + * Wallet API endpoints + */ +class WalletRestApi { + + /** + * Constructor + * @param {pushtx.HttpServer} httpServer - HTTP server + */ + constructor(httpServer) { + this.httpServer = httpServer + + // Establish routes + const urlencodedParser = bodyParser.urlencoded({ extended: true }) + + this.httpServer.app.get( + '/wallet', + authMgr.checkAuthentication.bind(authMgr), + apiHelper.validateEntitiesParams.bind(apiHelper), + this.getWallet.bind(this), + HttpServer.sendAuthError + ) + + this.httpServer.app.post( + '/wallet', + urlencodedParser, + authMgr.checkAuthentication.bind(authMgr), + apiHelper.validateEntitiesParams.bind(apiHelper), + this.postWallet.bind(this), + HttpServer.sendAuthError + ) + } + + /** + * Handle wallet GET request + * @param {object} req - http request object + * @param {object} res - http response object + */ + async getWallet(req, res) { + try { + // Check request params + if (!apiHelper.checkEntitiesParams(req.query)) + return HttpServer.sendError(res, errors.multiaddr.NOACT) + + // Parse params + const entities = apiHelper.parseEntitiesParams(req.query) + + const result = await walletService.getFullWalletInfo( + entities.active, + entities.legacy, + entities.bip49, + entities.bip84, + entities.pubkey + ) + + const ret = JSON.stringify(result, null, 2) + HttpServer.sendRawData(res, ret) + + } catch(e) { + HttpServer.sendError(res, e) + + } finally { + if (debugApi) { + const strParams = + `${req.query.active ? req.query.active : ''} \ + ${req.query.new ? req.query.new : ''} \ + ${req.query.pubkey ? req.query.pubkey : ''} \ + ${req.query.bip49 ? req.query.bip49 : ''} \ + ${req.query.bip84 ? req.query.bip84 : ''}` + + Logger.info(`API : Completed GET /wallet ${strParams}`) + } + } + } + + /** + * Handle wallet POST request + * @param {object} req - http request object + * @param {object} res - http response object + */ + async postWallet(req, res) { + try { + // Check request params + if (!apiHelper.checkEntitiesParams(req.body)) + return HttpServer.sendError(res, errors.multiaddr.NOACT) + + // Parse params + const entities = apiHelper.parseEntitiesParams(req.body) + + const result = await walletService.getFullWalletInfo( + entities.active, + entities.legacy, + entities.bip49, + entities.bip84, + entities.pubkey + ) + + HttpServer.sendOkDataOnly(res, result) + + } catch(e) { + HttpServer.sendError(res, e) + + } finally { + if (debugApi) { + const strParams = + `${req.body.active ? req.body.active : ''} \ + ${req.body.new ? req.body.new : ''} \ + ${req.body.pubkey ? req.body.pubkey : ''} \ + ${req.body.bip49 ? req.body.bip49 : ''} \ + ${req.body.bip84 ? req.body.bip84 : ''}` + + Logger.info(`API : Completed POST /wallet ${strParams}`) + } + } + } + +} + +module.exports = WalletRestApi diff --git a/lib/wallet/wallet-service.js b/lib/wallet/wallet-service.js index 78f75be..64ca4bc 100644 --- a/lib/wallet/wallet-service.js +++ b/lib/wallet/wallet-service.js @@ -25,8 +25,73 @@ class WalletService { */ constructor() {} + /** + * Get full wallet information + * @param {object} active - mapping of active entities + * @param {object} legacy - mapping of new legacy addresses + * @param {object} bip49 - mapping of new bip49 addresses + * @param {object} bip84 - mapping of new bip84 addresses + * @param {object} pubkeys - mapping of new pubkeys/addresses + * @returns {Promise} + */ + async getFullWalletInfo(active, legacy, bip49, bip84, pubkeys) { + // Check parameters + const validParams = this._checkEntities(active, legacy, bip49, bip84, pubkeys) + + if (!validParams) { + const info = new WalletInfo() + const ret = this._formatGetFullWalletInfoResult(info) + return Promise.resolve(ret) + } + + // Merge all entities into active mapping + active = this._mergeEntities(active, legacy, bip49, bip84, pubkeys) + + // Initialize a WalletInfo object + const walletInfo = new WalletInfo(active) + + try { + // Add the new xpubs + await util.seriesCall(legacy.xpubs, this._newBIP44) + await util.seriesCall(bip49.xpubs, this._newBIP49) + await util.seriesCall(bip84.xpubs, this._newBIP84) + // Load hd accounts info + await walletInfo.ensureHdAccounts() + await walletInfo.loadHdAccountsInfo() + // Add the new addresses + await db.addAddresses(legacy.addrs) + await db.addAddresses(bip49.addrs) + await db.addAddresses(bip84.addrs) + await db.addAddresses(pubkeys.addrs) + // Ensure addresses exist + await walletInfo.ensureAddresses() + // Force import of addresses associated to paynyms + // if dojo relies on a local index + if (keys.indexer.active != 'third_party_explorer') + await this._forceEnsureAddressesForActivePubkeys(active) + // Filter the addresses + await walletInfo.filterAddresses() + // Load the utxos + await walletInfo.loadUtxos() + // Load the addresses + await walletInfo.loadAddressesInfo() + // Load the most recent transactions + await walletInfo.loadTransactions(0, null, true) + // Postprocessing + await walletInfo.postProcessAddresses() + await walletInfo.postProcessHdAccounts() + // Format the result + return this._formatGetFullWalletInfoResult(walletInfo) + + } catch(e) { + Logger.error(e, 'WalletService.getWalletInfo()') + return Promise.reject({status:'error', error:'internal server error'}) + } + } + /** * Get wallet information + * @deprecated * @param {object} active - mapping of active entities * @param {object} legacy - mapping of new legacy addresses * @param {object} bip49 - mapping of new bip49 addresses @@ -86,8 +151,28 @@ class WalletService { } } + /** + * Prepares the result to be returned by getFullWalletInfo() + * @param {WalletInfo} info + * @returns {object} + */ + _formatGetFullWalletInfoResult(info) { + let ret = info.toPojo() + + delete ret['n_tx'] + + ret.addresses = ret.addresses.map(x => { + delete x['derivation'] + delete x['created'] + return x + }) + + return ret + } + /** * Prepares the result to be returned by getWalletInfo() + * @deprecated * @param {WalletInfo} info * @returns {object} */ @@ -108,6 +193,7 @@ class WalletService { /** * Get wallet unspent outputs + * @deprecated * @param {object} active - mapping of active entities * @param {object} legacy - mapping of new legacy addresses * @param {object} bip49 - mapping of new bip49 addresses @@ -167,7 +253,7 @@ class WalletService { } /** - * Get a subset of wallet transaction + * Get a subset of wallet transactions * @param {object} entities - mapping of active entities * @param {integer} page - page of transactions to be returned * @param {integer} count - number of transactions returned per page