pull/1624/head
Audric Ackermann 4 years ago
parent c2298c4c30
commit 4514714d60
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -25,6 +25,8 @@ const pathFailureThreshold = 3;
// some naming issue here it seems) // some naming issue here it seems)
let guardNodes: Array<SnodePool.Snode> = []; let guardNodes: Array<SnodePool.Snode> = [];
export const ed25519Str = (ed25519Key: string) => `(...${ed25519Key.substr(58)})`;
export async function buildNewOnionPathsOneAtATime() { export async function buildNewOnionPathsOneAtATime() {
// this function may be called concurrently make sure we only have one inflight // this function may be called concurrently make sure we only have one inflight
return allowOnlyOneAtATime('buildNewOnionPaths', async () => { return allowOnlyOneAtATime('buildNewOnionPaths', async () => {
@ -50,9 +52,15 @@ export async function dropSnodeFromPath(snodeEd25519: string) {
); );
if (pathWithSnodeIndex === -1) { if (pathWithSnodeIndex === -1) {
window.log.warn(
`Could not drop ${ed25519Str(snodeEd25519)} from path index: ${pathWithSnodeIndex}`
);
return; 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 // make a copy now so we don't alter the real one while doing stuff here
const oldPaths = _.cloneDeep(onionPaths); const oldPaths = _.cloneDeep(onionPaths);
@ -116,7 +124,9 @@ export async function getOnionPath(toExclude?: SnodePool.Snode): Promise<Array<S
*/ */
export async function incrementBadPathCountOrDrop(guardNodeEd25519: string) { export async function incrementBadPathCountOrDrop(guardNodeEd25519: string) {
const pathIndex = onionPaths.findIndex(p => p[0].pubkey_ed25519 === guardNodeEd25519); const pathIndex = onionPaths.findIndex(p => 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) { if (pathIndex === -1) {
window.log.info('Did not find path with this guard node'); 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 '); window.log.warn('No such path starts with this guard node ');
return; return;
} }
window.log.info(
`Dropping path starting with guard node ${ed25519Str(
guardNodeEd25519
)}; index:${failingPathIndex}`
);
onionPaths = onionPaths.filter(p => p[0].pubkey_ed25519 !== guardNodeEd25519); onionPaths = onionPaths.filter(p => p[0].pubkey_ed25519 !== guardNodeEd25519);
const edKeys = guardNodes const edKeys = guardNodes
@ -175,7 +190,7 @@ async function dropPathStartingWithGuardNode(guardNodeEd25519: string) {
async function testGuardNode(snode: SnodePool.Snode) { async function testGuardNode(snode: SnodePool.Snode) {
const { log } = window; 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 // Send a post request and make sure it is OK
const endpoint = '/storage_rpc/v1'; const endpoint = '/storage_rpc/v1';
@ -242,7 +257,7 @@ async function selectGuardNodes(): Promise<Array<SnodePool.Snode>> {
// The use of await inside while is intentional: // The use of await inside while is intentional:
// we only want to repeat if the await fails // we only want to repeat if the await fails
// eslint-disable-next-line-no-await-in-loop // eslint-disable-next-line-no-await-in-loop
while (selectedGuardNodes.length < 3) { while (selectedGuardNodes.length < desiredGuardCount) {
if (shuffled.length < desiredGuardCount) { if (shuffled.length < desiredGuardCount) {
log.error('Not enought nodes in the pool'); log.error('Not enought nodes in the pool');
break; break;
@ -261,11 +276,10 @@ async function selectGuardNodes(): Promise<Array<SnodePool.Snode>> {
selectedGuardNodes = _.concat(selectedGuardNodes, goodNodes); selectedGuardNodes = _.concat(selectedGuardNodes, goodNodes);
} }
if (guardNodes.length < desiredGuardCount) { if (selectedGuardNodes.length < desiredGuardCount) {
log.error(`COULD NOT get enough guard nodes, only have: ${guardNodes.length}`); log.error(`Cound't get enough guard nodes, only have: ${guardNodes.length}`);
} }
guardNodes = selectedGuardNodes;
log.info('new guard nodes: ', guardNodes);
const edKeys = guardNodes.map(n => n.pubkey_ed25519); const edKeys = guardNodes.map(n => n.pubkey_ed25519);
@ -277,7 +291,7 @@ async function selectGuardNodes(): Promise<Array<SnodePool.Snode>> {
async function buildNewOnionPathsWorker() { async function buildNewOnionPathsWorker() {
const { log } = window; const { log } = window;
log.info('LokiSnodeAPI::buildNewOnionPaths - building new onion paths'); log.info('LokiSnodeAPI::buildNewOnionPaths - building new onion paths...');
const allNodes = await SnodePool.getRandomSnodePool(); 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 guard nodes is still empty (the old nodes are now invalid), select new ones:
if (guardNodes.length < minimumGuardCount) { if (guardNodes.length < minimumGuardCount) {
// TODO: don't throw away potentially good guard nodes // TODO: don't throw away potentially good guard nodes
guardNodes = await selectGuardNodes(); guardNodes = await selectGuardNodes();
}
} }
// TODO: select one guard node and 2 other nodes randomly // 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' 'LokiSnodeAPI::buildNewOnionPaths - Too few nodes to build an onion path! Refreshing pool and retrying'
); );
await SnodePool.refreshRandomPool(); await SnodePool.refreshRandomPool();
debugger;
await buildNewOnionPathsOneAtATime(); await buildNewOnionPathsOneAtATime();
return; return;
} }
@ -345,8 +357,6 @@ async function buildNewOnionPathsWorker() {
} }
onionPaths.push(path); onionPaths.push(path);
} }
console.warn('guards', guards);
console.warn('onionPaths', onionPaths);
log.info(`Built ${onionPaths.length} onion paths`); log.info(`Built ${onionPaths.length} onion paths`);
} }

@ -3,7 +3,6 @@
import { OnionPaths } from '.'; import { OnionPaths } from '.';
import { import {
FinalRelayOptions, FinalRelayOptions,
RequestError,
sendOnionRequestLsrpcDest, sendOnionRequestLsrpcDest,
snodeHttpsAgent, snodeHttpsAgent,
SnodeResponse, SnodeResponse,
@ -92,6 +91,7 @@ export const getOnionPathForSending = async () => {
const initOptionsWithDefaults = (options: OnionFetchBasicOptions) => { const initOptionsWithDefaults = (options: OnionFetchBasicOptions) => {
const defaultFetchBasicOptions = { const defaultFetchBasicOptions = {
retry: 0, retry: 0,
noJson: false,
}; };
return _.defaults(options, defaultFetchBasicOptions); 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, factor: 1,
minTimeout: 1000, minTimeout: 1000,
onFailedAttempt: e => { onFailedAttempt: e => {

@ -268,21 +268,9 @@ export async function getSnodePoolFromSnodes() {
const nodesToRequest = _.sampleSize(existingSnodePool, 3); const nodesToRequest = _.sampleSize(existingSnodePool, 3);
const results = await Promise.all( const results = await Promise.all(
nodesToRequest.map(async node => { nodesToRequest.map(async node => {
return pRetry( // this call is already retried if the snode does not reply
async () => { // at least when onion requests enabled
return getSnodePoolFromSnode(node); return getSnodePoolFromSnode(node);
},
{
retries: 3,
factor: 1,
minTimeout: 1000,
onFailedAttempt: e => {
window.log.warn(
`getSnodePoolFromSnode attempt #${e.attemptNumber} failed. ${e.retriesLeft} retries left...`
);
},
}
);
}) })
); );

@ -13,12 +13,6 @@ import { fromBase64ToArrayBuffer, toHex } from '../utils/String';
import pRetry from 'p-retry'; import pRetry from 'p-retry';
import { incrementBadPathCountOrDrop } from '../onions/onionPath'; 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) // 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<string, number> = {}; const snodeFailureCount: Record<string, number> = {};
@ -332,7 +326,6 @@ const debug = false;
// Process a response as it arrives from `fetch`, handling // Process a response as it arrives from `fetch`, handling
// http errors and attempting to decrypt the body with `sharedKey` // 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 // tslint:disable-next-line: cyclomatic-complexity
async function processOnionResponse( async function processOnionResponse(
response: Response, response: Response,
@ -497,9 +490,6 @@ export async function incrementBadSnodeCountOrDrop(snodeEd25519: string, associa
const oldFailureCount = snodeFailureCount[snodeEd25519] || 0; const oldFailureCount = snodeFailureCount[snodeEd25519] || 0;
const newFailureCount = oldFailureCount + 1; const newFailureCount = oldFailureCount + 1;
snodeFailureCount[snodeEd25519] = newFailureCount; snodeFailureCount[snodeEd25519] = newFailureCount;
window.log.warn(
`Couldn't reach snode at: ${snodeEd25519}; setting his failure count to ${newFailureCount}`
);
if (newFailureCount >= snodeFailureThreshold) { if (newFailureCount >= snodeFailureThreshold) {
window.log.warn(`Failure threshold reached for: ${snodeEd25519}; dropping it.`); 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 // if dropSnodeFromPath throws, it means there is an issue patching up the path, increment the whole path issues count
await OnionPaths.incrementBadPathCountOrDrop(snodeEd25519); 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( export async function lokiOnionFetch(
targetNode: Snode, targetNode: Snode,
@ -746,7 +740,7 @@ export async function lokiOnionFetch(
return onionFetchRetryable(targetNode, body, associatedWith); return onionFetchRetryable(targetNode, body, associatedWith);
}, },
{ {
retries: 5, retries: 10,
factor: 1, factor: 1,
minTimeout: 1000, minTimeout: 1000,
onFailedAttempt: e => { onFailedAttempt: e => {

@ -7,6 +7,7 @@ import * as Data from '../../../ts/data/data';
import { allowOnlyOneAtATime } from '../utils/Promise'; import { allowOnlyOneAtATime } from '../utils/Promise';
import pRetry from 'p-retry'; 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 * 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); _.remove(randomSnodePool, x => x.pubkey_ed25519 === snodeEd25519);
window.log.warn( 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<void> {
return; return;
} }
try { 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). // fetch the snode pool from one of the seed nodes (see the catch).
await pRetry( await pRetry(
async () => { async () => {
@ -284,7 +287,7 @@ export async function refreshRandomPool(): Promise<void> {
randomSnodePool = commonNodes; randomSnodePool = commonNodes;
}, },
{ {
retries: 2, retries: 3,
factor: 1, factor: 1,
minTimeout: 1000, minTimeout: 1000,
onFailedAttempt: e => { onFailedAttempt: e => {

Loading…
Cancel
Save