canonical.go raw

   1  package event
   2  
   3  import (
   4  	"next.orly.dev/pkg/nostr/encoders/hex"
   5  	"next.orly.dev/pkg/nostr/encoders/ints"
   6  	"next.orly.dev/pkg/nostr/encoders/text"
   7  	"github.com/minio/sha256-simd"
   8  )
   9  
  10  // ToCanonical converts the event to the canonical encoding used to derive the
  11  // event ID.
  12  func (ev *E) ToCanonical(dst []byte) (b []byte) {
  13  	b = dst
  14  	// Pre-allocate buffer if nil to reduce reallocations
  15  	if b == nil {
  16  		// Estimate size: [0," + hex(pubkey) + "," + timestamp + "," + kind + "," + tags + "," + content + ]
  17  		estimatedSize := 5 + 2*len(ev.Pubkey) + 20 + 10 + 100
  18  		if ev.Tags != nil {
  19  			for _, tag := range *ev.Tags {
  20  				for _, elem := range tag.T {
  21  					estimatedSize += len(elem)*2 + 10 // escaped element + overhead
  22  				}
  23  			}
  24  		}
  25  		estimatedSize += len(ev.Content)*2 + 10 // escaped content + overhead
  26  		b = make([]byte, 0, estimatedSize)
  27  	}
  28  	b = append(b, "[0,\""...)
  29  	b = hex.EncAppend(b, ev.Pubkey)
  30  	b = append(b, "\","...)
  31  	b = ints.New(ev.CreatedAt).Marshal(b)
  32  	b = append(b, ',')
  33  	b = ints.New(ev.Kind).Marshal(b)
  34  	b = append(b, ',')
  35  	if ev.Tags != nil {
  36  		b = ev.Tags.Marshal(b)
  37  	} else {
  38  		b = append(b, '[')
  39  		b = append(b, ']')
  40  	}
  41  	b = append(b, ',')
  42  	b = text.AppendQuote(b, ev.Content, text.NostrEscape)
  43  	b = append(b, ']')
  44  	return
  45  }
  46  
  47  // GetIDBytes returns the raw SHA256 hash of the canonical form of an event.E.
  48  func (ev *E) GetIDBytes() []byte { return Hash(ev.ToCanonical(nil)) }
  49  
  50  // Hash is a little helper generate a hash and return a slice instead of an
  51  // array.
  52  func Hash(in []byte) (out []byte) {
  53  	h := sha256.Sum256(in)
  54  	return h[:]
  55  }
  56