Skip to content

Commit 0d3091e

Browse files
authored
Merge pull request #1430 from session-foundation/release/1.16.1
- Improved filtering of invalid nodes and add OnionPathEmptyError for better error handling. - If the onion path is empty we want to refresh our snodepool cache from seed
1 parent 72c875b commit 0d3091e

File tree

7 files changed

+36
-17
lines changed

7 files changed

+36
-17
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "session-desktop",
33
"productName": "Session",
44
"description": "Private messaging from your desktop",
5-
"version": "1.16.0",
5+
"version": "1.16.1",
66
"license": "GPL-3.0",
77
"author": {
88
"name": "Session Foundation",

ts/session/apis/seed_node_api/SeedNodeAPI.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,9 @@ async function getSnodesFromSeedUrl(urlObj: URL): Promise<Array<any>> {
294294
);
295295
throw new Error(`getSnodesFromSeedUrl: json.result is empty from ${urlObj.href}`);
296296
}
297-
// Filter 0.0.0.0 nodes which haven't submitted uptime proofs
297+
// NOTE Filter out nodes that have missing ip addresses since they are not valid or 0.0.0.0 nodes which haven't submitted uptime proofs
298298
const validNodes = result.service_node_states.filter(
299-
(snode: any) => snode.public_ip !== '0.0.0.0'
299+
(snode: any) => snode.public_ip && snode.public_ip !== '0.0.0.0'
300300
);
301301

302302
if (validNodes.length === 0) {

ts/session/apis/snode_api/getServiceNodesList.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ async function getSnodePoolFromSnode(targetNode: Snode): Promise<Array<Snode>> {
3939
return [];
4040
}
4141

42-
// Filter 0.0.0.0 nodes which haven't submitted uptime proofs
42+
// NOTE Filter out nodes that have missing ip addresses since they are not valid or 0.0.0.0 nodes which haven't submitted uptime proofs
4343
const snodes: Array<Snode> = json.result.service_node_states
44-
.filter((snode: any) => snode.public_ip !== '0.0.0.0')
44+
.filter((snode: any) => snode.public_ip && snode.public_ip !== '0.0.0.0')
4545
.map((snode: any) => ({
4646
ip: snode.public_ip,
4747
port: snode.storage_port,
@@ -95,7 +95,7 @@ async function getSnodePoolFromSnodes() {
9595
})
9696
);
9797

98-
// we want those at least `requiredSnodesForAgreement` snodes common between all the result
98+
// we want those at least `requiredSnodesForAgreement` snodes common between all the results
9999
const commonSnodes = intersectionWith(
100100
results[0],
101101
results[1],

ts/session/apis/snode_api/getSwarmFor.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ async function requestSnodesForPubkeyWithTargetNodeRetryable(
5555
throw new Error('requestSnodesForPubkey: Invalid json (empty)');
5656
}
5757

58+
// NOTE Filter out 0.0.0.0 nodes which haven't submitted uptime proofs
5859
const snodes = body.snodes.filter((tSnode: any) => tSnode.ip !== '0.0.0.0');
5960
GetNetworkTime.handleTimestampOffsetFromNetwork('get_swarm', body.t);
6061
return snodes;

ts/session/onions/onionPath.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { ed25519Str } from '../utils/String';
2020
import { SnodePool } from '../apis/snode_api/snodePool';
2121
import { SnodePoolConstants } from '../apis/snode_api/snodePoolConstants';
2222
import { desiredGuardCount, minimumGuardCount, ONION_REQUEST_HOPS } from './onionPathConstants';
23+
import { OnionPathEmptyError } from '../utils/errors';
2324

2425
export function getOnionPathMinTimeout() {
2526
return DURATION.SECONDS;
@@ -158,11 +159,14 @@ export async function getOnionPath({ toExclude }: { toExclude?: Snode }): Promis
158159
window.inboxStore?.dispatch(updateOnionPaths([]));
159160
}
160161
} else {
161-
const ipsOnly = onionPaths.map(m =>
162-
m.map(c => {
163-
return { ip: c.ip };
164-
})
165-
);
162+
const ipsOnly = onionPaths
163+
// NOTE Filter out nodes that have missing ip addresses since they are not valid
164+
.filter(m => m.filter(c => c.ip))
165+
.map(m =>
166+
m.map(c => {
167+
return { ip: c.ip };
168+
})
169+
);
166170
if (!_.isEqual(window.inboxStore?.getState().onionPaths.snodePaths, ipsOnly)) {
167171
window.inboxStore?.dispatch(updateOnionPaths(ipsOnly));
168172
}
@@ -171,11 +175,11 @@ export async function getOnionPath({ toExclude }: { toExclude?: Snode }): Promis
171175
if (!toExclude) {
172176
// no need to exclude a node, then just return a random path from the list of path
173177
if (!onionPaths || onionPaths.length === 0) {
174-
throw new Error('No onion paths available');
178+
throw new OnionPathEmptyError();
175179
}
176180
const randomPathNoExclude = _.sample(onionPaths);
177181
if (!randomPathNoExclude) {
178-
throw new Error('No onion paths available');
182+
throw new OnionPathEmptyError();
179183
}
180184
return randomPathNoExclude;
181185
}
@@ -186,11 +190,11 @@ export async function getOnionPath({ toExclude }: { toExclude?: Snode }): Promis
186190
);
187191

188192
if (!onionPathsWithoutExcluded || onionPathsWithoutExcluded.length === 0) {
189-
throw new Error('No onion paths available after filtering');
193+
throw new OnionPathEmptyError();
190194
}
191195
const randomPath = _.sample(onionPathsWithoutExcluded);
192196
if (!randomPath) {
193-
throw new Error('No onion paths available after filtering');
197+
throw new OnionPathEmptyError();
194198
}
195199
return randomPath;
196200
}

ts/session/onions/onionSend.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import {
2222
import { PROTOCOLS } from '../constants';
2323
import { OnionV4 } from './onionv4';
2424
import { MergedAbortSignal, WithAbortSignal, WithTimeoutMs } from '../apis/snode_api/requestWith';
25+
import { OnionPathEmptyError } from '../utils/errors';
26+
import { SnodePool } from '../apis/snode_api/snodePool';
2527

2628
export type OnionFetchOptions = {
2729
method: string;
@@ -146,7 +148,7 @@ const sendViaOnionV4ToNonSnodeWithRetries = async (
146148
);
147149
}
148150
if (!pathNodes) {
149-
throw new Error('getOnionPathForSending is empty');
151+
throw new OnionPathEmptyError();
150152
}
151153

152154
/**
@@ -243,6 +245,11 @@ const sendViaOnionV4ToNonSnodeWithRetries = async (
243245
);
244246
} catch (e) {
245247
window?.log?.warn('sendViaOnionV4ToNonSnodeWithRetries failed ', e.message, throwErrors);
248+
// NOTE if there are no snodes available, we want to refresh the snode pool from the seed
249+
if (e instanceof OnionPathEmptyError) {
250+
window?.log?.warn('endViaOnionV4ToNonSnodeWithRetries failed, no path available, refreshing snode pool');
251+
void SnodePool.forceRefreshRandomSnodePool();
252+
}
246253
if (throwErrors) {
247254
throw e;
248255
}

ts/session/utils/errors.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ class BaseError extends Error {
8181
}
8282
}
8383

84+
// NOTE If you make a custom error with a custom message, make sure to restore the prototype chain again using the new class prototype.
85+
// OTHERWISE instanceof CHECKS WILL BE FALSE
8486
export class SigningFailed extends BaseError {}
8587
export class InvalidSigningType extends BaseError {}
8688
export class GroupV2SigningFailed extends SigningFailed {}
@@ -99,10 +101,15 @@ export class RetrieveDisplayNameError extends BaseError {
99101
Object.setPrototypeOf(this, RetrieveDisplayNameError.prototype);
100102
}
101103
}
102-
103104
export class EmptyDisplayNameError extends BaseError {
104105
constructor(message = 'display name is empty') {
105106
super(message);
106107
Object.setPrototypeOf(this, EmptyDisplayNameError.prototype);
107108
}
108109
}
110+
export class OnionPathEmptyError extends BaseError {
111+
constructor(message = 'onion path is empty') {
112+
super(message);
113+
Object.setPrototypeOf(this, OnionPathEmptyError.prototype);
114+
}
115+
}

0 commit comments

Comments
 (0)