You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			87 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			87 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			TypeScript
		
	
| import { isArray } from 'lodash';
 | |
| import { Snode } from '../../../data/data';
 | |
| import { processOnionRequestErrorAtDestination, SnodeResponse } from './onions';
 | |
| import { snodeRpc } from './sessionRpc';
 | |
| import { NotEmptyArrayOfBatchResults, SnodeApiSubRequests } from './SnodeRequestTypes';
 | |
| 
 | |
| /**
 | |
|  * This is the equivalent to the batch send on sogs. The target node runs each sub request and returns a list of all the sub status and bodies.
 | |
|  * If the global status code is not 200, an exception is thrown.
 | |
|  * The body is already parsed from json and is enforced to be an Array of at least one element
 | |
|  * @param subRequests the list of requests to do
 | |
|  * @param targetNode the node to do the request to, once all the onion routing is done
 | |
|  * @param timeout the timeout at which we should cancel this request.
 | |
|  * @param associatedWith used mostly for handling 421 errors, we need the pubkey the change is associated to
 | |
|  * @param method can be either batch or sequence. A batch call will run all calls even if one of them fails. A sequence call will stop as soon as the first one fails
 | |
|  */
 | |
| export async function doSnodeBatchRequest(
 | |
|   subRequests: Array<SnodeApiSubRequests>,
 | |
|   targetNode: Snode,
 | |
|   timeout: number,
 | |
|   associatedWith?: string,
 | |
|   method: 'batch' | 'sequence' = 'batch'
 | |
| ): Promise<NotEmptyArrayOfBatchResults> {
 | |
|   // console.warn(
 | |
|   //   `doSnodeBatchRequest "${method}":`,
 | |
|   //   subRequests.map(m => m.method),
 | |
|   //   subRequests
 | |
|   // );
 | |
|   const result = await snodeRpc({
 | |
|     method,
 | |
|     params: { requests: subRequests },
 | |
|     targetNode,
 | |
|     associatedWith,
 | |
|     timeout,
 | |
|   });
 | |
|   if (!result) {
 | |
|     window?.log?.warn(
 | |
|       `doSnodeBatchRequest - sessionRpc could not talk to ${targetNode.ip}:${targetNode.port}`
 | |
|     );
 | |
|     throw new Error(
 | |
|       `doSnodeBatchRequest - sessionRpc could not talk to ${targetNode.ip}:${targetNode.port}`
 | |
|     );
 | |
|   }
 | |
|   const decoded = decodeBatchRequest(result);
 | |
| 
 | |
|   if (decoded?.length) {
 | |
|     for (let index = 0; index < decoded.length; index++) {
 | |
|       const resultRow = decoded[index];
 | |
|       await processOnionRequestErrorAtDestination({
 | |
|         statusCode: resultRow.code,
 | |
|         body: JSON.stringify(resultRow.body),
 | |
|         associatedWith,
 | |
|         destinationSnodeEd25519: targetNode.pubkey_ed25519,
 | |
|       });
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return decoded;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Make sure the global batch status code is 200, parse the content as json and return it
 | |
|  */
 | |
| function decodeBatchRequest(snodeResponse: SnodeResponse): NotEmptyArrayOfBatchResults {
 | |
|   try {
 | |
|     // console.warn('decodeBatch: ', snodeResponse);
 | |
|     if (snodeResponse.status !== 200) {
 | |
|       throw new Error(`decodeBatchRequest invalid status code: ${snodeResponse.status}`);
 | |
|     }
 | |
|     const parsed = JSON.parse(snodeResponse.body);
 | |
| 
 | |
|     if (!isArray(parsed.results)) {
 | |
|       throw new Error('decodeBatchRequest results is not an array');
 | |
|     }
 | |
| 
 | |
|     if (!parsed.results.length) {
 | |
|       throw new Error('decodeBatchRequest results an empty array');
 | |
|     }
 | |
| 
 | |
|     return parsed.results;
 | |
|   } catch (e) {
 | |
|     window.log.error('decodeBatchRequest failed with ', e.message);
 | |
|     throw e;
 | |
|   }
 | |
|   // "{"results":[{"body":"retrieve signature verification failed","code":401}]}"
 | |
| }
 |