diff --git a/ts/session/onions/onionPath.ts b/ts/session/onions/onionPath.ts index e6b747296..38bb7a47c 100644 --- a/ts/session/onions/onionPath.ts +++ b/ts/session/onions/onionPath.ts @@ -25,6 +25,8 @@ const pathFailureThreshold = 3; // some naming issue here it seems) let guardNodes: Array = []; +export const ed25519Str = (ed25519Key: string) => `(...${ed25519Key.substr(58)})`; + export async function buildNewOnionPathsOneAtATime() { // this function may be called concurrently make sure we only have one inflight return allowOnlyOneAtATime('buildNewOnionPaths', async () => { @@ -50,9 +52,15 @@ export async function dropSnodeFromPath(snodeEd25519: string) { ); if (pathWithSnodeIndex === -1) { + window.log.warn( + `Could not drop ${ed25519Str(snodeEd25519)} from path index: ${pathWithSnodeIndex}` + ); + return; } - window.log.info(`dropping snode ${snodeEd25519} from path index: ${pathWithSnodeIndex}`); + window.log.info( + `dropping snode ${ed25519Str(snodeEd25519)} from path index: ${pathWithSnodeIndex}` + ); // make a copy now so we don't alter the real one while doing stuff here const oldPaths = _.cloneDeep(onionPaths); @@ -116,7 +124,9 @@ export async function getOnionPath(toExclude?: SnodePool.Snode): Promise p[0].pubkey_ed25519 === guardNodeEd25519); - window.log.info('\t\tincrementBadPathCountOrDrop starting with guard', guardNodeEd25519); + window.log.info( + `\t\tincrementBadPathCountOrDrop starting with guard ${ed25519Str(guardNodeEd25519)}` + ); if (pathIndex === -1) { window.log.info('Did not find path with this guard node'); @@ -158,6 +168,11 @@ async function dropPathStartingWithGuardNode(guardNodeEd25519: string) { window.log.warn('No such path starts with this guard node '); return; } + window.log.info( + `Dropping path starting with guard node ${ed25519Str( + guardNodeEd25519 + )}; index:${failingPathIndex}` + ); onionPaths = onionPaths.filter(p => p[0].pubkey_ed25519 !== guardNodeEd25519); const edKeys = guardNodes @@ -175,7 +190,7 @@ async function dropPathStartingWithGuardNode(guardNodeEd25519: string) { async function testGuardNode(snode: SnodePool.Snode) { const { log } = window; - log.info('Testing a candidate guard node ', snode); + log.info(`Testing a candidate guard node ${ed25519Str(snode.pubkey_ed25519)}`); // Send a post request and make sure it is OK const endpoint = '/storage_rpc/v1'; @@ -242,7 +257,7 @@ async function selectGuardNodes(): Promise> { // The use of await inside while is intentional: // we only want to repeat if the await fails // eslint-disable-next-line-no-await-in-loop - while (selectedGuardNodes.length < 3) { + while (selectedGuardNodes.length < desiredGuardCount) { if (shuffled.length < desiredGuardCount) { log.error('Not enought nodes in the pool'); break; @@ -261,11 +276,10 @@ async function selectGuardNodes(): Promise> { selectedGuardNodes = _.concat(selectedGuardNodes, goodNodes); } - if (guardNodes.length < desiredGuardCount) { - log.error(`COULD NOT get enough guard nodes, only have: ${guardNodes.length}`); + if (selectedGuardNodes.length < desiredGuardCount) { + log.error(`Cound't get enough guard nodes, only have: ${guardNodes.length}`); } - - log.info('new guard nodes: ', guardNodes); + guardNodes = selectedGuardNodes; const edKeys = guardNodes.map(n => n.pubkey_ed25519); @@ -277,7 +291,7 @@ async function selectGuardNodes(): Promise> { async function buildNewOnionPathsWorker() { const { log } = window; - log.info('LokiSnodeAPI::buildNewOnionPaths - building new onion paths'); + log.info('LokiSnodeAPI::buildNewOnionPaths - building new onion paths...'); const allNodes = await SnodePool.getRandomSnodePool(); @@ -300,12 +314,11 @@ async function buildNewOnionPathsWorker() { ); } } - - // If guard nodes is still empty (the old nodes are now invalid), select new ones: - if (guardNodes.length < minimumGuardCount) { - // TODO: don't throw away potentially good guard nodes - guardNodes = await selectGuardNodes(); - } + } + // If guard nodes is still empty (the old nodes are now invalid), select new ones: + if (guardNodes.length < minimumGuardCount) { + // TODO: don't throw away potentially good guard nodes + guardNodes = await selectGuardNodes(); } // TODO: select one guard node and 2 other nodes randomly @@ -316,7 +329,6 @@ async function buildNewOnionPathsWorker() { 'LokiSnodeAPI::buildNewOnionPaths - Too few nodes to build an onion path! Refreshing pool and retrying' ); await SnodePool.refreshRandomPool(); - debugger; await buildNewOnionPathsOneAtATime(); return; } @@ -345,8 +357,6 @@ async function buildNewOnionPathsWorker() { } onionPaths.push(path); } - console.warn('guards', guards); - console.warn('onionPaths', onionPaths); log.info(`Built ${onionPaths.length} onion paths`); } diff --git a/ts/session/onions/onionSend.ts b/ts/session/onions/onionSend.ts index 48c10843c..1ac85a851 100644 --- a/ts/session/onions/onionSend.ts +++ b/ts/session/onions/onionSend.ts @@ -3,7 +3,6 @@ import { OnionPaths } from '.'; import { FinalRelayOptions, - RequestError, sendOnionRequestLsrpcDest, snodeHttpsAgent, SnodeResponse, @@ -92,6 +91,7 @@ export const getOnionPathForSending = async () => { const initOptionsWithDefaults = (options: OnionFetchBasicOptions) => { const defaultFetchBasicOptions = { retry: 0, + noJson: false, }; return _.defaults(options, defaultFetchBasicOptions); }; @@ -177,7 +177,7 @@ export const sendViaOnion = async ( }); }, { - retries: 5, + retries: 10, // each path can fail 3 times before being dropped, we have 3 paths at most factor: 1, minTimeout: 1000, onFailedAttempt: e => { diff --git a/ts/session/snode_api/SNodeAPI.ts b/ts/session/snode_api/SNodeAPI.ts index cccbf78b4..cd343d4aa 100644 --- a/ts/session/snode_api/SNodeAPI.ts +++ b/ts/session/snode_api/SNodeAPI.ts @@ -268,21 +268,9 @@ export async function getSnodePoolFromSnodes() { const nodesToRequest = _.sampleSize(existingSnodePool, 3); const results = await Promise.all( nodesToRequest.map(async node => { - return pRetry( - async () => { - return getSnodePoolFromSnode(node); - }, - { - retries: 3, - factor: 1, - minTimeout: 1000, - onFailedAttempt: e => { - window.log.warn( - `getSnodePoolFromSnode attempt #${e.attemptNumber} failed. ${e.retriesLeft} retries left...` - ); - }, - } - ); + // this call is already retried if the snode does not reply + // at least when onion requests enabled + return getSnodePoolFromSnode(node); }) ); diff --git a/ts/session/snode_api/onions.ts b/ts/session/snode_api/onions.ts index 8fecb0a8c..a6da856c5 100644 --- a/ts/session/snode_api/onions.ts +++ b/ts/session/snode_api/onions.ts @@ -13,12 +13,6 @@ import { fromBase64ToArrayBuffer, toHex } from '../utils/String'; import pRetry from 'p-retry'; import { incrementBadPathCountOrDrop } from '../onions/onionPath'; -export enum RequestError { - BAD_PATH = 'BAD_PATH', - OTHER = 'OTHER', - ABORTED = 'ABORTED', -} - // hold the ed25519 key of a snode against the time it fails. Used to remove a snode only after a few failures (snodeFailureThreshold failures) const snodeFailureCount: Record = {}; @@ -332,7 +326,6 @@ const debug = false; // Process a response as it arrives from `fetch`, handling // http errors and attempting to decrypt the body with `sharedKey` -// May return false BAD_PATH, indicating that we should try a new path. // tslint:disable-next-line: cyclomatic-complexity async function processOnionResponse( response: Response, @@ -497,9 +490,6 @@ export async function incrementBadSnodeCountOrDrop(snodeEd25519: string, associa const oldFailureCount = snodeFailureCount[snodeEd25519] || 0; const newFailureCount = oldFailureCount + 1; snodeFailureCount[snodeEd25519] = newFailureCount; - window.log.warn( - `Couldn't reach snode at: ${snodeEd25519}; setting his failure count to ${newFailureCount}` - ); if (newFailureCount >= snodeFailureThreshold) { window.log.warn(`Failure threshold reached for: ${snodeEd25519}; dropping it.`); @@ -524,6 +514,10 @@ export async function incrementBadSnodeCountOrDrop(snodeEd25519: string, associa // if dropSnodeFromPath throws, it means there is an issue patching up the path, increment the whole path issues count await OnionPaths.incrementBadPathCountOrDrop(snodeEd25519); } + } else { + window.log.warn( + `Couldn't reach snode at: ${snodeEd25519}; setting his failure count to ${newFailureCount}` + ); } } @@ -733,7 +727,7 @@ async function onionFetchRetryable( } /** - * If the fetch returnes BAD_PATH we retry this call with a new path at most 3 times. If another error happens, we return it. If we have a result we just return it. + * If the fetch throws a retryable error we retry this call with a new path at most 3 times. If another error happens, we return it. If we have a result we just return it. */ export async function lokiOnionFetch( targetNode: Snode, @@ -746,7 +740,7 @@ export async function lokiOnionFetch( return onionFetchRetryable(targetNode, body, associatedWith); }, { - retries: 5, + retries: 10, factor: 1, minTimeout: 1000, onFailedAttempt: e => { diff --git a/ts/session/snode_api/snodePool.ts b/ts/session/snode_api/snodePool.ts index f65826113..1ed64be90 100644 --- a/ts/session/snode_api/snodePool.ts +++ b/ts/session/snode_api/snodePool.ts @@ -7,6 +7,7 @@ import * as Data from '../../../ts/data/data'; import { allowOnlyOneAtATime } from '../utils/Promise'; import pRetry from 'p-retry'; +import { ed25519Str } from '../onions/onionPath'; /** * If we get less than this snode in a swarm, we fetch new snodes for this pubkey @@ -117,7 +118,9 @@ export function dropSnodeFromSnodePool(snodeEd25519: string) { _.remove(randomSnodePool, x => x.pubkey_ed25519 === snodeEd25519); window.log.warn( - `Marking ${snodeEd25519} as unreachable, ${randomSnodePool.length} snodes remaining in randomPool` + `Marking ${ed25519Str(snodeEd25519)} as unreachable, ${ + randomSnodePool.length + } snodes remaining in randomPool` ); } } @@ -271,7 +274,7 @@ export async function refreshRandomPool(): Promise { return; } try { - // let this request try 3 (2+1) times. If all those requests end up without having a consensus, + // let this request try 3 (3+1) times. If all those requests end up without having a consensus, // fetch the snode pool from one of the seed nodes (see the catch). await pRetry( async () => { @@ -284,7 +287,7 @@ export async function refreshRandomPool(): Promise { randomSnodePool = commonNodes; }, { - retries: 2, + retries: 3, factor: 1, minTimeout: 1000, onFailedAttempt: e => {