event.mx raw

   1  package event
   2  
   3  import (
   4  	"git.smesh.lol/moxie/pkg/mxutil"
   5  	"bytes"
   6  	"crypto/sha256"
   7  	"encoding/hex"
   8  	"fmt"
   9  	"io"
  10  
  11  	"crypto/secp256k1"
  12  	"git.smesh.lol/nostr/pkg/ints"
  13  	"git.smesh.lol/nostr/pkg/kind"
  14  	"git.smesh.lol/nostr/pkg/tag"
  15  	"git.smesh.lol/nostr/pkg/text"
  16  	"git.smesh.lol/nostr/pkg/lol/chk"
  17  	"git.smesh.lol/nostr/pkg/lol/errorf"
  18  )
  19  
  20  // E is the primary datatype of nostr.
  21  type E struct {
  22  	ID        []byte
  23  	Pubkey    []byte
  24  	CreatedAt int64
  25  	Kind      uint16
  26  	Tags      *tag.S
  27  	Content   []byte
  28  	Sig       []byte
  29  }
  30  
  31  const (
  32  	jId        = "id"
  33  	jPubkey    = "pubkey"
  34  	jCreatedAt = "created_at"
  35  	jKind      = "kind"
  36  	jTags      = "tags"
  37  	jContent   = "content"
  38  	jSig       = "sig"
  39  )
  40  
  41  func New() (e *E) { return &E{} }
  42  
  43  func (ev *E) Free() {
  44  	ev.ID = nil
  45  	ev.Pubkey = nil
  46  	ev.Tags = nil
  47  	ev.Content = nil
  48  	ev.Sig = nil
  49  }
  50  
  51  func (ev *E) Clone() (e *E) {
  52  	clone := &E{CreatedAt: ev.CreatedAt, Kind: ev.Kind}
  53  	if ev.ID != nil {
  54  		clone.ID = []byte{:len(ev.ID)}
  55  		copy(clone.ID, ev.ID)
  56  	}
  57  	if ev.Pubkey != nil {
  58  		clone.Pubkey = []byte{:len(ev.Pubkey)}
  59  		copy(clone.Pubkey, ev.Pubkey)
  60  	}
  61  	if ev.Content != nil {
  62  		clone.Content = []byte{:len(ev.Content)}
  63  		copy(clone.Content, ev.Content)
  64  	}
  65  	if ev.Sig != nil {
  66  		clone.Sig = []byte{:len(ev.Sig)}
  67  		copy(clone.Sig, ev.Sig)
  68  	}
  69  	if ev.Tags != nil {
  70  		clone.Tags = tag.NewS()
  71  		for _, tg := range ev.Tags.T {
  72  			if tg != nil {
  73  				newTag := tag.NewWithCap(len(tg.T))
  74  				for _, element := range tg.T {
  75  					el := []byte{:len(element)}
  76  					copy(el, element)
  77  					newTag.T = push(newTag.T, el)
  78  				}
  79  				clone.Tags.Append(newTag)
  80  			}
  81  		}
  82  	}
  83  	return clone
  84  }
  85  
  86  func (ev *E) EstimateSize() (size int32) {
  87  	size = len(ev.ID)*2 + len(ev.Pubkey)*2 + len(ev.Sig)*2 + len(ev.Content)*2
  88  	if ev.Tags == nil {
  89  		return
  90  	}
  91  	for _, v := range ev.Tags.T {
  92  		for _, w := range (*v).T {
  93  			size += len(w) * 2
  94  		}
  95  	}
  96  	return
  97  }
  98  
  99  func (ev *E) Marshal(dst []byte) (b []byte) {
 100  	b = dst
 101  	if b == nil {
 102  		b = []byte{:0:ev.EstimateSize()+100}
 103  	}
 104  	b = b | "{"
 105  	b = b | "\""
 106  	b = b | jId
 107  	b = b | `":"`
 108  	hexStart := len(b)
 109  	b = b | []byte{:2*sha256.Size}
 110  	hex.Encode(b[hexStart:], ev.ID)
 111  	b = b | `","`
 112  	b = b | jPubkey
 113  	b = b | `":"`
 114  	hexStart = len(b)
 115  	b = b | []byte{:2*secp256k1.PubKeyBytesLen}
 116  	hex.Encode(b[hexStart:], ev.Pubkey)
 117  	b = b | `","`
 118  	b = b | jCreatedAt
 119  	b = b | `":`
 120  	b = ints.New(ev.CreatedAt).Marshal(b)
 121  	b = b | `,"`
 122  	b = b | jKind
 123  	b = b | `":`
 124  	b = ints.New(ev.Kind).Marshal(b)
 125  	b = b | `,"`
 126  	b = b | jTags
 127  	b = b | `":`
 128  	if ev.Tags != nil {
 129  		b = ev.Tags.Marshal(b)
 130  	} else {
 131  		b = b | "[]"
 132  	}
 133  	b = b | `,"`
 134  	b = b | jContent
 135  	b = b | `":"`
 136  	b = text.NostrEscape(b, ev.Content)
 137  	b = b | `","`
 138  	b = b | jSig
 139  	b = b | `":"`
 140  	if len(ev.Sig) > 0 {
 141  		hexStart = len(b)
 142  		b = b | []byte{:2*secp256k1.SignatureSize}
 143  		hex.Encode(b[hexStart:], ev.Sig)
 144  	}
 145  	b = b | `"}`
 146  	return
 147  }
 148  
 149  func (ev *E) MarshalJSON() (b []byte, err error) {
 150  	b = ev.Marshal(nil)
 151  	return
 152  }
 153  
 154  func (ev *E) Serialize() (b []byte) { return ev.Marshal(nil) }
 155  
 156  func (ev *E) Unmarshal(b []byte) (rem []byte, err error) {
 157  	key := []byte{:0:9}
 158  	for ; len(b) > 0; b = b[1:] {
 159  		if isWhitespace(b[0]) {
 160  			continue
 161  		}
 162  		if b[0] == '{' {
 163  			b = b[1:]
 164  			goto BetweenKeys
 165  		}
 166  	}
 167  	goto eof
 168  BetweenKeys:
 169  	for ; len(b) > 0; b = b[1:] {
 170  		if isWhitespace(b[0]) {
 171  			continue
 172  		}
 173  		if b[0] == '"' {
 174  			b = b[1:]
 175  			goto InKey
 176  		}
 177  	}
 178  	goto eof
 179  InKey:
 180  	for ; len(b) > 0; b = b[1:] {
 181  		if b[0] == '"' {
 182  			b = b[1:]
 183  			goto InKV
 184  		}
 185  		key = mxutil.Ensure(key, 1)
 186  		key = push(key, b[0])
 187  	}
 188  	goto eof
 189  InKV:
 190  	for ; len(b) > 0; b = b[1:] {
 191  		if isWhitespace(b[0]) {
 192  			continue
 193  		}
 194  		if b[0] == ':' {
 195  			b = b[1:]
 196  			goto InVal
 197  		}
 198  	}
 199  	goto eof
 200  InVal:
 201  	for len(b) > 0 && isWhitespace(b[0]) {
 202  		b = b[1:]
 203  	}
 204  	// An empty key is legal JSON (`{"":1}`) and switch key[0] panicked on it,
 205  	// so a malformed EVENT envelope crashed the relay. filter.F guards this the
 206  	// same way.
 207  	if len(key) < 1 {
 208  		err = errorf.E([]byte("event: empty key"))
 209  		return
 210  	}
 211  	switch key[0] {
 212  	case jId[0]:
 213  		if !bytes.Equal(jId, key) {
 214  			goto invalid
 215  		}
 216  		var id []byte
 217  		if id, b, err = text.UnmarshalHex(b); chk.E(err) {
 218  			return
 219  		}
 220  		if len(id) != sha256.Size {
 221  			err = errorf.E(
 222  				[]byte("invalid id, require %d got %d"), sha256.Size, len(id),
 223  			)
 224  			return
 225  		}
 226  		ev.ID = id
 227  		goto BetweenKV
 228  	case jPubkey[0]:
 229  		if !bytes.Equal(jPubkey, key) {
 230  			goto invalid
 231  		}
 232  		var pk []byte
 233  		if pk, b, err = text.UnmarshalHex(b); chk.E(err) {
 234  			return
 235  		}
 236  		if len(pk) != secp256k1.PubKeyBytesLen {
 237  			err = errorf.E(
 238  				[]byte("invalid pubkey, require %d got %d"),
 239  				secp256k1.PubKeyBytesLen, len(pk),
 240  			)
 241  			return
 242  		}
 243  		ev.Pubkey = pk
 244  		goto BetweenKV
 245  	case jKind[0]:
 246  		if !bytes.Equal(jKind, key) {
 247  			goto invalid
 248  		}
 249  		k := kind.New(0)
 250  		if b, err = k.Unmarshal(b); chk.E(err) {
 251  			return
 252  		}
 253  		ev.Kind = k.ToU16()
 254  		goto BetweenKV
 255  	case jTags[0]:
 256  		if !bytes.Equal(jTags, key) {
 257  			goto invalid
 258  		}
 259  		ev.Tags = tag.NewS()
 260  		if b, err = ev.Tags.Unmarshal(b); chk.E(err) {
 261  			return
 262  		}
 263  		goto BetweenKV
 264  	case jSig[0]:
 265  		if !bytes.Equal(jSig, key) {
 266  			goto invalid
 267  		}
 268  		var sig []byte
 269  		if sig, b, err = text.UnmarshalHex(b); chk.E(err) {
 270  			return
 271  		}
 272  		if len(sig) != 0 && len(sig) != secp256k1.SignatureSize {
 273  			sig = nil
 274  		}
 275  		ev.Sig = sig
 276  		goto BetweenKV
 277  	case jContent[0]:
 278  		if key[1] == jContent[1] {
 279  			if !bytes.Equal(jContent, key) {
 280  				goto invalid
 281  			}
 282  			if ev.Content, b, err = text.UnmarshalQuoted(b); chk.T(err) {
 283  				return
 284  			}
 285  			goto BetweenKV
 286  		} else if key[1] == jCreatedAt[1] {
 287  			if !bytes.Equal(jCreatedAt, key) {
 288  				goto invalid
 289  			}
 290  			i := ints.New(0)
 291  			if b, err = i.Unmarshal(b); chk.T(err) {
 292  				return
 293  			}
 294  			ev.CreatedAt = i.Int64()
 295  			goto BetweenKV
 296  		} else {
 297  			goto invalid
 298  		}
 299  	default:
 300  		goto invalid
 301  	}
 302  BetweenKV:
 303  	key = key[:0]
 304  	for ; len(b) > 0; b = b[1:] {
 305  		if isWhitespace(b[0]) {
 306  			continue
 307  		}
 308  		switch {
 309  		case len(b) == 0:
 310  			return
 311  		case b[0] == '}':
 312  			b = b[1:]
 313  			goto AfterClose
 314  		case b[0] == ',':
 315  			b = b[1:]
 316  			goto BetweenKeys
 317  		case b[0] == '"':
 318  			b = b[1:]
 319  			goto InKey
 320  		}
 321  	}
 322  	goto AfterClose
 323  AfterClose:
 324  	rem = b
 325  	return
 326  invalid:
 327  	err = fmt.Errorf(
 328  		"invalid key,\n'%s'\n'%s'\n'%s'", string(b), string(b[:]),
 329  		string(b),
 330  	)
 331  	return
 332  eof:
 333  	err = io.EOF
 334  	return
 335  }
 336  
 337  func (ev *E) UnmarshalJSON(b []byte) (err error) {
 338  	_, err = ev.Unmarshal(b)
 339  	return
 340  }
 341  
 342  func isWhitespace(b byte) (ok bool) {
 343  	return b == ' ' || b == '\t' || b == '\n' || b == '\r'
 344  }
 345  
 346  // S is an array of event.E that sorts in reverse chronological order. It is a
 347  // struct holding the slice, not a named slice: methods belong on the struct
 348  // that holds the slice (moxie bans named slice types).
 349  type S struct {
 350  	E []*E
 351  }
 352  
 353  func (ev *S) Len() (n int32) { return len(ev.E) }
 354  func (ev *S) Less(i, j int32) (ok bool) { return ev.E[i].CreatedAt > ev.E[j].CreatedAt }
 355  func (ev *S) Swap(i, j int32) { ev.E[i], ev.E[j] = ev.E[j], ev.E[i] }
 356  
 357  type C chan *E
 358