Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fetch snode storage version and filter for edge snode #3124

Merged
merged 4 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [windows-2022, macos-11, ubuntu-20.04]
os: [windows-2022, macos-12, ubuntu-20.04]
env:
SIGNAL_ENV: production
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [windows-2022, macos-11, ubuntu-20.04]
os: [windows-2022, macos-12, ubuntu-20.04]
env:
SIGNAL_ENV: production
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [windows-2022, macos-11, ubuntu-20.04]
os: [windows-2022, macos-12, ubuntu-20.04]
env:
SIGNAL_ENV: production
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "session-desktop",
"productName": "Session",
"description": "Private messaging from your desktop",
"version": "1.12.4",
"version": "1.12.5",
"license": "GPL-3.0",
"author": {
"name": "Oxen Labs",
Expand Down
1 change: 1 addition & 0 deletions ts/data/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface Snode {
port: number;
pubkey_x25519: string;
pubkey_ed25519: string;
storage_server_version: Array<number>;
}

export type SwarmNode = Snode & {
Expand Down
23 changes: 22 additions & 1 deletion ts/node/migration/sessionMigrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
CLOSED_GROUP_V2_KEY_PAIRS_TABLE,
CONVERSATIONS_TABLE,
GUARD_NODE_TABLE,
ITEMS_TABLE,
LAST_HASHES_TABLE,
MESSAGES_TABLE,
NODES_FOR_PUBKEY_TABLE,
Expand All @@ -26,7 +27,7 @@ import {
rebuildFtsTable,
} from '../database_utility';

import { SettingsKey } from '../../data/settings-key';
import { SettingsKey, SNODE_POOL_ITEM_ID } from '../../data/settings-key';
import { sleepFor } from '../../session/utils/Promise';
import { sqlNode } from '../sql';
import MIGRATION_HELPERS from './helpers';
Expand Down Expand Up @@ -105,6 +106,7 @@ const LOKI_SCHEMA_VERSIONS = [
updateToSessionSchemaVersion34,
updateToSessionSchemaVersion35,
updateToSessionSchemaVersion36,
updateToSessionSchemaVersion37,
];

function updateToSessionSchemaVersion1(currentVersion: number, db: BetterSqlite3.Database) {
Expand Down Expand Up @@ -1949,6 +1951,25 @@ function updateToSessionSchemaVersion36(currentVersion: number, db: BetterSqlite
console.log(`updateToSessionSchemaVersion${targetVersion}: success!`);
}

function updateToSessionSchemaVersion37(currentVersion: number, db: BetterSqlite3.Database) {
const targetVersion = 37;
if (currentVersion >= targetVersion) {
return;
}

console.log(`updateToSessionSchemaVersion${targetVersion}: starting...`);

db.transaction(() => {
console.info(`clearing ${SNODE_POOL_ITEM_ID} cache`);
db.prepare(`DELETE FROM ${ITEMS_TABLE} WHERE id = $snodePoolId;`).run({
snodePoolId: SNODE_POOL_ITEM_ID,
});
writeSessionSchemaVersion(targetVersion, db);
})();

console.log(`updateToSessionSchemaVersion${targetVersion}: success!`);
}

export function printTableColumns(table: string, db: BetterSqlite3.Database) {
console.info(db.pragma(`table_info('${table}');`));
}
Expand Down
3 changes: 3 additions & 0 deletions ts/session/apis/seed_node_api/SeedNodeAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export async function fetchSnodePoolFromSeedNodeWithRetries(
port: snode.storage_port,
pubkey_x25519: snode.pubkey_x25519,
pubkey_ed25519: snode.pubkey_ed25519,
storage_server_version: snode.storage_server_version,
}));
window?.log?.info(
'SeedNodeAPI::fetchSnodePoolFromSeedNodeWithRetries - Refreshed random snode pool with',
Expand Down Expand Up @@ -140,6 +141,7 @@ export interface SnodeFromSeed {
storage_port: number;
pubkey_x25519: string;
pubkey_ed25519: string;
storage_server_version: Array<number>;
}

const getSnodeListFromSeednodeOneAtAtime = async (seedNodes: Array<string>) =>
Expand Down Expand Up @@ -241,6 +243,7 @@ async function getSnodesFromSeedUrl(urlObj: URL): Promise<Array<any>> {
storage_port: true,
pubkey_x25519: true,
pubkey_ed25519: true,
storage_server_version: true,
},
},
};
Expand Down
1 change: 1 addition & 0 deletions ts/session/apis/snode_api/SnodeRequestTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ type FetchSnodeListParams = {
storage_port: true;
pubkey_x25519: true;
pubkey_ed25519: true;
storage_server_version: true;
};
};

Expand Down
6 changes: 4 additions & 2 deletions ts/session/apis/snode_api/getServiceNodesList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ function buildSnodeListRequests(): Array<GetServiceNodesSubRequest> {
storage_port: true,
pubkey_x25519: true,
pubkey_ed25519: true,
storage_server_version: true,
},
},
},
Expand Down Expand Up @@ -49,14 +50,15 @@ async function getSnodePoolFromSnode(targetNode: Snode): Promise<Array<Snode>> {
}

// Filter 0.0.0.0 nodes which haven't submitted uptime proofs
const snodes = json.result.service_node_states
const snodes: Array<Snode> = json.result.service_node_states
.filter((snode: any) => snode.public_ip !== '0.0.0.0')
.map((snode: any) => ({
ip: snode.public_ip,
port: snode.storage_port,
pubkey_x25519: snode.pubkey_x25519,
pubkey_ed25519: snode.pubkey_ed25519,
})) as Array<Snode>;
storage_server_version: snode.storage_server_version,
}));
GetNetworkTime.handleTimestampOffsetFromNetwork('get_service_nodes', json.t);

// we the return list by the snode is already made of uniq snodes
Expand Down
69 changes: 58 additions & 11 deletions ts/session/onions/onionPath.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/* eslint-disable import/no-mutable-exports */
/* eslint-disable no-await-in-loop */
import _, { compact } from 'lodash';
import _, { compact, isFinite, isNumber, sample } from 'lodash';
import pRetry from 'p-retry';
// eslint-disable-next-line import/no-named-default
import { default as insecureNodeFetch } from 'node-fetch';

import semver from 'semver';
import { Data, Snode } from '../../data/data';
import * as SnodePool from '../apis/snode_api/snodePool';
import { UserUtils } from '../utils';
Expand All @@ -15,11 +16,16 @@ import { ERROR_CODE_NO_CONNECT } from '../apis/snode_api/SNodeAPI';
import { OnionPaths } from '.';
import { APPLICATION_JSON } from '../../types/MIME';
import { ed25519Str } from '../utils/String';
import { DURATION } from '../constants';

const desiredGuardCount = 3;
const minimumGuardCount = 2;
const ONION_REQUEST_HOPS = 3;

export function getOnionPathMinTimeout() {
return DURATION.SECONDS;
}
Bilb marked this conversation as resolved.
Show resolved Hide resolved

export let onionPaths: Array<Array<Snode>> = [];

/**
Expand Down Expand Up @@ -498,16 +504,27 @@ async function buildNewOnionPathsWorker() {

for (let i = 0; i < maxPath; i += 1) {
const path = [guards[i]];
for (let j = 0; j < nodesNeededPerPaths; j += 1) {
const randomWinner = _.sample(otherNodes);
if (!randomWinner) {
throw new Error('randomWinner unset during path building task');

do {
// selection of the last snode (edge snode) needs at least v2.8.0
if (path.length === nodesNeededPerPaths) {
const randomEdgeSnode = getRandomEdgeSnode(otherNodes);
otherNodes = otherNodes.filter(n => {
return n.pubkey_ed25519 !== randomEdgeSnode?.pubkey_ed25519;
});
path.push(randomEdgeSnode);
} else {
const snode = sample(otherNodes);
if (!snode) {
throw new Error('no more snode found for path building');
}
otherNodes = otherNodes.filter(n => {
return n.pubkey_ed25519 !== snode?.pubkey_ed25519;
});

path.push(snode);
}
otherNodes = otherNodes.filter(n => {
return n.pubkey_ed25519 !== randomWinner?.pubkey_ed25519;
});
path.push(randomWinner);
}
} while (path.length <= nodesNeededPerPaths);
Bilb marked this conversation as resolved.
Show resolved Hide resolved
onionPaths.push(path);
}

Expand All @@ -516,7 +533,7 @@ async function buildNewOnionPathsWorker() {
{
retries: 3, // 4 total
factor: 1,
minTimeout: 1000,
minTimeout: OnionPaths.getOnionPathMinTimeout(),
Bilb marked this conversation as resolved.
Show resolved Hide resolved
onFailedAttempt: e => {
window?.log?.warn(
`buildNewOnionPathsWorker attempt #${e.attemptNumber} failed. ${e.retriesLeft} retries left... Error: ${e.message}`
Expand All @@ -525,3 +542,33 @@ async function buildNewOnionPathsWorker() {
}
);
}

export function getRandomEdgeSnode(snodes: Array<Snode>) {
const allSnodesWithv280 = snodes.filter(snode => {
const snodeStorageVersion = snode.storage_server_version;

if (
!snodeStorageVersion ||
!Array.isArray(snodeStorageVersion) ||
snodeStorageVersion.length !== 3 ||
snodeStorageVersion.some(m => !isNumber(m) || !isFinite(m))
) {
return false;
}
const storageVersionAsString = `${snodeStorageVersion[0]}.${snodeStorageVersion[1]}.${snodeStorageVersion[2]}`;
Bilb marked this conversation as resolved.
Show resolved Hide resolved
const verifiedStorageVersion = semver.valid(storageVersionAsString);
if (!verifiedStorageVersion) {
return false;
}
if (semver.lt(verifiedStorageVersion, '2.8.0')) {
return false;
}
return true;
});

const randomEdgeSnode = sample(allSnodesWithv280);
if (!randomEdgeSnode) {
throw new Error('did not find a single snode which can be the edge');
}
return randomEdgeSnode;
}
Loading
Loading