tinyrelayfile
tinyrelay/internal/syncprotocol/negentropy.go/

internal/syncprotocol/negentropy.go raw
1package syncprotocol
2
3import (
4 "fmt"
5
6 "fiatjaf.com/nostr"
7 "fiatjaf.com/nostr/nip77/negentropy"
8 "fiatjaf.com/nostr/nip77/negentropy/storage/vector"
9)
10
11// Item identifies one event in a reconciliation index.
12type Item struct {
13 ID string
14 Timestamp int64
15}
16
17// Result contains one negentropy response and the IDs discovered by the
18// initiator when reconciliation completes.
19type Result struct {
20 Response string
21 Have []string
22 Need []string
23 Done bool
24}
25
26// Session holds one stateful negentropy exchange. A session is used by one
27// initiator or responder for the lifetime of that exchange.
28type Session struct {
29 neg *negentropy.Negentropy
30 initiator bool
31}
32
33// NewSession builds a sealed negentropy index from the relay's event IDs and
34// timestamps. The initiator flag selects the side that starts the exchange
35// and receives Have and Need IDs.
36func NewSession(items []Item, initiator bool) (*Session, error) {
37 vec := vector.New()
38 for _, item := range items {
39 id, err := nostr.IDFromHex(item.ID)
40 if err != nil {
41 return nil, fmt.Errorf("invalid sync item id: %w", err)
42 }
43 vec.Insert(nostr.Timestamp(item.Timestamp), id)
44 }
45 vec.Seal()
46 return &Session{neg: negentropy.New(vec, 60_000, initiator, initiator), initiator: initiator}, nil
47}
48
49// Start begins an initiator session and returns its protocol message.
50func (s *Session) Start() (string, error) {
51 if !s.initiator {
52 return "", fmt.Errorf("only the initiator can start a negentropy session")
53 }
54 return s.neg.Start(), nil
55}
56
57// Reconcile consumes one peer message and returns the next response. An
58// initiator receives IDs present on each side once the exchange is complete.
59func (s *Session) Reconcile(message string) (Result, error) {
60 if message == "" {
61 return Result{}, fmt.Errorf("empty negentropy message")
62 }
63 response, err := s.neg.Reconcile(message)
64 if err != nil {
65 return Result{}, fmt.Errorf("reconcile negentropy message: %w", err)
66 }
67 result := Result{Response: response, Done: s.initiator && response == ""}
68 if s.initiator {
69 result.Have = drainIDs(s.neg.Haves)
70 result.Need = drainIDs(s.neg.HaveNots)
71 }
72 return result, nil
73}
74
75func drainIDs(ids <-chan nostr.ID) []string {
76 if ids == nil {
77 return nil
78 }
79 values := make([]string, 0)
80 for id := range ids {
81 values = append(values, id.Hex())
82 }
83 return values
84}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs