feat(block2): relay pool — publish to many relays, discover deduped, tolerate failures

Relay-strategy hardening (network-trust §3): don't hang on one relay.
- NostrChannel interface: what the transports need (sign, publish, subscribe,
  reqOnce). Implemented by NostrConnection (single relay) and the new RelayPool.
- RelayPool: connects to all configured relays (skips unreachable ones, throws
  only when none are reachable), publishes to every live relay (accepted if any
  accepts), merges discovery deduped by event id, and survives a relay dropping.
- Transports now depend on NostrChannel, not a concrete connection.
- SocialService.openSession takes ALL configured relays (was relays.first);
  createOffersCubit passes the whole list.

commons_core 63 tests green (3 new pool tests over two in-process relays);
app_seeds social/market 18 green.
This commit is contained in:
vjrj 2026-07-10 10:35:39 +02:00
parent 492bc62025
commit 2a6b4b0947
10 changed files with 246 additions and 24 deletions

View file

@ -172,9 +172,9 @@ Future<OffersCubit> createOffersCubit(
final relays = await settings.relayUrls();
if (relays.isEmpty) return OffersCubit(null);
try {
final session = await social.openSession(relays.first);
final session = await social.openSession(relays);
return OffersCubit(session.offers, onDispose: session.close);
} catch (_) {
return OffersCubit(null); // offline / relay unreachable
return OffersCubit(null); // offline / no relay reachable
}
}