mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-03-27 02:02:48 +01:00
A race-condition exists between setting of the (unprotected) status and the callback which sets the status upon receiving an OK. The message is sent which can receive an OK in separate goroutine (setting status) prior to the status being set to 'sent.' The OK can be received prior to the status being set. This fix also sets the status to PublishStatusFailed if the WriteJSON call fails.
go-nostr
A set of useful things for Nostr Protocol implementations.
Install go-nostr:
go get github.com/nbd-wtf/go-nostr
Generating a key
package main
import (
"fmt"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip19"
)
func main() {
sk := nostr.GeneratePrivateKey()
pk, _ := nostr.GetPublicKey(sk)
nsec, _ := nip19.EncodePrivateKey(sk)
npub, _ := nip19.EncodePublicKey(pk)
fmt.Println("sk:", sk)
fmt.Println("pk:", pk)
fmt.Println(nsec)
fmt.Println(npub)
}
Subscribing to a single relay
relay, err := nostr.RelayConnect(context.Background(), "wss://nostr.zebedee.cloud")
if err != nil {
panic(err)
}
npub := "npub1422a7ws4yul24p0pf7cacn7cghqkutdnm35z075vy68ggqpqjcyswn8ekc"
var filters nostr.Filters
if _, v, err := nip19.Decode(npub); err == nil {
pub := v.(string)
filters = []nostr.Filter{{
Kinds: []int{1},
Authors: []string{pub},
Limit: 1,
}}
} else {
panic(err)
}
ctx, cancel := context.WithCancel(context.Background())
sub := relay.Subscribe(ctx, filters)
go func() {
<-sub.EndOfStoredEvents
// handle end of stored events (EOSE, see NIP-15)
}()
for ev := range sub.Events {
// handle returned event.
// channel will stay open until the ctx is cancelled (in this case, by calling cancel())
fmt.Println(ev.ID)
}
Publishing to two relays
sk := nostr.GeneratePrivateKey()
pub, _ := nostr.GetPublicKey(sk)
ev := nostr.Event{
PubKey: pub,
CreatedAt: nostr.Now(),
Kind: 1,
Tags: nil,
Content: "Hello World!",
}
// calling Sign sets the event ID field and the event Sig field
ev.Sign(sk)
// publish the event to two relays
for _, url := range []string{"wss://nostr.zebedee.cloud", "wss://nostr-pub.wellorder.net"} {
relay, e := nostr.RelayConnect(context.Background(), url)
if e != nil {
fmt.Println(e)
continue
}
fmt.Println("published to ", url, relay.Publish(context.Background(), ev))
}
Authenticating with NIP-42
For this section, the user needs access to a relay implementing NIP-42. E.g., https://github.com/fiatjaf/relayer with a relay implementing the relayer.Auther interface.
func main() {
url := "ws://localhost:7447"
// Once the connection is initiated the server will send "AUTH" with the challenge string.
relay, err := nostr.RelayConnect(context.Background(), url)
if err != nil {
panic(err)
}
// Initialize test user.
sk := nostr.GeneratePrivateKey()
pub, _ := nostr.GetPublicKey(sk)
npub, _ := nip19.EncodePublicKey(pub)
// Relay.Challenges channel will receive the "AUTH" command.
challenge := <-relay.Challenges
// Create the auth event to send back.
// The user will be authenticated as pub.
event := nip42.CreateUnsignedAuthEvent(challenge, pub, url)
event.Sign(sk)
// Set-up context with 3 second time out.
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
// Send the event by calling relay.Auth.
// Returned status is either success, fail, or sent (if no reply given in the 3 second timeout).
auth_status := relay.Auth(ctx, event)
fmt.Printf("authenticated as %s: %s\n", npub, auth_status)
}
Example script
go run example/example.go
Languages
C
83.3%
Go
15.9%
Assembly
0.7%
Just
0.1%