2022-12-04 07:42:04 +00:00
|
|
|
// @ts-ignore
|
|
|
|
import stringify from "json-stringify-deterministic";
|
|
|
|
// @ts-ignore
|
|
|
|
import crypto from "hypercore-crypto";
|
2022-12-04 10:39:59 +00:00
|
|
|
// @ts-ignore
|
|
|
|
import sodium from "sodium-universal";
|
2022-12-04 07:42:04 +00:00
|
|
|
import b4a from "b4a";
|
2023-03-19 14:35:54 +00:00
|
|
|
import RPC from "@lumeweb/rpc";
|
2023-03-19 15:10:57 +00:00
|
|
|
export const RPC_PROTOCOL_SYMBOL = Symbol.for("lumeweb");
|
2022-12-04 07:42:04 +00:00
|
|
|
export function isPromise(obj) {
|
|
|
|
return (!!obj &&
|
|
|
|
(typeof obj === "object" || typeof obj === "function") &&
|
|
|
|
typeof obj.then === "function");
|
2022-08-01 03:02:03 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
Forked from https://github.com/hughsk/flat
|
|
|
|
*/
|
|
|
|
export function flatten(target, opts = {}) {
|
|
|
|
opts = opts || {};
|
|
|
|
const delimiter = opts.delimiter || ".";
|
|
|
|
const maxDepth = opts.maxDepth;
|
|
|
|
const transformKey = opts.transformKey || ((key) => (isNaN(parseInt(key)) ? key : ""));
|
|
|
|
const output = [];
|
|
|
|
function step(object, prev, currentDepth) {
|
|
|
|
currentDepth = currentDepth || 1;
|
2022-08-01 03:11:31 +00:00
|
|
|
if (!Array.isArray(object)) {
|
2022-08-14 13:27:02 +00:00
|
|
|
object = Object.keys(object ?? {});
|
2022-08-01 03:11:31 +00:00
|
|
|
}
|
|
|
|
object.forEach(function (key) {
|
2022-08-01 03:02:03 +00:00
|
|
|
const value = object[key];
|
|
|
|
const isarray = opts.safe && Array.isArray(value);
|
|
|
|
const type = Object.prototype.toString.call(value);
|
2022-12-04 07:42:04 +00:00
|
|
|
const isbuffer = b4a.isBuffer(value);
|
2022-08-01 03:02:03 +00:00
|
|
|
const isobject = type === "[object Object]" || type === "[object Array]";
|
|
|
|
const newKey = prev
|
|
|
|
? prev + delimiter + transformKey(key)
|
|
|
|
: transformKey(key);
|
|
|
|
if (!isarray &&
|
|
|
|
!isbuffer &&
|
|
|
|
isobject &&
|
|
|
|
Object.keys(value).length &&
|
|
|
|
(!opts.maxDepth || currentDepth < maxDepth)) {
|
|
|
|
return step(value, newKey, currentDepth + 1);
|
|
|
|
}
|
|
|
|
output.push(`${newKey}=${value}`);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
step(target);
|
|
|
|
return output;
|
|
|
|
}
|
2022-12-04 07:42:04 +00:00
|
|
|
export function validateResponse(relay, response, timestamped = false) {
|
|
|
|
const field = response.signedField || "data";
|
|
|
|
// @ts-ignore
|
2023-03-18 16:11:41 +00:00
|
|
|
let json = response[field];
|
2022-12-04 07:42:04 +00:00
|
|
|
if (typeof json !== "string") {
|
|
|
|
json = stringify(json);
|
|
|
|
}
|
|
|
|
const updated = response.updated;
|
|
|
|
if (timestamped && updated) {
|
|
|
|
json = updated.toString() + json;
|
|
|
|
}
|
|
|
|
return !!crypto.verify(b4a.from(json), b4a.from(response.signature, "hex"), relay);
|
|
|
|
}
|
|
|
|
export function validateTimestampedResponse(relay, response) {
|
|
|
|
return validateResponse(relay, response, true);
|
2022-08-27 19:13:00 +00:00
|
|
|
}
|
2022-12-04 10:39:59 +00:00
|
|
|
export function hashQuery(query) {
|
|
|
|
const clonedQuery = {
|
|
|
|
module: query.module,
|
|
|
|
method: query.method,
|
|
|
|
data: query.data,
|
|
|
|
};
|
|
|
|
const queryHash = Buffer.allocUnsafe(32);
|
|
|
|
sodium.crypto_generichash(queryHash, Buffer.from(stringify(clonedQuery)));
|
|
|
|
return queryHash.toString("hex");
|
|
|
|
}
|
2023-03-18 16:11:41 +00:00
|
|
|
export function createHash(data) {
|
|
|
|
const buffer = b4a.from(data);
|
|
|
|
let hash = b4a.allocUnsafe(32);
|
|
|
|
sodium.crypto_generichash(hash, buffer);
|
|
|
|
return hash;
|
|
|
|
}
|
2023-03-19 14:35:54 +00:00
|
|
|
export function setupStream(stream) {
|
|
|
|
const existing = stream[RPC_PROTOCOL_SYMBOL];
|
|
|
|
if (existing) {
|
|
|
|
return existing;
|
|
|
|
}
|
|
|
|
stream[RPC_PROTOCOL_SYMBOL] = new RPC(stream);
|
|
|
|
return stream[RPC_PROTOCOL_SYMBOL];
|
|
|
|
}
|