2022-12-30 09:40:14 +00:00
|
|
|
import type { Plugin, PluginAPI } from "@lumeweb/relay-types";
|
|
|
|
import DHTFlood from "@lumeweb/dht-flood";
|
|
|
|
import { Message, Query, MessageType } from "./messages.js";
|
|
|
|
import EventEmitter from "events";
|
|
|
|
import NodeCache from "node-cache";
|
|
|
|
import b4a from "b4a";
|
|
|
|
import { SignedRegistryEntry } from "./types.js";
|
|
|
|
import { verifyEntry } from "./utils.js";
|
|
|
|
|
|
|
|
const PROTOCOL = "lumeweb.registry";
|
|
|
|
|
|
|
|
const events = new EventEmitter();
|
|
|
|
let messenger: DHTFlood;
|
|
|
|
let api: PluginAPI;
|
|
|
|
let memStore: NodeCache;
|
|
|
|
|
|
|
|
function setup() {
|
|
|
|
messenger = new DHTFlood({
|
|
|
|
swarm: api.swarm,
|
|
|
|
protocol: PROTOCOL,
|
|
|
|
id: api.identity.publicKeyRaw as Buffer,
|
|
|
|
});
|
|
|
|
|
|
|
|
messenger.on("message", (data: Buffer, origin: Buffer) => {
|
|
|
|
try {
|
|
|
|
let response = Message.fromBinary(data);
|
|
|
|
switch (response.type) {
|
|
|
|
case MessageType.CREATE:
|
|
|
|
events.emit("create", response, origin);
|
|
|
|
break;
|
|
|
|
case MessageType.CREATED:
|
|
|
|
events.emit("created", response, origin);
|
|
|
|
break;
|
|
|
|
case MessageType.RESPONSE:
|
|
|
|
events.emit("response", response, origin);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} catch {}
|
|
|
|
|
|
|
|
try {
|
|
|
|
let query = Query.fromBinary(data);
|
2022-12-31 19:52:30 +00:00
|
|
|
events.emit("query", query, origin);
|
2022-12-30 09:40:14 +00:00
|
|
|
} catch {}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function entryFromMessage(message: Message): SignedRegistryEntry {
|
|
|
|
return {
|
|
|
|
pk: message.pubkey,
|
|
|
|
data: message.data,
|
|
|
|
revision: message.revision,
|
|
|
|
signature: message.signature,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function sendDirectOrBroadcast(message: Message, pubkey: Buffer) {
|
|
|
|
let peer = api.swarm._allConnections.get(pubkey);
|
|
|
|
let data = Message.toBinary(message);
|
|
|
|
if (peer) {
|
|
|
|
messenger.send(peer, data, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
messenger.broadcast(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getEntry(
|
|
|
|
pubkey: Uint8Array
|
|
|
|
): Promise<SignedRegistryEntry | boolean> {
|
|
|
|
let pubkeyHex = b4a.from(pubkey).toString("hex");
|
|
|
|
if (memStore) {
|
|
|
|
return await memStore.get<SignedRegistryEntry>(pubkeyHex);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function setEntry(entry: SignedRegistryEntry): Promise<boolean> {
|
|
|
|
let pubkeyHex = b4a.from(entry.pk).toString("hex");
|
|
|
|
if (memStore) {
|
|
|
|
return await memStore.set<SignedRegistryEntry>(pubkeyHex, entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const plugin: Plugin = {
|
|
|
|
name: "registry",
|
|
|
|
async plugin(_api: PluginAPI): Promise<void> {
|
|
|
|
api = _api;
|
|
|
|
setup();
|
|
|
|
|
2022-12-31 20:00:30 +00:00
|
|
|
if (api.pluginConfig.bool("store.memory")) {
|
2022-12-30 09:40:14 +00:00
|
|
|
memStore = new NodeCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
events.on("create", async (message: Message, origin: Buffer) => {
|
|
|
|
let newEntry = entryFromMessage(message);
|
2022-12-31 19:52:56 +00:00
|
|
|
if (!newEntry.signature?.length) {
|
|
|
|
return;
|
|
|
|
}
|
2022-12-30 09:40:14 +00:00
|
|
|
if (!verifyEntry(newEntry)) {
|
|
|
|
return;
|
|
|
|
}
|
2022-12-30 10:08:41 +00:00
|
|
|
if (newEntry.data.length > 48) {
|
|
|
|
return;
|
|
|
|
}
|
2022-12-30 09:40:14 +00:00
|
|
|
let entry = (await getEntry(newEntry.pk)) as SignedRegistryEntry;
|
|
|
|
|
2022-12-31 21:38:54 +00:00
|
|
|
async function setAndRespond(entry: SignedRegistryEntry) {
|
2022-12-30 09:40:14 +00:00
|
|
|
await setEntry(newEntry);
|
|
|
|
sendDirectOrBroadcast(
|
|
|
|
Message.create({
|
|
|
|
type: MessageType.CREATED,
|
2022-12-31 21:38:54 +00:00
|
|
|
pubkey: entry.pk,
|
|
|
|
revision: entry.revision,
|
|
|
|
signature: entry.signature,
|
|
|
|
data: entry.data,
|
2022-12-30 09:40:14 +00:00
|
|
|
}),
|
|
|
|
origin
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry) {
|
|
|
|
if (newEntry.revision <= entry.revision) {
|
2022-12-31 21:38:54 +00:00
|
|
|
setAndRespond(newEntry);
|
|
|
|
return;
|
2022-12-30 09:40:14 +00:00
|
|
|
}
|
2022-12-31 21:38:54 +00:00
|
|
|
setAndRespond(entry);
|
|
|
|
return;
|
2022-12-30 09:40:14 +00:00
|
|
|
}
|
2022-12-31 21:38:54 +00:00
|
|
|
setAndRespond(newEntry);
|
2022-12-30 09:40:14 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
events.on("query", async (query: Query, origin: Buffer) => {
|
|
|
|
let entry = (await getEntry(query.pubkey)) as SignedRegistryEntry;
|
|
|
|
|
|
|
|
if (entry) {
|
|
|
|
sendDirectOrBroadcast(
|
|
|
|
Message.create({
|
|
|
|
type: MessageType.RESPONSE,
|
|
|
|
pubkey: entry.pk,
|
|
|
|
revision: entry.revision,
|
|
|
|
signature: entry.signature,
|
|
|
|
data: entry.data,
|
|
|
|
}),
|
|
|
|
origin
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
export default plugin;
|