feat: adding proper typings to snode expire code

I dont think the expire value returned is correct, needs debugging
pull/2971/head
William Grant 2 years ago
parent f2dbcfa305
commit ae15275206

@ -10,50 +10,41 @@ import { doSnodeBatchRequest } from './batchRequest';
import { GetNetworkTime } from './getNetworkTime'; import { GetNetworkTime } from './getNetworkTime';
import { getSwarmFor } from './snodePool'; import { getSwarmFor } from './snodePool';
import { SnodeSignature } from './snodeSignatures'; import { SnodeSignature } from './snodeSignatures';
import { ExpireMessageResultItem, ExpireMessagesResultsContent } from './types';
async function verifySignature({ async function verifyExpireMsgsResponseSignature({
pubkey, pubkey,
snodePubkey, snodePubkey,
expiryApplied,
signature,
messageHashes, messageHashes,
updatedHashes, expiry,
unchangedHashes, signature,
}: { updated,
unchanged,
}: ExpireMessageResultItem & {
pubkey: string; pubkey: string;
snodePubkey: any; snodePubkey: any;
expiryApplied: number;
signature: string;
messageHashes: Array<string>; messageHashes: Array<string>;
updatedHashes: Array<string>;
// only used when shorten or extend is in the request
unchangedHashes?: Record<string, string>;
}): Promise<boolean> { }): Promise<boolean> {
if (!expiryApplied || isEmpty(messageHashes) || isEmpty(signature)) { if (!expiry || isEmpty(messageHashes) || isEmpty(signature)) {
window.log.warn('WIP: [verifySignature] missing argument'); window.log.warn('WIP: [verifyExpireMsgsSignature] missing argument');
return false; return false;
} }
const edKeyPrivBytes = fromHexToArray(snodePubkey); const edKeyPrivBytes = fromHexToArray(snodePubkey);
/* PUBKEY_HEX || EXPIRY || RMSGs... || UMSGs... || CMSG_EXPs... const hashes = [...messageHashes, ...updated];
where RMSGs are the requested expiry hashes, if (unchanged && Object.keys(unchanged).length > 0) {
UMSGs are the actual updated hashes, and
CMSG_EXPs are (HASH || EXPIRY) values, ascii-sorted by hash, for the unchanged message hashes included in the "unchanged" field.
*/
const hashes = [...messageHashes, ...updatedHashes];
if (unchangedHashes && Object.keys(unchangedHashes).length > 0) {
hashes.push( hashes.push(
...Object.entries(unchangedHashes) ...Object.entries(unchanged)
.map(([key, value]: [string, string]) => { .map(([key, value]: [string, number]) => {
return `${key}${value}`; return `${key}${value}`;
}) })
.sort() .sort()
); );
} }
const verificationString = `${pubkey}${expiryApplied}${hashes.join('')}`; const verificationString = `${pubkey}${expiry}${hashes.join('')}`;
const verificationData = StringUtils.encode(verificationString, 'utf8'); const verificationData = StringUtils.encode(verificationString, 'utf8');
window.log.debug('WIP: [verifySignature] verificationString', verificationString); // window.log.debug('WIP: [verifyExpireMsgsSignature] verificationString', verificationString);
const sodium = await getSodiumRenderer(); const sodium = await getSodiumRenderer();
try { try {
@ -65,24 +56,25 @@ async function verifySignature({
return isValid; return isValid;
} catch (e) { } catch (e) {
window.log.warn('WIP: [verifySignature] failed with: ', e.message); window.log.warn('WIP: [verifyExpireMsgsSignature] failed with: ', e.message);
return false; return false;
} }
} }
async function processExpirationResults( type ExpireRequestResponseResults = Record<string, { hashes: Array<string>; expiry: number }>;
async function processExpireRequestResponse(
pubkey: string, pubkey: string,
targetNode: Snode, targetNode: Snode,
swarm: Record<string, any>, swarm: ExpireMessagesResultsContent,
messageHashes: Array<string> messageHashes: Array<string>
) { ): Promise<ExpireRequestResponseResults> {
if (isEmpty(swarm)) { if (isEmpty(swarm)) {
throw Error(`[expireOnNodes] failed! ${messageHashes}`); throw Error(`[expireOnNodes] failed! ${messageHashes}`);
} }
// TODO need proper typing for swarm and results const results: ExpireRequestResponseResults = {};
const results: Record<string, { hashes: Array<string>; expiry: number }> = {}; window.log.debug(`WIP: [processExpireRequestResponse] initial results: `, swarm, messageHashes);
window.log.debug(`WIP: [processExpirationResults] start`, swarm, messageHashes);
for (const nodeKey of Object.keys(swarm)) { for (const nodeKey of Object.keys(swarm)) {
if (!isEmpty(swarm[nodeKey].failed)) { if (!isEmpty(swarm[nodeKey].failed)) {
@ -93,24 +85,24 @@ async function processExpirationResults(
targetNode.pubkey_ed25519 targetNode.pubkey_ed25519
}${reason && statusCode && ` due to an error ${reason} (${statusCode})`}` }${reason && statusCode && ` due to an error ${reason} (${statusCode})`}`
); );
// TODO This might be a redundant step // Make sure to clear the result since it failed
results[nodeKey] = { hashes: [], expiry: 0 }; results[nodeKey] = { hashes: [], expiry: 0 };
} }
const updatedHashes = swarm[nodeKey].updated; const updatedHashes = swarm[nodeKey].updated;
const unchangedHashes = swarm[nodeKey].unchanged; const unchangedHashes = swarm[nodeKey].unchanged;
const expiryApplied = swarm[nodeKey].expiry; const expiry = swarm[nodeKey].expiry;
const signature = swarm[nodeKey].signature; const signature = swarm[nodeKey].signature;
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
const isValid = await verifySignature({ const isValid = await verifyExpireMsgsResponseSignature({
pubkey, pubkey,
snodePubkey: nodeKey, snodePubkey: nodeKey,
expiryApplied,
signature,
messageHashes, messageHashes,
updatedHashes, expiry,
unchangedHashes, signature,
updated: updatedHashes,
unchanged: unchangedHashes,
}); });
if (!isValid) { if (!isValid) {
@ -119,7 +111,7 @@ async function processExpirationResults(
messageHashes messageHashes
); );
} }
results[nodeKey] = { hashes: updatedHashes, expiry: expiryApplied }; results[nodeKey] = { hashes: updatedHashes, expiry };
} }
return results; return results;
@ -135,25 +127,41 @@ async function expireOnNodes(targetNode: Snode, expireRequest: UpdateExpiryOnNod
'batch' 'batch'
); );
if (!result || result.length !== 1 || result[0]?.code !== 200 || !result[0]?.body) { if (!result || result.length !== 1) {
window?.log?.warn( window?.log?.warn(
`WIP: [expireOnNodes] - sessionRpc could not talk to ${targetNode.ip}:${targetNode.port}` `WIP: [expireOnNodes] - There was an issue with the results. sessionRpc ${targetNode.ip}:${
targetNode.port
} expireRequest ${JSON.stringify(expireRequest)}`
); );
return false; return false;
} }
// TODOLATER make sure that this code still works once disappearing messages is merged
// do a basic check to know if we have something kind of looking right (status 200 should always be there for a retrieve)
const firstResult = result[0];
if (firstResult.code !== 200) {
window?.log?.warn(`WIP: [expireOnNods] result is not 200 but ${firstResult.code}`);
return false;
}
try { try {
// TODOLATER make sure that this code still works once disappearing messages is merged const bodyFirstResult = firstResult.body;
const parsed = result[0].body; const expirationResults = await processExpireRequestResponse(
const expirationResults = await processExpirationResults(
expireRequest.params.pubkey, expireRequest.params.pubkey,
targetNode, targetNode,
parsed.swarm, bodyFirstResult.swarm as ExpireMessagesResultsContent,
expireRequest.params.messages expireRequest.params.messages
); );
const firstExpirationResult = Object.entries(expirationResults).at(0);
window.log.debug( window.log.debug(
'WIP: expireOnNodes attempt complete. Here are the results', `WIP: expireOnNodes attempt complete. Here are the results from one of the snodes.\nmessageHash: ${
expirationResults firstExpirationResult?.[0]
} \nexpires at: ${
firstExpirationResult?.[1]?.expiry
? new Date(firstExpirationResult?.[1]?.expiry).toUTCString()
: 'unknown'
}\nnow: ${new Date(GetNetworkTime.getNowWithNetworkOffset()).toUTCString()}`
); );
return true; return true;
@ -202,6 +210,11 @@ async function buildExpireRequest(
} }
const expiry = GetNetworkTime.getNowWithNetworkOffset() + expireTimer; const expiry = GetNetworkTime.getNowWithNetworkOffset() + expireTimer;
window.log.debug(
`WIP: [buildExpireRequest] messageHash: ${messageHash} should expire at ${new Date(
expiry
).toUTCString()}`
);
const signResult = await SnodeSignature.generateUpdateExpirySignature({ const signResult = await SnodeSignature.generateUpdateExpirySignature({
shortenOrExtend, shortenOrExtend,
timestamp: expiry, timestamp: expiry,

@ -21,3 +21,24 @@ export type RetrieveRequestResult = {
}; };
export type RetrieveMessagesResultsBatched = Array<RetrieveRequestResult>; export type RetrieveMessagesResultsBatched = Array<RetrieveRequestResult>;
/** inherits from https://api.oxen.io/storage-rpc/#/recursive?id=recursive but we only care about these values */
export type ExpireMessageResultItem = {
/** the expiry timestamp that was applied (which might be different from the request expiry */
expiry: number;
/** ( PUBKEY_HEX || EXPIRY || RMSGs... || UMSGs... || CMSG_EXPs... )
where RMSGs are the requested expiry hashes,
UMSGs are the actual updated hashes, and
CMSG_EXPs are (HASH || EXPIRY) values, ascii-sorted by hash, for the unchanged message hashes included in the "unchanged" field.
The signature uses the node's ed25519 pubkey.
*/
signature: string;
/** Record of <found hashes, current expiries>, but did not get updated due to "shorten"/"extend" in the request. This field is only included when "shorten /extend" is explicitly given. */
unchanged?: Record<string, number>;
/** ascii-sorted list of hashes that had their expiries changed (messages that were not found, and messages excluded by the shorten/extend options, are not included) */
updated: Array<string>;
failed?: boolean;
};
/** <pubkey, ExpireMessageResultItem> */
export type ExpireMessagesResultsContent = Record<string, ExpireMessageResultItem>;

Loading…
Cancel
Save