| 
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -314,8 +314,11 @@ class LokiSnodeAPI {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    } catch (e) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // ECONNREFUSED likely means it's just offline...
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // ECONNRESET seems to retry and fail as ECONNREFUSED (so likely a node going offline)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // ETIMEDOUT not sure what to do about these
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // retry for now but maybe we should be marking bad...
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (e.code === 'ECONNREFUSED') {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.markRandomNodeUnreachable(node);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.markRandomNodeUnreachable(node, { versionPoolFailure: true });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        const randomNodesLeft = this.getRandomPoolLength();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // clean up these error messages to be a little neater
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        log.warn(
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -408,29 +411,25 @@ class LokiSnodeAPI {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            const verionStart = Date.now();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            const t = this.randomSnodePool.length;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            const noticeEvery = parseInt(t / 10, 10);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            const finalPromise = this.randomSnodePool.reduce(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              async (p, node) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                if (p) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                  await p;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                  c += 1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                  if (c % noticeEvery === 0) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    // give stats
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    const diff = Date.now() - verionStart;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    log.info(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                      `${c}/${t} pool version status update, has taken ${diff.toLocaleString()}ms`
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    Object.keys(this.versionPools).forEach(version => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                      const nodes = this.versionPools[version].length;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                      log.info(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                        `version ${version} has ${nodes.toLocaleString()} snodes`
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                      );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                return this.getVersion(node);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            // eslint-disable-next-line no-restricted-syntax
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            for (const node of this.randomSnodePool) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              c += 1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              // eslint-disable-next-line no-await-in-loop
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              await this.getVersion(node);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              if (c % noticeEvery === 0) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                // give stats
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                const diff = Date.now() - verionStart;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                log.info(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                  `${c}/${t} pool version status update, has taken ${diff.toLocaleString()}ms`
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                Object.keys(this.versionPools).forEach(version => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                  const nodes = this.versionPools[version].length;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                  log.info(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    `version ${version} has ${nodes.toLocaleString()} snodes`
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                  );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            await finalPromise;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            log.info('Versions retrieved from network!');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            this.versionsRetrieved = true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          } catch (e) {
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -522,21 +521,43 @@ class LokiSnodeAPI {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return filteredNodes;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  markRandomNodeUnreachable(snode) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const snodeVersion = this.versionMap[`${snode.ip}:${snode.port}`];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (this.versionPools[snodeVersion]) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      this.versionPools[snodeVersion] = _.without(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.versionPools[snodeVersion],
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        _.find(this.versionPools[snodeVersion], {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          ip: snode.ip,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          port: snode.port,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        })
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  markRandomNodeUnreachable(snode, options = {}) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // avoid retries when we can't get the version because they're offline
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (!options.versionPoolFailure) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const snodeVersion = this.versionMap[`${snode.ip}:${snode.port}`];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (this.versionPools[snodeVersion]) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.versionPools[snodeVersion] = _.without(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          this.versionPools[snodeVersion],
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          snode
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      } else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (snodeVersion) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          // reverse map (versionMap) is out of sync with versionPools
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          log.error(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            'loki_snode:::markRandomNodeUnreachable - No snodes for version',
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            snodeVersion,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            'retrying in 10s'
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        } else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          // we don't know our version yet
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          log.warn(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            'loki_snode:::markRandomNodeUnreachable - No version for snode',
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            `${snode.ip}:${snode.port}`,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            'retrying in 10s'
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // make sure we don't retry past 15 mins (10s * 100 ~ 1000s)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (options.retries < 100) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          setTimeout(() => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            this.markRandomNodeUnreachable(snode, {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              ...options,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              retries: options.retries + 1,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          }, 10000);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.randomSnodePool = _.without(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      this.randomSnodePool,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      _.find(this.randomSnodePool, { ip: snode.ip, port: snode.port })
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this.randomSnodePool = _.without(this.randomSnodePool, snode);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  async updateLastHash(snode, hash, expiresAt) {
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
			
			 | 
			 | 
			
				
 
 |