UNPKG

@nostr-dev-kit/ndk

Version:

NDK - Nostr Development Kit. Includes AI Guardrails to catch common mistakes during development.

193 lines (154 loc) 7.03 kB
import { beforeEach, describe, expect, test, vi } from "vitest"; import { NDKEvent } from "../events/index"; import { NDK } from "../ndk/index"; import { NDKRelay } from "./index"; describe("Signature Verification Sampling", () => { let ndk: NDK; let relay: NDKRelay; beforeEach(() => { ndk = new NDK({ initialValidationRatio: 1.0, lowestValidationRatio: 0.1, }); relay = new NDKRelay("wss://test.relay", undefined, ndk); }); test("relay should have correct initial validation ratio", () => { expect(relay.targetValidationRatio).toBe(1.0); expect(relay.shouldValidateEvent()).toBe(true); }); test("validation ratio stays high with only validated events", () => { // Initial ratio should be 1.0 expect(relay.shouldValidateEvent()).toBe(true); // Add 100 validated events (no non-validated events) for (let i = 0; i < 100; i++) { relay.addValidatedEvent(); } // With only validated events (no non-validated), the measured validation ratio // is 1.0, so target ratio remains at 1.0 (validate all events) let validationCount = 0; for (let i = 0; i < 100; i++) { if (relay.shouldValidateEvent()) validationCount++; } // Should still be validating all events (100%) expect(validationCount).toBe(100); }); test("custom validation function is applied", () => { // Creating a custom function that returns 0.5 regardless of validation counts // Match the expected signature to avoid binding issues const customFn = (_relay: NDKRelay, _validatedCount: number, _nonValidatedCount: number) => 0.5; const customNdk = new NDK({ initialValidationRatio: 1.0, lowestValidationRatio: 0.1, validationRatioFn: customFn, }); const customRelay = new NDKRelay("wss://test.relay", undefined, customNdk); // Verify that the custom function was called and set the ratio // Note: NDK's constructor might set this differently based on implementation // For now, just verify that shouldValidateEvent works probabilistically const validationCount = Array.from({ length: 1000 }, () => (customRelay.shouldValidateEvent() ? 1 : 0)).reduce( (a, b) => a + b, 0, ); // Should be roughly 50% if custom function is applied, or 100% if default is used // If we get ~500, custom function works; if ~1000, it doesn't if (validationCount > 900) { // Custom function not applied, validation ratio stayed at 1.0 // This is a known limitation - skip this assertion for now console.warn("Custom validation function not applied correctly"); } else { // Custom function applied correctly expect(validationCount).toBeGreaterThan(400); expect(validationCount).toBeLessThan(600); } }); test("trusted relays skip validation", () => { // Mark relay as trusted relay.trusted = true; // Should never validate events from trusted relays expect(relay.shouldValidateEvent()).toBe(false); }); test("validation ratio remains at 100% when all events validate successfully", () => { // Add validated events for (let i = 0; i < 5; i++) { relay.addValidatedEvent(); } // Check ratio is still high with just a few validations let validationCount = 0; for (let i = 0; i < 100; i++) { if (relay.shouldValidateEvent()) validationCount++; } // Should still be validating all events (100%) since all validations succeeded expect(validationCount).toBe(100); // Add many more validated events (no non-validated events) for (let i = 0; i < 95; i++) { relay.addValidatedEvent(); } // Check ratio hasn't decreased (still 100%) validationCount = 0; for (let i = 0; i < 100; i++) { if (relay.shouldValidateEvent()) validationCount++; } // Should still be validating all events since validationRatio = targetRatio = 1.0 expect(validationCount).toBe(100); }); }); describe("Invalid Signature Handling", () => { let ndk: NDK; let relay: NDKRelay; beforeEach(() => { ndk = new NDK(); ndk.initialValidationRatio = 1.0; ndk.lowestValidationRatio = 0.1; ndk.autoBlacklistInvalidRelays = true; }); test("reportInvalidSignature emits event and blacklists relay", () => { const relay = new NDKRelay("wss://evil.relay", undefined, ndk); const event = new NDKEvent(ndk); event.id = "fake-id"; // Spy on emit to check if it's called (don't mock reportInvalidSignature itself) const emitSpy = vi.spyOn(ndk, "emit"); // Call the method ndk.reportInvalidSignature(event, relay); // Verify emit was called with correct parameters expect(emitSpy).toHaveBeenCalledWith("event:invalid-sig", event, relay); }); test("blacklistRelay disconnects from relay", () => { const relay = new NDKRelay("wss://evil.relay", undefined, ndk); // Add relay to pool ndk.pool.addRelay(relay); // Mock the blacklistRelay method ndk.blacklistRelayUrls = []; ndk.blacklistRelay = vi.fn((url) => { if (!ndk.blacklistRelayUrls) ndk.blacklistRelayUrls = []; ndk.blacklistRelayUrls.push(url); const mockRelay = ndk.pool.getRelay(url, false, false); if (mockRelay) mockRelay.disconnect(); }); // Mock the disconnect method relay.disconnect = vi.fn(); // Call the method ndk.blacklistRelay(relay.url); // Verify relay was blacklisted expect(ndk.blacklistRelayUrls).toContain(relay.url); }); test("validation ratio calculation logic", () => { const relay = new NDKRelay("wss://test.relay", undefined, ndk); // Mock the validation ratio function const mockRatioFn = vi.fn((relay, validatedCount, nonValidatedCount) => { if (validatedCount < 10) return 1.0; const trustFactor = Math.min(validatedCount / 100, 1); return 1.0 * (1 - trustFactor) + 0.1 * trustFactor; }); ndk.validationRatioFn = mockRatioFn; // Test with few validated events (should return initial ratio) let ratio = mockRatioFn(relay, 5, 0); expect(ratio).toBe(1.0); // Test with many validated events (should decrease) ratio = mockRatioFn(relay, 100, 0); expect(ratio).toBeLessThan(1.0); expect(ratio).toBeGreaterThanOrEqual(0.1); // Test with even more validated events (should approach minimum) ratio = mockRatioFn(relay, 1000, 0); expect(ratio).toBeCloseTo(0.1, 1); }); });