Create RoboPool

This commit is contained in:
koalasat 2024-10-18 11:07:53 +02:00
parent 463608f9b2
commit 3edd9280a5
No known key found for this signature in database
GPG Key ID: 2F7F61C6146AB157
3 changed files with 125 additions and 42 deletions

View File

@ -14,6 +14,7 @@ import { coordinatorDefaultValues } from './Coordinator.model';
import { updateExchangeInfo } from './Exchange.model';
import eventToPublicOrder from '../utils/nostr';
import { SubCloser } from 'nostr-tools/lib/types/pool';
import RoboPool from '../services/RoboPool';
type FederationHooks = 'onFederationUpdate';
@ -60,11 +61,7 @@ export class Federation {
if (tesnetHost) settings.network = 'testnet';
this.connection = null;
const relays = [
'ws://4t4jxmivv6uqej6xzx2jx3fxh75gtt65v3szjoqmc4ugdlhipzdat6yd.onion/nostr',
// 'ws://ngdk7ocdzmz5kzsysa3om6du7ycj2evxp2f2olfkyq37htx3gllwp2yd.onion/nostr'
];
this.relayPool.trustedRelayURLs = new Set<string>(relays);
this.roboPool = new RoboPool(settings, origin);
}
public coordinators: Record<string, Coordinator>;
@ -75,40 +72,27 @@ export class Federation {
public hooks: Record<FederationHooks, Array<() => void>>;
public relayPool: SimplePool = new SimplePool();
public relaySubscriptions: SubCloser[] = [];
public roboPool: RoboPool;
setConnection = (settings: Settings): void => {
this.connection = settings.connection;
if (this.connection === 'nostr') {
this.connectNostr(settings);
this.roboPool.connect();
this.loadBookNostr();
} else {
this.relayPool.close(Array.from(this.relayPool.trustedRelayURLs));
this.roboPool.close();
this.loadBook();
}
};
connectNostr = (settings: Settings): void => {
loadBookNostr = (): void => {
this.loading = true;
this.book = {};
this.exchange.loadingCache = this.relayPool.trustedRelayURLs.size;
this.exchange.loadingCache = this.roboPool.relays.length;
const authors = Object.values(defaultFederation)
.map((f) => f.nostrHexPubkey)
.filter((item) => item !== undefined);
const sub = this.relayPool.subscribeMany(
Array.from(this.relayPool.trustedRelayURLs),
[
{
authors,
kinds: [38383],
'#n': [settings.network],
},
],
{
this.roboPool.subscribeBook({
onevent: (event) => {
const { dTag, publicOrder } = eventToPublicOrder(event);
if (publicOrder) {
@ -123,9 +107,7 @@ export class Federation {
this.updateExchange();
this.triggerHook('onFederationUpdate');
},
},
);
this.relaySubscriptions.push(sub);
});
};
addCoordinator = (

View File

@ -0,0 +1,101 @@
import { Event } from 'nostr-tools';
import { Settings } from '../../models';
import defaultFederation from '../../../static/federation.json';
import { Origins } from '../../models/Coordinator.model';
interface RoboPoolEvents {
onevent: (event: Event) => void;
oneose: () => void;
}
class RoboPool {
constructor(settings: Settings, origin: string) {
this.network = settings.network ?? 'mainnet';
this.relays = Object.values(defaultFederation)
.map((coord) => {
const url = coord[this.network][settings.selfhostedClient ? 'onion' : origin];
if (!url) return;
return `ws://${url.replace(/^https?:\/\//, '')}/nostr`;
})
.filter((item) => item !== undefined);
}
public relays: string[];
public network: string;
public webSockets: WebSocket[] = [];
private messageHandlers: Array<(url: string, event: MessageEvent) => void> = [];
connect = () => {
this.relays.forEach((url) => {
if (this.webSockets.find((w: WebSocket) => w.url === url)) return;
let ws: WebSocket;
const connect = () => {
ws = new WebSocket(url);
// Add event listeners for the WebSocket
ws.onopen = () => {
console.log(`Connected to ${url}`);
};
ws.onmessage = (event) => {
this.messageHandlers.forEach((handler) => handler(url, event));
};
ws.onerror = (error) => {
console.error(`WebSocket error on ${url}:`, error);
};
ws.onclose = () => {
console.log(`Disconnected from ${url}. Attempting to reconnect...`);
setTimeout(connect, 1000); // Reconnect after 1 second
};
};
connect();
this.webSockets.push(ws);
});
};
close = () => {
this.webSockets.forEach((ws) => ws.close());
};
sendMessage = (message: string) => {
const send = (index: number, message: string) => {
const ws = this.webSockets[index];
if (ws.readyState === WebSocket.OPEN) {
ws.send(message);
} else if (ws.readyState === WebSocket.CONNECTING) {
setTimeout(send, 500, index, message);
}
};
this.webSockets.forEach((_ws, index) => send(index, message));
};
subscribeBook = (events: RoboPoolEvents) => {
const authors = Object.values(defaultFederation)
.map((f) => f.nostrHexPubkey)
.filter((item) => item !== undefined);
const request = ['REQ', 'subscribeBook', { authors, kinds: [38383], '#n': [this.network] }];
this.messageHandlers.push((_url: string, messageEvent: MessageEvent) => {
const jsonMessage = JSON.parse(messageEvent.data);
if (jsonMessage[0] === 'EVENT') {
events.onevent(jsonMessage[2]);
} else if (jsonMessage[0] === 'EOSE') {
events.oneose();
}
});
this.sendMessage(JSON.stringify(request));
};
}
export default RoboPool;

View File

@ -32,7 +32,7 @@ class RoboGenerator {
setTimeout(() => {
this.waitingForLibrary = false;
}, 2500);
}, 3000);
}
public generate: (hash: string, size: 'small' | 'large') => Promise<string> = async (