NDK makes multiple optimizations possible to create a performant client.
Signature validation is typically the most computationally expensive operation in a nostr client. Thus, NDK attempts to reduce the number of signature verifications that need to be done as much as possible.
In order to create performant clients, it’s very useful to offload this computation to a service worker, to avoid blocking the main thread.
// Using with vite
const sigWorker = import.meta.env.DEV ?
new Worker(new URL('@nostr-dev-kit/ndk/workers/sig-verification?worker', import.meta.url), { type: 'module' }) : new NDKSigVerificationWorker();
const ndk = new NDK();
ndk.signatureVerificationWorker = worker
Since signature verification will thus be done asynchronously, it’s important to listen for invalid signatures and handle them appropriately; you should always warn your users when they are receiving invalid signatures from a relay and/or immediately disconnect from an evil relay.
ndk.on("event:invalid-sig", (event) => {
const { relay } = event;
console.error("Invalid signature coming from relay", relay.url);
});
Another parameter we can tweak is how many signatures we verify. By default, NDK will verify every signature, but you can change this by setting a per-relay verification rate.
ndk.initialValidationRatio = 0.5; // Only verify 50% of the signatures for each relay
ndk.lowestValidationRatio = 0.01; // Never verify less than 1% of the signatures for each relay
NDK will then begin verifying signatures from each relay and, as signatures as verified, it will reduce the verification rate for that relay.
If you need further control on how the verification rate is adjusted, you can provide a validation ratio function. This function will be called periodically and the returning value will be used to adjust the verification rate.
ndk.validationRatioFn = (relay: NDKRelay, validatedEvents: number, nonValidatedEvents: number): number => {
// write your own custom function here
return validatedEvents / (validatedEvents + nonValidatedEvents);
}