lns mapping method rewrite

pull/1091/head
Vincent 5 years ago
parent 23b7bda0c9
commit 8992234dd7

@ -756,73 +756,127 @@ class LokiSnodeAPI {
} }
} }
async getLnsMapping(lnsName) { async getLnsMapping(lnsName, timeout) {
// Returns { pubkey, error }
// pubkey is:
// null when there is confirmed to be no LNS mapping
// undefined when unconfirmee
// 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 _ = window.Lodash;
const input = Buffer.from(lnsName); const input = Buffer.from(lnsName);
const output = await window.blake2b(input); const output = await window.blake2b(input);
const nameHash = dcodeIO.ByteBuffer.wrap(output).toString('base64'); const nameHash = dcodeIO.ByteBuffer.wrap(output).toString('base64');
// Return value of null represents a timeout
const timeoutResponse = { timedOut: true };
const timeoutPromise = (cb, interval) => () => new Promise(resolve => setTimeout(() => cb(resolve), interval));
const onTimeout = timeoutPromise(resolve => resolve(timeoutResponse), timeout || Number.MAX_SAFE_INTEGER);
// Get nodes capable of doing LNS // Get nodes capable of doing LNS
const lnsNodes = this.getNodesMinVersion('2.0.3'); let lnsNodes = await this.getNodesMinVersion(window.CONSTANTS.LNS_CAPABLE_NODES_VERSION);
// randomPool should already be shuffled lnsNodes = _.shuffle(lnsNodes);
// lnsNodes = _.shuffle(lnsNodes);
// Loop until 3 confirmations // Enough nodes?
if (lnsNodes.length < numRequiredConfirms) {
error = window.i18n('lnsTooFewNodes');
return {pubkey, error};
}
// We don't trust any single node, so we accumulate const confirmedNodes = [];
// answers here and select a dominating answer
const allResults = [];
let ciphertextHex = null;
while (!ciphertextHex) { let cipherResolve;
if (lnsNodes.length < 3) { // eslint-disable-next-line no-unused-vars
log.error('Not enough nodes for lns lookup'); const cipherPromise = () => new Promise((resolve, _reject) => {
return false; cipherResolve = resolve;
} });
// extract 3 and make requests in parallel
const nodes = lnsNodes.splice(0, 3);
// eslint-disable-next-line no-await-in-loop const decryptHex = async cipherHex => {
const results = await Promise.all( const ciphertext = new Uint8Array(
nodes.map(node => this._requestLnsMapping(node, nameHash)) StringView.hexToArrayBuffer(cipherHex)
); );
results.forEach(res => { const res = await window.decryptLnsEntry(lnsName, ciphertext);
if ( const pubicKey = StringView.arrayBufferToHex(res);
res &&
res.result &&
res.result.status === 'OK' &&
res.result.entries &&
res.result.entries.length > 0
) {
allResults.push(results[0].result.entries[0].encrypted_value);
}
});
const [winner, count] = _.maxBy( return pubicKey;
_.entries(_.countBy(allResults)), }
x => x[1]
); const fetchFromNode = async node => {
const res = await this._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;
if (count >= 3) { // null represents no LNS mapping
// eslint-disable-next-lint prefer-destructuring if (ciphertextHex === null){
ciphertextHex = winner; error = window.i18n('lnsMappingNotFound');
}
cipherResolve({ciphertextHex});
}
}
} }
} }
const ciphertext = new Uint8Array( const nodes = lnsNodes.splice(0, numRequests);
StringView.hexToArrayBuffer(ciphertextHex)
); // Start fetching from nodes
Promise.resolve(nodes.map(async node => fetchFromNode(node)));
const res = await window.decryptLnsEntry(lnsName, ciphertext); // Timeouts (optional parameter)
// Wait for cipher to be found; race against timeout
const { timedOut } = await Promise.race([cipherPromise, onTimeout].map(f => f()));
const pubkey = StringView.arrayBufferToHex(res); if (timedOut) {
error = window.i18n('lnsLookupTimeout');
return { pubkey, error };
}
return pubkey; pubkey = ciphertextHex === null
? null
: await decryptHex(ciphertextHex);
return {pubkey, error};
} }
// get snodes for pubkey from random snode // get snodes for pubkey from random snode

Loading…
Cancel
Save