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

@@ -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) {

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"
_ "modernc.org/sqlite"
)

View File

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

View File

@@ -7,7 +7,7 @@ import (
"os"
"testing"
"fiatjaf.com/nostrlib/sdk/hints/sqlh"
"fiatjaf.com/nostr/sdk/hints/sqlh"
_ "github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/stretchr/testify/require"