mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-07 03:18:02 +02:00
Add option to use nostr-wasm to verify events
This commit is contained in:
parent
8a24016fa7
commit
958a8506f4
5
.changeset/sixty-dancers-admire.md
Normal file
5
.changeset/sixty-dancers-admire.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"nostrudel": minor
|
||||
---
|
||||
|
||||
Add option to use nostr-wasm to verify events
|
@ -66,7 +66,8 @@
|
||||
"nanoid": "^5.0.4",
|
||||
"ngeohash": "^0.6.3",
|
||||
"nostr-idb": "^2.1.1",
|
||||
"nostr-tools": "^2.4.0",
|
||||
"nostr-tools": "^2.5.0",
|
||||
"nostr-wasm": "^0.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-chartjs-2": "^5.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { AbstractRelay, verifyEvent } from "nostr-tools";
|
||||
import { AbstractRelay } from "nostr-tools";
|
||||
import { logger } from "../helpers/debug";
|
||||
import { validateRelayURL } from "../helpers/relay";
|
||||
import { offlineMode } from "../services/offline-mode";
|
||||
import Subject from "./subject";
|
||||
import verifyEventMethod from "../services/verify-event";
|
||||
|
||||
export default class RelayPool {
|
||||
relays = new Map<string, AbstractRelay>();
|
||||
@ -28,7 +29,7 @@ export default class RelayPool {
|
||||
url = validateRelayURL(url);
|
||||
const key = url.toString();
|
||||
if (!this.relays.has(key)) {
|
||||
const newRelay = new AbstractRelay(key, { verifyEvent });
|
||||
const newRelay = new AbstractRelay(key, { verifyEvent: verifyEventMethod });
|
||||
this.relays.set(key, newRelay);
|
||||
this.onRelayCreated.next(newRelay);
|
||||
}
|
||||
|
@ -4,10 +4,7 @@ import { logger } from "../helpers/debug";
|
||||
import { safeRelayUrl } from "../helpers/relay";
|
||||
import WasmRelay from "./wasm-relay";
|
||||
import MemoryRelay from "../classes/memory-relay";
|
||||
|
||||
function fakeVerify(event: NostrEvent): event is VerifiedEvent {
|
||||
return (event[verifiedSymbol] = true);
|
||||
}
|
||||
import { fakeVerifyEvent } from "./verify-event";
|
||||
|
||||
// save the local relay from query params to localStorage
|
||||
const params = new URLSearchParams(location.search);
|
||||
@ -22,7 +19,10 @@ if (paramRelay) {
|
||||
export const NOSTR_RELAY_TRAY_URL = "ws://localhost:4869/";
|
||||
export async function checkNostrRelayTray() {
|
||||
return new Promise((res) => {
|
||||
const test = new AbstractRelay(NOSTR_RELAY_TRAY_URL, { verifyEvent: fakeVerify });
|
||||
const test = new AbstractRelay(NOSTR_RELAY_TRAY_URL, {
|
||||
// presume events from the cache are already verified
|
||||
verifyEvent: fakeVerifyEvent,
|
||||
});
|
||||
test
|
||||
.connect()
|
||||
.then(() => {
|
||||
@ -54,14 +54,14 @@ async function createRelay() {
|
||||
} else if (localRelayURL.startsWith("nostr-idb://")) {
|
||||
return createInternalRelay();
|
||||
} else if (safeRelayUrl(localRelayURL)) {
|
||||
return new AbstractRelay(safeRelayUrl(localRelayURL)!, { verifyEvent: fakeVerify });
|
||||
return new AbstractRelay(safeRelayUrl(localRelayURL)!, { verifyEvent: fakeVerifyEvent });
|
||||
}
|
||||
} else if (window.satellite) {
|
||||
return new AbstractRelay(await window.satellite.getLocalRelay(), { verifyEvent: fakeVerify });
|
||||
return new AbstractRelay(await window.satellite.getLocalRelay(), { verifyEvent: fakeVerifyEvent });
|
||||
} else if (window.CACHE_RELAY_ENABLED) {
|
||||
const protocol = location.protocol === "https:" ? "wss:" : "ws:";
|
||||
return new AbstractRelay(new URL(protocol + location.host + "/local-relay").toString(), {
|
||||
verifyEvent: fakeVerify,
|
||||
verifyEvent: fakeVerifyEvent,
|
||||
});
|
||||
}
|
||||
return createInternalRelay();
|
||||
|
@ -11,6 +11,7 @@ import createDefer, { Deferred } from "../classes/deferred";
|
||||
import { truncatedId } from "../helpers/nostr/event";
|
||||
import { NostrConnectAccount } from "./account";
|
||||
import { safeRelayUrl } from "../helpers/relay";
|
||||
import { alwaysVerify } from "./verify-event";
|
||||
|
||||
export function isErrorResponse(response: any): response is NostrConnectErrorResponse {
|
||||
return !!response.error;
|
||||
@ -112,6 +113,7 @@ export class NostrConnectClient {
|
||||
private requests = new Map<string, Deferred<any>>();
|
||||
private auths = new Set<string>();
|
||||
async handleEvent(event: NostrEvent) {
|
||||
if (!alwaysVerify(event)) return;
|
||||
if (this.provider && event.pubkey !== this.provider) return;
|
||||
|
||||
const to = event.tags.find(isPTag)?.[1];
|
||||
|
@ -8,6 +8,7 @@ import { localRelay } from "./local-relay";
|
||||
import { MONITOR_STATS_KIND, SELF_REPORTED_KIND, getRelayURL } from "../helpers/nostr/relay-stats";
|
||||
import relayPoolService from "./relay-pool";
|
||||
import { Filter } from "nostr-tools";
|
||||
import { alwaysVerify } from "./verify-event";
|
||||
|
||||
const MONITOR_PUBKEY = "151c17c9d234320cf0f189af7b761f63419fd6c38c6041587a008b7682e4640f";
|
||||
const MONITOR_RELAY = "wss://history.nostr.watch";
|
||||
@ -24,6 +25,8 @@ class RelayStatsService {
|
||||
}
|
||||
|
||||
handleEvent(event: NostrEvent, cache = true) {
|
||||
if (!alwaysVerify(event)) return;
|
||||
|
||||
// ignore all events before NIP-66 start date
|
||||
if (event.created_at < 1704196800) return;
|
||||
|
||||
|
@ -12,6 +12,7 @@ import EventStore from "../classes/event-store";
|
||||
import Subject from "../classes/subject";
|
||||
import BatchKindLoader, { createCoordinate } from "../classes/batch-kind-loader";
|
||||
import relayPoolService from "./relay-pool";
|
||||
import { alwaysVerify } from "./verify-event";
|
||||
|
||||
export type RequestOptions = {
|
||||
/** Always request the event from the relays */
|
||||
@ -43,6 +44,7 @@ class ReplaceableEventsService {
|
||||
dbLog = this.log.extend("database");
|
||||
|
||||
handleEvent(event: NostrEvent, saveToCache = true) {
|
||||
if (!alwaysVerify(event)) return;
|
||||
const cord = getEventCoordinate(event);
|
||||
|
||||
const subject = this.subjects.get(cord);
|
||||
|
@ -10,6 +10,7 @@ export type AppSettingsV0 = {
|
||||
autoShowMedia: boolean;
|
||||
proxyUserMedia: boolean;
|
||||
showReactions: boolean;
|
||||
/** @deprecated */
|
||||
showSignatureVerification: boolean;
|
||||
|
||||
autoPayWithWebLN: boolean;
|
||||
@ -53,6 +54,7 @@ export const defaultSettings: AppSettings = {
|
||||
proxyUserMedia: false,
|
||||
loadOpenGraphData: true,
|
||||
showReactions: true,
|
||||
/** @deprecated */
|
||||
showSignatureVerification: false,
|
||||
noteDifficulty: null,
|
||||
|
||||
|
44
src/services/verify-event/index.ts
Normal file
44
src/services/verify-event/index.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { NostrEvent, VerifiedEvent, verifiedSymbol, verifyEvent } from "nostr-tools";
|
||||
import { logger } from "../../helpers/debug";
|
||||
|
||||
const localStorageKey = "verify-event-method";
|
||||
|
||||
const log = logger.extend("VerifyEvent");
|
||||
let selectedMethod = "default";
|
||||
let verifyEventMethod: typeof verifyEvent;
|
||||
let alwaysVerify: typeof verifyEvent;
|
||||
|
||||
export function fakeVerifyEvent(event: NostrEvent): event is VerifiedEvent {
|
||||
return (event[verifiedSymbol] = true);
|
||||
}
|
||||
|
||||
try {
|
||||
selectedMethod = localStorage.getItem(localStorageKey) ?? "default";
|
||||
|
||||
switch (selectedMethod) {
|
||||
case "wasm":
|
||||
if (!("WebAssembly" in window)) throw new Error("WebAssembly not supported");
|
||||
log("Loading WebAssembly module");
|
||||
verifyEventMethod = alwaysVerify = (await import("./wasm")).default;
|
||||
log("Loaded");
|
||||
break;
|
||||
case "none":
|
||||
log("Using fake verify event method");
|
||||
verifyEventMethod = fakeVerifyEvent;
|
||||
alwaysVerify = verifyEvent;
|
||||
break;
|
||||
case "default":
|
||||
default:
|
||||
log("Using nostr-tools default");
|
||||
verifyEventMethod = alwaysVerify = verifyEvent;
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize event verification method, falling back to default");
|
||||
console.log(error);
|
||||
|
||||
verifyEventMethod = alwaysVerify = verifyEvent;
|
||||
}
|
||||
|
||||
export { alwaysVerify, selectedMethod };
|
||||
export default verifyEventMethod;
|
7
src/services/verify-event/wasm.ts
Normal file
7
src/services/verify-event/wasm.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { setNostrWasm, verifyEvent } from "nostr-tools/wasm";
|
||||
import { initNostrWasm } from "nostr-wasm";
|
||||
|
||||
const wasm = await initNostrWasm();
|
||||
setNostrWasm(wasm);
|
||||
|
||||
export default verifyEvent;
|
3
src/types/nostr-tools-wasm.d.ts
vendored
Normal file
3
src/types/nostr-tools-wasm.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
declare module "nostr-tools/wasm" {
|
||||
export * from "nostr-tools/lib/types/wasm.d.ts";
|
||||
}
|
@ -40,7 +40,7 @@ export default function DisplaySettings() {
|
||||
<FormLabel htmlFor="theme" mb="0">
|
||||
Theme
|
||||
</FormLabel>
|
||||
<Select id="theme" {...register("theme")}>
|
||||
<Select id="theme" {...register("theme")} maxW="sm">
|
||||
<option value="default">Default</option>
|
||||
<option value="chakraui">ChakraUI</option>
|
||||
</Select>
|
||||
@ -49,7 +49,7 @@ export default function DisplaySettings() {
|
||||
<FormLabel htmlFor="colorMode" mb="0">
|
||||
Color Mode
|
||||
</FormLabel>
|
||||
<Select id="colorMode" {...register("colorMode")}>
|
||||
<Select id="colorMode" {...register("colorMode")} maxW="sm">
|
||||
<option value="system">System Default</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="dark">Dark</option>
|
||||
@ -67,7 +67,7 @@ export default function DisplaySettings() {
|
||||
<FormLabel htmlFor="maxPageWidth" mb="0">
|
||||
Max Page width
|
||||
</FormLabel>
|
||||
<Select id="maxPageWidth" {...register("maxPageWidth")}>
|
||||
<Select id="maxPageWidth" {...register("maxPageWidth")} maxW="sm">
|
||||
<option value="none">None</option>
|
||||
<option value="md">Medium (~768px)</option>
|
||||
<option value="lg">Large (~992px)</option>
|
||||
@ -123,7 +123,12 @@ export default function DisplaySettings() {
|
||||
<FormLabel htmlFor="muted-words" mb="0">
|
||||
Muted words
|
||||
</FormLabel>
|
||||
<Textarea id="muted-words" {...register("mutedWords")} placeholder="Broccoli, Spinach, Artichoke..." />
|
||||
<Textarea
|
||||
id="muted-words"
|
||||
{...register("mutedWords")}
|
||||
placeholder="Broccoli, Spinach, Artichoke..."
|
||||
maxW="2xl"
|
||||
/>
|
||||
<FormHelperText>
|
||||
<span>
|
||||
Comma separated list of words, phrases or hashtags you never want to see in notes. (case insensitive)
|
||||
|
@ -13,10 +13,53 @@ import {
|
||||
Input,
|
||||
Link,
|
||||
FormErrorMessage,
|
||||
Select,
|
||||
Button,
|
||||
Text,
|
||||
} from "@chakra-ui/react";
|
||||
import { useLocalStorage } from "react-use";
|
||||
|
||||
import { safeUrl } from "../../helpers/parse";
|
||||
import { AppSettings } from "../../services/settings/migrations";
|
||||
import { PerformanceIcon } from "../../components/icons";
|
||||
import { selectedMethod } from "../../services/verify-event";
|
||||
|
||||
function VerifyEventSettings() {
|
||||
const [verifyEventMethod, setVerifyEventMethod] = useLocalStorage<string>("verify-event-method", "default", {
|
||||
raw: true,
|
||||
});
|
||||
|
||||
console.log(selectedMethod, verifyEventMethod);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormControl>
|
||||
<FormLabel htmlFor="verifyEventMethod" mb="0">
|
||||
Verify event method
|
||||
</FormLabel>
|
||||
<Select value={verifyEventMethod} onChange={(e) => setVerifyEventMethod(e.target.value)} maxW="sm">
|
||||
<option value="default">Default</option>
|
||||
<option value="wasm">WebAssembly</option>
|
||||
<option value="none">None</option>
|
||||
</Select>
|
||||
<FormHelperText>Default: All events signatures are checked</FormHelperText>
|
||||
<FormHelperText>WebAssembly: Events signatures are checked in a separate thread</FormHelperText>
|
||||
<FormHelperText>None: Only Profiles, Follows, and replaceable event signatures are checked</FormHelperText>
|
||||
|
||||
{selectedMethod !== verifyEventMethod && (
|
||||
<>
|
||||
<Text color="blue.500" mt="2">
|
||||
NOTE: You must reload the app for this setting to take effect
|
||||
</Text>
|
||||
<Button colorScheme="primary" size="sm" onClick={() => location.reload()}>
|
||||
Reload App
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</FormControl>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function PerformanceSettings() {
|
||||
const { register, formState } = useFormContext<AppSettings>();
|
||||
@ -86,15 +129,6 @@ export default function PerformanceSettings() {
|
||||
</Flex>
|
||||
<FormHelperText>Enabled: Show reactions on notes</FormHelperText>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<Flex alignItems="center">
|
||||
<FormLabel htmlFor="showSignatureVerification" mb="0">
|
||||
Show signature verification
|
||||
</FormLabel>
|
||||
<Switch id="showSignatureVerification" {...register("showSignatureVerification")} />
|
||||
</Flex>
|
||||
<FormHelperText>Enabled: show signature verification on notes</FormHelperText>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<Flex alignItems="center">
|
||||
<FormLabel htmlFor="autoDecryptDMs" mb="0">
|
||||
@ -104,6 +138,7 @@ export default function PerformanceSettings() {
|
||||
</Flex>
|
||||
<FormHelperText>Enabled: automatically decrypt direct messages</FormHelperText>
|
||||
</FormControl>
|
||||
<VerifyEventSettings />
|
||||
</Flex>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
|
@ -16,12 +16,13 @@ import {
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import { EventTemplate, NostrEvent, UnsignedEvent, getEventHash, verifyEvent } from "nostr-tools";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import VerticalPageLayout from "../../../components/vertical-page-layout";
|
||||
import BackButton from "../../../components/router/back-button";
|
||||
import Play from "../../../components/icons/play";
|
||||
import EventEditor from "./event-editor";
|
||||
import { EventTemplate, NostrEvent, UnsignedEvent, getEventHash, verifyEvent } from "nostr-tools";
|
||||
import dayjs from "dayjs";
|
||||
import { processEvent } from "./process";
|
||||
import { WritingIcon } from "../../../components/icons";
|
||||
import { useSigningContext } from "../../../providers/global/signing-provider";
|
||||
|
10
yarn.lock
10
yarn.lock
@ -5473,10 +5473,10 @@ nostr-tools@^2.3.2:
|
||||
optionalDependencies:
|
||||
nostr-wasm v0.1.0
|
||||
|
||||
nostr-tools@^2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-2.4.0.tgz#bc2140a95ce0be8d4900bd852f652d811562753e"
|
||||
integrity sha512-xQC7XdGeh0gLyprcKhvx5lwr7OQ+ZOiQ9C6GpzlVAj+EBv+AiN8kySb57t3uJoG1HK15oT9jf++MmQLwhp1xNQ==
|
||||
nostr-tools@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-2.5.0.tgz#083c8a22eb88c65f30d88a25e200ea2274348663"
|
||||
integrity sha512-G02O3JYNCfhx9NDjd3NOCw/5ck8PX5hiOIhHKpsXyu49ZtZbxGH3OLP9tf0fpUZ+EVWdjIYFR689sV0i7+TOng==
|
||||
dependencies:
|
||||
"@noble/ciphers" "^0.5.1"
|
||||
"@noble/curves" "1.2.0"
|
||||
@ -5487,7 +5487,7 @@ nostr-tools@^2.4.0:
|
||||
optionalDependencies:
|
||||
nostr-wasm v0.1.0
|
||||
|
||||
nostr-wasm@v0.1.0:
|
||||
nostr-wasm@^0.1.0, nostr-wasm@v0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/nostr-wasm/-/nostr-wasm-0.1.0.tgz#17af486745feb2b7dd29503fdd81613a24058d94"
|
||||
integrity sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==
|
||||
|
Loading…
x
Reference in New Issue
Block a user