diff --git a/basic/Dockerfile b/basic/Dockerfile index 0d35b64..47fea58 100644 --- a/basic/Dockerfile +++ b/basic/Dockerfile @@ -1,8 +1,8 @@ -FROM golang:1.15.5 +FROM golang:1.18 WORKDIR /go/src/app COPY ./ . RUN go get -d -v ./... RUN go install -v ./... -RUN cd basic && make +RUN cd basic && make diff --git a/basic/README b/basic/README.md similarity index 100% rename from basic/README rename to basic/README.md diff --git a/basic/cleanup.go b/basic/cleanup.go deleted file mode 100644 index 773c015..0000000 --- a/basic/cleanup.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - "time" - - "github.com/jmoiron/sqlx" -) - -// every hour, delete all very old events -func cleanupRoutine(db *sqlx.DB) { - for { - time.Sleep(60 * time.Minute) - db.Exec(`DELETE FROM event WHERE created_at < $1`, time.Now().AddDate(0, -3, 0)) - } -} diff --git a/basic/delete.go b/basic/delete.go deleted file mode 100644 index 1f6d5cf..0000000 --- a/basic/delete.go +++ /dev/null @@ -1,6 +0,0 @@ -package main - -func (b *BasicRelay) DeleteEvent(id string, pubkey string) error { - _, err := b.DB.Exec("DELETE FROM events WHERE id = $1 AND pubkey = $2") - return err -} diff --git a/basic/docker-compose.yml b/basic/docker-compose.yml index ee6176b..918a509 100644 --- a/basic/docker-compose.yml +++ b/basic/docker-compose.yml @@ -13,7 +13,7 @@ services: condition: service_healthy ports: - 2700:2700 - command: "./basic/relayer" + command: "./basic/relayer-basic" postgres: image: postgres @@ -29,4 +29,4 @@ services: test: ["CMD-SHELL", "pg_isready -U nostr"] # database username here - nostr, should be changed if other user interval: 10s timeout: 5s - retries: 5 \ No newline at end of file + retries: 5 diff --git a/basic/main.go b/basic/main.go index 3a4fbd1..ce15b8e 100644 --- a/basic/main.go +++ b/basic/main.go @@ -1,44 +1,69 @@ package main import ( + "encoding/json" "fmt" + "time" + "github.com/fiatjaf/go-nostr" "github.com/fiatjaf/relayer" - "github.com/jmoiron/sqlx" - "github.com/jmoiron/sqlx/reflectx" + "github.com/fiatjaf/relayer/storage/postgresql" "github.com/kelseyhightower/envconfig" ) -type BasicRelay struct { +type Relay struct { PostgresDatabase string `envconfig:"POSTGRESQL_DATABASE"` - - DB *sqlx.DB } -func (b *BasicRelay) Name() string { +func (r *Relay) Name() string { return "BasicRelay" } -func (b *BasicRelay) Init() error { - err := envconfig.Process("", b) +func (r *Relay) Storage() relayer.Storage { + return &postgresql.PostgresBackend{DatabaseURL: r.PostgresDatabase} +} + +func (r *Relay) Init() error { + err := envconfig.Process("", r) if err != nil { return fmt.Errorf("couldn't process envconfig: %w", err) } - if db, err := initDB(b.PostgresDatabase); err != nil { - return fmt.Errorf("failed to open database: %w", err) - } else { - db.Mapper = reflectx.NewMapperFunc("json", sqlx.NameMapper) - b.DB = db - } + // every hour, delete all very old events + go func() { + db := r.Storage().(*postgresql.PostgresBackend) - go cleanupRoutine(b.DB) + for { + time.Sleep(60 * time.Minute) + db.DB.Exec(`DELETE FROM event WHERE created_at < $1`, time.Now().AddDate(0, -3, 0)) // 3 months + } + }() return nil } -func main() { - var b BasicRelay +func (r *Relay) AcceptEvent(evt *nostr.Event) bool { + // block events that are too large + jsonb, _ := json.Marshal(evt) + if len(jsonb) > 10000 { + return false + } - relayer.Start(&b) + return true +} + +func (r *Relay) BeforeSave(evt *nostr.Event) { + // do nothing +} + +func (r *Relay) AfterSave(evt *nostr.Event) { + // delete all but the 100 most recent ones for each key + r.Storage().(*postgresql.PostgresBackend).DB.Exec(`DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND created_at < ( + SELECT created_at FROM event WHERE pubkey = $1 + ORDER BY created_at DESC OFFSET 100 LIMIT 1 + )`, evt.PubKey, evt.Kind) +} + +func main() { + relayer.Start(&Relay{}) } diff --git a/basic/query.go b/basic/query.go deleted file mode 100644 index 85f01cd..0000000 --- a/basic/query.go +++ /dev/null @@ -1,158 +0,0 @@ -package main - -import ( - "database/sql" - "encoding/hex" - "errors" - "fmt" - "strconv" - "strings" - "time" - - "github.com/fiatjaf/go-nostr" - "github.com/rs/zerolog/log" -) - -func (b *BasicRelay) QueryEvents(filter *nostr.Filter) (events []nostr.Event, err error) { - var conditions []string - var params []any - - if filter == nil { - err = errors.New("filter cannot be null") - return - } - - if filter.IDs != nil { - if len(filter.IDs) > 500 { - // too many ids, fail everything - return - } - - likeids := make([]string, 0, len(filter.IDs)) - for _, id := range filter.IDs { - // to prevent sql attack here we will check if - // these ids are valid 32byte hex - parsed, err := hex.DecodeString(id) - if err != nil || len(parsed) <= 32 { - continue - } - likeids = append(likeids, fmt.Sprintf("id LIKE '%x%%'", parsed)) - } - if len(likeids) == 0 { - // ids being [] mean you won't get anything - return - } - conditions = append(conditions, "("+strings.Join(likeids, " OR ")+")") - } - - if filter.Authors != nil { - if len(filter.Authors) > 500 { - // too many authors, fail everything - return - } - - likekeys := make([]string, 0, len(filter.Authors)) - for _, key := range filter.Authors { - // to prevent sql attack here we will check if - // these keys are valid 32byte hex - parsed, err := hex.DecodeString(key) - if err != nil || len(parsed) != 32 { - continue - } - likekeys = append(likekeys, fmt.Sprintf("pubkey LIKE '%x%%'", parsed)) - } - if len(likekeys) == 0 { - // authors being [] mean you won't get anything - return - } - conditions = append(conditions, "("+strings.Join(likekeys, " OR ")+")") - } - - if filter.Kinds != nil { - if len(filter.Kinds) > 10 { - // too many kinds, fail everything - return - } - - if len(filter.Kinds) == 0 { - // kinds being [] mean you won't get anything - return - } - // no sql injection issues since these are ints - inkinds := make([]string, len(filter.Kinds)) - for i, kind := range filter.Kinds { - inkinds[i] = strconv.Itoa(kind) - } - conditions = append(conditions, `kind IN (`+strings.Join(inkinds, ",")+`)`) - } - - tagQuery := make([]string, 0, 1) - for _, values := range filter.Tags { - if len(values) == 0 { - // any tag set to [] is wrong - return - } - - // add these tags to the query - tagQuery = append(tagQuery, values...) - - if len(tagQuery) > 10 { - // too many tags, fail everything - return - } - } - - if len(tagQuery) > 0 { - arrayBuild := make([]string, len(tagQuery)) - for i, tagValue := range tagQuery { - arrayBuild[i] = "?" - params = append(params, tagValue) - } - - // we use a very bad implementation in which we only check the tag values and - // ignore the tag names - conditions = append(conditions, - "tagvalues && ARRAY["+strings.Join(arrayBuild, ",")+"]") - } - - if filter.Since != nil { - conditions = append(conditions, "created_at > ?") - params = append(params, filter.Since.Unix()) - } - if filter.Until != nil { - conditions = append(conditions, "created_at < ?") - params = append(params, filter.Until.Unix()) - } - - if len(conditions) == 0 { - // fallback - conditions = append(conditions, "true") - } - - query := b.DB.Rebind(`SELECT - id, pubkey, created_at, kind, tags, content, sig - FROM event WHERE ` + - strings.Join(conditions, " AND ") + - " ORDER BY created_at LIMIT 100") - - rows, err := b.DB.Query(query, params...) - if err != nil && err != sql.ErrNoRows { - log.Warn().Err(err).Interface("filter", filter).Str("query", query). - Msg("failed to fetch events") - return nil, fmt.Errorf("failed to fetch events: %w", err) - } - - for rows.Next() { - var evt nostr.Event - var timestamp int64 - err := rows.Scan(&evt.ID, &evt.PubKey, ×tamp, - &evt.Kind, &evt.Tags, &evt.Content, &evt.Sig) - if err != nil { - return nil, fmt.Errorf("failed to scan row: %w", err) - } - evt.CreatedAt = time.Unix(timestamp, 0) - events = append(events, evt) - } - - return events, nil -} diff --git a/basic/save.go b/basic/save.go deleted file mode 100644 index 0c9b4ee..0000000 --- a/basic/save.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - "encoding/json" - "errors" - "fmt" - "strings" - "time" - - "github.com/fiatjaf/go-nostr" -) - -func (b *BasicRelay) SaveEvent(evt *nostr.Event) error { - // disallow large contents - if len(evt.Content) > 1000 { - return errors.New("event content too large") - } - - // react to different kinds of events - if evt.Kind == nostr.KindSetMetadata || evt.Kind == nostr.KindContactList || (10000 <= evt.Kind && evt.Kind < 20000) { - // delete past events from this user - b.DB.Exec(`DELETE FROM event WHERE pubkey = $1 AND kind = $2`, evt.PubKey, evt.Kind) - } else if evt.Kind == nostr.KindRecommendServer { - // delete past recommend_server events equal to this one - b.DB.Exec(`DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND content = $3`, - evt.PubKey, evt.Kind, evt.Content) - } else { - // delete all but the 100 most recent ones - b.DB.Exec(`DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND created_at < ( - SELECT created_at FROM event WHERE pubkey = $1 - ORDER BY created_at DESC OFFSET 100 LIMIT 1 - )`, - evt.PubKey, evt.Kind) - } - - // insert - tagsj, _ := json.Marshal(evt.Tags) - _, err := b.DB.Exec(` - INSERT INTO event (id, pubkey, created_at, kind, tags, content, sig) - VALUES ($1, $2, $3, $4, $5, $6, $7) - `, evt.ID, evt.PubKey, evt.CreatedAt.Unix(), evt.Kind, tagsj, evt.Content, evt.Sig) - if err != nil { - if strings.Index(err.Error(), "UNIQUE") != -1 { - // already exists - return nil - } - - return fmt.Errorf("failed to save event %s: %w", evt.ID, err) - } - - // delete ephemeral events after a minute - go func() { - time.Sleep(75 * time.Second) - b.DB.Exec("DELETE FROM event WHERE id = $1", evt.ID) - }() - - return nil -} diff --git a/expensive/.gitignore b/expensive/.gitignore new file mode 100644 index 0000000..d7d0b22 --- /dev/null +++ b/expensive/.gitignore @@ -0,0 +1 @@ +relayer-expensive diff --git a/expensive/Makefile b/expensive/Makefile new file mode 100644 index 0000000..28eabee --- /dev/null +++ b/expensive/Makefile @@ -0,0 +1,2 @@ +relayer-expensive: $(shell find .. -name "*.go") + CC=$$(which musl-gcc) go build -ldflags='-s -w -linkmode external -extldflags "-static"' -o ./relayer-expensive diff --git a/expensive/README.md b/expensive/README.md new file mode 100644 index 0000000..fb300d3 --- /dev/null +++ b/expensive/README.md @@ -0,0 +1,31 @@ +expensive-relay, a sybil-free corner of nostr +============================================= + + - a nostr relay implementation based on relayer. + - uses postgres, which I think must be over version 12 since it uses generated columns. + - requires users to manually register themselves to be able to publish events and pay a fee. this should prevent spam. + - aside from that it's basically the same thing as relayer basic. + +running +------- + +this requires a recent CLN version with Commando. + +grab a binary from the releases page and run it with the following environment variables: + + POSTGRESQL_DATABASE=postgresql://... + CLN_NODE_ID=02fed8723... + CLN_HOST=127.0.0.1:9735 + CLN_RUNE=... + TICKET_PRICE_SATS=500 + +adjust the values above accordingly. + +compiling +--------- + +if you know Go you already know this: + + go install github.com/fiatjaf/relayer/expensive + +or something like that. diff --git a/expensive/handler.go b/expensive/handler.go new file mode 100644 index 0000000..f0657d8 --- /dev/null +++ b/expensive/handler.go @@ -0,0 +1,68 @@ +package main + +import ( + "encoding/json" + "net/http" +) + +func handleWebpage(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html") + w.Write([]byte(` + +
this is a nostr relay that only accepts events published from keys that pay a registration fee. this is an antispam measure. you can still be banned if you're spamming or doing something bad.
+to register your nostr public key, type it below and click the button.
+ + + +
+
+
+
+ `))
+}
+
+func handleInvoice(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ invoice, err := generateInvoice(r.URL.Query().Get("pubkey"))
+ if err != nil {
+ json.NewEncoder(w).Encode(struct {
+ Error string `json:"error"`
+ }{err.Error()})
+ } else {
+ json.NewEncoder(w).Encode(struct {
+ Invoice string `json:"bolt11"`
+ }{invoice})
+ }
+}
diff --git a/expensive/lightning.go b/expensive/lightning.go
new file mode 100644
index 0000000..845a3f5
--- /dev/null
+++ b/expensive/lightning.go
@@ -0,0 +1,78 @@
+package main
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+
+ lnsocket "github.com/jb55/lnsocket/go"
+ "github.com/tidwall/gjson"
+)
+
+func generateLabel(pubkey string) string { return fmt.Sprintf("relayer-expensive:ticket:%s", pubkey) }
+
+func generateInvoice(pubkey string) (string, error) {
+ cln := lnsocket.LNSocket{}
+ cln.GenKey()
+
+ err := cln.ConnectAndInit(r.CLNHost, r.CLNNodeId)
+ if err != nil {
+ return "", err
+ }
+ defer cln.Disconnect()
+
+ // check if there is an invoice already
+ jparams, _ := json.Marshal(map[string]any{
+ "label": generateLabel(pubkey),
+ })
+ result, _ := cln.Rpc(r.CLNRune, "listinvoices", string(jparams))
+ if gjson.Get(result, "invoices.#").Int() == 1 {
+ return gjson.Get(result, "invoices.1.bolt11").String(), nil
+ }
+
+ // otherwise generate an invoice
+ jparams, _ = json.Marshal(map[string]any{
+ "amount_msat": r.TicketPriceSats * 1000,
+ "label": generateLabel(pubkey),
+ "description": fmt.Sprintf("%s's ticket for writing to relayer-expensive", pubkey),
+ })
+ result, err = cln.Rpc(r.CLNRune, "invoice", string(jparams))
+ if err != nil {
+ return "", err
+ }
+
+ resErr := gjson.Get(result, "error")
+ if resErr.Type != gjson.Null {
+ if resErr.Type == gjson.JSON {
+ return "", errors.New(resErr.Get("message").String())
+ } else if resErr.Type == gjson.String {
+ return "", errors.New(resErr.String())
+ }
+ return "", fmt.Errorf("Unknown commando error: '%v'", resErr)
+ }
+
+ invoice := gjson.Get(result, "result.bolt11")
+ if invoice.Type != gjson.String {
+ return "", fmt.Errorf("No bolt11 result found in invoice response, got %v", result)
+ }
+
+ return invoice.String(), nil
+}
+
+func checkInvoicePaidOk(pubkey string) bool {
+ cln := lnsocket.LNSocket{}
+ cln.GenKey()
+
+ err := cln.ConnectAndInit(r.CLNHost, r.CLNNodeId)
+ if err != nil {
+ return false
+ }
+ defer cln.Disconnect()
+
+ jparams, _ := json.Marshal(map[string]any{
+ "label": generateLabel(pubkey),
+ })
+ result, _ := cln.Rpc(r.CLNRune, "listinvoices", string(jparams))
+
+ return gjson.Get(result, "invoices.0.paid").String() == "paid"
+}
diff --git a/expensive/main.go b/expensive/main.go
new file mode 100644
index 0000000..b843430
--- /dev/null
+++ b/expensive/main.go
@@ -0,0 +1,84 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "time"
+
+ "github.com/fiatjaf/go-nostr"
+ "github.com/fiatjaf/relayer"
+ "github.com/fiatjaf/relayer/storage/postgresql"
+ "github.com/kelseyhightower/envconfig"
+)
+
+type Relay struct {
+ PostgresDatabase string `envconfig:"POSTGRESQL_DATABASE"`
+ CLNNodeId string `envconfig:"CLN_NODE_ID"`
+ CLNHost string `envconfig:"CLN_HOST"`
+ CLNRune string `envconfig:"CLN_RUNE"`
+ TicketPriceSats int64 `envconfig:"TICKET_PRICE_SATS"`
+}
+
+var r = &Relay{}
+
+func (r *Relay) Name() string {
+ return "ExpensiveRelay"
+}
+
+func (r *Relay) Storage() relayer.Storage {
+ return &postgresql.PostgresBackend{DatabaseURL: r.PostgresDatabase}
+}
+
+func (r *Relay) Init() error {
+ err := envconfig.Process("", r)
+ if err != nil {
+ return fmt.Errorf("couldn't process envconfig: %w", err)
+ }
+
+ // every hour, delete all very old events
+ go func() {
+ db := r.Storage().(*postgresql.PostgresBackend)
+
+ for {
+ time.Sleep(60 * time.Minute)
+ db.DB.Exec(`DELETE FROM event WHERE created_at < $1`, time.Now().AddDate(0, -6, 0)) // 6 months
+ }
+ }()
+
+ // special handlers
+ relayer.Router.Path("/").HandlerFunc(handleWebpage)
+ relayer.Router.Path("/invoice").HandlerFunc(handleInvoice)
+
+ return nil
+}
+
+func (r *Relay) AcceptEvent(evt *nostr.Event) bool {
+ // only accept they have a good preimage for a paid invoice for their public key
+ if !checkInvoicePaidOk(evt.PubKey) {
+ return false
+ }
+
+ // block events that are too large
+ jsonb, _ := json.Marshal(evt)
+ if len(jsonb) > 100000 {
+ return false
+ }
+
+ return true
+}
+
+func (r *Relay) BeforeSave(evt *nostr.Event) {
+ // do nothing
+}
+
+func (r *Relay) AfterSave(evt *nostr.Event) {
+ // delete all but the 1000 most recent ones for each key
+ r.Storage().(*postgresql.PostgresBackend).DB.Exec(`DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND created_at < (
+ SELECT created_at FROM event WHERE pubkey = $1
+ ORDER BY created_at DESC OFFSET 1000 LIMIT 1
+ )`, evt.PubKey, evt.Kind)
+}
+
+func main() {
+ relayer.Start(r)
+}
diff --git a/go.mod b/go.mod
index bf52b84..20335fe 100644
--- a/go.mod
+++ b/go.mod
@@ -6,17 +6,52 @@ require (
github.com/fiatjaf/go-nostr v0.7.3
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.4.2
+ github.com/jb55/lnsocket/go v0.0.0-20220315220004-e1e6b88a0bfc
github.com/jmoiron/sqlx v1.3.1
github.com/kelseyhightower/envconfig v1.4.0
- github.com/lib/pq v1.8.0
github.com/rs/cors v1.7.0
github.com/rs/zerolog v1.20.0
+ github.com/tidwall/gjson v1.14.1
)
require (
+ github.com/aead/siphash v1.0.1 // indirect
+ github.com/btcsuite/btcd v0.23.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
+ github.com/btcsuite/btcd/btcutil v1.1.1 // indirect
+ github.com/btcsuite/btcd/btcutil/psbt v1.1.4 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
+ github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect
+ github.com/btcsuite/btcwallet v0.15.1 // indirect
+ github.com/btcsuite/btcwallet/wallet/txauthor v1.2.3 // indirect
+ github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 // indirect
+ github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0 // indirect
+ github.com/btcsuite/btcwallet/walletdb v1.4.0 // indirect
+ github.com/btcsuite/btcwallet/wtxmgr v1.5.0 // indirect
+ github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect
+ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
+ github.com/decred/dcrd/lru v1.0.0 // indirect
+ github.com/go-errors/errors v1.0.1 // indirect
+ github.com/kkdai/bstream v1.0.0 // indirect
+ github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect
+ github.com/lightninglabs/neutrino v0.14.2 // indirect
+ github.com/lightningnetwork/lnd v0.15.0-beta // indirect
+ github.com/lightningnetwork/lnd/clock v1.1.0 // indirect
+ github.com/lightningnetwork/lnd/queue v1.1.0 // indirect
+ github.com/lightningnetwork/lnd/ticker v1.1.0 // indirect
+ github.com/lightningnetwork/lnd/tlv v1.0.3 // indirect
+ github.com/lightningnetwork/lnd/tor v1.0.1 // indirect
+ github.com/miekg/dns v1.1.43 // indirect
+ github.com/tidwall/match v1.1.1 // indirect
+ github.com/tidwall/pretty v1.2.0 // indirect
github.com/valyala/fastjson v1.6.3 // indirect
+ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
+ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
+ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
+ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
)
+
+replace github.com/jb55/lnsocket/go => /home/fiatjaf/comp/lnsocket/go
diff --git a/go.sum b/go.sum
index 0e80f3e..d4895f4 100644
--- a/go.sum
+++ b/go.sum
@@ -1,24 +1,69 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw=
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U=
+github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
+github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
+github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
+github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
+github.com/btcsuite/btcd v0.22.0-beta.0.20220204213055-eaf0459ff879/go.mod h1:osu7EoKiL36UThEgzYPqdRaxeo0NU8VoXqgcnwpey0g=
+github.com/btcsuite/btcd v0.22.0-beta.0.20220207191057-4dc4ff7963b4/go.mod h1:7alexyj/lHlOtr2PJK7L/+HDJZpcGDn/pAU98r7DY08=
+github.com/btcsuite/btcd v0.22.0-beta.0.20220316175102-8d5c75c28923/go.mod h1:taIcYprAW2g6Z9S0gGUxyR+zDwimyDMK5ePOX+iJ2ds=
github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y=
+github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
+github.com/btcsuite/btcd v0.23.1 h1:IB8cVQcC2X5mHbnfirLG5IZnkWYNTPlLZVrxUYSotbE=
+github.com/btcsuite/btcd v0.23.1/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
+github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
+github.com/btcsuite/btcd/btcec/v2 v2.1.1/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
+github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
+github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A=
+github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE=
+github.com/btcsuite/btcd/btcutil v1.1.1 h1:hDcDaXiP0uEzR8Biqo2weECKqEw0uHDZ9ixIWevVQqY=
+github.com/btcsuite/btcd/btcutil v1.1.1/go.mod h1:nbKlBMNm9FGsdvKvu0essceubPiAcI57pYBNnsLAa34=
+github.com/btcsuite/btcd/btcutil/psbt v1.1.4 h1:Edx4AfBn+YPam2KP5AobDitulGp4r1Oibm8oruzkMdI=
+github.com/btcsuite/btcd/btcutil/psbt v1.1.4/go.mod h1:9AyU6EQVJ9Iw9zPyNT1lcdHd6cnEZdno5wLu5FY74os=
+github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
+github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o=
+github.com/btcsuite/btcwallet v0.15.1 h1:SKfh/l2Bgz9sJwHZvfiVbZ8Pl3N/8fFcWWXzsAPz9GU=
+github.com/btcsuite/btcwallet v0.15.1/go.mod h1:7OFsQ8ypiRwmr67hE0z98uXgJgXGAihE79jCib9x6ag=
+github.com/btcsuite/btcwallet/wallet/txauthor v1.2.3 h1:M2yr5UlULvpqtxUqpMxTME/pA92Z9cpqeyvAFk9lAg0=
+github.com/btcsuite/btcwallet/wallet/txauthor v1.2.3/go.mod h1:T2xSiKGpUkSLCh68aF+FMXmKK9mFqNdHl9VaqOr+JjU=
+github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 h1:BtEN5Empw62/RVnZ0VcJaVtVlBijnLlJY+dwjAye2Bg=
+github.com/btcsuite/btcwallet/wallet/txrules v1.2.0/go.mod h1:AtkqiL7ccKWxuLYtZm8Bu8G6q82w4yIZdgq6riy60z0=
+github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0 h1:wZnOolEAeNOHzHTnznw/wQv+j35ftCIokNrnOTOU5o8=
+github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs=
+github.com/btcsuite/btcwallet/walletdb v1.3.5/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU=
+github.com/btcsuite/btcwallet/walletdb v1.4.0 h1:/C5JRF+dTuE2CNMCO/or5N8epsrhmSM4710uBQoYPTQ=
+github.com/btcsuite/btcwallet/walletdb v1.4.0/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU=
+github.com/btcsuite/btcwallet/wtxmgr v1.5.0 h1:WO0KyN4l6H3JWnlFxfGR7r3gDnlGT7W2cL8vl6av4SU=
+github.com/btcsuite/btcwallet/wtxmgr v1.5.0/go.mod h1:TQVDhFxseiGtZwEPvLgtfyxuNUDsIdaJdshvWzR0HJ4=
+github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
+github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
+github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA=
+github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
+github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -27,89 +72,280 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
+github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
+github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
+github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
+github.com/dvyukov/go-fuzz v0.0.0-20210602112143-b1f3d6f4ef4e h1:qTP1telKJHlToHlwPQNmVg4yfMDMHe4Z3SYmzkrvA2M=
+github.com/fergusstrange/embedded-postgres v1.10.0 h1:YnwF6xAQYmKLAXXrrRx4rHDLih47YJwVPvg8jeKfdNg=
github.com/fiatjaf/go-nostr v0.7.3 h1:Z95U2qTTj3a41+ybopYjfKZ+bMpNFM43ARA7B4IOYVs=
github.com/fiatjaf/go-nostr v0.7.3/go.mod h1:pbJCdZzBKPB8wgQDipS5POnOV1afhcFzJUIwJ5qKSrU=
+github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
+github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
+github.com/jackc/pgconn v1.10.0 h1:4EYhlDVEMsJ30nNj0mmgwIUXoq7e9sMJrVC2ED6QlCU=
+github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
+github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
+github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI=
+github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
+github.com/jackc/pgtype v1.8.1 h1:9k0IXtdJXHJbyAWQgbWr1lU+MEhPXZz6RIXxfR5oxXs=
+github.com/jackc/pgx/v4 v4.13.0 h1:JCjhT5vmhMAf/YwBHLvrBn4OGdIQBiFG6ym8Zmdx570=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmoiron/sqlx v1.3.1 h1:aLN7YINNZ7cYOPK3QC83dbM6KT0NMqVMw961TqrejlE=
github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
+github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
+github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
+github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
+github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
+github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8=
+github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA=
+github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
+github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
-github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
-github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
+github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc=
+github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk=
+github.com/lightninglabs/neutrino v0.14.2 h1:yrnZUCYMZ5ECtXhgDrzqPq2oX8awoAN2D/cgCewJcCo=
+github.com/lightninglabs/neutrino v0.14.2/go.mod h1:OICUeTCn+4Tu27YRJIpWvvqySxx4oH4vgdP33Sw9RDc=
+github.com/lightningnetwork/lightning-onion v1.0.2-0.20220211021909-bb84a1ccb0c5 h1:TkKwqFcQTGYoI+VEqyxA8rxpCin8qDaYX0AfVRinT3k=
+github.com/lightningnetwork/lnd v0.15.0-beta h1:smzYjJqL4nGuj4qrAWdikrPzPJ8fcPRFHQ86S2tHR1M=
+github.com/lightningnetwork/lnd v0.15.0-beta/go.mod h1:Tm7LZrYeR2JQH1gEOKmd0NTCgjJ1Bnujkx4lcz9b5+A=
+github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg=
+github.com/lightningnetwork/lnd/clock v1.1.0 h1:/yfVAwtPmdx45aQBoXQImeY7sOIEr7IXlImRMBOZ7GQ=
+github.com/lightningnetwork/lnd/clock v1.1.0/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg=
+github.com/lightningnetwork/lnd/healthcheck v1.2.2 h1:im+qcpgSuteqRCGeorT9yqVXuLrS6A7/acYzGgarMS4=
+github.com/lightningnetwork/lnd/kvdb v1.3.1 h1:gEz3zudNNRrCLEvqRaktYoKwsUblyHX+MKjR0aI3QnM=
+github.com/lightningnetwork/lnd/queue v1.0.1/go.mod h1:vaQwexir73flPW43Mrm7JOgJHmcEFBWWSl9HlyASoms=
+github.com/lightningnetwork/lnd/queue v1.1.0 h1:YpCJjlIvVxN/R7ww2aNiY8ex7U2fucZDLJ67tI3HFx8=
+github.com/lightningnetwork/lnd/queue v1.1.0/go.mod h1:YTkTVZCxz8tAYreH27EO3s8572ODumWrNdYW2E/YKxg=
+github.com/lightningnetwork/lnd/ticker v1.0.0/go.mod h1:iaLXJiVgI1sPANIF2qYYUJXjoksPNvGNYowB8aRbpX0=
+github.com/lightningnetwork/lnd/ticker v1.1.0 h1:ShoBiRP3pIxZHaETndfQ5kEe+S4NdAY1hiX7YbZ4QE4=
+github.com/lightningnetwork/lnd/ticker v1.1.0/go.mod h1:ubqbSVCn6RlE0LazXuBr7/Zi6QT0uQo++OgIRBxQUrk=
+github.com/lightningnetwork/lnd/tlv v1.0.2/go.mod h1:fICAfsqk1IOsC1J7G9IdsWX1EqWRMqEDCNxZJSKr9C4=
+github.com/lightningnetwork/lnd/tlv v1.0.3 h1:0xBZcPuXagP6f7TY/RnLNR4igE21ov6qUdTr5NyvhhI=
+github.com/lightningnetwork/lnd/tlv v1.0.3/go.mod h1:dzR/aZetBri+ZY/fHbwV06fNn/3UID6htQzbHfREFdo=
+github.com/lightningnetwork/lnd/tor v1.0.1 h1:A11FrpU0Y//g+fA827W4VnjOeoIvExONdchlLX8wYkA=
+github.com/lightningnetwork/lnd/tor v1.0.1/go.mod h1:RDtaAdwfAm+ONuPYwUhNIH1RAvKPv+75lHPOegUcz64=
+github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
+github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE=
+github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
+github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+github.com/nwaples/rardecode v1.1.2 h1:Cj0yZY6T1Zx1R7AhTbyGSALm44/Mmq+BAPc4B/p/d3M=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/pierrec/lz4/v4 v4.1.8 h1:ieHkV+i2BRzngO4Wd/3HGowuZStgq6QkPsD1eolNAO4=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
+github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
+github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
+github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
+github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
+github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
+github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
+github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo=
+github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
+github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
+github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA=
github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE=
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
+github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
+github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
+go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
+go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw=
+go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU=
+go.etcd.io/etcd/client/v2 v2.305.0 h1:ftQ0nOOHMcbMS3KIaDQ0g5Qcd6bhaBrQT6b89DfwLTs=
+go.etcd.io/etcd/client/v3 v3.5.0 h1:62Eh0XOro+rDwkrypAGDfgmNh5Joq+z+W9HZdlXMzek=
+go.etcd.io/etcd/pkg/v3 v3.5.0 h1:ntrg6vvKRW26JRmHTE0iNlDgYK6JX3hg/4cD62X0ixk=
+go.etcd.io/etcd/raft/v3 v3.5.0 h1:kw2TmO3yFTgE+F0mdKkG7xMxkit2duBDa2Hu6D/HMlw=
+go.etcd.io/etcd/server/v3 v3.5.0 h1:jk8D/lwGEDlQU9kZXUFMSANkE22Sg5+mW27ip8xcF9E=
+go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 h1:sO4WKdPAudZGKPcpZT4MJn6JaDmpyLrMPDGGyA1SttE=
+go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g=
+go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg=
+go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8=
+go.opentelemetry.io/otel/sdk v0.20.0 h1:JsxtGXd06J8jrnya7fdI/U/MR6yXA5DtbZy+qoHQlr8=
+go.opentelemetry.io/otel/sdk/export/metric v0.20.0 h1:c5VRjxCXdQlx1HjzwGdQHzZaVI82b5EbBgOu2ljD92g=
+go.opentelemetry.io/otel/sdk/metric v0.20.0 h1:7ao1wpzHRVKf0OQ7GIxiQJA6X7DLX9o14gmVon7mMK8=
+go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw=
+go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
+golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
+google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced h1:c5geK1iMU3cDKtFrCVQIcjR3W+JOZMuhIyICMCTbtus=
+google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
+google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
+google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
+sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
diff --git a/handlers.go b/handlers.go
index f7f960f..537d446 100644
--- a/handlers.go
+++ b/handlers.go
@@ -35,6 +35,11 @@ var upgrader = websocket.Upgrader{
func handleWebsocket(relay Relay) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
+ store := relay.Storage()
+ advancedQuerier, _ := store.(AdvancedQuerier)
+ advancedDeleter, _ := store.(AdvancedDeleter)
+ advancedSaver, _ := store.(AdvancedSaver)
+
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Warn().Err(err).Msg("failed to upgrade websocket")
@@ -111,6 +116,18 @@ func handleWebsocket(relay Relay) func(http.ResponseWriter, *http.Request) {
hash := sha256.Sum256(serialized)
evt.ID = hex.EncodeToString(hash[:])
+ // block too many indexable tags
+ t := 0
+ for _, tag := range evt.Tags {
+ if len(tag[0]) == 1 {
+ t++
+ }
+ }
+ if t > 3 {
+ notice = "too many indexable tags"
+ return
+ }
+
// check signature (requires the ID to be set)
if ok, err := evt.CheckSignature(); err != nil {
notice = "signature verification error"
@@ -124,21 +141,40 @@ func handleWebsocket(relay Relay) func(http.ResponseWriter, *http.Request) {
// event deletion -- nip09
for _, tag := range evt.Tags {
if len(tag) >= 2 && tag[0] == "e" {
- if err := relay.DeleteEvent(tag[1], evt.PubKey); err != nil {
+ if advancedDeleter != nil {
+ advancedDeleter.BeforeDelete(tag[1], evt.PubKey)
+ }
+
+ if err := store.DeleteEvent(tag[1], evt.PubKey); err != nil {
notice = fmt.Sprintf("failed to delete: %s", err.Error())
return
}
+
+ if advancedDeleter != nil {
+ advancedDeleter.AfterDelete(tag[1], evt.PubKey)
+ }
}
}
return
}
- err = relay.SaveEvent(&evt)
- if err != nil {
+ if !relay.AcceptEvent(&evt) {
+ return
+ }
+
+ if advancedSaver != nil {
+ advancedSaver.BeforeSave(&evt)
+ }
+
+ if err := store.SaveEvent(&evt); err != nil {
notice = err.Error()
return
}
+ if advancedSaver != nil {
+ advancedSaver.AfterSave(&evt)
+ }
+
notifyListeners(&evt)
break
case "REQ":
@@ -159,12 +195,21 @@ func handleWebsocket(relay Relay) func(http.ResponseWriter, *http.Request) {
return
}
- events, err := relay.QueryEvents(&filters[i])
+ if advancedQuerier != nil {
+ advancedQuerier.BeforeQuery(&filters[i])
+ }
+
+ events, err := store.QueryEvents(&filters[i])
if err == nil {
for _, event := range events {
ws.WriteJSON([]interface{}{"EVENT", id, event})
}
}
+
+ if advancedQuerier != nil {
+ advancedQuerier.AfterQuery(&filters[i])
+ }
+
ws.WriteJSON([]interface{}{"EOSE", id})
}
diff --git a/interface.go b/interface.go
index 37ad03d..a89a06b 100644
--- a/interface.go
+++ b/interface.go
@@ -10,9 +10,8 @@ var Log = log
type Relay interface {
Name() string
Init() error
- SaveEvent(*nostr.Event) error
- DeleteEvent(id string, pubkey string) error
- QueryEvents(*nostr.Filter) ([]nostr.Event, error)
+ AcceptEvent(*nostr.Event) bool
+ Storage() Storage
}
type Injector interface {
@@ -22,3 +21,26 @@ type Injector interface {
type Informationer interface {
GetNIP11InformationDocument() nip11.RelayInformationDocument
}
+
+type AdvancedQuerier interface {
+ BeforeQuery(*nostr.Filter)
+ AfterQuery(*nostr.Filter)
+}
+
+type AdvancedDeleter interface {
+ BeforeDelete(id string, pubkey string)
+ AfterDelete(id string, pubkey string)
+}
+
+type AdvancedSaver interface {
+ BeforeSave(*nostr.Event)
+ AfterSave(*nostr.Event)
+}
+
+type Storage interface {
+ Init() error
+
+ QueryEvents(filter *nostr.Filter) (events []nostr.Event, err error)
+ DeleteEvent(id string, pubkey string) error
+ SaveEvent(event *nostr.Event) error
+}
diff --git a/start.go b/start.go
index 84b8ad1..295a237 100644
--- a/start.go
+++ b/start.go
@@ -32,17 +32,23 @@ func Start(relay Relay) {
// expose this Log instance so implementations can use it
Log = log.With().Str("name", relay.Name()).Logger()
+ // allow implementations to do initialization stuff
+ if err := relay.Init(); err != nil {
+ Log.Fatal().Err(err).Msg("failed to start")
+ }
+
+ // initialize storage
+ if err := relay.Storage().Init(); err != nil {
+ log.Fatal().Err(err).Msg("error initializing storage")
+ return
+ }
+
// catch the websocket call before anything else
Router.Path("/").Headers("Upgrade", "websocket").HandlerFunc(handleWebsocket(relay))
// nip-11, relay information
Router.Path("/").Headers("Accept", "application/nostr+json").HandlerFunc(handleNIP11(relay))
- // allow implementations to do initialization stuff
- if err := relay.Init(); err != nil {
- Log.Fatal().Err(err).Msg("failed to start")
- }
-
// wait for events to come from implementations, if this is implemented
if inj, ok := relay.(Injector); ok {
go func() {
diff --git a/storage/postgresql/delete.go b/storage/postgresql/delete.go
new file mode 100644
index 0000000..1d8c902
--- /dev/null
+++ b/storage/postgresql/delete.go
@@ -0,0 +1,6 @@
+package postgresql
+
+func (b PostgresBackend) DeleteEvent(id string, pubkey string) error {
+ _, err := b.DB.Exec("DELETE FROM events WHERE id = $1 AND pubkey = $2")
+ return err
+}
diff --git a/basic/postgresql.go b/storage/postgresql/init.go
similarity index 74%
rename from basic/postgresql.go
rename to storage/postgresql/init.go
index c804347..30d4e80 100644
--- a/basic/postgresql.go
+++ b/storage/postgresql/init.go
@@ -1,20 +1,23 @@
-package main
+package postgresql
import (
"github.com/fiatjaf/relayer"
"github.com/jmoiron/sqlx"
- _ "github.com/lib/pq"
+ "github.com/jmoiron/sqlx/reflectx"
)
-func initDB(dburl string) (*sqlx.DB, error) {
- db, err := sqlx.Connect("postgres", dburl)
+func (b *PostgresBackend) Init() error {
+ db, err := sqlx.Connect("postgres", b.DatabaseURL)
if err != nil {
- return nil, err
+ return err
}
- _, err = db.Exec(`
+ db.Mapper = reflectx.NewMapperFunc("json", sqlx.NameMapper)
+ b.DB = db
+
+ _, err = b.DB.Exec(`
CREATE FUNCTION tags_to_tagvalues(jsonb) RETURNS text[]
- AS 'SELECT array_agg(t->>1) FROM (SELECT jsonb_array_elements($1) AS t)s;'
+ AS 'SELECT array_agg(t->>1) FROM (SELECT jsonb_array_elements($1) AS t)s WHERE length(array_agg(t->>0)) = 1;'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
@@ -38,5 +41,5 @@ CREATE INDEX IF NOT EXISTS kindidx ON event (kind);
CREATE INDEX IF NOT EXISTS arbitrarytagvalues ON event USING gin (tagvalues);
`)
relayer.Log.Print(err)
- return db, nil
+ return nil
}
diff --git a/storage/postgresql/postgresql.go b/storage/postgresql/postgresql.go
new file mode 100644
index 0000000..aa55ddc
--- /dev/null
+++ b/storage/postgresql/postgresql.go
@@ -0,0 +1,10 @@
+package postgresql
+
+import (
+ "github.com/jmoiron/sqlx"
+)
+
+type PostgresBackend struct {
+ *sqlx.DB
+ DatabaseURL string
+}
diff --git a/whitelisted/query.go b/storage/postgresql/query.go
similarity index 94%
rename from whitelisted/query.go
rename to storage/postgresql/query.go
index e77cf8d..74b73c0 100644
--- a/whitelisted/query.go
+++ b/storage/postgresql/query.go
@@ -1,4 +1,4 @@
-package main
+package postgresql
import (
"database/sql"
@@ -10,10 +10,10 @@ import (
"time"
"github.com/fiatjaf/go-nostr"
- "github.com/rs/zerolog/log"
+ "github.com/fiatjaf/relayer"
)
-func (b *ClosedRelay) QueryEvents(filter *nostr.Filter) (events []nostr.Event, err error) {
+func (b PostgresBackend) QueryEvents(filter *nostr.Filter) (events []nostr.Event, err error) {
var conditions []string
var params []any
@@ -137,7 +137,9 @@ func (b *ClosedRelay) QueryEvents(filter *nostr.Filter) (events []nostr.Event, e
rows, err := b.DB.Query(query, params...)
if err != nil && err != sql.ErrNoRows {
- log.Warn().Err(err).Interface("filter", filter).Str("query", query).
+ relayer.Log.Warn().Err(err).
+ Interface("filter", filter).
+ Str("query", query).
Msg("failed to fetch events")
return nil, fmt.Errorf("failed to fetch events: %w", err)
}
diff --git a/whitelisted/save.go b/storage/postgresql/save.go
similarity index 76%
rename from whitelisted/save.go
rename to storage/postgresql/save.go
index bfd8a7f..7381c4e 100644
--- a/whitelisted/save.go
+++ b/storage/postgresql/save.go
@@ -1,4 +1,4 @@
-package main
+package postgresql
import (
"encoding/json"
@@ -9,16 +9,7 @@ import (
"github.com/fiatjaf/go-nostr"
)
-func (b *ClosedRelay) SaveEvent(evt *nostr.Event) error {
- // disallow anything from non-authorized pubkeys
- for _, pubkey := range b.AuthorizedPubkeys {
- if pubkey == evt.PubKey {
- goto save
- }
- }
- return fmt.Errorf("event from '%s' not allowed here", evt.PubKey)
-
-save:
+func (b *PostgresBackend) SaveEvent(evt *nostr.Event) error {
// react to different kinds of events
if evt.Kind == nostr.KindSetMetadata || evt.Kind == nostr.KindContactList || (10000 <= evt.Kind && evt.Kind < 20000) {
// delete past events from this user
@@ -27,8 +18,6 @@ save:
// delete past recommend_server events equal to this one
b.DB.Exec(`DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND content = $3`,
evt.PubKey, evt.Kind, evt.Content)
- } else {
- // do not delete any, this is a closed relay so we trust everybody to not spam
}
// insert
diff --git a/whitelisted/README b/whitelisted/README.md
similarity index 91%
rename from whitelisted/README
rename to whitelisted/README.md
index 7b0c5a3..d7f035f 100644
--- a/whitelisted/README
+++ b/whitelisted/README.md
@@ -3,7 +3,7 @@ whitelisted relay
- a basic relay implementation based on relayer.
- uses postgres, which I think must be over version 12 since it uses generated columns.
- - only accepts events from specific pubkeys defined via the environment variable `AUTHORIZED_PUBKEYS` (comma-separated).
+ - only accepts events from specific pubkeys defined via the environment variable `WHITELIST` (comma-separated).
running
-------
diff --git a/whitelisted/delete.go b/whitelisted/delete.go
deleted file mode 100644
index d9c5c40..0000000
--- a/whitelisted/delete.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package main
-
-func (b *ClosedRelay) DeleteEvent(id string, pubkey string) error {
- _, err := b.DB.Exec("DELETE FROM events WHERE id = $1 AND pubkey = $2")
- return err
-}
diff --git a/whitelisted/main.go b/whitelisted/main.go
index 5419740..e21a5b0 100644
--- a/whitelisted/main.go
+++ b/whitelisted/main.go
@@ -1,43 +1,59 @@
package main
import (
+ "encoding/json"
"fmt"
+ "github.com/fiatjaf/go-nostr"
"github.com/fiatjaf/relayer"
- "github.com/jmoiron/sqlx"
- "github.com/jmoiron/sqlx/reflectx"
+ "github.com/fiatjaf/relayer/storage/postgresql"
"github.com/kelseyhightower/envconfig"
)
-type ClosedRelay struct {
- PostgresDatabase string `envconfig:"POSTGRESQL_DATABASE"`
- AuthorizedPubkeys []string `envconfig:"AUTHORIZED_PUBKEYS"`
-
- DB *sqlx.DB
+type Relay struct {
+ PostgresDatabase string `envconfig:"POSTGRESQL_DATABASE"`
+ Whitelist []string `envconfig:"WHITELIST"`
}
-func (b *ClosedRelay) Name() string {
- return "ClosedRelay"
+func (r *Relay) Name() string {
+ return "WhitelistedRelay"
}
-func (b *ClosedRelay) Init() error {
- err := envconfig.Process("", b)
+func (r *Relay) Storage() relayer.Storage {
+ return &postgresql.PostgresBackend{DatabaseURL: r.PostgresDatabase}
+}
+
+func (r *Relay) Init() error {
+ err := envconfig.Process("", r)
if err != nil {
return fmt.Errorf("couldn't process envconfig: %w", err)
}
- if db, err := initDB(b.PostgresDatabase); err != nil {
- return fmt.Errorf("failed to open database: %w", err)
- } else {
- db.Mapper = reflectx.NewMapperFunc("json", sqlx.NameMapper)
- b.DB = db
- }
-
return nil
}
-func main() {
- var b ClosedRelay
+func (r *Relay) AcceptEvent(evt *nostr.Event) bool {
+ // disallow anything from non-authorized pubkeys
+ found := false
+ for _, pubkey := range r.Whitelist {
+ if pubkey == evt.PubKey {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return false
+ }
- relayer.Start(&b)
+ // block events that are too large
+ jsonb, _ := json.Marshal(evt)
+ if len(jsonb) > 100000 {
+ return false
+ }
+
+ return true
+}
+
+func main() {
+ relayer.Start(&Relay{})
}
diff --git a/whitelisted/postgresql.go b/whitelisted/postgresql.go
deleted file mode 100644
index c804347..0000000
--- a/whitelisted/postgresql.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package main
-
-import (
- "github.com/fiatjaf/relayer"
- "github.com/jmoiron/sqlx"
- _ "github.com/lib/pq"
-)
-
-func initDB(dburl string) (*sqlx.DB, error) {
- db, err := sqlx.Connect("postgres", dburl)
- if err != nil {
- return nil, err
- }
-
- _, err = db.Exec(`
-CREATE FUNCTION tags_to_tagvalues(jsonb) RETURNS text[]
- AS 'SELECT array_agg(t->>1) FROM (SELECT jsonb_array_elements($1) AS t)s;'
- LANGUAGE SQL
- IMMUTABLE
- RETURNS NULL ON NULL INPUT;
-
-CREATE TABLE IF NOT EXISTS event (
- id text NOT NULL,
- pubkey text NOT NULL,
- created_at integer NOT NULL,
- kind integer NOT NULL,
- tags jsonb NOT NULL,
- content text NOT NULL,
- sig text NOT NULL,
-
- tagvalues text[] GENERATED ALWAYS AS (tags_to_tagvalues(tags)) STORED
-);
-
-CREATE UNIQUE INDEX IF NOT EXISTS ididx ON event USING btree (id text_pattern_ops);
-CREATE INDEX IF NOT EXISTS pubkeyprefix ON event USING btree (pubkey text_pattern_ops);
-CREATE INDEX IF NOT EXISTS timeidx ON event (created_at);
-CREATE INDEX IF NOT EXISTS kindidx ON event (kind);
-CREATE INDEX IF NOT EXISTS arbitrarytagvalues ON event USING gin (tagvalues);
- `)
- relayer.Log.Print(err)
- return db, nil
-}