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.
		
		
		
		
		
			
		
			
				
	
	
		
			129 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
			
		
		
	
	
			129 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
| /* eslint-disable class-methods-use-this */
 | |
| /* global window, Buffer, StringView, dcodeIO */
 | |
| 
 | |
| class LokiSnodeAPI {
 | |
|   // ************** NOTE ***************
 | |
|   // This is not used by anything yet,
 | |
|   // but should be. Do not remove!!!
 | |
|   // ***********************************
 | |
|   async getLnsMapping(lnsName, timeout) {
 | |
|     // Returns { pubkey, error }
 | |
|     // pubkey is
 | |
|     //      undefined when unconfirmed or no mapping found
 | |
|     //      string    when found
 | |
|     // timeout parameter optional (ms)
 | |
| 
 | |
|     // How many nodes to fetch data from?
 | |
|     const numRequests = 5;
 | |
| 
 | |
|     // How many nodes must have the same response value?
 | |
|     const numRequiredConfirms = 3;
 | |
| 
 | |
|     let ciphertextHex;
 | |
|     let pubkey;
 | |
|     let error;
 | |
| 
 | |
|     const _ = window.Lodash;
 | |
| 
 | |
|     const input = Buffer.from(lnsName);
 | |
|     const output = await window.blake2b(input);
 | |
|     const nameHash = dcodeIO.ByteBuffer.wrap(output).toString('base64');
 | |
| 
 | |
|     // Timeouts
 | |
|     const maxTimeoutVal = 2 ** 31 - 1;
 | |
|     const timeoutPromise = () =>
 | |
|       new Promise((_resolve, reject) =>
 | |
|         setTimeout(() => reject(), timeout || maxTimeoutVal)
 | |
|       );
 | |
| 
 | |
|     // Get nodes capable of doing LNS
 | |
|     const lnsNodes = await window.SnodePool.getNodesMinVersion(
 | |
|       window.CONSTANTS.LNS_CAPABLE_NODES_VERSION
 | |
|     );
 | |
| 
 | |
|     // Enough nodes?
 | |
|     if (lnsNodes.length < numRequiredConfirms) {
 | |
|       error = { lnsTooFewNodes: window.i18n('lnsTooFewNodes') };
 | |
|       return { pubkey, error };
 | |
|     }
 | |
| 
 | |
|     const confirmedNodes = [];
 | |
| 
 | |
|     // Promise is only resolved when a consensus is found
 | |
|     let cipherResolve;
 | |
|     const cipherPromise = () =>
 | |
|       new Promise(resolve => {
 | |
|         cipherResolve = resolve;
 | |
|       });
 | |
| 
 | |
|     const decryptHex = async cipherHex => {
 | |
|       const ciphertext = new Uint8Array(StringView.hexToArrayBuffer(cipherHex));
 | |
| 
 | |
|       const res = await window.decryptLnsEntry(lnsName, ciphertext);
 | |
|       const publicKey = StringView.arrayBufferToHex(res);
 | |
| 
 | |
|       return publicKey;
 | |
|     };
 | |
| 
 | |
|     const fetchFromNode = async node => {
 | |
|       const res = await window.NewSnodeAPI._requestLnsMapping(node, nameHash);
 | |
| 
 | |
|       // Do validation
 | |
|       if (res && res.result && res.result.status === 'OK') {
 | |
|         const hasMapping = res.result.entries && res.result.entries.length > 0;
 | |
| 
 | |
|         const resValue = hasMapping
 | |
|           ? res.result.entries[0].encrypted_value
 | |
|           : null;
 | |
| 
 | |
|         confirmedNodes.push(resValue);
 | |
| 
 | |
|         if (confirmedNodes.length >= numRequiredConfirms) {
 | |
|           if (ciphertextHex) {
 | |
|             // Result already found, dont worry
 | |
|             return;
 | |
|           }
 | |
| 
 | |
|           const [winner, count] = _.maxBy(
 | |
|             _.entries(_.countBy(confirmedNodes)),
 | |
|             x => x[1]
 | |
|           );
 | |
| 
 | |
|           if (count >= numRequiredConfirms) {
 | |
|             ciphertextHex = winner === String(null) ? null : winner;
 | |
| 
 | |
|             // null represents no LNS mapping
 | |
|             if (ciphertextHex === null) {
 | |
|               error = { lnsMappingNotFound: window.i18n('lnsMappingNotFound') };
 | |
|             }
 | |
| 
 | |
|             cipherResolve({ ciphertextHex });
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     };
 | |
| 
 | |
|     const nodes = lnsNodes.splice(0, numRequests);
 | |
| 
 | |
|     // Start fetching from nodes
 | |
|     nodes.forEach(node => fetchFromNode(node));
 | |
| 
 | |
|     // Timeouts (optional parameter)
 | |
|     // Wait for cipher to be found; race against timeout
 | |
|     // eslint-disable-next-line more/no-then
 | |
|     await Promise.race([cipherPromise, timeoutPromise].map(f => f()))
 | |
|       .then(async () => {
 | |
|         if (ciphertextHex !== null) {
 | |
|           pubkey = await decryptHex(ciphertextHex);
 | |
|         }
 | |
|       })
 | |
|       .catch(() => {
 | |
|         error = { lnsLookupTimeout: window.i18n('lnsLookupTimeout') };
 | |
|       });
 | |
| 
 | |
|     return { pubkey, error };
 | |
|   }
 | |
| }
 | |
| 
 | |
| module.exports = LokiSnodeAPI;
 |