event.mx raw

   1  package nostr
   2  
   3  import (
   4  	"smesh.lol/web/common/helpers"
   5  	"smesh.lol/web/common/jsbridge/schnorr"
   6  )
   7  
   8  // Event is a Nostr event (NIP-01).
   9  type Event struct {
  10  	ID        string `json:"id"`
  11  	PubKey    string `json:"pubkey"`
  12  	CreatedAt int64  `json:"created_at"`
  13  	Kind      int    `json:"kind"`
  14  	Tags      Tags   `json:"tags"`
  15  	Content   string `json:"content"`
  16  	Sig       string `json:"sig"`
  17  }
  18  
  19  // Serialize returns the canonical JSON array for ID computation:
  20  // [0,<pubkey>,<created_at>,<kind>,<tags>,<content>]
  21  func (e *Event) Serialize() string {
  22  	buf := []byte{:0:256}
  23  	buf = append(buf, "[0,"...)
  24  	buf = append(buf, helpers.JsonString(e.PubKey)...)
  25  	buf = append(buf, ',')
  26  	buf = append(buf, helpers.Itoa(e.CreatedAt)...)
  27  	buf = append(buf, ',')
  28  	buf = append(buf, helpers.Itoa(int64(e.Kind))...)
  29  	buf = append(buf, ',')
  30  	buf = serializeTags(buf, e.Tags)
  31  	buf = append(buf, ',')
  32  	buf = append(buf, helpers.JsonString(e.Content)...)
  33  	buf = append(buf, ']')
  34  	return string(buf)
  35  }
  36  
  37  // ComputeID computes and sets the event ID (SHA-256 of serialized form).
  38  func (e *Event) ComputeID() string {
  39  	ser := e.Serialize()
  40  	hash := schnorr.SHA256Sum([]byte(ser))
  41  	e.ID = helpers.HexEncode(hash)
  42  	return e.ID
  43  }
  44  
  45  // CheckID verifies the event ID matches the content.
  46  func (e *Event) CheckID() bool {
  47  	ser := e.Serialize()
  48  	hash := schnorr.SHA256Sum([]byte(ser))
  49  	expected := helpers.HexEncode(hash)
  50  	return e.ID == expected
  51  }
  52  
  53  // Sign computes the event ID and signs it with the given secret key.
  54  // Sets ID, PubKey, and Sig. Returns false on failure.
  55  func (e *Event) Sign(seckey [32]byte, auxRand [32]byte) bool {
  56  	pk, ok := schnorr.PubKeyFromSecKey(seckey[:])
  57  	if !ok {
  58  		return false
  59  	}
  60  	e.PubKey = helpers.HexEncode(pk)
  61  	e.ComputeID()
  62  	idBytes := helpers.HexDecode(e.ID)
  63  	if len(idBytes) != 32 {
  64  		return false
  65  	}
  66  	sig, ok := schnorr.SignSchnorr(seckey[:], idBytes, auxRand[:])
  67  	if !ok {
  68  		return false
  69  	}
  70  	e.Sig = helpers.HexEncode(sig)
  71  	return true
  72  }
  73  
  74  // CheckSig verifies the event signature against its pubkey and ID.
  75  func (e *Event) CheckSig() bool {
  76  	pk := helpers.HexDecode(e.PubKey)
  77  	if len(pk) != 32 {
  78  		return false
  79  	}
  80  	id := helpers.HexDecode(e.ID)
  81  	if len(id) != 32 {
  82  		return false
  83  	}
  84  	if len(e.Sig) != 128 {
  85  		return false
  86  	}
  87  	sig := helpers.HexDecode(e.Sig)
  88  	if len(sig) != 64 {
  89  		return false
  90  	}
  91  	return schnorr.VerifySchnorr(pk, id, sig)
  92  }
  93  
  94  // ToJSON returns the event as a JSON object string.
  95  func (e *Event) ToJSON() string {
  96  	buf := []byte{:0:512}
  97  	buf = append(buf, `{"id":`...)
  98  	buf = append(buf, helpers.JsonString(e.ID)...)
  99  	buf = append(buf, `,"pubkey":`...)
 100  	buf = append(buf, helpers.JsonString(e.PubKey)...)
 101  	buf = append(buf, `,"created_at":`...)
 102  	buf = append(buf, helpers.Itoa(e.CreatedAt)...)
 103  	buf = append(buf, `,"kind":`...)
 104  	buf = append(buf, helpers.Itoa(int64(e.Kind))...)
 105  	buf = append(buf, `,"tags":`...)
 106  	buf = serializeTags(buf, e.Tags)
 107  	buf = append(buf, `,"content":`...)
 108  	buf = append(buf, helpers.JsonString(e.Content)...)
 109  	buf = append(buf, `,"sig":`...)
 110  	buf = append(buf, helpers.JsonString(e.Sig)...)
 111  	buf = append(buf, '}')
 112  	return string(buf)
 113  }
 114  
 115  func serializeTags(buf []byte, tags Tags) []byte {
 116  	buf = append(buf, '[')
 117  	for i, tag := range tags {
 118  		if i > 0 {
 119  			buf = append(buf, ',')
 120  		}
 121  		buf = append(buf, '[')
 122  		for j, s := range tag {
 123  			if j > 0 {
 124  				buf = append(buf, ',')
 125  			}
 126  			buf = append(buf, helpers.JsonString(s)...)
 127  		}
 128  		buf = append(buf, ']')
 129  	}
 130  	buf = append(buf, ']')
 131  	return buf
 132  }
 133