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;