cleaner logic, fix segfault, update readme

This commit is contained in:
Barry Deen 2024-09-07 23:39:23 -04:00
parent dd9763e967
commit 0505740f1e
3 changed files with 133 additions and 42 deletions

View File

@ -1,7 +1,17 @@
# Relay Metadata
RELAY_NAME="utxo WoT relay" RELAY_NAME="utxo WoT relay"
RELAY_PUBKEY="e2ccf7cf20403f3f2a4a55b328f0de3be38558a7d5f33632fdaaefc726c1c8eb" RELAY_PUBKEY="e2ccf7cf20403f3f2a4a55b328f0de3be38558a7d5f33632fdaaefc726c1c8eb" # not your npub!
RELAY_DESCRIPTION="Only notes in utxo WoT" RELAY_DESCRIPTION="Only notes in utxo WoT"
RELAY_URL="wss://wot.utxo.one" RELAY_URL="wss://wot.utxo.one"
# where we should store the database
DB_PATH="db" DB_PATH="db"
# where we should store the index.html and static files
INDEX_PATH="templates/index.html" INDEX_PATH="templates/index.html"
STATIC_PATH="templates/static" STATIC_PATH="templates/static"
# relay behavior
# how often to refresh the relay's view of the WoT in HOURS
REFRESH_INTERVAL_HOURS=24

View File

@ -1,6 +1,6 @@
# WOT Relay # WoT Relay
WOT Relay is a Nostr relay that saves all the notes that people you follow, and people they follow are posting. WOT Relay is a Nostr relay that saves all the notes that people you follow, and people they follow are posting. It's built on the [Khatru](https://khatru.nostr.technology) framework.
## Prerequisites ## Prerequisites
@ -37,6 +37,7 @@ RELAY_DESCRIPTION="Your relay description"
DB_PATH="/home/ubuntu/wot-relay/db" # any path you would like the database to be saved. DB_PATH="/home/ubuntu/wot-relay/db" # any path you would like the database to be saved.
INDEX_PATH="/home/ubuntu/wot-relay/templates/index.html" # path to the index.html file INDEX_PATH="/home/ubuntu/wot-relay/templates/index.html" # path to the index.html file
STATIC_PATH="/home/ubuntu/wot-relay/templates/static" # path to the static folder STATIC_PATH="/home/ubuntu/wot-relay/templates/static" # path to the static folder
REFRESH_INTERVAL=24 # interval in hours to refresh the web of trust
``` ```
### 4. Build the project ### 4. Build the project
@ -65,10 +66,9 @@ Description=WOT Relay Service
After=network.target After=network.target
[Service] [Service]
ExecStart=/path/to/wot-relay ExecStart=/home/ubuntu/wot-relay/wot-relay #change this to your path
WorkingDirectory=/path/to/wot-relay WorkingDirectory=/home/ubuntu/wot-relay #change this to your path
Restart=always Restart=always
EnvironmentFile=/path/to/.env
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
@ -94,7 +94,63 @@ sudo systemctl start wot-relay
sudo systemctl enable wot-relay sudo systemctl enable wot-relay
``` ```
### 6. Start the Project with Docker Compose (optional) #### Permission Issues on Some Systems
the relay may not have permissions to read and write to the database. To fix this, you can change the permissions of the database folder:
```bash
sudo chmod -R 777 /path/to/db
```
### 6. Serving over nginx (optional)
You can serve the relay over nginx by adding the following configuration to your nginx configuration file:
```nginx
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://localhost:3334;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
Replace `yourdomain.com` with your actual domain name.
After adding the configuration, restart nginx:
```bash
sudo systemctl restart nginx
```
### 7. Install Certbot (optional)
If you want to serve the relay over HTTPS, you can use Certbot to generate an SSL certificate.
```bash
sudo apt-get update
sudo apt-get install certbot python3-certbot-nginx
```
After installing Certbot, run the following command to generate an SSL certificate:
```bash
sudo certbot --nginx
```
Follow the instructions to generate the certificate.
### 8. Access the relay
Once everything is set up, the relay will be running on `localhost:3334` or your domain name if you set up nginx.
## Start the Project with Docker Compose
To start the project using Docker Compose, follow these steps: To start the project using Docker Compose, follow these steps:

95
main.go
View File

@ -7,6 +7,7 @@ import (
"log" "log"
"net/http" "net/http"
"os" "os"
"strconv"
"sync" "sync"
"time" "time"
@ -26,6 +27,7 @@ type Config struct {
RelayURL string RelayURL string
IndexPath string IndexPath string
StaticPath string StaticPath string
RefreshInterval int
} }
var pool *nostr.SimplePool var pool *nostr.SimplePool
@ -36,19 +38,27 @@ var mu sync.Mutex
var trustNetworkFilter *blobloom.Filter var trustNetworkFilter *blobloom.Filter
var trustNetworkFilterMu sync.Mutex var trustNetworkFilterMu sync.Mutex
var seedRelays []string var seedRelays []string
var booted bool
var oneHopNetwork []string
func main() { func main() {
booted = false
green := "\033[32m" green := "\033[32m"
reset := "\033[0m" reset := "\033[0m"
art := ` art := `
__ __ ___. _____ ___________ __ 888 888 88888888888 8888888b. 888
/ \ / \ ____\_ |__ _____/ ____\ \__ ___/______ __ __ _______/ |_ 888 o 888 888 888 Y88b 888
\ \/\/ // __ \| __ \ / _ \ __\ | | \_ __ \ | \/ ___/\ __\ 888 d8b 888 888 888 888 888
\ /\ ___/| \_\ \ ( <_> ) | | | | | \/ | /\___ \ | | 888 d888b 888 .d88b. 888 888 d88P .d88b. 888 8888b. 888 888
\__/\ / \___ >___ / \____/|__| |____| |__| |____//____ > |__| 888d88888b888 d88""88b 888 8888888P" d8P Y8b 888 "88b 888 888
\/ \/ \/ \/ 88888P Y88888 888 888 888 888 T88b 88888888 888 .d888888 888 888
` 8888P Y8888 Y88..88P 888 888 T88b Y8b. 888 888 888 Y88b 888
888P Y888 "Y88P" 888 888 T88b "Y8888 888 "Y888888 "Y88888
888
Y8b d88P
powered by: khatru "Y88P"
`
fmt.Println(green + art + reset) fmt.Println(green + art + reset)
log.Println("🚀 booting up web of trust relay") log.Println("🚀 booting up web of trust relay")
@ -104,7 +114,6 @@ func main() {
mu.Unlock() mu.Unlock()
go refreshTrustNetwork(relay, ctx) go refreshTrustNetwork(relay, ctx)
go archiveTrustedNotes(relay, ctx)
mux := relay.Router() mux := relay.Router()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
@ -138,6 +147,13 @@ func main() {
func LoadConfig() Config { func LoadConfig() Config {
godotenv.Load(".env") godotenv.Load(".env")
if os.Getenv("REFRESH_INTERVAL_HOURS") == "" {
os.Setenv("REFRESH_INTERVAL_HOURS", "24")
}
refreshInterval, _ := strconv.Atoi(os.Getenv("REFRESH_INTERVAL_HOURS"))
log.Println("🔄 refresh interval set to", refreshInterval, "hours")
config := Config{ config := Config{
RelayName: getEnv("RELAY_NAME"), RelayName: getEnv("RELAY_NAME"),
RelayPubkey: getEnv("RELAY_PUBKEY"), RelayPubkey: getEnv("RELAY_PUBKEY"),
@ -146,6 +162,7 @@ func LoadConfig() Config {
RelayURL: getEnv("RELAY_URL"), RelayURL: getEnv("RELAY_URL"),
IndexPath: getEnv("INDEX_PATH"), IndexPath: getEnv("INDEX_PATH"),
StaticPath: getEnv("STATIC_PATH"), StaticPath: getEnv("STATIC_PATH"),
RefreshInterval: refreshInterval,
} }
return config return config
@ -174,10 +191,10 @@ func updateTrustNetworkFilter() {
} }
} }
func refreshTrustNetwork(relay *khatru.Relay, ctx context.Context) []string { func refreshTrustNetwork(relay *khatru.Relay, ctx context.Context) {
runTrustNetworkRefresh := func() { runTrustNetworkRefresh := func() {
timeoutCtx, cancel := context.WithTimeout(ctx, 3*time.Second) timeoutCtx, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel() defer cancel()
filters := []nostr.Filter{{ filters := []nostr.Filter{{
@ -188,25 +205,22 @@ func refreshTrustNetwork(relay *khatru.Relay, ctx context.Context) []string {
log.Println("🔍 fetching owner's follows") log.Println("🔍 fetching owner's follows")
for ev := range pool.SubManyEose(timeoutCtx, seedRelays, filters) { for ev := range pool.SubManyEose(timeoutCtx, seedRelays, filters) {
for _, contact := range ev.Event.Tags.GetAll([]string{"p"}) { for _, contact := range ev.Event.Tags.GetAll([]string{"p"}) {
appendPubkey(contact[1]) appendOneHopNetwork(contact[1])
} }
} }
follows := make([]string, len(trustNetwork))
copy(follows, trustNetwork)
log.Println("🌐 building web of trust graph") log.Println("🌐 building web of trust graph")
for i := 0; i < len(follows); i += 200 { for i := 0; i < len(oneHopNetwork); i += 100 {
timeout, cancel := context.WithTimeout(ctx, 3*time.Second) timeout, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel() defer cancel()
end := i + 200 end := i + 100
if end > len(follows) { if end > len(oneHopNetwork) {
end = len(follows) end = len(oneHopNetwork)
} }
filters = []nostr.Filter{{ filters = []nostr.Filter{{
Authors: follows[i:end], Authors: oneHopNetwork[i:end],
Kinds: []int{nostr.KindContactList, nostr.KindRelayListMetadata, nostr.KindProfileMetadata}, Kinds: []int{nostr.KindContactList, nostr.KindRelayListMetadata, nostr.KindProfileMetadata},
}} }}
@ -231,18 +245,11 @@ func refreshTrustNetwork(relay *khatru.Relay, ctx context.Context) []string {
log.Println("🔗 relays discovered:", len(relays)) log.Println("🔗 relays discovered:", len(relays))
} }
runTrustNetworkRefresh() for {
updateTrustNetworkFilter()
ticker := time.NewTicker(24 * time.Hour)
defer ticker.Stop()
for range ticker.C {
runTrustNetworkRefresh() runTrustNetworkRefresh()
updateTrustNetworkFilter() updateTrustNetworkFilter()
archiveTrustedNotes(relay, ctx)
} }
return trustNetwork
} }
func appendRelay(relay string) { func appendRelay(relay string) {
@ -274,10 +281,25 @@ func appendPubkey(pubkey string) {
trustNetwork = append(trustNetwork, pubkey) trustNetwork = append(trustNetwork, pubkey)
} }
func appendOneHopNetwork(pubkey string) {
mu.Lock()
defer mu.Unlock()
for _, pk := range oneHopNetwork {
if pk == pubkey {
return
}
}
if len(pubkey) != 64 {
return
}
oneHopNetwork = append(oneHopNetwork, pubkey)
}
func archiveTrustedNotes(relay *khatru.Relay, ctx context.Context) { func archiveTrustedNotes(relay *khatru.Relay, ctx context.Context) {
log.Println("⏳ waiting for trust network to be populated") timeout, cancel := context.WithTimeout(ctx, time.Duration(config.RefreshInterval)*time.Hour)
time.Sleep(1 * time.Minute)
timeout, cancel := context.WithTimeout(ctx, 24*time.Hour)
defer cancel() defer cancel()
filters := []nostr.Filter{{ filters := []nostr.Filter{{
@ -297,7 +319,8 @@ func archiveTrustedNotes(relay *khatru.Relay, ctx context.Context) {
}} }}
log.Println("📦 archiving trusted notes...") log.Println("📦 archiving trusted notes...")
var i int64 var trustedNotes uint64
var untrustedNotes uint64
trustNetworkFilterMu.Lock() trustNetworkFilterMu.Lock()
for ev := range pool.SubMany(timeout, seedRelays, filters) { for ev := range pool.SubMany(timeout, seedRelays, filters) {
if trustNetworkFilter.Has(xxhash.Sum64([]byte(ev.Event.PubKey))) { if trustNetworkFilter.Has(xxhash.Sum64([]byte(ev.Event.PubKey))) {
@ -305,9 +328,11 @@ func archiveTrustedNotes(relay *khatru.Relay, ctx context.Context) {
continue continue
} }
relay.AddEvent(ctx, ev.Event) relay.AddEvent(ctx, ev.Event)
i++ trustedNotes++
} else {
untrustedNotes++
} }
} }
trustNetworkFilterMu.Unlock() trustNetworkFilterMu.Unlock()
fmt.Println("📦 archived", i, "trusted notes") log.Println("📦 archived", trustedNotes, "trusted notes and discarded", untrustedNotes, "untrusted notes")
} }