more conversions.

This commit is contained in:
fiatjaf
2025-04-15 00:00:03 -03:00
parent f9e4a5efa3
commit 376834cbf9
117 changed files with 450 additions and 1019 deletions

View File

@@ -13,7 +13,7 @@ import (
var defaultConnectionOptions = &ws.DialOptions{
CompressionMode: ws.CompressionContextTakeover,
HTTPHeader: http.Header{
textproto.CanonicalMIMEHeaderKey("User-Agent"): {"fiatjaf.com/nostrlib"},
textproto.CanonicalMIMEHeaderKey("User-Agent"): {"fiatjaf.com/nostr"},
},
}

View File

@@ -9,8 +9,8 @@ import (
"time"
jsoniter "github.com/json-iterator/go"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip19"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip19"
)
func main() {

View File

@@ -18,7 +18,7 @@ type Keyer interface {
// User is an entity that has a public key (although they can't sign anything).
type User interface {
// GetPublicKey returns the public key associated with this user.
GetPublicKey(ctx context.Context) (string, error)
GetPublicKey(ctx context.Context) (PubKey, error)
}
// Signer is a User that can also sign events.
@@ -35,9 +35,9 @@ type Signer interface {
type Cipher interface {
// Encrypt encrypts a plaintext message for a recipient.
// Returns the encrypted message as a base64-encoded string.
Encrypt(ctx context.Context, plaintext string, recipientPublicKey string) (base64ciphertext string, err error)
Encrypt(ctx context.Context, plaintext string, recipient PubKey) (base64ciphertext string, err error)
// Decrypt decrypts a base64-encoded ciphertext from a sender.
// Returns the decrypted plaintext.
Decrypt(ctx context.Context, base64ciphertext string, senderPublicKey string) (plaintext string, err error)
Decrypt(ctx context.Context, base64ciphertext string, sender PubKey) (plaintext string, err error)
}

View File

@@ -5,8 +5,8 @@ import (
"errors"
"time"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip46"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip46"
)
var _ nostr.Keyer = (*BunkerSigner)(nil)

View File

@@ -4,9 +4,9 @@ import (
"context"
"fmt"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip44"
"fiatjaf.com/nostrlib/nip49"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip44"
"fiatjaf.com/nostr/nip49"
)
var _ nostr.Keyer = (*EncryptedKeySigner)(nil)

View File

@@ -7,11 +7,11 @@ import (
"strings"
"time"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip05"
"fiatjaf.com/nostrlib/nip19"
"fiatjaf.com/nostrlib/nip46"
"fiatjaf.com/nostrlib/nip49"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip05"
"fiatjaf.com/nostr/nip19"
"fiatjaf.com/nostr/nip46"
"fiatjaf.com/nostr/nip49"
"github.com/puzpuzpuz/xsync/v3"
)

View File

@@ -3,7 +3,7 @@ package keyer
import (
"context"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
var _ nostr.Keyer = (*ManualSigner)(nil)

View File

@@ -3,8 +3,8 @@ package keyer
import (
"context"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip44"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip44"
"github.com/puzpuzpuz/xsync/v3"
)

View File

@@ -4,7 +4,7 @@ import (
"context"
"fmt"
"github.com/nbd-wtf/go-nostr"
"fiatjaf.com/nostr"
)
var (

View File

@@ -10,27 +10,20 @@ import (
"fmt"
"strings"
"fiatjaf.com/nostr"
"github.com/btcsuite/btcd/btcec/v2"
)
// ComputeSharedSecret returns a shared secret key used to encrypt messages.
// The private and public keys should be hex encoded.
// Uses the Diffie-Hellman key exchange (ECDH) (RFC 4753).
func ComputeSharedSecret(pub string, sk string) (sharedSecret []byte, err error) {
privKeyBytes, err := hex.DecodeString(sk)
if err != nil {
return nil, fmt.Errorf("error decoding sender private key: %w", err)
}
privKey, _ := btcec.PrivKeyFromBytes(privKeyBytes)
func ComputeSharedSecret(pub nostr.PubKey, sk [32]byte) (sharedSecret []byte, err error) {
privKey, _ := btcec.PrivKeyFromBytes(sk[:])
// adding 02 to signal that this is a compressed public key (33 bytes)
pubKeyBytes, err := hex.DecodeString("02" + pub)
pubKey, err := btcec.ParsePubKey(append([]byte{2}, pub[:]...))
if err != nil {
return nil, fmt.Errorf("error decoding hex string of receiver public key '%s': %w", "02"+pub, err)
}
pubKey, err := btcec.ParsePubKey(pubKeyBytes)
if err != nil {
return nil, fmt.Errorf("error parsing receiver public key '%s': %w", "02"+pub, err)
return nil, fmt.Errorf("error parsing receiver public key '%s': %w", "02"+hex.EncodeToString(pub[:]), err)
}
return btcec.GenerateSharedSecret(privKey, pubKey), nil

View File

@@ -1,10 +1,11 @@
package nip04
import (
"encoding/hex"
"strings"
"testing"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
"github.com/stretchr/testify/require"
)
@@ -13,10 +14,8 @@ func TestSharedKeysAreTheSame(t *testing.T) {
sk1 := nostr.GeneratePrivateKey()
sk2 := nostr.GeneratePrivateKey()
pk1, err := nostr.GetPublicKey(sk1)
require.NoError(t, err)
pk2, err := nostr.GetPublicKey(sk2)
require.NoError(t, err)
pk1 := nostr.GetPublicKey(sk1)
pk2 := nostr.GetPublicKey(sk2)
ss1, err := ComputeSharedSecret(pk2, sk1)
require.NoError(t, err)
@@ -57,10 +56,10 @@ func TestEncryptionAndDecryptionWithMultipleLengths(t *testing.T) {
}
func TestNostrToolsCompatibility(t *testing.T) {
sk1 := "92996316beebf94171065a714cbf164d1f56d7ad9b35b329d9fc97535bf25352"
sk2 := "591c0c249adfb9346f8d37dfeed65725e2eea1d7a6e99fa503342f367138de84"
pk2, _ := nostr.GetPublicKey(sk2)
shared, _ := ComputeSharedSecret(pk2, sk1)
sk1, _ := hex.DecodeString("92996316beebf94171065a714cbf164d1f56d7ad9b35b329d9fc97535bf25352")
sk2, _ := hex.DecodeString("591c0c249adfb9346f8d37dfeed65725e2eea1d7a6e99fa503342f367138de84")
pk2 := nostr.GetPublicKey([32]byte(sk2))
shared, _ := ComputeSharedSecret(pk2, [32]byte(sk1))
ciphertext := "A+fRnU4aXS4kbTLfowqAww==?iv=QFYUrl5or/n/qamY79ze0A=="
plaintext, _ := Decrypt(ciphertext, shared)
require.Equal(t, "hello", plaintext, "invalid decryption of nostr-tools payload")

View File

@@ -7,8 +7,8 @@ import (
"regexp"
"strings"
"fiatjaf.com/nostr"
jsoniter "github.com/json-iterator/go"
"fiatjaf.com/nostrlib"
)
var NIP05_REGEX = regexp.MustCompile(`^(?:([\w.+-]+)@)?([\w_-]+(\.[\w_-]+)+)$`)
@@ -40,16 +40,17 @@ func QueryIdentifier(ctx context.Context, fullname string) (*nostr.ProfilePointe
return nil, err
}
pubkey, ok := result.Names[name]
pubkeyh, ok := result.Names[name]
if !ok {
return nil, fmt.Errorf("no entry for name '%s'", name)
}
if !nostr.IsValidPublicKey(pubkey) {
return nil, fmt.Errorf("got an invalid public key '%s'", pubkey)
pubkey, err := nostr.PubKeyFromHex(pubkeyh)
if err != nil {
return nil, fmt.Errorf("got an invalid public key '%s'", pubkeyh)
}
relays, _ := result.Relays[pubkey]
relays, _ := result.Relays[pubkeyh]
return &nostr.ProfilePointer{
PublicKey: pubkey,
Relays: relays,

View File

@@ -1,6 +1,6 @@
package nip10
import "fiatjaf.com/nostrlib"
import "fiatjaf.com/nostr"
func GetThreadRoot(tags nostr.Tags) *nostr.EventPointer {
for _, tag := range tags {

View File

@@ -7,7 +7,7 @@ import (
"time"
jsoniter "github.com/json-iterator/go"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
// Fetch fetches the NIP-11 metadata for a relay.

View File

@@ -9,7 +9,7 @@ import (
"runtime"
"strconv"
nostr "fiatjaf.com/nostrlib"
nostr "fiatjaf.com/nostr"
)
var (

View File

@@ -8,7 +8,7 @@ import (
"testing"
"time"
nostr "fiatjaf.com/nostrlib"
nostr "fiatjaf.com/nostr"
"github.com/stretchr/testify/require"
)

View File

@@ -1,6 +1,6 @@
package nip14
import "fiatjaf.com/nostrlib"
import "fiatjaf.com/nostr"
func GetSubject(tags nostr.Tags) string {
for _, tag := range tags {

View File

@@ -5,8 +5,8 @@ import (
"fmt"
"strings"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip59"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip59"
)
func GetDMRelays(ctx context.Context, pubkey string, pool *nostr.SimplePool, relaysToQuery []string) []string {

View File

@@ -3,11 +3,10 @@ package nip19
import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"fiatjaf.com/nostr"
"github.com/btcsuite/btcd/btcutil/bech32"
"fiatjaf.com/nostrlib"
)
func Decode(bech32string string) (prefix string, value any, err error) {
@@ -22,12 +21,21 @@ func Decode(bech32string string) (prefix string, value any, err error) {
}
switch prefix {
case "npub", "nsec", "note":
case "nsec":
if len(data) != 32 {
return prefix, nil, fmt.Errorf("data should be 32 bytes (%d)", len(data))
return prefix, nil, fmt.Errorf("nsec should be 32 bytes (%d)", len(data))
}
return prefix, hex.EncodeToString(data[0:32]), nil
return prefix, [32]byte(data[0:32]), nil
case "note":
if len(data) != 32 {
return prefix, nil, fmt.Errorf("note should be 32 bytes (%d)", len(data))
}
return prefix, [32]byte(data[0:32]), nil
case "npub":
if len(data) != 32 {
return prefix, nil, fmt.Errorf("npub should be 32 bytes (%d)", len(data))
}
return prefix, nostr.PubKey(data[0:32]), nil
case "nprofile":
var result nostr.ProfilePointer
curr := 0
@@ -35,7 +43,7 @@ func Decode(bech32string string) (prefix string, value any, err error) {
t, v := readTLVEntry(data[curr:])
if v == nil {
// end here
if result.PublicKey == "" {
if result.PublicKey == nostr.ZeroPK {
return prefix, result, fmt.Errorf("no pubkey found for nprofile")
}
@@ -47,7 +55,7 @@ func Decode(bech32string string) (prefix string, value any, err error) {
if len(v) != 32 {
return prefix, nil, fmt.Errorf("pubkey should be 32 bytes (%d)", len(v))
}
result.PublicKey = hex.EncodeToString(v)
result.PublicKey = nostr.PubKey(v)
case TLVRelay:
result.Relays = append(result.Relays, string(v))
default:
@@ -63,7 +71,7 @@ func Decode(bech32string string) (prefix string, value any, err error) {
t, v := readTLVEntry(data[curr:])
if v == nil {
// end here
if result.ID == "" {
if result.ID == nostr.ZeroID {
return prefix, result, fmt.Errorf("no id found for nevent")
}
@@ -75,19 +83,19 @@ func Decode(bech32string string) (prefix string, value any, err error) {
if len(v) != 32 {
return prefix, nil, fmt.Errorf("id should be 32 bytes (%d)", len(v))
}
result.ID = hex.EncodeToString(v)
result.ID = nostr.ID(v)
case TLVRelay:
result.Relays = append(result.Relays, string(v))
case TLVAuthor:
if len(v) != 32 {
return prefix, nil, fmt.Errorf("author should be 32 bytes (%d)", len(v))
}
result.Author = hex.EncodeToString(v)
result.Author = nostr.PubKey(v)
case TLVKind:
if len(v) != 4 {
return prefix, nil, fmt.Errorf("invalid uint32 value for integer (%v)", v)
}
result.Kind = int(binary.BigEndian.Uint32(v))
result.Kind = uint16(binary.BigEndian.Uint32(v))
default:
// ignore
}
@@ -101,7 +109,7 @@ func Decode(bech32string string) (prefix string, value any, err error) {
t, v := readTLVEntry(data[curr:])
if v == nil {
// end here
if result.Kind == 0 || result.Identifier == "" || result.PublicKey == "" {
if result.Kind == 0 || result.Identifier == "" || result.PublicKey == nostr.ZeroPK {
return prefix, result, fmt.Errorf("incomplete naddr")
}
@@ -117,9 +125,9 @@ func Decode(bech32string string) (prefix string, value any, err error) {
if len(v) != 32 {
return prefix, nil, fmt.Errorf("author should be 32 bytes (%d)", len(v))
}
result.PublicKey = hex.EncodeToString(v)
result.PublicKey = nostr.PubKey(v)
case TLVKind:
result.Kind = int(binary.BigEndian.Uint32(v))
result.Kind = uint16(binary.BigEndian.Uint32(v))
default:
// ignore
}
@@ -131,13 +139,8 @@ func Decode(bech32string string) (prefix string, value any, err error) {
return prefix, data, fmt.Errorf("unknown tag %s", prefix)
}
func EncodePrivateKey(privateKeyHex string) (string, error) {
b, err := hex.DecodeString(privateKeyHex)
if err != nil {
return "", fmt.Errorf("failed to decode private key hex: %w", err)
}
bits5, err := bech32.ConvertBits(b, 8, 5, true)
func EncodeNsec(sk [32]byte) (string, error) {
bits5, err := bech32.ConvertBits(sk[:], 8, 5, true)
if err != nil {
return "", err
}
@@ -145,79 +148,44 @@ func EncodePrivateKey(privateKeyHex string) (string, error) {
return bech32.Encode("nsec", bits5)
}
func EncodePublicKey(publicKeyHex string) (string, error) {
b, err := hex.DecodeString(publicKeyHex)
if err != nil {
return "", fmt.Errorf("failed to decode public key hex: %w", err)
}
bits5, err := bech32.ConvertBits(b, 8, 5, true)
if err != nil {
return "", err
}
return bech32.Encode("npub", bits5)
func EncodeNpub(pk nostr.PubKey) string {
bits5, _ := bech32.ConvertBits(pk[:], 8, 5, true)
npub, _ := bech32.Encode("npub", bits5)
return npub
}
func EncodeNote(eventIDHex string) (string, error) {
b, err := hex.DecodeString(eventIDHex)
if err != nil {
return "", fmt.Errorf("failed to decode event id hex: %w", err)
}
bits5, err := bech32.ConvertBits(b, 8, 5, true)
if err != nil {
return "", err
}
return bech32.Encode("note", bits5)
}
func EncodeProfile(publicKeyHex string, relays []string) (string, error) {
func EncodeNprofile(pk nostr.PubKey, relays []string) string {
buf := &bytes.Buffer{}
pubkey, err := hex.DecodeString(publicKeyHex)
if err != nil {
return "", fmt.Errorf("invalid pubkey '%s': %w", publicKeyHex, err)
}
writeTLVEntry(buf, TLVDefault, pubkey)
writeTLVEntry(buf, TLVDefault, pk[:])
for _, url := range relays {
writeTLVEntry(buf, TLVRelay, []byte(url))
}
bits5, err := bech32.ConvertBits(buf.Bytes(), 8, 5, true)
if err != nil {
return "", fmt.Errorf("failed to convert bits: %w", err)
}
bits5, _ := bech32.ConvertBits(buf.Bytes(), 8, 5, true)
return bech32.Encode("nprofile", bits5)
nprofile, _ := bech32.Encode("nprofile", bits5)
return nprofile
}
func EncodeEvent(eventIDHex string, relays []string, author string) (string, error) {
func EncodeNevent(id nostr.ID, relays []string, author nostr.PubKey) string {
buf := &bytes.Buffer{}
id, err := hex.DecodeString(eventIDHex)
if err != nil || len(id) != 32 {
return "", fmt.Errorf("invalid id '%s': %w", eventIDHex, err)
}
writeTLVEntry(buf, TLVDefault, id)
writeTLVEntry(buf, TLVDefault, id[:])
for _, url := range relays {
writeTLVEntry(buf, TLVRelay, []byte(url))
}
if pubkey, _ := hex.DecodeString(author); len(pubkey) == 32 {
writeTLVEntry(buf, TLVAuthor, pubkey)
if author != nostr.ZeroPK {
writeTLVEntry(buf, TLVAuthor, author[:])
}
bits5, err := bech32.ConvertBits(buf.Bytes(), 8, 5, true)
if err != nil {
return "", fmt.Errorf("failed to convert bits: %w", err)
}
return bech32.Encode("nevent", bits5)
bits5, _ := bech32.ConvertBits(buf.Bytes(), 8, 5, true)
nevent, _ := bech32.Encode("nevent", bits5)
return nevent
}
func EncodeEntity(publicKey string, kind int, identifier string, relays []string) (string, error) {
func EncodeNaddr(pk nostr.PubKey, kind uint16, identifier string, relays []string) string {
buf := &bytes.Buffer{}
writeTLVEntry(buf, TLVDefault, []byte(identifier))
@@ -226,20 +194,13 @@ func EncodeEntity(publicKey string, kind int, identifier string, relays []string
writeTLVEntry(buf, TLVRelay, []byte(url))
}
pubkey, err := hex.DecodeString(publicKey)
if err != nil {
return "", fmt.Errorf("invalid pubkey '%s': %w", pubkey, err)
}
writeTLVEntry(buf, TLVAuthor, pubkey)
writeTLVEntry(buf, TLVAuthor, pk[:])
kindBytes := make([]byte, 4)
binary.BigEndian.PutUint32(kindBytes, uint32(kind))
writeTLVEntry(buf, TLVKind, kindBytes)
bits5, err := bech32.ConvertBits(buf.Bytes(), 8, 5, true)
if err != nil {
return "", fmt.Errorf("failed to convert bits: %w", err)
}
return bech32.Encode("naddr", bits5)
bits5, _ := bech32.ConvertBits(buf.Bytes(), 8, 5, true)
naddr, _ := bech32.Encode("naddr", bits5)
return naddr
}

View File

@@ -1,21 +1,24 @@
package nip19
import (
"encoding/hex"
"testing"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestEncodeNpub(t *testing.T) {
npub, err := EncodePublicKey("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d")
assert.NoError(t, err)
npub := EncodeNpub(nostr.MustPubKeyFromHex("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"))
assert.Equal(t, "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6", npub, "produced an unexpected npub string")
}
func TestEncodeNsec(t *testing.T) {
nsec, err := EncodePrivateKey("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d")
skh := "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"
var sk [32]byte
hex.Decode(sk[:], []byte(skh))
nsec, err := EncodeNsec(sk)
assert.NoError(t, err)
assert.Equal(t, "nsec180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsgyumg0", nsec, "produced an unexpected nsec string")
}
@@ -63,20 +66,22 @@ func TestDecodeNprofile(t *testing.T) {
}
func TestEncodeNprofile(t *testing.T) {
nprofile, err := EncodeProfile("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", []string{
"wss://r.x.com",
"wss://djbas.sadkb.com",
})
nprofile := EncodeNprofile(
nostr.MustPubKeyFromHex("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"),
[]string{
"wss://r.x.com",
"wss://djbas.sadkb.com",
},
)
assert.NoError(t, err)
assert.Equal(t,
"nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p",
nprofile, "produced an unexpected nprofile string: %s", nprofile)
}
func TestEncodeDecodeNaddr(t *testing.T) {
naddr, err := EncodeEntity(
"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d",
naddr := EncodeNaddr(
nostr.MustPubKeyFromHex("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"),
nostr.KindArticle,
"banana",
[]string{
@@ -84,7 +89,6 @@ func TestEncodeDecodeNaddr(t *testing.T) {
"wss://nostr.banana.com",
})
assert.NoError(t, err)
assert.Equal(t,
"naddr1qqrxyctwv9hxzqfwwaehxw309aex2mrp0yhxummnw3ezuetcv9khqmr99ekhjer0d4skjm3wv4uxzmtsd3jjucm0d5q3vamnwvaz7tmwdaehgu3wvfskuctwvyhxxmmdqgsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8grqsqqqa28a3lkds",
naddr, "produced an unexpected naddr string: %s", naddr)
@@ -116,14 +120,12 @@ func TestDecodeNaddrWithoutRelays(t *testing.T) {
}
func TestEncodeDecodeNEvent(t *testing.T) {
nevent, err := EncodeEvent(
"45326f5d6962ab1e3cd424e758c3002b8665f7b0d8dcee9fe9e288d7751ac194",
nevent := EncodeNevent(
nostr.MustIDFromHex("45326f5d6962ab1e3cd424e758c3002b8665f7b0d8dcee9fe9e288d7751ac194"),
[]string{"wss://banana.com"},
"7fa56f5d6962ab1e3cd424e758c3002b8665f7b0d8dcee9fe9e288d7751abb88",
nostr.MustPubKeyFromHex("7fa56f5d6962ab1e3cd424e758c3002b8665f7b0d8dcee9fe9e288d7751abb88"),
)
assert.NoError(t, err)
expectedNEvent := "nevent1qqsy2vn0t45k92c78n2zfe6ccvqzhpn977cd3h8wnl579zxhw5dvr9qpzpmhxue69uhkyctwv9hxztnrdaksygrl54h466tz4v0re4pyuavvxqptsejl0vxcmnhfl60z3rth2x4m3q04ndyp"
assert.Equal(t, expectedNEvent, nevent)

View File

@@ -3,7 +3,7 @@ package nip19
import (
"fmt"
"github.com/nbd-wtf/go-nostr"
"fiatjaf.com/nostr"
)
func EncodePointer(pointer nostr.Pointer) string {

View File

@@ -1,10 +1,9 @@
package nip19
import (
"github.com/nbd-wtf/go-nostr"
"fiatjaf.com/nostr"
)
func NeventFromRelayEvent(ie nostr.RelayEvent) string {
v, _ := EncodeEvent(ie.ID, []string{ie.Relay.URL}, ie.PubKey)
return v
return EncodeNevent(ie.ID, []string{ie.Relay.URL}, ie.PubKey)
}

View File

@@ -6,9 +6,9 @@ import (
"regexp"
"strings"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip19"
"github.com/nbd-wtf/go-nostr/nip73"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip19"
"fiatjaf.com/nostr/nip73"
)
type Block struct {
@@ -54,7 +54,7 @@ func Parse(content string) iter.Seq[Block] {
var pointer nostr.Pointer
switch prefix {
case "npub":
pointer = nostr.ProfilePointer{PublicKey: data.(string)}
pointer = nostr.ProfilePointer{PublicKey: data.(nostr.PubKey)}
case "nprofile", "nevent", "naddr":
pointer = data.(nostr.Pointer)
case "note", "nsec":

View File

@@ -5,8 +5,8 @@ import (
"slices"
"testing"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip73"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip73"
"github.com/stretchr/testify/require"
)
@@ -19,9 +19,9 @@ func TestParse(t *testing.T) {
"hello, nostr:nprofile1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8yc5usxdg wrote nostr:nevent1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8ychxp5v4!",
[]Block{
{Text: "hello, ", Start: 0},
{Text: "nostr:nprofile1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8yc5usxdg", Start: 7, Pointer: nostr.ProfilePointer{PublicKey: "cc6b9fea033f59c3c39a0407c5f1bfee439b077508d918cfdc0d6fd431d39393"}},
{Text: "nostr:nprofile1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8yc5usxdg", Start: 7, Pointer: nostr.ProfilePointer{PublicKey: nostr.MustPubKeyFromHex("cc6b9fea033f59c3c39a0407c5f1bfee439b077508d918cfdc0d6fd431d39393")}},
{Text: " wrote ", Start: 83},
{Text: "nostr:nevent1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8ychxp5v4", Start: 90, Pointer: nostr.EventPointer{ID: "cc6b9fea033f59c3c39a0407c5f1bfee439b077508d918cfdc0d6fd431d39393"}},
{Text: "nostr:nevent1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8ychxp5v4", Start: 90, Pointer: nostr.EventPointer{ID: nostr.MustIDFromHex("cc6b9fea033f59c3c39a0407c5f1bfee439b077508d918cfdc0d6fd431d39393")}},
{Text: "!", Start: 164},
},
},

View File

@@ -1,82 +0,0 @@
package nip27
import (
"iter"
"regexp"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip19"
)
type Reference struct {
Text string
Start int
End int
Pointer nostr.Pointer
}
var mentionRegex = regexp.MustCompile(`\bnostr:((note|npub|naddr|nevent|nprofile)1\w+)\b`)
// Deprecated: this is useless, use Parse() isntead (but the semantics is different)
func ParseReferences(evt nostr.Event) iter.Seq[Reference] {
return func(yield func(Reference) bool) {
for _, ref := range mentionRegex.FindAllStringSubmatchIndex(evt.Content, -1) {
reference := Reference{
Text: evt.Content[ref[0]:ref[1]],
Start: ref[0],
End: ref[1],
}
nip19code := evt.Content[ref[2]:ref[3]]
if prefix, data, err := nip19.Decode(nip19code); err == nil {
switch prefix {
case "npub":
pointer := nostr.ProfilePointer{
PublicKey: data.(string), Relays: []string{},
}
tag := evt.Tags.FindWithValue("p", pointer.PublicKey)
if tag != nil && len(tag) >= 3 {
pointer.Relays = []string{tag[2]}
}
if nostr.IsValidPublicKey(pointer.PublicKey) {
reference.Pointer = pointer
}
case "nprofile":
pointer := data.(nostr.ProfilePointer)
tag := evt.Tags.FindWithValue("p", pointer.PublicKey)
if tag != nil && len(tag) >= 3 {
pointer.Relays = append(pointer.Relays, tag[2])
}
if nostr.IsValidPublicKey(pointer.PublicKey) {
reference.Pointer = pointer
}
case "note":
// we don't even bother here because people using note1 codes aren't including relay hints anyway
reference.Pointer = nostr.EventPointer{ID: data.(string), Relays: nil}
case "nevent":
pointer := data.(nostr.EventPointer)
tag := evt.Tags.FindWithValue("e", pointer.ID)
if tag != nil && len(tag) >= 3 {
pointer.Relays = append(pointer.Relays, tag[2])
if pointer.Author == "" && len(tag) >= 5 && nostr.IsValidPublicKey(tag[4]) {
pointer.Author = tag[4]
}
}
reference.Pointer = pointer
case "naddr":
pointer := data.(nostr.EntityPointer)
tag := evt.Tags.FindWithValue("a", pointer.AsTagReference())
if tag != nil && len(tag) >= 3 {
pointer.Relays = append(pointer.Relays, tag[2])
}
reference.Pointer = pointer
}
}
if !yield(reference) {
return
}
}
}
}

View File

@@ -1,46 +0,0 @@
package nip27
import (
"slices"
"testing"
"github.com/nbd-wtf/go-nostr"
"github.com/stretchr/testify/require"
)
func TestParseReferences(t *testing.T) {
evt := nostr.Event{
Tags: nostr.Tags{
{"p", "cc6b9fea033f59c3c39a0407c5f1bfee439b077508d918cfdc0d6fd431d39393", "wss://xawr.com"},
{"e", "a84c5de86efc2ec2cff7bad077c4171e09146b633b7ad117fffe088d9579ac33", "wss://other.com", "reply"},
{"e", "cc6b9fea033f59c3c39a0407c5f1bfee439b077508d918cfdc0d6fd431d39393", "wss://nasdj.com"},
},
Content: "hello, nostr:nprofile1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8yc5usxdg wrote nostr:nevent1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8ychxp5v4!",
}
expected := []Reference{
{
Text: "nostr:nprofile1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8yc5usxdg",
Start: 7,
End: 83,
Pointer: nostr.ProfilePointer{
PublicKey: "cc6b9fea033f59c3c39a0407c5f1bfee439b077508d918cfdc0d6fd431d39393",
Relays: []string{"wss://xawr.com"},
},
},
{
Text: "nostr:nevent1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8ychxp5v4",
Start: 90,
End: 164,
Pointer: nostr.EventPointer{
ID: "cc6b9fea033f59c3c39a0407c5f1bfee439b077508d918cfdc0d6fd431d39393",
Relays: []string{"wss://nasdj.com"},
Author: "",
},
},
}
got := slices.Collect(ParseReferences(evt))
require.EqualValues(t, expected, got)
}

View File

@@ -6,7 +6,7 @@ import (
"slices"
"strings"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
type GroupAddress struct {

View File

@@ -3,7 +3,7 @@ package nip29
import (
"slices"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
type Role struct {

View File

@@ -1,6 +1,6 @@
package nip31
import "fiatjaf.com/nostrlib"
import "fiatjaf.com/nostr"
func GetAlt(event nostr.Event) string {
for _, tag := range event.Tags {

View File

@@ -4,7 +4,7 @@ import (
"strings"
"github.com/bluekeyes/go-gitdiff/gitdiff"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
type Patch struct {

View File

@@ -4,7 +4,7 @@ import (
"context"
"fmt"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
type Repository struct {

View File

@@ -3,7 +3,7 @@ package nip34
import (
"strings"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
type RepositoryState struct {

View File

@@ -3,7 +3,7 @@ package nip40
import (
"strconv"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
// GetExpiration returns the expiration timestamp for this event, or -1 if no "expiration" tag exists or

View File

@@ -5,7 +5,7 @@ import (
"strings"
"time"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
// CreateUnsignedAuthEvent creates an event which should be sent via an "AUTH" command.

View File

@@ -12,6 +12,7 @@ import (
"io"
"math"
"fiatjaf.com/nostr"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"golang.org/x/crypto/chacha20"
@@ -153,10 +154,12 @@ func Decrypt(b64ciphertextWrapped string, conversationKey [32]byte) (string, err
return string(unpadded), nil
}
func GenerateConversationKey(pub string, sk string) ([32]byte, error) {
var maxThreshold, _ = hex.DecodeString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141")
func GenerateConversationKey(pub nostr.PubKey, sk [32]byte) ([32]byte, error) {
var ck [32]byte
if sk >= "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141" || sk == "0000000000000000000000000000000000000000000000000000000000000000" {
if bytes.Compare(sk[:], maxThreshold) != -1 || sk == [32]byte{} {
return ck, fmt.Errorf("invalid private key: x coordinate %s is not on the secp256k1 curve", sk)
}
@@ -220,20 +223,13 @@ func calcPadding(sLen int) int {
}
// code adapted from nip04.ComputeSharedSecret()
func computeSharedSecret(pub string, sk string) (sharedSecret [32]byte, err error) {
privKeyBytes, err := hex.DecodeString(sk)
if err != nil {
return sharedSecret, fmt.Errorf("error decoding sender private key: %w", err)
}
privKey, _ := btcec.PrivKeyFromBytes(privKeyBytes)
func computeSharedSecret(pub nostr.PubKey, sk [32]byte) (sharedSecret [32]byte, err error) {
privKey, _ := btcec.PrivKeyFromBytes(sk[:])
pubKeyBytes, err := hex.DecodeString("02" + pub)
pubKey, err := btcec.ParsePubKey(append([]byte{2}, pub[:]...))
if err != nil {
return sharedSecret, fmt.Errorf("error decoding hex string of receiver public key '%s': %w", "02"+pub, err)
}
pubKey, err := btcec.ParsePubKey(pubKeyBytes)
if err != nil {
return sharedSecret, fmt.Errorf("error parsing receiver public key '%s': %w", "02"+pub, err)
return sharedSecret, fmt.Errorf("error parsing receiver public key '%s': %w",
"02"+hex.EncodeToString(pub[:]), err)
}
var point, result secp256k1.JacobianPoint

View File

@@ -8,14 +8,22 @@ import (
"strings"
"testing"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
"github.com/stretchr/testify/require"
)
func assertCryptPriv(t *testing.T, sk1 string, sk2 string, conversationKey string, salt string, plaintext string, expected string) {
func assertCryptPriv(t *testing.T, skh1 string, skh2 string, conversationKey string, salt string, plaintext string, expected string) {
sk1 := [32]byte{}
hex.Decode(sk1[:], []byte(skh1))
sk2 := [32]byte{}
hex.Decode(sk2[:], []byte(skh2))
k1, err := hexDecode32Array(conversationKey)
require.NoErrorf(t, err, "hex decode failed for conversation key: %v", err)
assertConversationKeyGenerationSec(t, sk1, sk2, conversationKey)
pub2 := nostr.GetPublicKey(sk2)
assertConversationKeyGenerationPub(t, skh1, hex.EncodeToString(pub2[:]), conversationKey)
customNonce, err := hex.DecodeString(salt)
require.NoErrorf(t, err, "hex decode failed for salt: %v", err)
@@ -39,7 +47,13 @@ func assertDecryptFail(t *testing.T, conversationKey string, _ string, ciphertex
}
func assertConversationKeyFail(t *testing.T, priv string, pub string, msg string) {
_, err := GenerateConversationKey(pub, priv)
var pub32 [32]byte
hex.Decode(pub32[:], []byte(pub))
var priv32 [32]byte
hex.Decode(priv32[:], []byte(priv))
_, err := GenerateConversationKey(pub32, priv32)
require.ErrorContains(t, err, msg)
}
@@ -47,18 +61,18 @@ func assertConversationKeyGenerationPub(t *testing.T, priv string, pub string, c
expectedConversationKey, err := hexDecode32Array(conversationKey)
require.NoErrorf(t, err, "hex decode failed for conversation key: %v", err)
actualConversationKey, err := GenerateConversationKey(pub, priv)
var pub32 [32]byte
hex.Decode(pub32[:], []byte(pub))
var priv32 [32]byte
hex.Decode(priv32[:], []byte(priv))
actualConversationKey, err := GenerateConversationKey(pub32, priv32)
require.NoErrorf(t, err, "conversation key generation failed: %v", err)
require.Equalf(t, expectedConversationKey, actualConversationKey, "wrong conversation key")
}
func assertConversationKeyGenerationSec(t *testing.T, sk1 string, sk2 string, conversationKey string) {
pub2, err := nostr.GetPublicKey(sk2)
require.NoErrorf(t, err, "failed to derive pubkey from sk2: %v", err)
assertConversationKeyGenerationPub(t, sk1, pub2, conversationKey)
}
func assertMessageKeyGeneration(t *testing.T, conversationKey string, salt string, chachaKey string, chachaSalt string, hmacKey string) bool {
convKey, err := hexDecode32Array(conversationKey)
require.NoErrorf(t, err, "hex decode failed for convKey: %v", err)
@@ -1061,7 +1075,7 @@ func TestMessageKeyGeneration033(t *testing.T) {
func TestMaxLength(t *testing.T) {
sk1 := nostr.GeneratePrivateKey()
sk2 := nostr.GeneratePrivateKey()
pub2, _ := nostr.GetPublicKey(sk2)
pub2 := nostr.GetPublicKey(sk2)
salt := make([]byte, 32)
rand.Read(salt)
conversationKey, _ := GenerateConversationKey(pub2, sk1)
@@ -1072,8 +1086,8 @@ func TestMaxLength(t *testing.T) {
}
assertCryptPub(t,
sk1,
pub2,
hex.EncodeToString(sk1[:]),
hex.EncodeToString(pub2[:]),
fmt.Sprintf("%x", conversationKey),
fmt.Sprintf("%x", salt),
plaintext,

View File

@@ -4,7 +4,7 @@ import (
"iter"
"strconv"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
func HyperLogLogEventPubkeyOffsetsAndReferencesForEvent(evt *nostr.Event) iter.Seq2[string, int] {

View File

@@ -3,7 +3,7 @@ package nip45
import (
"strconv"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
// HyperLogLogEventPubkeyOffsetForFilter returns the deterministic pubkey offset that will be used

View File

@@ -3,15 +3,13 @@ package nip46
import (
"fmt"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip04"
"fiatjaf.com/nostrlib/nip44"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip44"
)
type Session struct {
PublicKey string
SharedKey []byte // nip04
ConversationKey [32]byte // nip44
PublicKey nostr.PubKey
ConversationKey [32]byte
}
type RelayReadWrite struct {
@@ -22,22 +20,18 @@ type RelayReadWrite struct {
func (s Session) ParseRequest(event *nostr.Event) (Request, error) {
var req Request
plain, err1 := nip44.Decrypt(event.Content, s.ConversationKey)
if err1 != nil {
var err2 error
plain, err2 = nip04.Decrypt(event.Content, s.SharedKey)
if err2 != nil {
return req, fmt.Errorf("failed to decrypt event from %s: (nip44: %w, nip04: %w)", event.PubKey, err1, err2)
}
plain, err := nip44.Decrypt(event.Content, s.ConversationKey)
if err != nil {
return req, fmt.Errorf("failed to decrypt event from %s: (nip44: %w)", event.PubKey, err)
}
err := json.Unmarshal([]byte(plain), &req)
err = json.Unmarshal([]byte(plain), &req)
return req, err
}
func (s Session) MakeResponse(
id string,
requester string,
requester nostr.PubKey,
result string,
err error,
) (resp Response, evt nostr.Event, error error) {

View File

@@ -8,10 +8,10 @@ import (
"strconv"
"sync/atomic"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip04"
"fiatjaf.com/nostr/nip44"
"github.com/mailru/easyjson"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip04"
"fiatjaf.com/nostrlib/nip44"
"github.com/puzpuzpuz/xsync/v3"
)
@@ -38,7 +38,7 @@ type BunkerClient struct {
// pool can be passed to reuse an existing pool, otherwise a new pool will be created.
func ConnectBunker(
ctx context.Context,
clientSecretKey string,
clientSecretKey nostr.PubKey,
bunkerURLOrNIP05 string,
pool *nostr.SimplePool,
onAuth func(string),
@@ -51,7 +51,7 @@ func ConnectBunker(
// assume it's a bunker url (will fail later if not)
secret := parsed.Query().Get("secret")
relays := parsed.Query()["relay"]
targetPublicKey := parsed.Host
targetPublicKey, _ := nostr.PubKeyFromHex(parsed.Host)
if parsed.Scheme == "" {
// could be a NIP-05
@@ -85,8 +85,8 @@ func ConnectBunker(
func NewBunker(
ctx context.Context,
clientSecretKey string,
targetPublicKey string,
clientSecretKey [32]byte,
targetPublicKey nostr.PubKey,
relays []string,
pool *nostr.SimplePool,
onAuth func(string),
@@ -95,8 +95,7 @@ func NewBunker(
pool = nostr.NewSimplePool(ctx)
}
clientPublicKey, _ := nostr.GetPublicKey(clientSecretKey)
sharedSecret, _ := nip04.ComputeSharedSecret(targetPublicKey, clientSecretKey)
clientPublicKey := nostr.GetPublicKey(clientSecretKey)
conversationKey, _ := nip44.GenerateConversationKey(targetPublicKey, clientSecretKey)
bunker := &BunkerClient{

View File

@@ -2,44 +2,41 @@ package nip46
import (
"context"
"encoding/hex"
"fmt"
"slices"
"sync"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip44"
"github.com/mailru/easyjson"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip04"
"fiatjaf.com/nostrlib/nip44"
)
var _ Signer = (*DynamicSigner)(nil)
type DynamicSigner struct {
sessionKeys []string
sessions []Session
sessions map[nostr.PubKey]Session
sync.Mutex
getHandlerSecretKey func(handlerPubkey string) (string, error)
getUserKeyer func(handlerPubkey string) (nostr.Keyer, error)
authorizeSigning func(event nostr.Event, from string, secret string) bool
authorizeEncryption func(from string, secret string) bool
getHandlerSecretKey func(handlerPubkey nostr.PubKey) ([32]byte, error)
getUserKeyer func(handlerPubkey nostr.PubKey) (nostr.Keyer, error)
authorizeSigning func(event nostr.Event, from nostr.PubKey, secret string) bool
authorizeEncryption func(from nostr.PubKey, secret string) bool
onEventSigned func(event nostr.Event)
getRelays func(pubkey string) map[string]RelayReadWrite
}
func NewDynamicSigner(
// the handler is the keypair we use to communicate with the NIP-46 client, decrypt requests, encrypt responses etc
getHandlerSecretKey func(handlerPubkey string) (string, error),
getHandlerSecretKey func(handlerPubkey nostr.PubKey) ([32]byte, error),
// this should correspond to the actual user on behalf of which we will respond to requests
getUserKeyer func(handlerPubkey string) (nostr.Keyer, error),
getUserKeyer func(handlerPubkey nostr.PubKey) (nostr.Keyer, error),
// this is called on every sign_event call, if it is nil it will be assumed that everything is authorized
authorizeSigning func(event nostr.Event, from string, secret string) bool,
authorizeSigning func(event nostr.Event, from nostr.PubKey, secret string) bool,
// this is called on every encrypt or decrypt calls, if it is nil it will be assumed that everything is authorized
authorizeEncryption func(from string, secret string) bool,
authorizeEncryption func(from nostr.PubKey, secret string) bool,
// unless it is nil, this is called after every event is signed
onEventSigned func(event nostr.Event),
@@ -53,34 +50,28 @@ func NewDynamicSigner(
authorizeSigning: authorizeSigning,
authorizeEncryption: authorizeEncryption,
onEventSigned: onEventSigned,
getRelays: getRelays,
}
}
func (p *DynamicSigner) GetSession(clientPubkey string) (Session, bool) {
idx, exists := slices.BinarySearch(p.sessionKeys, clientPubkey)
func (p *DynamicSigner) GetSession(clientPubkey nostr.PubKey) (Session, bool) {
session, exists := p.sessions[clientPubkey]
if exists {
return p.sessions[idx], true
return session, true
}
return Session{}, false
}
func (p *DynamicSigner) setSession(clientPubkey string, session Session) {
func (p *DynamicSigner) setSession(clientPubkey nostr.PubKey, session Session) {
p.Lock()
defer p.Unlock()
idx, exists := slices.BinarySearch(p.sessionKeys, clientPubkey)
_, exists := p.sessions[clientPubkey]
if exists {
return
}
// add to pool
p.sessionKeys = append(p.sessionKeys, "") // bogus append just to increase the capacity
p.sessions = append(p.sessions, Session{})
copy(p.sessionKeys[idx+1:], p.sessionKeys[idx:])
copy(p.sessions[idx+1:], p.sessions[idx:])
p.sessionKeys[idx] = clientPubkey
p.sessions[idx] = session
p.sessions[clientPubkey] = session
}
func (p *DynamicSigner) HandleRequest(ctx context.Context, event *nostr.Event) (
@@ -99,7 +90,10 @@ func (p *DynamicSigner) HandleRequest(ctx context.Context, event *nostr.Event) (
return req, resp, eventResponse, fmt.Errorf("invalid \"p\" tag")
}
handlerPubkey := handler[1]
handlerPubkey, err := nostr.PubKeyFromHex(handler[1])
if err != nil {
return req, resp, eventResponse, fmt.Errorf("%x is invalid pubkey: %w", handler[1], err)
}
handlerSecret, err := p.getHandlerSecretKey(handlerPubkey)
if err != nil {
return req, resp, eventResponse, fmt.Errorf("no private key for %s: %w", handlerPubkey, err)
@@ -109,18 +103,10 @@ func (p *DynamicSigner) HandleRequest(ctx context.Context, event *nostr.Event) (
return req, resp, eventResponse, fmt.Errorf("failed to get user keyer for %s: %w", handlerPubkey, err)
}
var session Session
idx, exists := slices.BinarySearch(p.sessionKeys, event.PubKey)
if exists {
session = p.sessions[idx]
} else {
session, exists := p.sessions[event.PubKey]
if !exists {
session = Session{}
session.SharedKey, err = nip04.ComputeSharedSecret(event.PubKey, handlerSecret)
if err != nil {
return req, resp, eventResponse, fmt.Errorf("failed to compute shared secret: %w", err)
}
session.ConversationKey, err = nip44.GenerateConversationKey(event.PubKey, handlerSecret)
if err != nil {
return req, resp, eventResponse, fmt.Errorf("failed to compute shared secret: %w", err)
@@ -150,7 +136,7 @@ func (p *DynamicSigner) HandleRequest(ctx context.Context, event *nostr.Event) (
}
result = "ack"
case "get_public_key":
result = session.PublicKey
result = hex.EncodeToString(session.PublicKey[:])
case "sign_event":
if len(req.Params) != 1 {
resultErr = fmt.Errorf("wrong number of arguments to 'sign_event'")
@@ -174,21 +160,14 @@ func (p *DynamicSigner) HandleRequest(ctx context.Context, event *nostr.Event) (
}
jrevt, _ := easyjson.Marshal(evt)
result = string(jrevt)
case "get_relays":
if p.getRelays == nil {
jrelays, _ := json.Marshal(p.getRelays(session.PublicKey))
result = string(jrelays)
} else {
result = "{}"
}
case "nip44_encrypt":
if len(req.Params) != 2 {
resultErr = fmt.Errorf("wrong number of arguments to 'nip04_encrypt'")
resultErr = fmt.Errorf("wrong number of arguments to 'nip44_encrypt'")
break
}
thirdPartyPubkey := req.Params[0]
if !nostr.IsValidPublicKey(thirdPartyPubkey) {
resultErr = fmt.Errorf("first argument to 'nip04_encrypt' is not a pubkey string")
thirdPartyPubkey, err := nostr.PubKeyFromHex(req.Params[0])
if err != nil {
resultErr = fmt.Errorf("first argument to 'nip44_encrypt' is not a valid pubkey string")
break
}
if p.authorizeEncryption != nil && !p.authorizeEncryption(event.PubKey, secret) {
@@ -208,9 +187,9 @@ func (p *DynamicSigner) HandleRequest(ctx context.Context, event *nostr.Event) (
resultErr = fmt.Errorf("wrong number of arguments to 'nip04_decrypt'")
break
}
thirdPartyPubkey := req.Params[0]
if !nostr.IsValidPublicKey(thirdPartyPubkey) {
resultErr = fmt.Errorf("first argument to 'nip04_decrypt' is not a pubkey string")
thirdPartyPubkey, err := nostr.PubKeyFromHex(req.Params[0])
if err != nil {
resultErr = fmt.Errorf("first argument to 'nip04_decrypt' is not a valid pubkey string")
break
}
if p.authorizeEncryption != nil && !p.authorizeEncryption(event.PubKey, secret) {

View File

@@ -5,8 +5,8 @@ import (
"net/url"
"strings"
"fiatjaf.com/nostr"
jsoniter "github.com/json-iterator/go"
"fiatjaf.com/nostrlib"
)
var json = jsoniter.ConfigFastest
@@ -34,7 +34,7 @@ func (r Response) String() string {
}
type Signer interface {
GetSession(clientPubkey string) (Session, bool)
GetSession(client nostr.PubKey) (Session, bool)
HandleRequest(context.Context, *nostr.Event) (req Request, resp Response, eventResponse nostr.Event, err error)
}
@@ -46,7 +46,7 @@ func IsValidBunkerURL(input string) bool {
if p.Scheme != "bunker" {
return false
}
if !nostr.IsValidPublicKey(p.Host) {
if _, err := nostr.PubKeyFromHex(p.Host); err != nil {
return false
}
if !strings.Contains(p.RawQuery, "relay=") {

View File

@@ -2,57 +2,45 @@ package nip46
import (
"context"
"encoding/hex"
"fmt"
"slices"
"sync"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip04"
"fiatjaf.com/nostr/nip44"
"github.com/mailru/easyjson"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip04"
"fiatjaf.com/nostrlib/nip44"
)
var _ Signer = (*StaticKeySigner)(nil)
type StaticKeySigner struct {
secretKey string
sessionKeys []string
sessions []Session
secretKey [32]byte
sessions map[nostr.PubKey]Session
sync.Mutex
RelaysToAdvertise map[string]RelayReadWrite
AuthorizeRequest func(harmless bool, from string, secret string) bool
AuthorizeRequest func(harmless bool, from nostr.PubKey, secret string) bool
}
func NewStaticKeySigner(secretKey string) StaticKeySigner {
func NewStaticKeySigner(secretKey [32]byte) StaticKeySigner {
return StaticKeySigner{
secretKey: secretKey,
RelaysToAdvertise: make(map[string]RelayReadWrite),
secretKey: secretKey,
}
}
func (p *StaticKeySigner) GetSession(clientPubkey string) (Session, bool) {
idx, exists := slices.BinarySearch(p.sessionKeys, clientPubkey)
if exists {
return p.sessions[idx], true
}
return Session{}, false
func (p *StaticKeySigner) GetSession(clientPubkey nostr.PubKey) (Session, bool) {
session, ok := p.sessions[clientPubkey]
return session, ok
}
func (p *StaticKeySigner) getOrCreateSession(clientPubkey string) (Session, error) {
func (p *StaticKeySigner) getOrCreateSession(clientPubkey nostr.PubKey) (Session, error) {
p.Lock()
defer p.Unlock()
idx, exists := slices.BinarySearch(p.sessionKeys, clientPubkey)
session, exists := p.sessions[clientPubkey]
if exists {
return p.sessions[idx], nil
}
shared, err := nip04.ComputeSharedSecret(clientPubkey, p.secretKey)
if err != nil {
return Session{}, fmt.Errorf("failed to compute shared secret: %w", err)
return session, nil
}
ck, err := nip44.GenerateConversationKey(clientPubkey, p.secretKey)
@@ -60,24 +48,14 @@ func (p *StaticKeySigner) getOrCreateSession(clientPubkey string) (Session, erro
return Session{}, fmt.Errorf("failed to compute shared secret: %w", err)
}
pubkey, err := nostr.GetPublicKey(p.secretKey)
if err != nil {
return Session{}, fmt.Errorf("failed to derive public key: %w", err)
}
session := Session{
pubkey := nostr.GetPublicKey(p.secretKey)
session = Session{
PublicKey: pubkey,
SharedKey: shared,
ConversationKey: ck,
}
// add to pool
p.sessionKeys = append(p.sessionKeys, "") // bogus append just to increase the capacity
p.sessions = append(p.sessions, Session{})
copy(p.sessionKeys[idx+1:], p.sessionKeys[idx:])
copy(p.sessions[idx+1:], p.sessions[idx:])
p.sessionKeys[idx] = clientPubkey
p.sessions[idx] = session
p.sessions[pubkey] = session
return session, nil
}
@@ -116,7 +94,7 @@ func (p *StaticKeySigner) HandleRequest(_ context.Context, event *nostr.Event) (
result = "ack"
harmless = true
case "get_public_key":
result = session.PublicKey
result = hex.EncodeToString(session.PublicKey[:])
harmless = true
case "sign_event":
if len(req.Params) != 1 {
@@ -136,18 +114,14 @@ func (p *StaticKeySigner) HandleRequest(_ context.Context, event *nostr.Event) (
}
jrevt, _ := easyjson.Marshal(evt)
result = string(jrevt)
case "get_relays":
jrelays, _ := json.Marshal(p.RelaysToAdvertise)
result = string(jrelays)
harmless = true
case "nip44_encrypt":
if len(req.Params) != 2 {
resultErr = fmt.Errorf("wrong number of arguments to 'nip04_encrypt'")
resultErr = fmt.Errorf("wrong number of arguments to 'nip44_encrypt'")
break
}
thirdPartyPubkey := req.Params[0]
if !nostr.IsValidPublicKey(thirdPartyPubkey) {
resultErr = fmt.Errorf("first argument to 'nip04_encrypt' is not a pubkey string")
thirdPartyPubkey, err := nostr.PubKeyFromHex(req.Params[0])
if err != nil {
resultErr = fmt.Errorf("first argument to 'nip04_encrypt' is not a valid pubkey string")
break
}
plaintext := req.Params[1]
@@ -168,9 +142,9 @@ func (p *StaticKeySigner) HandleRequest(_ context.Context, event *nostr.Event) (
resultErr = fmt.Errorf("wrong number of arguments to 'nip04_decrypt'")
break
}
thirdPartyPubkey := req.Params[0]
if !nostr.IsValidPublicKey(thirdPartyPubkey) {
resultErr = fmt.Errorf("first argument to 'nip04_decrypt' is not a pubkey string")
thirdPartyPubkey, err := nostr.PubKeyFromHex(req.Params[0])
if err != nil {
resultErr = fmt.Errorf("first argument to 'nip04_decrypt' is not a valid pubkey string")
break
}
ciphertext := req.Params[1]
@@ -191,9 +165,9 @@ func (p *StaticKeySigner) HandleRequest(_ context.Context, event *nostr.Event) (
resultErr = fmt.Errorf("wrong number of arguments to 'nip04_encrypt'")
break
}
thirdPartyPubkey := req.Params[0]
if !nostr.IsValidPublicKey(thirdPartyPubkey) {
resultErr = fmt.Errorf("first argument to 'nip04_encrypt' is not a pubkey string")
thirdPartyPubkey, err := nostr.PubKeyFromHex(req.Params[0])
if err != nil {
resultErr = fmt.Errorf("first argument to 'nip04_encrypt' is not a valid pubkey string")
break
}
plaintext := req.Params[1]
@@ -214,9 +188,9 @@ func (p *StaticKeySigner) HandleRequest(_ context.Context, event *nostr.Event) (
resultErr = fmt.Errorf("wrong number of arguments to 'nip04_decrypt'")
break
}
thirdPartyPubkey := req.Params[0]
if !nostr.IsValidPublicKey(thirdPartyPubkey) {
resultErr = fmt.Errorf("first argument to 'nip04_decrypt' is not a pubkey string")
thirdPartyPubkey, err := nostr.PubKeyFromHex(req.Params[0])
if err != nil {
resultErr = fmt.Errorf("first argument to 'nip04_decrypt' is not a valid pubkey string")
break
}
ciphertext := req.Params[1]

View File

@@ -4,22 +4,23 @@ import (
"context"
"fmt"
"fiatjaf.com/nostrlib/nip05"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip05"
)
func queryWellKnownNostrJson(ctx context.Context, fullname string) (pubkey string, relays []string, err error) {
func queryWellKnownNostrJson(ctx context.Context, fullname string) (pubkey nostr.PubKey, relays []string, err error) {
result, name, err := nip05.Fetch(ctx, fullname)
if err != nil {
return "", nil, err
return nostr.ZeroPK, nil, err
}
pubkey, ok := result.Names[name]
pubkeyh, ok := result.Names[name]
if !ok {
return "", nil, fmt.Errorf("no entry found for the '%s' name", name)
return nostr.ZeroPK, nil, fmt.Errorf("no entry found for the '%s' name", name)
}
relays, _ = result.NIP46[pubkey]
relays, _ = result.NIP46[pubkeyh]
if !ok {
return "", nil, fmt.Errorf("no bunker relays found for the '%s' name", name)
return nostr.ZeroPK, nil, fmt.Errorf("no bunker relays found for the '%s' name", name)
}
return pubkey, relays, nil

View File

@@ -4,7 +4,7 @@ import (
"strconv"
"time"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
type CalendarEventKind int

View File

@@ -4,7 +4,7 @@ import (
"strconv"
"time"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
type LiveEvent struct {

View File

@@ -5,8 +5,8 @@ import (
"math/rand"
"github.com/mailru/easyjson"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip44"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip44"
)
// GiftWrap takes a 'rumor', encrypts it with our own key, making a 'seal', then encrypts that with a nonce key and

View File

@@ -6,7 +6,7 @@ import (
"fmt"
"strconv"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
type HistoryEntry struct {

View File

@@ -9,7 +9,7 @@ import (
"github.com/elnosh/gonuts/cashu/nuts/nut02"
"github.com/elnosh/gonuts/cashu/nuts/nut04"
"github.com/elnosh/gonuts/cashu/nuts/nut05"
"fiatjaf.com/nostrlib/nip60/client"
"fiatjaf.com/nostr/nip60/client"
)
type lightningSwapStatus int

View File

@@ -7,8 +7,8 @@ import (
"github.com/elnosh/gonuts/cashu"
"github.com/elnosh/gonuts/cashu/nuts/nut05"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip60/client"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip60/client"
)
func (w *Wallet) PayBolt11(ctx context.Context, invoice string, opts ...SendOption) (string, error) {

View File

@@ -7,8 +7,8 @@ import (
"github.com/elnosh/gonuts/cashu"
"github.com/elnosh/gonuts/cashu/nuts/nut10"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip60/client"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip60/client"
)
type receiveSettings struct {

View File

@@ -6,7 +6,7 @@ import (
"github.com/elnosh/gonuts/cashu"
"github.com/elnosh/gonuts/cashu/nuts/nut04"
"fiatjaf.com/nostrlib/nip60/client"
"fiatjaf.com/nostr/nip60/client"
)
func (w *Wallet) SendExternal(

View File

@@ -11,8 +11,8 @@ import (
"github.com/elnosh/gonuts/cashu/nuts/nut02"
"github.com/elnosh/gonuts/cashu/nuts/nut10"
"github.com/elnosh/gonuts/cashu/nuts/nut11"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip60/client"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip60/client"
)
type SendOption func(opts *sendSettings)

View File

@@ -10,7 +10,7 @@ import (
"github.com/elnosh/gonuts/cashu/nuts/nut02"
"github.com/elnosh/gonuts/cashu/nuts/nut03"
"github.com/elnosh/gonuts/cashu/nuts/nut10"
"fiatjaf.com/nostrlib/nip60/client"
"fiatjaf.com/nostr/nip60/client"
)
type swapSettings struct {

View File

@@ -6,7 +6,7 @@ import (
"fmt"
"github.com/elnosh/gonuts/cashu"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
type Token struct {

View File

@@ -11,7 +11,7 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
type Wallet struct {

View File

@@ -10,8 +10,8 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/elnosh/gonuts/cashu"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/keyer"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/keyer"
"github.com/stretchr/testify/require"
"golang.org/x/exp/rand"
)

View File

@@ -5,7 +5,7 @@ import (
"slices"
"github.com/elnosh/gonuts/cashu"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
type Info struct {

View File

@@ -9,8 +9,8 @@ import (
"slices"
"strings"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip60"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip60"
)
var NutzapsNotAccepted = errors.New("user doesn't accept nutzaps")

View File

@@ -4,11 +4,11 @@ import (
"encoding/hex"
"encoding/json"
"fiatjaf.com/nostr"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/elnosh/gonuts/cashu"
"github.com/elnosh/gonuts/crypto"
"github.com/nbd-wtf/go-nostr"
)
func VerifyNutzap(

View File

@@ -3,7 +3,7 @@ package nip70
import (
"strings"
"github.com/nbd-wtf/go-nostr"
"fiatjaf.com/nostr"
)
func IsProtected(event nostr.Event) bool {

View File

@@ -8,7 +8,7 @@ import (
"github.com/mailru/easyjson"
jwriter "github.com/mailru/easyjson/jwriter"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
"github.com/tidwall/gjson"
)

View File

@@ -7,8 +7,8 @@ import (
"github.com/fiatjaf/eventstore"
"github.com/fiatjaf/eventstore/slicestore"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip77"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip77"
)
func main() {

View File

@@ -1,41 +0,0 @@
package nip77
import (
"sync"
)
type idlistpool struct {
initialsize int
pool [][]string
sync.Mutex
}
func newidlistpool(initialsize int) *idlistpool {
ilp := idlistpool{
initialsize: initialsize,
pool: make([][]string, 1, 2),
}
ilp.pool[0] = make([]string, 0, initialsize)
return &ilp
}
func (ilp *idlistpool) grab() []string {
ilp.Lock()
defer ilp.Unlock()
l := len(ilp.pool)
if l > 0 {
idlist := ilp.pool[l-1]
ilp.pool = ilp.pool[0 : l-1]
return idlist
}
idlist := make([]string, 0, ilp.initialsize)
return idlist
}
func (ilp *idlistpool) giveback(idlist []string) {
idlist = idlist[:0]
ilp.pool = append(ilp.pool, idlist)
}

View File

@@ -4,16 +4,16 @@ import (
"context"
"fmt"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip77/negentropy"
"github.com/nbd-wtf/go-nostr/nip77/negentropy/storage/empty"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip77/negentropy"
"fiatjaf.com/nostr/nip77/negentropy/storage/empty"
)
func FetchIDsOnly(
ctx context.Context,
url string,
filter nostr.Filter,
) (<-chan string, error) {
) (<-chan nostr.ID, error) {
id := "go-nostr-tmp" // for now we can't have more than one subscription in the same connection
neg := negentropy.New(empty.Empty{}, 1024*1024)
@@ -56,7 +56,7 @@ func FetchIDsOnly(
return nil, fmt.Errorf("failed to write to relay: %w", err)
}
ch := make(chan string)
ch := make(chan nostr.ID)
go func() {
for id := range neg.HaveNots {
ch <- id

View File

@@ -3,7 +3,7 @@ package negentropy
import (
"fmt"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
func (n *Negentropy) readTimestamp(reader *StringHexReader) (nostr.Timestamp, error) {
@@ -42,12 +42,12 @@ func (n *Negentropy) readBound(reader *StringHexReader) (Bound, error) {
return Bound{}, fmt.Errorf("failed to decode bound length: %w", err)
}
id, err := reader.ReadString(length * 2)
if err != nil {
pfb := make([]byte, length)
if err := reader.ReadHexBytes(pfb); err != nil {
return Bound{}, fmt.Errorf("failed to read bound id: %w", err)
}
return Bound{Item{timestamp, id}}, nil
return Bound{timestamp, pfb}, nil
}
func (n *Negentropy) writeTimestamp(w *StringHexWriter, timestamp nostr.Timestamp) {
@@ -71,26 +71,25 @@ func (n *Negentropy) writeTimestamp(w *StringHexWriter, timestamp nostr.Timestam
func (n *Negentropy) writeBound(w *StringHexWriter, bound Bound) {
n.writeTimestamp(w, bound.Timestamp)
writeVarInt(w, len(bound.ID)/2)
w.WriteHex(bound.Item.ID)
writeVarInt(w, len(bound.IDPrefix))
w.WriteBytes(bound.IDPrefix)
}
func getMinimalBound(prev, curr Item) Bound {
if curr.Timestamp != prev.Timestamp {
return Bound{Item{curr.Timestamp, ""}}
return Bound{curr.Timestamp, nil}
}
sharedPrefixBytes := 0
for i := 0; i < 32; i += 2 {
if curr.ID[i:i+2] != prev.ID[i:i+2] {
for i := 0; i < 31; i++ {
if curr.ID[i] != prev.ID[i] {
break
}
sharedPrefixBytes++
}
// sharedPrefixBytes + 1 to include the first differing byte, or the entire ID if identical.
return Bound{Item{curr.Timestamp, curr.ID[:(sharedPrefixBytes+1)*2]}}
return Bound{curr.Timestamp, curr.ID[:(sharedPrefixBytes + 1)]}
}
func readVarInt(reader *StringHexReader) (int, error) {

View File

@@ -9,9 +9,9 @@ import (
"sync"
"testing"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip77/negentropy"
"fiatjaf.com/nostrlib/nip77/negentropy/storage/vector"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip77/negentropy"
"fiatjaf.com/nostr/nip77/negentropy/storage/vector"
"github.com/stretchr/testify/require"
)

View File

@@ -1,12 +1,13 @@
package negentropy
import (
"encoding/hex"
"fmt"
"math"
"strings"
"unsafe"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
const (
@@ -15,7 +16,7 @@ const (
buckets = 16
)
var InfiniteBound = Bound{Item: Item{Timestamp: maxTimestamp}}
var InfiniteBound = Bound{Timestamp: maxTimestamp}
type Negentropy struct {
storage Storage
@@ -25,8 +26,8 @@ type Negentropy struct {
lastTimestampIn nostr.Timestamp
lastTimestampOut nostr.Timestamp
Haves chan string
HaveNots chan string
Haves chan nostr.ID
HaveNots chan nostr.ID
}
func New(storage Storage, frameSizeLimit int) *Negentropy {
@@ -39,8 +40,8 @@ func New(storage Storage, frameSizeLimit int) *Negentropy {
return &Negentropy{
storage: storage,
frameSizeLimit: frameSizeLimit,
Haves: make(chan string, buckets*4),
HaveNots: make(chan string, buckets*4),
Haves: make(chan nostr.ID, buckets*4),
HaveNots: make(chan nostr.ID, buckets*4),
}
}
@@ -158,9 +159,10 @@ func (n *Negentropy) reconcileAux(reader *StringHexReader) (string, error) {
}
// what they have
theirItems := make(map[string]struct{}, numIds)
theirItems := make(map[nostr.ID]struct{}, numIds)
for i := 0; i < numIds; i++ {
if id, err := reader.ReadString(64); err != nil {
var id [32]byte
if err := reader.ReadHexBytes(id[:]); err != nil {
return "", fmt.Errorf("failed to read id (#%d/%d) in list: %w", i, numIds, err)
} else {
theirItems[id] = struct{}{}
@@ -203,11 +205,11 @@ func (n *Negentropy) reconcileAux(reader *StringHexReader) (string, error) {
for index, item := range n.storage.Range(lower, upper) {
if n.frameSizeLimit-200 < fullOutput.Len()/2+responseIds.Len()/2 {
endBound = Bound{item}
endBound = Bound{item.Timestamp, item.ID[:]}
upper = index
break
}
responseIds.WriteString(item.ID)
responseIds.WriteString(hex.EncodeToString(item.ID[:]))
responses++
}
@@ -254,7 +256,7 @@ func (n *Negentropy) SplitRange(lower, upper int, upperBound Bound, output *Stri
writeVarInt(output, numElems)
for _, item := range n.storage.Range(lower, upper) {
output.WriteHex(item.ID)
output.WriteBytes(item.ID[:])
}
} else {
itemsPerBucket := numElems / buckets

View File

@@ -5,7 +5,7 @@ import (
"encoding/binary"
"encoding/hex"
"fiatjaf.com/nostrlib/nip77/negentropy"
"fiatjaf.com/nostr/nip77/negentropy"
)
type Accumulator struct {

View File

@@ -3,8 +3,8 @@ package empty
import (
"iter"
"github.com/nbd-wtf/go-nostr/nip77/negentropy"
"github.com/nbd-wtf/go-nostr/nip77/negentropy/storage"
"fiatjaf.com/nostr/nip77/negentropy"
"fiatjaf.com/nostr/nip77/negentropy/storage"
)
var acc storage.Accumulator

View File

@@ -1,7 +1,6 @@
package vector
import (
"encoding/hex"
"fmt"
"iter"
"slices"
@@ -24,7 +23,7 @@ func New() *Vector {
}
}
func (v *Vector) Insert(createdAt nostr.Timestamp, id string) {
func (v *Vector) Insert(createdAt nostr.Timestamp, id nostr.ID) {
if len(id) != 64 {
panic(fmt.Errorf("bad id size for added item: expected %d bytes, got %d", 32, len(id)/2))
}
@@ -68,10 +67,8 @@ func (v *Vector) FindLowerBound(begin, end int, bound negentropy.Bound) int {
func (v *Vector) Fingerprint(begin, end int) string {
v.acc.Reset()
tmp := make([]byte, 32)
for _, item := range v.Range(begin, end) {
hex.Decode(tmp, []byte(item.ID))
v.acc.AddBytes(tmp)
v.acc.AddBytes(item.ID[:])
}
return v.acc.GetFingerprint(end - begin)

View File

@@ -1,11 +1,11 @@
package negentropy
import (
"bytes"
"cmp"
"fmt"
"strings"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
const FingerprintSize = 16
@@ -33,23 +33,26 @@ func (v Mode) String() string {
type Item struct {
Timestamp nostr.Timestamp
ID string
ID nostr.ID
}
func ItemCompare(a, b Item) int {
if a.Timestamp == b.Timestamp {
return strings.Compare(a.ID, b.ID)
return bytes.Compare(a.ID[:], b.ID[:])
}
return cmp.Compare(a.Timestamp, b.Timestamp)
}
func (i Item) String() string { return fmt.Sprintf("Item<%d:%s>", i.Timestamp, i.ID) }
func (i Item) String() string { return fmt.Sprintf("Item<%d:%x>", i.Timestamp, i.ID[:]) }
type Bound struct{ Item }
type Bound struct {
Timestamp nostr.Timestamp
IDPrefix []byte
}
func (b Bound) String() string {
if b.Timestamp == InfiniteBound.Timestamp {
return "Bound<infinite>"
}
return fmt.Sprintf("Bound<%d:%s>", b.Timestamp, b.ID)
return fmt.Sprintf("Bound<%d:%x>", b.Timestamp, b.IDPrefix)
}

View File

@@ -6,9 +6,9 @@ import (
"sync"
"testing"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip77/negentropy"
"fiatjaf.com/nostrlib/nip77/negentropy/storage/vector"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip77/negentropy"
"fiatjaf.com/nostr/nip77/negentropy/storage/vector"
"github.com/stretchr/testify/require"
)

View File

@@ -5,14 +5,14 @@ import (
"fmt"
"sync"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip77/negentropy"
"fiatjaf.com/nostrlib/nip77/negentropy/storage/vector"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip77/negentropy"
"fiatjaf.com/nostr/nip77/negentropy/storage/vector"
)
type direction struct {
label string
items chan string
items chan nostr.ID
source nostr.RelayStore
target nostr.RelayStore
}
@@ -91,7 +91,9 @@ func NegentropySync(
}()
wg := sync.WaitGroup{}
pool := newidlistpool(50)
pool := sync.Pool{
New: func() any { return make([]nostr.ID, 0, 50) },
}
// Define sync directions
directions := [][]direction{
@@ -105,11 +107,11 @@ func NegentropySync(
go func(dir direction) {
defer wg.Done()
seen := make(map[string]struct{})
seen := make(map[nostr.ID]struct{})
doSync := func(ids []string) {
doSync := func(ids []nostr.ID) {
defer wg.Done()
defer pool.giveback(ids)
defer pool.Put(ids)
if len(ids) == 0 {
return
@@ -124,7 +126,7 @@ func NegentropySync(
}
}
ids := pool.grab()
ids := pool.Get().([]nostr.ID)
for item := range dir.items {
if _, ok := seen[item]; ok {
continue
@@ -135,7 +137,7 @@ func NegentropySync(
if len(ids) == 50 {
wg.Add(1)
go doSync(ids)
ids = pool.grab()
ids = pool.Get().([]nostr.ID)
}
}
wg.Add(1)

View File

@@ -5,7 +5,7 @@ import (
"math"
"net"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
func DecodeRequest(req Request) (MethodParams, error) {

View File

@@ -4,7 +4,7 @@ import (
"strconv"
"strings"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
type IMeta []IMetaEntry

View File

@@ -3,7 +3,7 @@ package nip92
import (
"testing"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
"github.com/stretchr/testify/require"
)

View File

@@ -3,7 +3,7 @@ package nip94
import (
"strings"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
func ParseFileMetadata(event nostr.Event) FileMetadata {

View File

@@ -14,7 +14,7 @@ import (
"strconv"
jsoniter "github.com/json-iterator/go"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
// Upload uploads a file to the provided req.Host.

View File

@@ -7,7 +7,7 @@ import (
"os"
"testing"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

View File

@@ -5,7 +5,7 @@ import (
"io"
"net/http"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostr"
)
// UploadRequest is a NIP96 upload request.

View File

@@ -4,7 +4,7 @@ import (
"context"
"fmt"
"github.com/nbd-wtf/go-nostr"
"fiatjaf.com/nostr"
)
// Check checks if a file exists on the media server by its hash

View File

@@ -4,7 +4,7 @@ import (
"strings"
"time"
"github.com/nbd-wtf/go-nostr"
"fiatjaf.com/nostr"
"github.com/valyala/fasthttp"
)

View File

@@ -4,7 +4,7 @@ import (
"context"
"fmt"
"github.com/nbd-wtf/go-nostr"
"fiatjaf.com/nostr"
)
// Delete deletes a file from the media server by its hash

View File

@@ -7,7 +7,7 @@ import (
"net/http"
"os"
"github.com/nbd-wtf/go-nostr"
"fiatjaf.com/nostr"
)
// Download downloads a file from the media server by its hash

View File

@@ -8,7 +8,7 @@ import (
"io"
"strconv"
"github.com/nbd-wtf/go-nostr"
"fiatjaf.com/nostr"
"github.com/valyala/fasthttp"
)

View File

@@ -2,9 +2,10 @@ package blossom
import (
"context"
"encoding/hex"
"fmt"
"github.com/nbd-wtf/go-nostr"
"fiatjaf.com/nostr"
)
// List retrieves a list of blobs from a specific pubkey
@@ -15,7 +16,7 @@ func (c *Client) List(ctx context.Context) ([]BlobDescriptor, error) {
}
bds := make([]BlobDescriptor, 0, 100)
err = c.httpCall(ctx, "GET", "list/"+pubkey, "", func() string {
err = c.httpCall(ctx, "GET", "list/"+hex.EncodeToString(pubkey[:]), "", func() string {
return c.authorizationHeader(ctx, func(evt *nostr.Event) {
evt.Tags = append(evt.Tags, nostr.Tag{"t", "list"})
})

View File

@@ -3,7 +3,7 @@ package blossom
import (
"encoding/json"
"github.com/nbd-wtf/go-nostr"
"fiatjaf.com/nostr"
)
// BlobDescriptor represents metadata about a blob stored on a media server

View File

@@ -10,7 +10,7 @@ import (
"os"
"path/filepath"
"github.com/nbd-wtf/go-nostr"
"fiatjaf.com/nostr"
)
// UploadFile uploads a file to the media server

View File

@@ -1,6 +1,6 @@
package hints
import "fiatjaf.com/nostrlib"
import "fiatjaf.com/nostr"
const END_OF_WORLD nostr.Timestamp = 2208999600 // 2040-01-01

View File

@@ -2,15 +2,14 @@ package lmdbh
import (
"bytes"
"encoding/hex"
"fmt"
"math"
"os"
"slices"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/sdk/hints"
"github.com/PowerDNS/lmdb-go/lmdb"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/sdk/hints"
)
var _ hints.HintsDB = (*LMDBHints)(nil)
@@ -63,7 +62,7 @@ func (lh *LMDBHints) Close() {
lh.env.Close()
}
func (lh *LMDBHints) Save(pubkey string, relay string, hintkey hints.HintKey, ts nostr.Timestamp) {
func (lh *LMDBHints) Save(pubkey nostr.PubKey, relay string, hintkey hints.HintKey, ts nostr.Timestamp) {
if now := nostr.Now(); ts > now {
ts = now
}
@@ -91,7 +90,7 @@ func (lh *LMDBHints) Save(pubkey string, relay string, hintkey hints.HintKey, ts
}
}
func (lh *LMDBHints) TopN(pubkey string, n int) []string {
func (lh *LMDBHints) TopN(pubkey nostr.PubKey, n int) []string {
type relayScore struct {
relay string
score int64
@@ -107,11 +106,10 @@ func (lh *LMDBHints) TopN(pubkey string, n int) []string {
}
defer cursor.Close()
prefix, _ := hex.DecodeString(pubkey)
k, v, err := cursor.Get(prefix, nil, lmdb.SetRange)
k, v, err := cursor.Get(pubkey[:], nil, lmdb.SetRange)
for ; err == nil; k, v, err = cursor.Get(nil, nil, lmdb.Next) {
// check if we're still in the prefix range
if len(k) < 32 || !bytes.Equal(k[:32], prefix) {
if len(k) < 32 || !bytes.Equal(k[:32], pubkey[:]) {
break
}
@@ -182,7 +180,7 @@ func (lh *LMDBHints) PrintScores() {
}
}
func (lh *LMDBHints) GetDetailedScores(pubkey string, n int) []hints.RelayScores {
func (lh *LMDBHints) GetDetailedScores(pubkey nostr.PubKey, n int) []hints.RelayScores {
type relayScore struct {
relay string
tss timestamps
@@ -199,11 +197,10 @@ func (lh *LMDBHints) GetDetailedScores(pubkey string, n int) []hints.RelayScores
}
defer cursor.Close()
prefix, _ := hex.DecodeString(pubkey)
k, v, err := cursor.Get(prefix, nil, lmdb.SetRange)
k, v, err := cursor.Get(pubkey[:], nil, lmdb.SetRange)
for ; err == nil; k, v, err = cursor.Get(nil, nil, lmdb.Next) {
// check if we're still in the prefix range
if len(k) < 32 || !bytes.Equal(k[:32], prefix) {
if len(k) < 32 || !bytes.Equal(k[:32], pubkey[:]) {
break
}

View File

@@ -4,12 +4,12 @@ import (
"encoding/binary"
"encoding/hex"
"github.com/nbd-wtf/go-nostr"
"fiatjaf.com/nostr"
)
func encodeKey(pubhintkey, relay string) []byte {
func encodeKey(pubhintkey nostr.PubKey, relay string) []byte {
k := make([]byte, 32+len(relay))
hex.Decode(k[0:32], []byte(pubhintkey))
copy(k[0:32], pubhintkey[:])
copy(k[32:], relay)
return k
}

View File

@@ -6,15 +6,15 @@ import (
"slices"
"sync"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/sdk/hints"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/sdk/hints"
)
var _ hints.HintsDB = (*HintDB)(nil)
type HintDB struct {
RelayBySerial []string
OrderedRelaysByPubKey map[string][]RelayEntry
OrderedRelaysByPubKey map[nostr.PubKey][]RelayEntry
sync.Mutex
}
@@ -22,11 +22,11 @@ type HintDB struct {
func NewHintDB() *HintDB {
return &HintDB{
RelayBySerial: make([]string, 0, 100),
OrderedRelaysByPubKey: make(map[string][]RelayEntry, 100),
OrderedRelaysByPubKey: make(map[nostr.PubKey][]RelayEntry, 100),
}
}
func (db *HintDB) Save(pubkey string, relay string, key hints.HintKey, ts nostr.Timestamp) {
func (db *HintDB) Save(pubkey nostr.PubKey, relay string, key hints.HintKey, ts nostr.Timestamp) {
if now := nostr.Now(); ts > now {
ts = now
}
@@ -67,7 +67,7 @@ func (db *HintDB) Save(pubkey string, relay string, key hints.HintKey, ts nostr.
db.OrderedRelaysByPubKey[pubkey] = entries
}
func (db *HintDB) TopN(pubkey string, n int) []string {
func (db *HintDB) TopN(pubkey nostr.PubKey, n int) []string {
db.Lock()
defer db.Unlock()
@@ -104,7 +104,7 @@ func (db *HintDB) PrintScores() {
}
}
func (db *HintDB) GetDetailedScores(pubkey string, n int) []hints.RelayScores {
func (db *HintDB) GetDetailedScores(pubkey nostr.PubKey, n int) []hints.RelayScores {
db.Lock()
defer db.Unlock()

View File

@@ -1,272 +0,0 @@
package sqlh
import (
"database/sql"
"fmt"
"strconv"
"strings"
"github.com/jmoiron/sqlx"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/sdk/hints"
)
type SQLHints struct {
*sqlx.DB
interop interop
saves [7]*sqlx.Stmt
topN *sqlx.Stmt
}
// NewSQLHints takes an sqlx.DB connection (db) and a database type name (driverName ).
// driverName must be either "postgres" or "sqlite3" -- this is so we can slightly change the queries.
func NewSQLHints(db *sql.DB, driverName string) (SQLHints, error) {
sh := SQLHints{DB: sqlx.NewDb(db, driverName)}
switch driverName {
case "sqlite3":
sh.interop = sqliteInterop
case "postgres":
sh.interop = postgresInterop
default:
return sh, fmt.Errorf("unknown database driver '%s'", driverName)
}
// db migrations
if txn, err := sh.Beginx(); err != nil {
return SQLHints{}, err
} else {
if _, err := txn.Exec(`CREATE TABLE IF NOT EXISTS nostr_sdk_db_version (version int)`); err != nil {
txn.Rollback()
return SQLHints{}, err
}
var version int
if err := txn.Get(&version, `SELECT version FROM nostr_sdk_db_version`); err != nil && err != sql.ErrNoRows {
txn.Rollback()
return SQLHints{}, err
}
if version == 0 {
if _, err := txn.Exec(`INSERT INTO nostr_sdk_db_version VALUES (0)`); err != nil {
txn.Rollback()
return SQLHints{}, err
}
version = 1
if _, err := txn.Exec(
`CREATE TABLE IF NOT EXISTS nostr_sdk_pubkey_relays (` +
`pubkey text, ` +
`relay text, ` +
`last_fetch_attempt integer, ` +
`most_recent_event_fetched integer, ` +
`last_in_relay_list integer, ` +
`last_in_tag integer, ` +
`last_in_nprofile integer, ` +
`last_in_nevent integer, ` +
`last_in_nip05 integer ` +
`)`,
); err != nil {
txn.Rollback()
return SQLHints{}, err
}
if _, err := txn.Exec(
`CREATE UNIQUE INDEX IF NOT EXISTS pkr ON nostr_sdk_pubkey_relays (pubkey, relay)`,
); err != nil {
txn.Rollback()
return SQLHints{}, err
}
if _, err := txn.Exec(
`CREATE INDEX IF NOT EXISTS bypk ON nostr_sdk_pubkey_relays (pubkey)`,
); err != nil {
txn.Rollback()
return SQLHints{}, err
}
}
if version == 1 {
version = 2
if _, err := txn.Exec(
`ALTER TABLE nostr_sdk_pubkey_relays DROP COLUMN last_in_tag`,
); err != nil {
txn.Rollback()
return SQLHints{}, err
}
if _, err := txn.Exec(
`ALTER TABLE nostr_sdk_pubkey_relays DROP COLUMN last_in_nprofile`,
); err != nil {
txn.Rollback()
return SQLHints{}, err
}
if _, err := txn.Exec(
`ALTER TABLE nostr_sdk_pubkey_relays DROP COLUMN last_in_nevent`,
); err != nil {
txn.Rollback()
return SQLHints{}, err
}
if _, err := txn.Exec(
`ALTER TABLE nostr_sdk_pubkey_relays DROP COLUMN last_in_nip05`,
); err != nil {
txn.Rollback()
return SQLHints{}, err
}
if _, err := txn.Exec(
`ALTER TABLE nostr_sdk_pubkey_relays ADD COLUMN last_in_hint integer`,
); err != nil {
txn.Rollback()
return SQLHints{}, err
}
}
if _, err := txn.Exec(
fmt.Sprintf(`UPDATE nostr_sdk_db_version SET version = %d`, version),
); err != nil {
txn.Rollback()
return SQLHints{}, err
}
if err := txn.Commit(); err != nil {
txn.Rollback()
return SQLHints{}, err
}
}
// prepare statements
for i := range hints.KeyBasePoints {
col := hints.HintKey(i).String()
stmt, err := sh.Preparex(
`INSERT INTO nostr_sdk_pubkey_relays (pubkey, relay, ` + col + `) VALUES (` + sh.interop.generateBindingSpots(0, 3) + `)
ON CONFLICT (pubkey, relay) DO UPDATE SET ` + col + ` = ` + sh.interop.maxFunc + `(` + sh.interop.generateBindingSpots(3, 1) + `, coalesce(excluded.` + col + `, 0))`,
)
if err != nil {
fmt.Println(
`INSERT INTO nostr_sdk_pubkey_relays (pubkey, relay, ` + col + `) VALUES (` + sh.interop.generateBindingSpots(0, 3) + `)
ON CONFLICT (pubkey, relay) DO UPDATE SET ` + col + ` = ` + sh.interop.maxFunc + `(` + sh.interop.generateBindingSpots(3, 1) + `, coalesce(excluded.` + col + `, 0))`,
)
return sh, fmt.Errorf("failed to prepare statement for %s: %w", col, err)
}
sh.saves[i] = stmt
}
{
stmt, err := sh.Preparex(
`SELECT relay FROM nostr_sdk_pubkey_relays WHERE pubkey = ` + sh.interop.generateBindingSpots(0, 1) + ` ORDER BY (` + sh.scorePartialQuery() + `) DESC LIMIT ` + sh.interop.generateBindingSpots(1, 1),
)
if err != nil {
return sh, fmt.Errorf("failed to prepare statement for querying: %w", err)
}
sh.topN = stmt
}
return sh, nil
}
func (sh SQLHints) TopN(pubkey string, n int) []string {
res := make([]string, 0, n)
err := sh.topN.Select(&res, pubkey, n)
if err != nil && err != sql.ErrNoRows {
nostr.InfoLogger.Printf("[sdk/hints/sql] unexpected error on query for %s: %s\n",
pubkey, err)
}
return res
}
func (sh SQLHints) Save(pubkey string, relay string, key hints.HintKey, ts nostr.Timestamp) {
if now := nostr.Now(); ts > now {
ts = now
}
_, err := sh.saves[key].Exec(pubkey, relay, ts, ts)
if err != nil {
nostr.InfoLogger.Printf("[sdk/hints/sql] unexpected error on insert for %s, %s, %d: %s\n",
pubkey, relay, ts, err)
}
}
func (sh SQLHints) PrintScores() {
fmt.Println("= print scores")
allpubkeys := make([]string, 0, 50)
if err := sh.Select(&allpubkeys, `SELECT DISTINCT pubkey FROM nostr_sdk_pubkey_relays`); err != nil {
panic(err)
}
allrelays := make([]struct {
PubKey string `db:"pubkey"`
Relay string `db:"relay"`
Score float64 `db:"score"`
}, 0, 20)
for _, pubkey := range allpubkeys {
fmt.Println("== relay scores for", pubkey)
if err := sh.Select(&allrelays,
`SELECT pubkey, relay, coalesce(`+sh.scorePartialQuery()+`, 0) AS score
FROM nostr_sdk_pubkey_relays WHERE pubkey = `+sh.interop.generateBindingSpots(0, 1)+` ORDER BY score DESC`, pubkey); err != nil {
panic(err)
}
for i, re := range allrelays {
fmt.Printf(" %3d :: %30s ::> %12d\n", i, re.Relay, int(re.Score))
}
}
}
func (sh SQLHints) GetDetailedScores(pubkey string, n int) []hints.RelayScores {
result := make([]hints.RelayScores, 0, n)
rows, err := sh.Queryx(
`SELECT relay, last_fetch_attempt, most_recent_event_fetched, last_in_relay_list, last_in_hint,
coalesce(`+sh.scorePartialQuery()+`, 0) AS score
FROM nostr_sdk_pubkey_relays
WHERE pubkey = `+sh.interop.generateBindingSpots(0, 1)+`
ORDER BY score DESC
LIMIT `+sh.interop.generateBindingSpots(1, 1),
pubkey, n)
if err != nil {
return nil
}
defer rows.Close()
for rows.Next() {
var rs hints.RelayScores
var scores [4]sql.NullInt64
err := rows.Scan(&rs.Relay, &scores[0], &scores[1], &scores[2], &scores[3], &rs.Sum)
if err != nil {
continue
}
for i, s := range scores {
if s.Valid {
rs.Scores[i] = nostr.Timestamp(s.Int64)
}
}
result = append(result, rs)
}
return result
}
func (sh SQLHints) scorePartialQuery() string {
calc := strings.Builder{}
calc.Grow(len(hints.KeyBasePoints) * (11 + 25 + 32 + 4 + 4 + 9 + 12 + 25 + 12 + 25 + 19 + 3))
for i, points := range hints.KeyBasePoints {
col := hints.HintKey(i).String()
multiplier := strconv.FormatInt(points, 10)
calc.WriteString(`(CASE WHEN `)
calc.WriteString(col)
calc.WriteString(` IS NOT NULL THEN 10000000000 * `)
calc.WriteString(multiplier)
calc.WriteString(` / power(`)
calc.WriteString(sh.interop.maxFunc)
calc.WriteString(`(1, (`)
calc.WriteString(sh.interop.getUnixEpochFunc)
calc.WriteString(` + 86400) - `)
calc.WriteString(col)
calc.WriteString(`), 1.3) ELSE 0 END)`)
if i != len(hints.KeyBasePoints)-1 {
calc.WriteString(` + `)
}
}
return calc.String()
}

View File

@@ -1,48 +0,0 @@
package sqlh
import (
"strconv"
"strings"
)
type interop struct {
maxFunc string
getUnixEpochFunc string
generateBindingSpots func(start, n int) string
}
var sqliteInterop = interop{
maxFunc: "max",
getUnixEpochFunc: "unixepoch()",
generateBindingSpots: func(_, n int) string {
b := strings.Builder{}
b.Grow(n * 2)
for i := range n {
if i == n-1 {
b.WriteString("?")
} else {
b.WriteString("?,")
}
}
return b.String()
},
}
var postgresInterop = interop{
maxFunc: "greatest",
getUnixEpochFunc: "extract(epoch from now())",
generateBindingSpots: func(start, n int) string {
b := strings.Builder{}
b.Grow(n * 2)
end := start + n
for i := start; i < end; i++ {
v := i + 1
b.WriteRune('$')
b.WriteString(strconv.Itoa(v))
if i != end-1 {
b.WriteRune(',')
}
}
return b.String()
},
}

View File

@@ -4,7 +4,7 @@ import (
"os"
"testing"
"github.com/nbd-wtf/go-nostr/sdk/hints/badgerh"
"fiatjaf.com/nostr/sdk/hints/badgerh"
)
func TestBadgerHints(t *testing.T) {

View File

@@ -7,7 +7,7 @@ import (
"os"
"testing"
"fiatjaf.com/nostrlib/sdk/hints/sqlh"
"fiatjaf.com/nostr/sdk/hints/sqlh"
"github.com/stretchr/testify/require"
_ "github.com/tursodatabase/go-libsql"
)

View File

@@ -4,7 +4,7 @@ import (
"os"
"testing"
"github.com/nbd-wtf/go-nostr/sdk/hints/lmdbh"
"fiatjaf.com/nostr/sdk/hints/lmdbh"
)
func TestLMDBHints(t *testing.T) {
@@ -18,4 +18,4 @@ func TestLMDBHints(t *testing.T) {
defer hdb.Close()
runTestWith(t, hdb)
}
}

View File

@@ -8,7 +8,7 @@ import (
"testing"
_ "github.com/mattn/go-sqlite3"
"fiatjaf.com/nostrlib/sdk/hints/sqlh"
"fiatjaf.com/nostr/sdk/hints/sqlh"
"github.com/stretchr/testify/require"
)

View File

@@ -3,7 +3,7 @@ package test
import (
"testing"
"fiatjaf.com/nostrlib/sdk/hints/memoryh"
"fiatjaf.com/nostr/sdk/hints/memoryh"
)
func TestMemoryHints(t *testing.T) {

Some files were not shown because too many files have changed in this diff Show More