go-nostr/relaypool.go

162 lines
3.3 KiB
Go
Raw Permalink Normal View History

2022-01-02 08:44:18 -03:00
package nostr
2021-01-31 11:05:09 -03:00
import (
2021-02-20 17:44:05 -03:00
"crypto/rand"
"encoding/hex"
2021-01-31 11:05:09 -03:00
"errors"
2021-02-07 07:56:55 -03:00
"fmt"
2021-01-31 11:05:09 -03:00
s "github.com/SaveTheRbtz/generic-sync-map-go"
2021-01-31 11:05:09 -03:00
)
2022-01-02 08:44:18 -03:00
type PublishStatus struct {
Relay string
Status Status
2022-01-02 08:44:18 -03:00
}
2021-01-31 11:05:09 -03:00
type RelayPool struct {
SecretKey *string
Policies s.MapOf[string, RelayPoolPolicy]
Relays s.MapOf[string, *Relay]
subscriptions s.MapOf[string, Filters]
eventStreams s.MapOf[string, chan EventMessage]
2021-01-31 11:05:09 -03:00
Notices chan *NoticeMessage
}
type RelayPoolPolicy interface {
ShouldRead(Filters) bool
ShouldWrite(*Event) bool
2021-01-31 11:05:09 -03:00
}
type SimplePolicy struct {
Read bool
Write bool
}
func (s SimplePolicy) ShouldRead(_ Filters) bool {
return s.Read
}
func (s SimplePolicy) ShouldWrite(_ *Event) bool {
return s.Write
}
2021-01-31 11:05:09 -03:00
type NoticeMessage struct {
Message string
Relay string
}
// New creates a new RelayPool with no relays in it
2022-01-02 08:44:18 -03:00
func NewRelayPool() *RelayPool {
2021-01-31 11:05:09 -03:00
return &RelayPool{
Policies: s.MapOf[string, RelayPoolPolicy]{},
Relays: s.MapOf[string, *Relay]{},
2021-01-31 11:05:09 -03:00
Notices: make(chan *NoticeMessage),
}
}
// Add adds a new relay to the pool, if policy is nil, it will be a simple
// read+write policy.
func (r *RelayPool) Add(url string, policy RelayPoolPolicy) error {
2021-01-31 11:05:09 -03:00
if policy == nil {
policy = SimplePolicy{Read: true, Write: true}
2021-01-31 11:05:09 -03:00
}
relay := NewRelay(url)
r.Policies.Store(relay.URL, policy)
r.Relays.Store(relay.URL, relay)
2021-01-31 11:05:09 -03:00
r.subscriptions.Range(func(id string, filters Filters) bool {
sub := relay.subscribe(id, filters)
eventStream, _ := r.eventStreams.Load(id)
go func(sub *Subscription) {
for evt := range sub.Events {
eventStream <- EventMessage{Relay: relay.URL, Event: evt}
}
}(sub)
2021-01-31 11:05:09 -03:00
return true
})
2021-02-20 17:44:05 -03:00
2021-02-07 07:56:55 -03:00
return nil
2021-01-31 11:05:09 -03:00
}
// Remove removes a relay from the pool.
func (r *RelayPool) Remove(url string) {
2022-01-02 08:44:18 -03:00
nm := NormalizeURL(url)
2021-02-20 17:44:05 -03:00
r.Relays.Delete(nm)
r.Policies.Delete(nm)
if relay, ok := r.Relays.Load(nm); ok {
relay.Close()
2021-01-31 11:05:09 -03:00
}
}
func (r *RelayPool) Sub(filters Filters) (string, chan EventMessage) {
2021-02-20 17:44:05 -03:00
random := make([]byte, 7)
rand.Read(random)
id := hex.EncodeToString(random)
2021-01-31 11:05:09 -03:00
r.subscriptions.Store(id, filters)
eventStream := make(chan EventMessage)
r.eventStreams.Store(id, eventStream)
r.Relays.Range(func(_ string, relay *Relay) bool {
sub := relay.subscribe(id, filters)
go func(sub *Subscription) {
for evt := range sub.Events {
eventStream <- EventMessage{Relay: relay.URL, Event: evt}
}
}(sub)
return true
})
2021-01-31 11:05:09 -03:00
return id, eventStream
2021-01-31 11:05:09 -03:00
}
2022-01-02 08:44:18 -03:00
func (r *RelayPool) PublishEvent(evt *Event) (*Event, chan PublishStatus, error) {
status := make(chan PublishStatus, 1)
2021-01-31 11:05:09 -03:00
if r.SecretKey == nil && (evt.PubKey == "" || evt.Sig == "") {
2021-02-20 17:44:05 -03:00
return nil, status, errors.New("PublishEvent needs either a signed event to publish or to have been configured with a .SecretKey.")
2021-01-31 11:05:09 -03:00
}
if evt.PubKey == "" {
sk, err := GetPublicKey(*r.SecretKey)
if err != nil {
return nil, status, fmt.Errorf("The pool's global SecretKey is invalid: %w", err)
}
evt.PubKey = sk
}
2021-01-31 11:05:09 -03:00
if evt.Sig == "" {
2021-02-07 07:56:55 -03:00
err := evt.Sign(*r.SecretKey)
if err != nil {
2021-02-20 17:44:05 -03:00
return nil, status, fmt.Errorf("Error signing event: %w", err)
2021-02-07 07:56:55 -03:00
}
2021-01-31 11:05:09 -03:00
}
r.Relays.Range(func(url string, relay *Relay) bool {
if r, ok := r.Policies.Load(url); !ok || !r.ShouldWrite(evt) {
return true
}
go func(relay *Relay) {
for resultStatus := range relay.Publish(*evt) {
status <- PublishStatus{relay.URL, resultStatus}
}
}(relay)
return true
})
2021-01-31 11:05:09 -03:00
2021-02-20 17:44:05 -03:00
return evt, status, nil
2021-01-31 11:05:09 -03:00
}