main.go raw

   1  package main
   2  
   3  import (
   4  	"encoding/json"
   5  	"fmt"
   6  	"net/url"
   7  	"os"
   8  	"time"
   9  
  10  	"github.com/gorilla/websocket"
  11  	"git.smesh.lol/orly/pkg/nostr/encoders/hex"
  12  	"git.smesh.lol/orly/pkg/nostr/interfaces/signer/p8k"
  13  )
  14  
  15  type mReq struct {
  16  	Method    string   `json:"method"`
  17  	Nsec      string   `json:"nsec,omitempty"`
  18  	Pubkey    string   `json:"pubkey,omitempty"`
  19  	Sig       string   `json:"sig,omitempty"`
  20  	Recipient string   `json:"recipient,omitempty"`
  21  	Content   string   `json:"content,omitempty"`
  22  	Relays    []string `json:"relays,omitempty"`
  23  	Alias     string   `json:"alias,omitempty"`
  24  }
  25  
  26  type mResp struct {
  27  	Method     string   `json:"method"`
  28  	OK         bool     `json:"ok,omitempty"`
  29  	Error      string   `json:"error,omitempty"`
  30  	Peer       string   `json:"peer,omitempty"`
  31  	Content    string   `json:"content,omitempty"`
  32  	Ts         int64    `json:"ts,omitempty"`
  33  	Groups     []string `json:"groups,omitempty"`
  34  	Pubkey     string   `json:"pubkey,omitempty"`
  35  	Subscribed bool     `json:"subscribed,omitempty"`
  36  	Relay      string   `json:"relay,omitempty"`
  37  	NumGroups  int      `json:"num_groups,omitempty"`
  38  }
  39  
  40  func main() {
  41  	base := "ws://127.0.0.1:8090"
  42  	if len(os.Args) > 1 {
  43  		base = os.Args[1]
  44  	}
  45  
  46  	alice, _ := p8k.New()
  47  	alice.Generate()
  48  	bob, _ := p8k.New()
  49  	bob.Generate()
  50  
  51  	aliceSec := hex.Enc(alice.Sec())
  52  	bobSec := hex.Enc(bob.Sec())
  53  	alicePub := hex.Enc(alice.Pub())
  54  	bobPub := hex.Enc(bob.Pub())
  55  
  56  	fmt.Printf("alice: %s\nbob:   %s\n\n", alicePub, bobPub)
  57  
  58  	// --- Phase 1: Basic DM flow ---
  59  
  60  	ac := dial(base)
  61  	defer ac.Close()
  62  	step("alice auth", func() {
  63  		send(ac, mReq{Method: "auth", Nsec: aliceSec})
  64  		r := expect(ac, "auth", 15)
  65  		check(r.OK, "auth", r.Error)
  66  	})
  67  
  68  	bc := dial(base)
  69  	defer bc.Close()
  70  	step("bob auth", func() {
  71  		send(bc, mReq{Method: "auth", Nsec: bobSec})
  72  		r := expect(bc, "auth", 15)
  73  		check(r.OK, "auth", r.Error)
  74  	})
  75  
  76  	step("alice publish_kp", func() {
  77  		send(ac, mReq{Method: "publish_kp"})
  78  		r := expect(ac, "publish_kp", 15)
  79  		check(r.OK, "publish_kp", r.Error)
  80  	})
  81  	step("bob publish_kp", func() {
  82  		send(bc, mReq{Method: "publish_kp"})
  83  		r := expect(bc, "publish_kp", 15)
  84  		check(r.OK, "publish_kp", r.Error)
  85  	})
  86  
  87  	step("alice subscribe", func() {
  88  		send(ac, mReq{Method: "subscribe"})
  89  		r := expect(ac, "subscribe", 10)
  90  		check(r.OK, "subscribe", r.Error)
  91  	})
  92  	step("bob subscribe", func() {
  93  		send(bc, mReq{Method: "subscribe"})
  94  		r := expect(bc, "subscribe", 10)
  95  		check(r.OK, "subscribe", r.Error)
  96  	})
  97  
  98  	fmt.Println("  waiting 3s for KP propagation...")
  99  	time.Sleep(3 * time.Second)
 100  
 101  	msg1 := "hello-" + time.Now().Format("150405")
 102  	step("alice send_dm → bob", func() {
 103  		send(ac, mReq{Method: "send_dm", Recipient: bobPub, Content: msg1})
 104  		r := expect(ac, "send_dm", 30)
 105  		check(r.OK, "send_dm", r.Error)
 106  	})
 107  
 108  	step("bob receive dm", func() {
 109  		r := expectDM(bc, 30)
 110  		if r.Content != msg1 {
 111  			fail("content mismatch: got %q want %q", r.Content, msg1)
 112  		}
 113  		fmt.Printf("content=%q ", r.Content)
 114  	})
 115  
 116  	// --- Phase 2: Status ---
 117  
 118  	step("alice status", func() {
 119  		send(ac, mReq{Method: "status"})
 120  		r := expect(ac, "status", 5)
 121  		check(r.OK, "status", r.Error)
 122  		if r.Pubkey != alicePub {
 123  			fail("wrong pubkey: %s", r.Pubkey)
 124  		}
 125  		if !r.Subscribed {
 126  			fail("not subscribed")
 127  		}
 128  		if r.NumGroups < 1 {
 129  			fail("no groups")
 130  		}
 131  		fmt.Printf("sub=%v groups=%d relay=%s ", r.Subscribed, r.NumGroups, r.Relay)
 132  	})
 133  
 134  	step("bob status", func() {
 135  		send(bc, mReq{Method: "status"})
 136  		r := expect(bc, "status", 5)
 137  		check(r.OK, "status", r.Error)
 138  		if !r.Subscribed {
 139  			fail("not subscribed")
 140  		}
 141  		fmt.Printf("sub=%v groups=%d ", r.Subscribed, r.NumGroups)
 142  	})
 143  
 144  	// --- Phase 3: Reset both + re-establish ---
 145  	// Both sides reset → clean slate → re-establish group from scratch.
 146  
 147  	step("alice reset", func() {
 148  		send(ac, mReq{Method: "reset"})
 149  		r := expect(ac, "reset", 15)
 150  		check(r.OK, "reset", r.Error)
 151  	})
 152  	step("bob reset", func() {
 153  		send(bc, mReq{Method: "reset"})
 154  		r := expect(bc, "reset", 15)
 155  		check(r.OK, "reset", r.Error)
 156  	})
 157  
 158  	step("bob status after reset", func() {
 159  		send(bc, mReq{Method: "status"})
 160  		r := expect(bc, "status", 5)
 161  		check(r.OK, "status", r.Error)
 162  		if r.Subscribed {
 163  			fail("should not be subscribed after reset")
 164  		}
 165  		if r.NumGroups != 0 {
 166  			fail("groups should be 0 after reset, got %d", r.NumGroups)
 167  		}
 168  		fmt.Printf("sub=%v groups=%d ", r.Subscribed, r.NumGroups)
 169  	})
 170  
 171  	step("alice re-publish_kp", func() {
 172  		send(ac, mReq{Method: "publish_kp"})
 173  		r := expect(ac, "publish_kp", 15)
 174  		check(r.OK, "publish_kp", r.Error)
 175  	})
 176  	step("bob re-publish_kp", func() {
 177  		send(bc, mReq{Method: "publish_kp"})
 178  		r := expect(bc, "publish_kp", 15)
 179  		check(r.OK, "publish_kp", r.Error)
 180  	})
 181  
 182  	step("alice re-subscribe", func() {
 183  		send(ac, mReq{Method: "subscribe"})
 184  		r := expect(ac, "subscribe", 10)
 185  		check(r.OK, "subscribe", r.Error)
 186  	})
 187  	step("bob re-subscribe", func() {
 188  		send(bc, mReq{Method: "subscribe"})
 189  		r := expect(bc, "subscribe", 10)
 190  		check(r.OK, "subscribe", r.Error)
 191  	})
 192  
 193  	time.Sleep(2 * time.Second)
 194  	msg2 := "after-reset-" + time.Now().Format("150405")
 195  	step("alice send_dm after reset", func() {
 196  		send(ac, mReq{Method: "send_dm", Recipient: bobPub, Content: msg2})
 197  		r := expect(ac, "send_dm", 30)
 198  		check(r.OK, "send_dm", r.Error)
 199  	})
 200  
 201  	step("bob receive after reset", func() {
 202  		r := expectDM(bc, 30)
 203  		if r.Content != msg2 {
 204  			fail("content mismatch: got %q want %q", r.Content, msg2)
 205  		}
 206  		fmt.Printf("content=%q ", r.Content)
 207  	})
 208  
 209  	// --- Phase 4: Alias resolution ---
 210  
 211  	step("resolve_alias jb55@damus.io", func() {
 212  		send(ac, mReq{Method: "resolve_alias", Alias: "jb55@damus.io"})
 213  		r := expect(ac, "resolve_alias", 15)
 214  		check(r.OK, "resolve_alias", r.Error)
 215  		if len(r.Pubkey) != 64 {
 216  			fail("invalid pubkey: %s", r.Pubkey)
 217  		}
 218  		// jb55's known pubkey
 219  		if r.Pubkey != "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245" {
 220  			fail("wrong pubkey: %s", r.Pubkey)
 221  		}
 222  		fmt.Printf("pubkey=%s..%s ", r.Pubkey[:8], r.Pubkey[56:])
 223  	})
 224  
 225  	step("list_groups final", func() {
 226  		send(ac, mReq{Method: "list_groups"})
 227  		r := expect(ac, "list_groups", 5)
 228  		check(r.OK, "list_groups", r.Error)
 229  		fmt.Printf("groups=%d ", len(r.Groups))
 230  	})
 231  
 232  	fmt.Println("\nALL PASS")
 233  }
 234  
 235  func dial(base string) *websocket.Conn {
 236  	u, _ := url.Parse(base + "/__marmot")
 237  	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
 238  	if err != nil {
 239  		fail("dial %s: %v", u, err)
 240  	}
 241  	return c
 242  }
 243  
 244  func send(c *websocket.Conn, req mReq) {
 245  	data, _ := json.Marshal(req)
 246  	c.WriteMessage(websocket.TextMessage, data)
 247  }
 248  
 249  func expect(c *websocket.Conn, method string, timeoutSec int) mResp {
 250  	c.SetReadDeadline(time.Now().Add(time.Duration(timeoutSec) * time.Second))
 251  	for {
 252  		_, raw, err := c.ReadMessage()
 253  		if err != nil {
 254  			fail("expect %s: %v", method, err)
 255  		}
 256  		var r mResp
 257  		json.Unmarshal(raw, &r)
 258  		if r.Method == method {
 259  			return r
 260  		}
 261  	}
 262  }
 263  
 264  func expectDM(c *websocket.Conn, timeoutSec int) mResp {
 265  	c.SetReadDeadline(time.Now().Add(time.Duration(timeoutSec) * time.Second))
 266  	for {
 267  		_, raw, err := c.ReadMessage()
 268  		if err != nil {
 269  			fail("expect dm_received: %v", err)
 270  		}
 271  		var r mResp
 272  		json.Unmarshal(raw, &r)
 273  		if r.Method == "dm_received" {
 274  			return r
 275  		}
 276  	}
 277  }
 278  
 279  func step(name string, fn func()) {
 280  	fmt.Printf("  %s... ", name)
 281  	fn()
 282  	fmt.Println("OK")
 283  }
 284  
 285  func check(ok bool, what, errMsg string) {
 286  	if !ok {
 287  		fail("%s: %s", what, errMsg)
 288  	}
 289  }
 290  
 291  func fail(format string, args ...any) {
 292  	fmt.Printf("FAIL: "+format+"\n", args...)
 293  	os.Exit(1)
 294  }
 295