diff --git a/docs/developer_guide/connection_manager.md b/docs/developer_guide/connection_manager.md index c4037d1f6..435cd347e 100644 --- a/docs/developer_guide/connection_manager.md +++ b/docs/developer_guide/connection_manager.md @@ -21,6 +21,16 @@ connectionManager.addConnection(new MoneroRpcConnection("http://example.com")); // set current connection connectionManager.setConnection(new MoneroRpcConnection("http://foo.bar", "admin", "password")); // connection is added if new +// create wallet with managed connections or set later +let walletFull = await monerojs.createWalletFull({ + path: "sample_wallet_full", + password: "supersecretpassword123", + networkType: "stagenet", + connectionManager: connectionManager, + seed: "hefty value scenic...", + restoreHeight: 573936, +}); + // check connection status await connectionManager.checkConnection(); console.log("Connection manager is connected: " + connectionManager.isConnected()); @@ -34,11 +44,8 @@ connectionManager.addListener(new class extends MoneroConnectionManagerListener } }); -// check connection status every 10 seconds -await connectionManager.startCheckingConnection(10000); - -// automatically switch to best available connection if disconnected -connectionManager.setAutoSwitch(true); +// check connections every 10 seconds (in order of priority) and switch to the best +await connectionManager.startPolling(10000); // get best available connection in order of priority then response time let bestConnection = await connectionManager.getBestAvailableConnection(); diff --git a/src/main/js/common/MoneroConnectionManager.js b/src/main/js/common/MoneroConnectionManager.js index 34cd80c40..75f6f5071 100644 --- a/src/main/js/common/MoneroConnectionManager.js +++ b/src/main/js/common/MoneroConnectionManager.js @@ -39,8 +39,8 @@ const ThreadPool = require("./ThreadPool"); *    }
* });

* - * // check connection status every 10 seconds
- * await connectionManager.startCheckingConnection(10000);

+ * // start polling for best connection every 10 seconds and automatically switch
+ * connectionManager.startPolling(10000);

* * // automatically switch to best available connection if disconnected
* connectionManager.setAutoSwitch(true);

@@ -59,6 +59,17 @@ const ThreadPool = require("./ThreadPool"); * */ class MoneroConnectionManager { + + /** + * Specify behavior when polling. + * + * One of PRIORITIZED (poll connections in order of priority until connected; default), CURRENT (poll current connection), or ALL (poll all connections). + */ + static PollType = { + PRIORITIZED: 0, + CURRENT: 1, + ALL: 2 + } /** * Construct a connection manager. @@ -67,8 +78,10 @@ class MoneroConnectionManager { */ constructor(proxyToWorker) { this._proxyToWorker = proxyToWorker !== false; - this._timeoutInMs = MoneroConnectionManager.DEFAULT_TIMEOUT; + this._timeoutMs = MoneroConnectionManager.DEFAULT_TIMEOUT; + this._autoSwitch = MoneroConnectionManager.DEFAULT_AUTO_SWITCH; this._connections = []; + this._responseTimes = new Map(); this._listeners = []; } @@ -103,14 +116,24 @@ class MoneroConnectionManager { this._listeners.splice(0, this._listeners.length); return this; } - + + /** + * Get all listeners. + * + * @return {MoneroConnectionManagerListener[]} all listeners + */ + getListeners() { + return this._listeners + } + /** * Add a connection. The connection may have an elevated priority for this manager to use. * - * @param {MoneroRpcConnection} connection - the connection to add + * @param {string|MoneroRpcConnection} uriOrConnection - uri or connection to add * @return {Promise} this connection manager for chaining */ - async addConnection(connection) { + async addConnection(uriOrConnection) { + let connection = typeof uriOrConnection === "string" ? new MoneroRpcConnection(uriOrConnection) : uriOrConnection; for (let aConnection of this._connections) { if (aConnection.getUri() === connection.getUri()) throw new MoneroError("Connection URI already exists"); } @@ -129,23 +152,57 @@ class MoneroConnectionManager { let connection = this.getConnectionByUri(uri); if (!connection) throw new MoneroError("No connection exists with URI: " + uri); GenUtils.remove(this._connections, connection); + this._responseTimes.delete(connection.getUri()); if (connection === this._currentConnection) { this._currentConnection = undefined; - this._onConnectionChanged(this._currentConnection); + await this._onConnectionChanged(this._currentConnection); } return this; } /** - * Indicates if the connection manager is connected to a node. + * Set the current connection. + * Provide a URI to select an existing connection without updating its credentials. + * Provide a MoneroRpcConnection to add new connection or replace existing connection with the same URI. + * Notify if current connection changes. + * Does not check the connection. * - * @return {boolean|undefined} true if the current connection is set, online, and not unauthenticated, undefined if unknown, false otherwise + * @param {string|MoneroRpcConnection} uriOrConnection - is the uri of the connection or the connection to make current (default undefined for no current connection) + * @return {Promise} this connection manager for chaining */ - isConnected() { - if (!this._currentConnection) return false; - return this._currentConnection.isConnected(); + async setConnection(uriOrConnection) { + + // handle uri + if (uriOrConnection && typeof uriOrConnection === "string") { + let connection = this.getConnectionByUri(uriOrConnection); + return this.setConnection(connection === undefined ? new MoneroRpcConnection(uriOrConnection) : connection); + } + + // handle connection + let connection = uriOrConnection; + if (this._currentConnection === connection) return this; + + // check if setting undefined connection + if (!connection) { + this._currentConnection = undefined; + await this._onConnectionChanged(undefined); + return this; + } + + // validate connection + if (!(connection instanceof MoneroRpcConnection)) throw new MoneroError("Must provide string or MoneroRpcConnection to set connection"); + if (!connection.getUri()) throw new MoneroError("Connection is missing URI"); + + // add or replace connection + let prevConnection = this.getConnectionByUri(connection.getUri()); + if (prevConnection) GenUtils.remove(this._connections, prevConnection); + await this.addConnection(connection); + this._currentConnection = connection; + await this._onConnectionChanged(this._currentConnection); + + return this; } - + /** * Get the current connection. * @@ -154,6 +211,16 @@ class MoneroConnectionManager { getConnection() { return this._currentConnection; } + + /** + * Indicates if this manager has a connection with the given URI. + * + * @param {string} uri URI of the connection to check + * @return {boolean} true if this manager has a connection with the given URI, false otherwise + */ + hasConnection(uri) { + return this.getConnectionByUri(uri) !== undefined; + } /** * Get a connection by URI. @@ -176,84 +243,64 @@ class MoneroConnectionManager { sortedConnections.sort(this._compareConnections.bind(this)); return sortedConnections; } - + /** - * Get the best available connection in order of priority then response time. + * Indicates if the connection manager is connected to a node. * - * @param {MoneroRpcConnection[]} excludedConnections - connections to be excluded from consideration (optional) - * @return {Promise} the best available connection in order of priority then response time, undefined if no connections available + * @return {boolean|undefined} true if the current connection is set, online, and not unauthenticated, undefined if unknown, false otherwise */ - async getBestAvailableConnection(excludedConnections) { - - // try connections within each ascending priority - for (let prioritizedConnections of this._getConnectionsInAscendingPriority()) { - try { - - // create promises to check connections - let that = this; - let checkPromises = []; - for (let connection of prioritizedConnections) { - if (excludedConnections && GenUtils.arrayContains(excludedConnections, connection)) continue; - checkPromises.push(new Promise(async function(resolve, reject) { - await connection.checkConnection(that._timeoutInMs); - if (connection.isConnected()) resolve(connection); - else reject(); - })); - } - - // use first available connection - let firstAvailable = await Promise.any(checkPromises); - if (firstAvailable) return firstAvailable; - } catch (err) { - if (!(err instanceof AggregateError)) throw new MoneroError(err); - } - } - return undefined; + isConnected() { + if (!this._currentConnection) return false; + return this._currentConnection.isConnected(); } - + /** - * Set the current connection. - * Provide a URI to select an existing connection without updating its credentials. - * Provide a MoneroRpcConnection to add new connection or replace existing connection with the same URI. - * Notify if current connection changes. - * Does not check the connection. + * Start polling connections. * - * @param {string|MoneroRpcConnection} uriOrConnection - is the uri of the connection or the connection to make current (default undefined for no current connection) + * @param {number} periodMs poll period in milliseconds (default 20s) + * @param {boolean} autoSwitch specifies to automatically switch to the best connection (default true unless changed) + * @param {number} timeoutMs specifies the timeout to poll a single connection (default 5s unless changed) + * @param {number} pollType one of PRIORITIZED (poll connections in order of priority until connected; default), CURRENT (poll current connection), or ALL (poll all connections) + * @param {MoneroRpcConnection[]} excludedConnections connections excluded from being polled * @return {MoneroConnectionManager} this connection manager for chaining */ - setConnection(uriOrConnection) { - - // handle uri - if (uriOrConnection && typeof uriOrConnection === "string") { - let connection = this.getConnectionByUri(uriOrConnection); - return this.setConnection(connection === undefined ? new MoneroRpcConnection(uriOrConnection) : connection); - } - - // handle connection - let connection = uriOrConnection; - if (this._currentConnection === connection) return this; - - // check if setting undefined connection - if (!connection) { - this._currentConnection = undefined; - this._onConnectionChanged(undefined); - return this; + startPolling(periodMs, autoSwitch, timeoutMs, pollType, excludedConnections) { + + // apply defaults + if (periodMs == undefined) periodMs = MoneroConnectionManager.DEFAULT_POLL_PERIOD; + if (autoSwitch !== undefined) this.setAutoSwitch(autoSwitch); + if (timeoutMs !== undefined) this.setTimeout(timeoutMs); + if (pollType === undefined) pollType = MoneroConnectionManager.PollType.PRIORITIZED; + + // stop polling + this.stopPolling(); + + // start polling + switch (pollType) { + case MoneroConnectionManager.PollType.CURRENT: + this._startPollingConnection(periodMs); + break; + case MoneroConnectionManager.PollType.ALL: + this._startPollingConnections(periodMs); + break; + case MoneroConnectionManager.PollType.PRIORITIZED: + default: + this._startPollingPrioritizedConnections(periodMs, excludedConnections); } - - // validate connection - if (!(connection instanceof MoneroRpcConnection)) throw new MoneroError("Must provide string or MoneroRpcConnection to set connection"); - if (!connection.getUri()) throw new MoneroError("Connection is missing URI"); + return this; + } - // add or replace connection - let prevConnection = this.getConnectionByUri(connection.getUri()); - if (prevConnection) GenUtils.remove(this._connections, prevConnection); - this.addConnection(connection); - this._currentConnection = connection; - this._onConnectionChanged(this._currentConnection); - + /** + * Stop polling connections. + * + * @return {MoneroConnectionManager} this connection manager for chaining + */ + stopPolling() { + if (this._poller) this._poller.stop(); + this._poller = undefined; return this; } - + /** * Check the current connection. If disconnected and auto switch enabled, switches to best available connection. * @@ -262,11 +309,14 @@ class MoneroConnectionManager { async checkConnection() { let connectionChanged = false; let connection = this.getConnection(); - if (connection && await connection.checkConnection(this._timeoutInMs)) connectionChanged = true; + if (connection) { + if (await connection.checkConnection(this._timeoutMs)) connectionChanged = true; + await this._processResponses([connection]); + } if (this._autoSwitch && !this.isConnected()) { let bestConnection = await this.getBestAvailableConnection([connection]); if (bestConnection) { - this.setConnection(bestConnection); + await this.setConnection(bestConnection); return this; } } @@ -280,28 +330,9 @@ class MoneroConnectionManager { * @return {Promise} this connection manager for chaining */ async checkConnections() { - - // check all connections - await Promise.all(this.checkConnectionPromises()); - - // auto switch to best connection - if (this._autoSwitch && !this.isConnected()) { - for (let prioritizedConnections of this._getConnectionsInAscendingPriority()) { - let bestConnection; - for (let prioritizedConnection of prioritizedConnections) { - if (prioritizedConnection.isConnected() && (!bestConnection || prioritizedConnection.getResponseTime() < bestConnection.getResponseTime())) { - bestConnection = prioritizedConnection; - } - } - if (bestConnection) { - this.setConnection(bestConnection); - break; - } - } - } - return this; + return this._checkConnections(this.getConnections()); } - + /** * Check all managed connections, returning a promise for each connection check. * Does not auto switch if disconnected. @@ -315,7 +346,7 @@ class MoneroConnectionManager { for (let connection of this.getConnections()) { checkPromises.push(pool.submit(async function() { try { - if (await connection.checkConnection(that._timeoutInMs) && connection === this._currentConnection) await that._onConnectionChanged(connection); + if (await connection.checkConnection(that._timeoutMs) && connection === this._currentConnection) await that._onConnectionChanged(connection); } catch (err) { // ignore error } @@ -326,44 +357,43 @@ class MoneroConnectionManager { } /** - * Check the connection and start checking the connection periodically. + * Get the best available connection in order of priority then response time. * - * @param {number} periodMs is the time between checks in milliseconds (default 10000 or 10 seconds) - * @return {Promise} this connection manager for chaining (after first checking the connection) + * @param {MoneroRpcConnection[]} excludedConnections - connections to be excluded from consideration (optional) + * @return {Promise} the best available connection in order of priority then response time, undefined if no connections available */ - async startCheckingConnection(periodMs) { - await this.checkConnection(); - if (!periodMs) periodMs = MoneroConnectionManager.DEFAULT_CHECK_CONNECTION_PERIOD; - if (this._checkLooper) return this; - let that = this; - let firstCheck = true; - this._checkLooper = new TaskLooper(async function() { - if (firstCheck) { - firstCheck = false; // skip first check - return; + async getBestAvailableConnection(excludedConnections) { + + // try connections within each ascending priority + for (let prioritizedConnections of this._getConnectionsInAscendingPriority()) { + try { + + // create promises to check connections + let that = this; + let checkPromises = []; + for (let connection of prioritizedConnections) { + if (excludedConnections && GenUtils.arrayContains(excludedConnections, connection)) continue; + checkPromises.push(new Promise(async function(resolve, reject) { + await connection.checkConnection(that._timeoutMs); + if (connection.isConnected()) resolve(connection); + else reject(); + })); + } + + // use first available connection + let firstAvailable = await Promise.any(checkPromises); + if (firstAvailable) return firstAvailable; + } catch (err) { + if (!(err instanceof AggregateError)) throw new MoneroError(err); } - try { await that.checkConnection(); } - catch (err) { console.error("Error checking connection: " + err); } - }); - this._checkLooper.start(periodMs); - return this; + } + return undefined; } /** - * Stop checking the connection status periodically. - * - * @return {MoneroConnectionManager} this connection manager for chaining - */ - stopCheckingConnection() { - if (this._checkLooper) this._checkLooper.stop(); - delete this._checkLooper; - return this; - } - - /** - * Automatically switch to best available connection if current connection is disconnected after being checked. + * Automatically switch to the best available connection as connections are polled, based on priority, response time, and consistency. * - * @param {boolean} autoSwitch specifies if the connection should switch on disconnect + * @param {boolean} autoSwitch specifies if the connection should auto switch to a better connection * @return {MoneroConnectionManager} this connection manager for chaining */ setAutoSwitch(autoSwitch) { @@ -383,11 +413,11 @@ class MoneroConnectionManager { /** * Set the maximum request time before its connection is considered offline. * - * @param {int} timeoutInMs - the timeout before the connection is considered offline + * @param {int} timeoutMs - the timeout before the connection is considered offline * @return {MoneroConnectionManager} this connection manager for chaining */ - setTimeout(timeoutInMs) { - this._timeoutInMs = timeoutInMs; + setTimeout(timeoutMs) { + this._timeoutMs = timeoutMs; return this; } @@ -397,13 +427,13 @@ class MoneroConnectionManager { * @return {int} the request timeout before a connection is considered offline */ getTimeout() { - return this._timeoutInMs; + return this._timeoutMs; } /** * Collect connectable peers of the managed connections. * - * @return {MoneroRpcConnection[]} connectable peers + * @return {Promise} connectable peers */ async getPeerConnections() { throw new MoneroError("Not implemented"); @@ -412,23 +442,23 @@ class MoneroConnectionManager { /** * Disconnect from the current connection. * - * @return {MoneroConnectionManager} this connection manager for chaining + * @return {Promise} this connection manager for chaining */ - disconnect() { - this.setConnection(undefined); + async disconnect() { + await this.setConnection(undefined); return this; } /** * Remove all connections. * - * @return {MoneroConnectonManager} this connection manager for chaining + * @return {Promise} this connection manager for chaining */ - clear() { + async clear() { this._connections.splice(0, this._connections.length); if (this._currentConnection) { this._currentConnection = undefined; - this._onConnectionChanged(undefined); + await this._onConnectionChanged(undefined); } return this; } @@ -440,22 +470,13 @@ class MoneroConnectionManager { */ reset() { this.removeListeners(); - this.stopCheckingConnection(); + this.stopPolling(); this.clear(); this._timeoutMs = MoneroConnectionManager.DEFAULT_TIMEOUT; - this._autoSwitch = false; + this._autoSwitch = MoneroConnectionManager.DEFAULT_AUTOSWITCH; return this; } - /** - * Get all listeners. - * - * @return {MoneroConnectionManagerListener[]} all listeners - */ - getListeners() { - return this._listeners - } - // ------------------------------ PRIVATE HELPERS --------------------------- async _onConnectionChanged(connection) { @@ -494,9 +515,131 @@ class MoneroConnectionManager { else return 1; // c1 is offline } } + + _startPollingConnection(periodMs) { + this._poller = new TaskLooper(async () => { + try { await this.checkConnection(); } + catch (err) { console.error("Error checking connection: " + err.message); } + }); + this._poller.start(periodMs); + return this; + } + + _startPollingConnections(periodMs) { + this._poller = new TaskLooper(async () => { + try { await this.checkConnections(); } + catch (err) { console.error("Error checking connections: " + err.message); } + }); + this._poller.start(periodMs); + return this; + } + + _startPollingPrioritizedConnections(periodMs, excludedConnections) { + this._poller = new TaskLooper(async () => { + try { await this._checkPrioritizedConnections(excludedConnections); } + catch (err) { console.error("Error checking connections: " + err.message); } + }); + this._poller.start(periodMs); + return this; + } + + async _checkPrioritizedConnections(excludedConnections) { + for (let prioritizedConnections of this._getConnectionsInAscendingPriority()) { + let hasConnection = await this._checkConnections(prioritizedConnections, excludedConnections); + if (hasConnection) return; + } + } + + async _checkConnections(connections, excludedConnections) { + try { + + // check connections in parallel + let that = this; + let checkPromises = []; + let hasConnection = false; + for (let connection of connections) { + if (excludedConnections && GenUtils.arrayContains(excludedConnections, connection)) continue; + checkPromises.push(new Promise(async function(resolve, reject) { + try { + let change = await connection.checkConnection(that._timeoutMs); + if (change && connection === that.getConnection()) await that._onConnectionChanged(connection); + if (connection.isConnected() && !hasConnection) { + hasConnection = true; + if (!that.isConnected() && that._autoSwitch) await that.setConnection(connection); // set first available connection if disconnected + } + resolve(); + } catch (err) { + reject(err); + } + })); + } + await Promise.all(checkPromises); + + // process responses + await this._processResponses(connections); + return hasConnection; + } catch (err) { + throw new MoneroError(err); + } + } + + async _processResponses(responses) { + + // add non-existing connections + for (let connection of responses) { + if (!this._responseTimes.has(connection.getUri())) this._responseTimes.set(connection.getUri(), []); + } + + // insert response times or undefined + this._responseTimes.forEach((times, connection) => { + times.unshift(GenUtils.arrayContains(responses, connection) ? connection.getResponseTime() : undefined); + + // remove old response times + if (times.length > MoneroConnectionManager.MIN_BETTER_RESPONSES) times.pop(); + }); + + // update best connection based on responses and priority + await this._updateBestConnectionInPriority(); + } + + async _updateBestConnectionInPriority() { + if (!this._autoSwitch) return; + for (let prioritizedConnections of this._getConnectionsInAscendingPriority()) { + if (await this._updateBestConnectionFromResponses(prioritizedConnections)) break; + } + } + + async _updateBestConnectionFromResponses(responses) { + let bestConnection = this.isConnected() ? this.getConnection() : undefined; + if (bestConnection && (this._responseTimes.has(bestConnection.getUri()) || this._responseTimes.get(bestConnection.getUri()).length < MoneroConnectionManager.MIN_BETTER_RESPONSES)) return bestConnection; + if (this.isConnected()) { + + // check if connection is consistently better + for (let connection of responses) { + if (connection === bestConnection) continue; + if (!this._responseTimes.has(connection.getUri()) || this._responseTimes.get(connection.getUri()).length < MIN_BETTER_RESPONSES) continue; + let better = true; + for (let i = 0; i < MIN_BETTER_RESPONSES; i++) { + if (this._responseTimes.get(connection.getUri())[i] === undefined || this._responseTimes.get(connection.getUri())[i] >= this._responseTimes.get(bestConnection.getUri())[i]) { + better = false; + break; + } + } + if (better) bestConnection = connection; + } + } else { + for (let connection of responses) { + if (connection.isConnected() && (!bestConnection || connection.getResponseTime() < bestConnection.getResponseTime())) bestConnection = connection; + } + } + if (bestConnection) await this.setConnection(bestConnection); + return bestConnection; + } } MoneroConnectionManager.DEFAULT_TIMEOUT = 5000; -MoneroConnectionManager.DEFAULT_CHECK_CONNECTION_PERIOD = 15000; +MoneroConnectionManager.DEFAULT_POLL_PERIOD = 20000; +MoneroConnectionManager.DEFAULT_AUTO_SWITCH = true; +MoneroConnectionManager.MIN_BETTER_RESPONSES = 3; -module.exports = MoneroConnectionManager; \ No newline at end of file +module.exports = MoneroConnectionManager; diff --git a/src/main/js/wallet/MoneroWallet.js b/src/main/js/wallet/MoneroWallet.js index 03a150c7f..2c2415264 100644 --- a/src/main/js/wallet/MoneroWallet.js +++ b/src/main/js/wallet/MoneroWallet.js @@ -1,6 +1,7 @@ const assert = require("assert"); const MoneroBlock = require("../daemon/model/MoneroBlock"); const BigInteger = require("../common/biginteger").BigInteger; +const MoneroConnectionManagerListener = require("../common/MoneroConnectionManagerListener") const MoneroError = require("../common/MoneroError"); const MoneroOutputQuery = require("./model/MoneroOutputQuery"); const MoneroTransferQuery = require("./model/MoneroTransferQuery"); @@ -93,6 +94,34 @@ class MoneroWallet { async getDaemonConnection() { throw new MoneroError("Not supported"); } + + /** + * Set the wallet's daemon connection manager. + * + * @param {MoneroConnectionManager} connectionManager manages connections to monerod + */ + async setConnectionManager(connectionManager) { + if (this._connectionManager) this._connectionManager.removeListener(this._connectionManagerListener); + this._connectionManager = connectionManager; + if (!connectionManager) return; + let that = this; + if (!this._connectionManagerListener) this._connectionManagerListener = new class extends MoneroConnectionManagerListener { + async onConnectionChanged(connection) { + await that.setDaemonConnection(connection); + } + }; + connectionManager.addListener(this._connectionManagerListener); + await this.setDaemonConnection(connectionManager.getConnection()); + } + + /** + * Get the wallet's daemon connection manager. + * + * @return {MoneroConnectionManager} the wallet's daemon connection manager + */ + getConnectionManager() { + return this._connectionManager; + } /** * Indicates if the wallet is connected to daemon. @@ -1276,7 +1305,9 @@ class MoneroWallet { * @param {boolean} save - specifies if the wallet should be saved before being closed (default false) */ async close(save) { - throw new MoneroError("Not supported"); + if (this._connectionManager) this._connectionManager.removeListener(this._connectionManagerListener); + this._connectionManager = undefined; + this._connectionManagerListener = undefined; } /** diff --git a/src/main/js/wallet/MoneroWalletFull.js b/src/main/js/wallet/MoneroWalletFull.js index 1f5a33b7a..bcce206da 100644 --- a/src/main/js/wallet/MoneroWalletFull.js +++ b/src/main/js/wallet/MoneroWalletFull.js @@ -170,6 +170,7 @@ class MoneroWalletFull extends MoneroWalletKeys { * @param {string} config.serverUri - uri of the wallet's daemon (optional) * @param {string} config.serverUsername - username to authenticate with the daemon (optional) * @param {string} config.serverPassword - password to authenticate with the daemon (optional) + * @param {MoneroConnectionManager} config.connectionManager - manage connections to monerod (optional) * @param {boolean} config.rejectUnauthorized - reject self-signed server certificates if true (defaults to true) * @param {boolean} config.proxyToWorker - proxies wallet operations to a worker in order to not block the main thread (default true) * @param {fs} config.fs - Node.js compatible file system to use (defaults to disk or in-memory FS if browser) @@ -189,19 +190,28 @@ class MoneroWalletFull extends MoneroWalletKeys { if (config.getPath() === undefined) config.setPath(""); if (config.getPath() && MoneroWalletFull.walletExists(config.getPath(), config.getFs())) throw new MoneroError("Wallet already exists: " + config.getPath()); if (config.getPassword() === undefined) config.setPassword(""); + + // set server from connection manager if provided + if (config.getConnectionManager()) { + if (config.getServer()) throw new MoneroError("Wallet can be initialized with a server or connection manager but not both"); + config.setServer(config.getConnectionManager().getConnection()); + } // create wallet + let wallet; if (config.getSeed() !== undefined) { if (config.getLanguage() !== undefined) throw new MoneroError("Cannot provide language when creating wallet from seed"); - return MoneroWalletFull._createWalletFromSeed(config); + wallet = await MoneroWalletFull._createWalletFromSeed(config); } else if (config.getPrivateSpendKey() !== undefined || config.getPrimaryAddress() !== undefined) { if (config.getSeedOffset() !== undefined) throw new MoneroError("Cannot provide seedOffset when creating wallet from keys"); - return MoneroWalletFull._createWalletFromKeys(config); + wallet = await MoneroWalletFull._createWalletFromKeys(config); } else { if (config.getSeedOffset() !== undefined) throw new MoneroError("Cannot provide seedOffset when creating random wallet"); if (config.getRestoreHeight() !== undefined) throw new MoneroError("Cannot provide restoreHeight when creating random wallet"); - return MoneroWalletFull._createWalletRandom(config); + wallet = await MoneroWalletFull._createWalletRandom(config); } + await wallet.setConnectionManager(config.getConnectionManager()); + return wallet; } static async _createWalletFromSeed(config) { diff --git a/src/main/js/wallet/MoneroWalletKeys.js b/src/main/js/wallet/MoneroWalletKeys.js index 171220995..fc0c3476e 100644 --- a/src/main/js/wallet/MoneroWalletKeys.js +++ b/src/main/js/wallet/MoneroWalletKeys.js @@ -301,11 +301,12 @@ class MoneroWalletKeys extends MoneroWallet { // decodeIntegratedAddress async close(save) { + await super.close(save); if (this._isClosed) return; // closing a closed wallet has no effect // save wallet if requested if (save) await this.save(); - + // queue task to use wasm module let that = this; return that._module.queueTask(async function() { diff --git a/src/main/js/wallet/MoneroWalletRpc.js b/src/main/js/wallet/MoneroWalletRpc.js index d83f33c27..298ee2c4d 100644 --- a/src/main/js/wallet/MoneroWalletRpc.js +++ b/src/main/js/wallet/MoneroWalletRpc.js @@ -293,9 +293,11 @@ class MoneroWalletRpc extends MoneroWallet { * @param {string} config.privateSpendKey - private spend key of the wallet to create (optional) * @param {number} config.restoreHeight - block height to start scanning from (defaults to 0 unless generating random wallet) * @param {string} config.language - language of the wallet's mnemonic phrase or seed (defaults to "English" or auto-detected) + * @param {MoneroRpcConnection} config.server - MoneroRpcConnection to a monero daemon (optional)
* @param {string} config.serverUri - uri of a daemon to use (optional, monero-wallet-rpc usually started with daemon config) * @param {string} config.serverUsername - username to authenticate with the daemon (optional) * @param {string} config.serverPassword - password to authenticate with the daemon (optional) + * @param {MoneroConnectionManager} config.connectionManager - manage connections to monerod (optional) * @param {boolean} config.rejectUnauthorized - reject self-signed server certificates if true (defaults to true) * @param {MoneroRpcConnection|object} config.server - MoneroRpcConnection or equivalent JS object providing daemon configuration (optional) * @param {boolean} config.saveCurrent - specifies if the current RPC wallet should be saved before being closed (default true) @@ -307,7 +309,7 @@ class MoneroWalletRpc extends MoneroWallet { if (config === undefined) throw new MoneroError("Must provide config to create wallet"); config = new MoneroWalletConfig(config); if (config.getSeed() !== undefined && (config.getPrimaryAddress() !== undefined || config.getPrivateViewKey() !== undefined || config.getPrivateSpendKey() !== undefined)) { - throw new MoneroError("Wallet may be initialized with a seed or keys but not both"); + throw new MoneroError("Wallet can be initialized with a seed or keys but not both"); } if (config.getNetworkType() !== undefined) throw new MoneroError("Cannot provide networkType when creating RPC wallet because server's network type is already set"); if (config.getAccountLookahead() !== undefined || config.getSubaddressLookahead() !== undefined) throw new MoneroError("monero-wallet-rpc does not support creating wallets with subaddress lookahead over rpc"); @@ -317,9 +319,15 @@ class MoneroWalletRpc extends MoneroWallet { if (config.getSeed() !== undefined) await this._createWalletFromSeed(config); else if (config.getPrivateSpendKey() !== undefined || config.getPrimaryAddress() !== undefined) await this._createWalletFromKeys(config); else await this._createWalletRandom(config); + + // set daemon or connection manager + if (config.getConnectionManager()) { + if (config.getServer()) throw new MoneroError("Wallet can be initialized with a server or connection manager but not both"); + await this.setConnectionManager(config.getConnectionManager()); + } else if (config.getServer()) { + await this.setDaemonConnection(config.getServer()); + } - // set daemon if provided - if (config.getServer()) return this.setDaemonConnection(config.getServer()); return this; } @@ -427,6 +435,7 @@ class MoneroWalletRpc extends MoneroWallet { */ async setDaemonConnection(uriOrRpcConnection, isTrusted, sslOptions) { let connection = !uriOrRpcConnection ? undefined : uriOrRpcConnection instanceof MoneroRpcConnection ? uriOrRpcConnection : new MoneroRpcConnection(uriOrRpcConnection); + if (connection) connection.setProxyToWorker(false); if (!sslOptions) sslOptions = new SslOptions(); let params = {}; params.address = connection ? connection.getUri() : "bad_uri"; // TODO monero-wallet-rpc: bad daemon uri necessary for offline? @@ -1506,6 +1515,7 @@ class MoneroWalletRpc extends MoneroWallet { } async close(save) { + await super.close(save); if (save === undefined) save = false; await this._clear(); await this.rpc.sendJsonRequest("close_wallet", {autosave_current: save}); diff --git a/src/main/js/wallet/model/MoneroWalletConfig.js b/src/main/js/wallet/model/MoneroWalletConfig.js index f3da0a5d0..8e02b5740 100644 --- a/src/main/js/wallet/model/MoneroWalletConfig.js +++ b/src/main/js/wallet/model/MoneroWalletConfig.js @@ -29,6 +29,7 @@ class MoneroWalletConfig { * @param {string} config.serverUri - uri of the wallet's server (optional) * @param {string} config.serverUsername - username of the wallet's server (optional) * @param {string} config.serverPassword - password of the wallet's server (optional) + * @param {MoneroConnectionManager} config.connectionManager - manage connections to monerod (optional) * @param {boolean} config.rejectUnauthorized - reject self-signed server certificates if true (default true) * @param {Uint8Array} config.keysData - wallet keys data to open (optional) * @param {Uint8Array} config.cacheData - wallet cache data to open (optional) @@ -42,7 +43,7 @@ class MoneroWalletConfig { // initialize internal config if (!config) config = {}; - else if (config instanceof MoneroWalletConfig) config = config.toJson(); + else if (config instanceof MoneroWalletConfig) config = Object.assign({}, config.config); else if (typeof config === "object") config = Object.assign({}, config); else throw new MoneroError("config must be a MoneroWalletConfig or JavaScript object"); this.config = config; @@ -66,13 +67,14 @@ class MoneroWalletConfig { } copy() { - return new MoneroWalletConfig(this.toJson()); + return new MoneroWalletConfig(this); } toJson() { let json = Object.assign({}, this.config); if (json.server) json.server = json.server.toJson(); - json.fs = undefined; // remove filesystem + json.fs = undefined; + json.connectionManager = undefined; return json; } @@ -120,8 +122,11 @@ class MoneroWalletConfig { } setServerUri(serverUri) { - if (!this.config.server) this.setServer(new MoneroRpcConnection(serverUri)); - else this.config.server.setUri(serverUri); + if (!serverUri) this.setServer(undefined); + else { + if (!this.config.server) this.setServer(new MoneroRpcConnection(serverUri)); + else this.config.server.setUri(serverUri); + } return this; } @@ -144,6 +149,15 @@ class MoneroWalletConfig { if (this.config.serverUsername && this.config.serverPassword) this.config.server.setCredentials(this.config.serverUsername, this.config.serverPassword); return this; } + + getConnectionManager() { + return this.config.connectionManager; + } + + setConnectionManager(connectionManager) { + this.config.connectionManager = connectionManager; + return this; + } getRejectUnauthorized() { return this.config.rejectUnauthorized; @@ -291,6 +305,6 @@ class MoneroWalletConfig { } } -MoneroWalletConfig.SUPPORTED_FIELDS = ["path", "password", "networkType", "server", "serverUri", "serverUsername", "serverPassword", "rejectUnauthorized", "seed", "seedOffset", "isMultisig", "primaryAddress", "privateViewKey", "privateSpendKey", "restoreHeight", "language", "saveCurrent", "proxyToWorker", "fs", "keysData", "cacheData", "accountLookahead", "subaddressLookahead"]; +MoneroWalletConfig.SUPPORTED_FIELDS = ["path", "password", "networkType", "server", "serverUri", "serverUsername", "serverPassword", "connectionManager", "rejectUnauthorized", "seed", "seedOffset", "isMultisig", "primaryAddress", "privateViewKey", "privateSpendKey", "restoreHeight", "language", "saveCurrent", "proxyToWorker", "fs", "keysData", "cacheData", "accountLookahead", "subaddressLookahead"]; module.exports = MoneroWalletConfig; \ No newline at end of file diff --git a/src/test/TestMoneroConnectionManager.js b/src/test/TestMoneroConnectionManager.js index 541a9081f..2b23feca2 100644 --- a/src/test/TestMoneroConnectionManager.js +++ b/src/test/TestMoneroConnectionManager.js @@ -16,13 +16,14 @@ class TestMoneroConnectionManager { it("Can manage connections", async function() { let err; let walletRpcs = []; + let connectionManager; try { // start monero-wallet-rpc instances as test server connections (can also use monerod servers) for (let i = 0; i < 5; i++) walletRpcs.push(await TestUtils.startWalletRpcProcess()); // create connection manager - let connectionManager = new MoneroConnectionManager(); + connectionManager = new MoneroConnectionManager(); // listen for changes let listener = new ConnectionChangeCollector(); @@ -44,6 +45,10 @@ class TestMoneroConnectionManager { assert.equal(orderedConnections[4].getUri(), (walletRpcs[1].getRpcConnection()).getUri()); for (let connection of orderedConnections) assert.equal(undefined, connection.isOnline()); + // test getting connection by uri + assert(connectionManager.hasConnection(walletRpcs[0].getRpcConnection().getUri())); + assert(connectionManager.getConnectionByUri(walletRpcs[0].getRpcConnection().getUri()) === walletRpcs[0].getRpcConnection()); + // test unknown connection let numExpectedChanges = 0; await connectionManager.setConnection(orderedConnections[0]); @@ -51,8 +56,8 @@ class TestMoneroConnectionManager { assert.equal(listener.changedConnections.length, ++numExpectedChanges); // auto connect to best available connection - connectionManager.setAutoSwitch(true); - await connectionManager.startCheckingConnection(TestUtils.SYNC_PERIOD_IN_MS); + connectionManager.startPolling(TestUtils.SYNC_PERIOD_IN_MS); + await GenUtils.waitFor(TestUtils.AUTO_CONNECT_TIMEOUT_MS); assert(connectionManager.isConnected()); let connection = connectionManager.getConnection(); assert(connection.isOnline()); @@ -60,17 +65,17 @@ class TestMoneroConnectionManager { assert.equal(listener.changedConnections.length, ++numExpectedChanges); assert(listener.changedConnections[listener.changedConnections.length - 1] === connection); connectionManager.setAutoSwitch(false); - connectionManager.stopCheckingConnection(); - connectionManager.disconnect(); + connectionManager.stopPolling(); + await connectionManager.disconnect(); assert.equal(listener.changedConnections.length, ++numExpectedChanges); assert(listener.changedConnections[listener.changedConnections.length - 1] === undefined); - // start periodically checking connection - await connectionManager.startCheckingConnection(TestUtils.SYNC_PERIOD_IN_MS); + // start periodically checking connection without auto switch + await connectionManager.startPolling(TestUtils.SYNC_PERIOD_IN_MS, false); // connect to best available connection in order of priority and response time connection = await connectionManager.getBestAvailableConnection(); - connectionManager.setConnection(connection); + await connectionManager.setConnection(connection); assert(connection === walletRpcs[4].getRpcConnection()); assert(connection.isOnline()); assert(connection.isAuthenticated()); @@ -100,10 +105,10 @@ class TestMoneroConnectionManager { // test connection order orderedConnections = connectionManager.getConnections(); assert(orderedConnections[0] === walletRpcs[4].getRpcConnection()); - assert(orderedConnections[1] === walletRpcs[2].getRpcConnection()); - assert(orderedConnections[2] === walletRpcs[3].getRpcConnection()); - assert(orderedConnections[3] === walletRpcs[0].getRpcConnection()); - assert.equal(orderedConnections[4].getUri(), walletRpcs[1].getRpcConnection().getUri()); + assert(orderedConnections[1] === walletRpcs[0].getRpcConnection()); + assert.equal(orderedConnections[2].getUri(), walletRpcs[1].getRpcConnection().getUri()); + assert(orderedConnections[3] === walletRpcs[2].getRpcConnection()); + assert(orderedConnections[4] === walletRpcs[3].getRpcConnection()); // check all connections await connectionManager.checkConnections(); @@ -149,7 +154,7 @@ class TestMoneroConnectionManager { // connect to specific endpoint without authentication connection = orderedConnections[1]; assert.equal(false, connection.isAuthenticated()); - connectionManager.setConnection(connection); + await connectionManager.setConnection(connection); assert.equal(false, connectionManager.isConnected()); assert.equal(listener.changedConnections.length, ++numExpectedChanges); @@ -174,7 +179,7 @@ class TestMoneroConnectionManager { for (let i = 0; i < orderedConnections.length; i++) assert(i <= 1 ? orderedConnections[i].isOnline() : !orderedConnections[i].isOnline()); // set connection to existing uri - connectionManager.setConnection(walletRpcs[0].getRpcConnection().getUri()); + await connectionManager.setConnection(walletRpcs[0].getRpcConnection().getUri()); assert(connectionManager.isConnected()); assert(walletRpcs[0].getRpcConnection() === connectionManager.getConnection()); assert.equal(TestUtils.WALLET_RPC_CONFIG.username, connectionManager.getConnection().getUsername()); @@ -183,15 +188,15 @@ class TestMoneroConnectionManager { assert(listener.changedConnections[listener.changedConnections.length - 1] === walletRpcs[0].getRpcConnection()); // set connection to new uri - connectionManager.stopCheckingConnection(); + connectionManager.stopPolling(); let uri = "http://localhost:49999"; - connectionManager.setConnection(uri); + await connectionManager.setConnection(uri); assert.equal(connectionManager.getConnection().getUri(), uri); assert.equal(listener.changedConnections.length, ++numExpectedChanges); assert.equal(uri, listener.changedConnections[listener.changedConnections.length - 1].getUri()); // set connection to empty string - connectionManager.setConnection(""); + await connectionManager.setConnection(""); assert.equal(undefined, connectionManager.getConnection()); assert.equal(listener.changedConnections.length, ++numExpectedChanges); @@ -211,10 +216,26 @@ class TestMoneroConnectionManager { // check connection promises await Promise.all(connectionManager.checkConnectionPromises()); + + // test polling current connection + await connectionManager.setConnection(); + assert.equal(connectionManager.isConnected(), false); + assert.equal(listener.changedConnections.length, ++numExpectedChanges); + connectionManager.startPolling(TestUtils.SYNC_PERIOD_IN_MS, undefined, undefined, MoneroConnectionManager.PollType.CURRENT, undefined); + await GenUtils.waitFor(TestUtils.AUTO_CONNECT_TIMEOUT_MS); + assert(connectionManager.isConnected()); + assert.equal(listener.changedConnections.length, ++numExpectedChanges); + + // test polling all connections + await connectionManager.setConnection(); + assert.equal(listener.changedConnections.length, ++numExpectedChanges); + connectionManager.startPolling(TestUtils.SYNC_PERIOD_IN_MS, undefined, undefined, MoneroConnectionManager.PollType.ALL, undefined); + await GenUtils.waitFor(TestUtils.AUTO_CONNECT_TIMEOUT_MS); + assert(connectionManager.isConnected()); + assert.equal(listener.changedConnections.length, ++numExpectedChanges); // shut down all connections connection = connectionManager.getConnection(); - await connectionManager.startCheckingConnection(TestUtils.SYNC_PERIOD_IN_MS); for (let connection of orderedConnections) connection._setFakeDisconnected(true); await GenUtils.waitFor(TestUtils.SYNC_PERIOD_IN_MS + 100); assert.equal(false, connection.isOnline()); @@ -228,6 +249,9 @@ class TestMoneroConnectionManager { } catch(err2) { err = err2; } + + // stop connection manager + if (connectionManager) connectionManager.reset(); // stop monero-wallet-rpc instances for (let walletRpc of walletRpcs) { diff --git a/src/test/TestMoneroWalletCommon.js b/src/test/TestMoneroWalletCommon.js index 63c1ad903..f227d8f01 100644 --- a/src/test/TestMoneroWalletCommon.js +++ b/src/test/TestMoneroWalletCommon.js @@ -4,6 +4,7 @@ const TestUtils = require("./utils/TestUtils"); const monerojs = require("../../index"); const Filter = monerojs.Filter; // TODO: don't export filter const LibraryUtils = monerojs.LibraryUtils; +const MoneroConnectionManager = monerojs.MoneroConnectionManager; const MoneroError = monerojs.MoneroError; const MoneroTxPriority = monerojs.MoneroTxPriority; const MoneroWalletRpc = monerojs.MoneroWalletRpc; @@ -384,7 +385,6 @@ class TestMoneroWalletCommon { if (err) throw err; }); - if (testConfig.testNonRelays) it("Can get the wallet's version", async function() { let version = await that.wallet.getVersion(); @@ -476,7 +476,65 @@ class TestMoneroWalletCommon { } // close wallet and throw if error occurred - await that.closeWallet(wallet); + if (wallet) await that.closeWallet(wallet); + if (err) throw err; + }); + + if (testConfig.testNonRelays) + it("Can use a connection manager", async function() { + let err; + let wallet; + try { + + // create connection manager with monerod connections + let connectionManager = new MoneroConnectionManager(); + let connection1 = new MoneroRpcConnection(await that.daemon.getRpcConnection()).setPriority(1); + let connection2 = new MoneroRpcConnection("localhost:48081").setPriority(2); + await connectionManager.setConnection(connection1); + await connectionManager.addConnection(connection2); + + // create wallet with connection manager + wallet = await that.createWallet(new MoneroWalletConfig().setServerUri("").setConnectionManager(connectionManager)); + assert.equal((await wallet.getDaemonConnection()).getUri(), (await that.daemon.getRpcConnection()).getUri()); + assert(await wallet.isConnectedToDaemon()); + + // set manager's connection + await connectionManager.setConnection(connection2); + await GenUtils.waitFor(TestUtils.AUTO_CONNECT_TIMEOUT_MS); + assert.equal((await wallet.getDaemonConnection()).getUri(), connection2.getUri()); + + // disconnect + await connectionManager.setConnection(); + assert.equal(await wallet.getDaemonConnection(), undefined); + assert(!await wallet.isConnectedToDaemon()); + + // start polling connections + connectionManager.startPolling(TestUtils.SYNC_PERIOD_IN_MS); + + // test that wallet auto connects + await GenUtils.waitFor(TestUtils.AUTO_CONNECT_TIMEOUT_MS); + assert.equal((await wallet.getDaemonConnection()).getUri(), connection1.getUri()); + assert(await wallet.isConnectedToDaemon()); + + // set to another connection manager + let connectionManager2 = new MoneroConnectionManager(); + await connectionManager2.setConnection(connection2); + await wallet.setConnectionManager(connectionManager2); + assert.equal((await wallet.getDaemonConnection()).getUri(), connection2.getUri()); + + // unset connection manager + await wallet.setConnectionManager(); + assert.equal(await wallet.getConnectionManager(), undefined); + assert.equal((await wallet.getDaemonConnection()).getUri(), connection2.getUri()); + + // stop polling and close + connectionManager.stopPolling(); + } catch (e) { + err = e; + } + + // close wallet and throw if error occurred + if (wallet) await that.closeWallet(wallet); if (err) throw err; }); diff --git a/src/test/TestMoneroWalletFull.js b/src/test/TestMoneroWalletFull.js index ebfa9b3c4..a36bc1895 100644 --- a/src/test/TestMoneroWalletFull.js +++ b/src/test/TestMoneroWalletFull.js @@ -97,7 +97,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { if (config.getPassword() === undefined) config.setPassword(TestUtils.WALLET_PASSWORD); if (config.getNetworkType() === undefined) config.setNetworkType(TestUtils.NETWORK_TYPE); if (!config.getRestoreHeight() && !random) config.setRestoreHeight(0); - if (!config.getServer() && config.getServerUri() === undefined) config.setServer(TestUtils.getDaemonRpcConnection()); + if (!config.getServer() && !config.getConnectionManager()) config.setServer(TestUtils.getDaemonRpcConnection()); if (config.getProxyToWorker() === undefined) config.setProxyToWorker(TestUtils.PROXY_TO_WORKER); if (config.getFs() === undefined) config.setFs(TestUtils.getDefaultFs()); diff --git a/src/test/TestMoneroWalletRpc.js b/src/test/TestMoneroWalletRpc.js index ba71509f8..eeb7537bd 100644 --- a/src/test/TestMoneroWalletRpc.js +++ b/src/test/TestMoneroWalletRpc.js @@ -67,7 +67,7 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { // open wallet try { await wallet.openWallet(config); - await wallet.setDaemonConnection(config.getServer(), true, undefined); // set daemon as trusted + await wallet.setDaemonConnection(await wallet.getDaemonConnection(), true, undefined); // set daemon as trusted if (await wallet.isConnectedToDaemon()) await wallet.startSyncing(TestUtils.SYNC_PERIOD_IN_MS); return wallet; } catch (err) { @@ -84,7 +84,7 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { if (!config.getPath()) config.setPath(GenUtils.getUUID()); if (config.getPassword() === undefined) config.setPassword(TestUtils.WALLET_PASSWORD); if (!config.getRestoreHeight() && !random) config.setRestoreHeight(0); - if (!config.getServer()) config.setServer(await this.daemon.getRpcConnection()); + if (!config.getServer() && !config.getConnectionManager()) config.setServer(await this.daemon.getRpcConnection()); // create client connected to internal monero-wallet-rpc executable let offline = config.getServerUri() === GenUtils.normalizeUri(TestUtils.OFFLINE_SERVER_URI); @@ -93,7 +93,7 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { // create wallet try { await wallet.createWallet(config); - await wallet.setDaemonConnection(config.getServer(), true, undefined); // set daemon as trusted + await wallet.setDaemonConnection(await wallet.getDaemonConnection(), true, undefined); // set daemon as trusted if (await wallet.isConnectedToDaemon()) await wallet.startSyncing(TestUtils.SYNC_PERIOD_IN_MS); return wallet; } catch (err) { diff --git a/src/test/TestSampleCode.js b/src/test/TestSampleCode.js index e4c67758a..ca6f9f233 100644 --- a/src/test/TestSampleCode.js +++ b/src/test/TestSampleCode.js @@ -131,6 +131,17 @@ class TestSampleCode { // set current connection connectionManager.setConnection(new MoneroRpcConnection("http://foo.bar", "admin", "password")); // connection is added if new + + // create wallet with managed connections or set later + let walletFull = await monerojs.createWalletFull({ + path: "./test_wallets/" + monerojs.GenUtils.getUUID(), // *** CHANGE README TO "sample_wallet_full" + password: "supersecretpassword123", + networkType: "testnet", + connectionManager: connectionManager, + seed: TestUtils.SEED, // *** REPLACE IN README *** + restoreHeight: TestUtils.FIRST_RECEIVE_HEIGHT, // *** REPLACE IN README *** + fs: TestUtils.getDefaultFs() // *** REPLACE IN README *** + }); // check connection status await connectionManager.checkConnection(); @@ -145,11 +156,8 @@ class TestSampleCode { } }); - // check connection status every 10 seconds - await connectionManager.startCheckingConnection(10000); - - // automatically switch to best available connection if disconnected - connectionManager.setAutoSwitch(true); + // check connections every 10 seconds (in order of priority) and switch to the best + await connectionManager.startPolling(10000); // get best available connection in order of priority then response time let bestConnection = await connectionManager.getBestAvailableConnection(); diff --git a/src/test/utils/TestUtils.js b/src/test/utils/TestUtils.js index 48e38ac3e..3d9fa76c1 100644 --- a/src/test/utils/TestUtils.js +++ b/src/test/utils/TestUtils.js @@ -315,6 +315,7 @@ TestUtils.WALLET_TX_TRACKER = new WalletTxTracker(); // used to track wallet txs TestUtils.PROXY_TO_WORKER = true; TestUtils.SYNC_PERIOD_IN_MS = 5000; // period between wallet syncs in milliseconds TestUtils.OFFLINE_SERVER_URI = "offline_server_uri"; // dummy server uri to remain offline because wallet2 connects to default if not given +TestUtils.AUTO_CONNECT_TIMEOUT_MS = 1000; // monero-wallet-rpc process management TestUtils.WALLET_RPC_PORT_START = 28084;