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 input = Buffer.from(lnsName);
const output = await window.blake2b(input);
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
const lnsNodes = this.getNodesMinVersion('2.0.3');
// randomPool should already be shuffled
// lnsNodes = _.shuffle(lnsNodes);
let lnsNodes = await this.getNodesMinVersion(window.CONSTANTS.LNS_CAPABLE_NODES_VERSION);
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
// answers here and select a dominating answer
const allResults = [];
let ciphertextHex = null;
const confirmedNodes = [];
while (!ciphertextHex) {
if (lnsNodes.length < 3) {
log.error('Not enough nodes for lns lookup');
return false;
}
// extract 3 and make requests in parallel
const nodes = lnsNodes.splice(0, 3);
let cipherResolve;
// eslint-disable-next-line no-unused-vars
const cipherPromise = () => new Promise((resolve, _reject) => {
cipherResolve = resolve;
});
// eslint-disable-next-line no-await-in-loop
const results = await Promise.all(
nodes.map(node => this._requestLnsMapping(node, nameHash))
const decryptHex = async cipherHex => {
const ciphertext = new Uint8Array(
StringView.hexToArrayBuffer(cipherHex)
);
results.forEach(res => {
if (
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 res = await window.decryptLnsEntry(lnsName, ciphertext);
const pubicKey = StringView.arrayBufferToHex(res);
const [winner, count] = _.maxBy(
_.entries(_.countBy(allResults)),
x => x[1]
);
return pubicKey;
}
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) {
// eslint-disable-next-lint prefer-destructuring
ciphertextHex = winner;
// null represents no LNS mapping
if (ciphertextHex === null){
error = window.i18n('lnsMappingNotFound');
}
cipherResolve({ciphertextHex});
}
}
}
}
const ciphertext = new Uint8Array(
StringView.hexToArrayBuffer(ciphertextHex)
);
const nodes = lnsNodes.splice(0, numRequests);
// 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

Loading…
Cancel
Save