keys.go raw

   1  package relaytester
   2  
   3  import (
   4  	"crypto/rand"
   5  	"fmt"
   6  	"time"
   7  
   8  	"next.orly.dev/pkg/lol/chk"
   9  	"next.orly.dev/pkg/nostr/interfaces/signer/p8k"
  10  	"next.orly.dev/pkg/nostr/encoders/bech32encoding"
  11  	"next.orly.dev/pkg/nostr/encoders/event"
  12  	"next.orly.dev/pkg/nostr/encoders/hex"
  13  	"next.orly.dev/pkg/nostr/encoders/kind"
  14  	"next.orly.dev/pkg/nostr/encoders/tag"
  15  )
  16  
  17  // KeyPair represents a test keypair.
  18  type KeyPair struct {
  19  	Secret *p8k.Signer
  20  	Pubkey []byte
  21  	Nsec   string
  22  	Npub   string
  23  }
  24  
  25  // GenerateKeyPair generates a new keypair for testing.
  26  func GenerateKeyPair() (kp *KeyPair, err error) {
  27  	kp = &KeyPair{}
  28  	if kp.Secret, err = p8k.New(); chk.E(err) {
  29  		return
  30  	}
  31  	if err = kp.Secret.Generate(); chk.E(err) {
  32  		return
  33  	}
  34  	kp.Pubkey = kp.Secret.Pub()
  35  	nsecBytes, err := bech32encoding.BinToNsec(kp.Secret.Sec())
  36  	if chk.E(err) {
  37  		return
  38  	}
  39  	kp.Nsec = string(nsecBytes)
  40  	npubBytes, err := bech32encoding.BinToNpub(kp.Pubkey)
  41  	if chk.E(err) {
  42  		return
  43  	}
  44  	kp.Npub = string(npubBytes)
  45  	return
  46  }
  47  
  48  // CreateEvent creates a signed event with the given parameters.
  49  func CreateEvent(signer *p8k.Signer, kindNum uint16, content string, tags *tag.S) (ev *event.E, err error) {
  50  	ev = event.New()
  51  	ev.CreatedAt = time.Now().Unix()
  52  	ev.Kind = kindNum
  53  	ev.Content = []byte(content)
  54  	if tags != nil {
  55  		ev.Tags = tags
  56  	} else {
  57  		ev.Tags = tag.NewS()
  58  	}
  59  	if err = ev.Sign(signer); chk.E(err) {
  60  		return
  61  	}
  62  	return
  63  }
  64  
  65  // CreateEventWithTags creates an event with specific tags.
  66  func CreateEventWithTags(signer *p8k.Signer, kindNum uint16, content string, tagPairs [][]string) (ev *event.E, err error) {
  67  	tags := tag.NewS()
  68  	for _, pair := range tagPairs {
  69  		if len(pair) >= 2 {
  70  			// Build tag fields as []byte variadic arguments
  71  			tagFields := make([][]byte, len(pair))
  72  			tagFields[0] = []byte(pair[0])
  73  			for i := 1; i < len(pair); i++ {
  74  				tagFields[i] = []byte(pair[i])
  75  			}
  76  			tags.Append(tag.NewFromBytesSlice(tagFields...))
  77  		}
  78  	}
  79  	return CreateEvent(signer, kindNum, content, tags)
  80  }
  81  
  82  // CreateReplaceableEvent creates a replaceable event (kind 0-3, 10000-19999).
  83  func CreateReplaceableEvent(signer *p8k.Signer, kindNum uint16, content string) (ev *event.E, err error) {
  84  	return CreateEvent(signer, kindNum, content, nil)
  85  }
  86  
  87  // CreateEphemeralEvent creates an ephemeral event (kind 20000-29999).
  88  func CreateEphemeralEvent(signer *p8k.Signer, kindNum uint16, content string) (ev *event.E, err error) {
  89  	return CreateEvent(signer, kindNum, content, nil)
  90  }
  91  
  92  // CreateDeleteEvent creates a deletion event (kind 5).
  93  func CreateDeleteEvent(signer *p8k.Signer, eventIDs [][]byte, reason string) (ev *event.E, err error) {
  94  	tags := tag.NewS()
  95  	for _, id := range eventIDs {
  96  		// e tags must contain hex-encoded event IDs
  97  		tags.Append(tag.NewFromBytesSlice([]byte("e"), []byte(hex.Enc(id))))
  98  	}
  99  	if reason != "" {
 100  		tags.Append(tag.NewFromBytesSlice([]byte("content"), []byte(reason)))
 101  	}
 102  	return CreateEvent(signer, kind.EventDeletion.K, reason, tags)
 103  }
 104  
 105  // CreateParameterizedReplaceableEvent creates a parameterized replaceable event (kind 30000-39999).
 106  func CreateParameterizedReplaceableEvent(signer *p8k.Signer, kindNum uint16, content string, dTag string) (ev *event.E, err error) {
 107  	tags := tag.NewS()
 108  	tags.Append(tag.NewFromBytesSlice([]byte("d"), []byte(dTag)))
 109  	return CreateEvent(signer, kindNum, content, tags)
 110  }
 111  
 112  // RandomID generates a random 32-byte ID.
 113  func RandomID() (id []byte, err error) {
 114  	id = make([]byte, 32)
 115  	if _, err = rand.Read(id); err != nil {
 116  		return nil, fmt.Errorf("failed to generate random ID: %w", err)
 117  	}
 118  	return
 119  }
 120  
 121  // MustHex decodes a hex string or panics.
 122  func MustHex(s string) []byte {
 123  	b, err := hex.Dec(s)
 124  	if err != nil {
 125  		panic(fmt.Sprintf("invalid hex: %s", s))
 126  	}
 127  	return b
 128  }
 129  
 130  // HexID returns the hex-encoded event ID.
 131  func HexID(ev *event.E) string {
 132  	return hex.Enc(ev.ID)
 133  }
 134