package bech32encoding import ( "bytes" "crypto/sha256" "encoding/binary" "smesh.lol/pkg/nostr/ec/bech32" "smesh.lol/pkg/nostr/ec/schnorr" "smesh.lol/pkg/nostr/bech32encoding/pointers" "smesh.lol/pkg/nostr/bech32encoding/tlv" "smesh.lol/pkg/nostr/hex" "smesh.lol/pkg/nostr/kind" "smesh.lol/pkg/lol/chk" "smesh.lol/pkg/lol/errorf" "smesh.lol/pkg/lol/log" ) var ( NoteHRP = []byte("note") NsecHRP = []byte("nsec") NpubHRP = []byte("npub") NprofileHRP = []byte("nprofile") NeventHRP = []byte("nevent") NentityHRP = []byte("naddr") ) func Decode(bech32string []byte) (prefix []byte, value any, err error) { var bits5 []byte if prefix, bits5, err = bech32.DecodeNoLimit(bech32string); chk.D(err) { return } var data []byte if data, err = bech32.ConvertBits(bits5, 5, 8, false); chk.D(err) { return prefix, nil, errorf.E([]byte("failed translating data into 8 bits: %s"), err.Error()) } buf := bytes.NewBuffer(data) switch { case bytes.Equal(prefix, NpubHRP) || bytes.Equal(prefix, NsecHRP) || bytes.Equal(prefix, NoteHRP): if len(data) < 32 { return prefix, nil, errorf.E([]byte("data is less than 32 bytes (%d)"), len(data)) } b := []byte{:schnorr.PubKeyBytesLen*2} hex.EncBytes(b, data[:32]) return prefix, b, nil case bytes.Equal(prefix, NprofileHRP): var result pointers.Profile for { t, v := tlv.ReadEntry(buf) if len(v) == 0 { if len(result.PublicKey) < 1 { return prefix, result, errorf.E([]byte("no pubkey found for nprofile")) } return prefix, result, nil } switch t { case tlv.Default: if len(v) < 32 { return prefix, nil, errorf.E([]byte("pubkey is less than 32 bytes (%d)"), len(v)) } result.PublicKey = []byte{:schnorr.PubKeyBytesLen*2} hex.EncBytes(result.PublicKey, v) case tlv.Relay: result.Relays = append(result.Relays, v) } } case bytes.Equal(prefix, NeventHRP): var result pointers.Event for { t, v := tlv.ReadEntry(buf) if v == nil { if len(result.ID) == 0 { return prefix, result, errorf.E([]byte("no id found for nevent")) } return prefix, result, nil } switch t { case tlv.Default: if len(v) < 32 { return prefix, nil, errorf.E([]byte("id is less than 32 bytes (%d)"), len(v)) } result.ID = v case tlv.Relay: result.Relays = append(result.Relays, v) case tlv.Author: if len(v) < 32 { return prefix, nil, errorf.E([]byte("author is less than 32 bytes (%d)"), len(v)) } result.Author = []byte{:schnorr.PubKeyBytesLen*2} hex.EncBytes(result.Author, v) case tlv.Kind: result.Kind = kind.New(binary.BigEndian.Uint32(v)) } } case bytes.Equal(prefix, NentityHRP): var result pointers.Entity for { t, v := tlv.ReadEntry(buf) if v == nil { if result.Kind.ToU16() == 0 || len(result.Identifier) < 1 || len(result.PublicKey) < 1 { return prefix, result, errorf.E([]byte("incomplete naddr")) } return prefix, result, nil } switch t { case tlv.Default: result.Identifier = v case tlv.Relay: result.Relays = append(result.Relays, v) case tlv.Author: if len(v) < 32 { return prefix, nil, errorf.E([]byte("author is less than 32 bytes (%d)"), len(v)) } result.PublicKey = []byte{:schnorr.PubKeyBytesLen*2} hex.EncBytes(result.PublicKey, v) case tlv.Kind: result.Kind = kind.New(binary.BigEndian.Uint32(v)) default: log.D.Ln([]byte("got a bogus TLV type code %d"), t) } } } return prefix, data, errorf.E([]byte("unknown tag %s"), prefix) } func EncodeNote(eventIDHex []byte) (s []byte, err error) { b := []byte{:sha256.Size} if _, err = hex.DecBytes(b, eventIDHex); chk.D(err) { return } var bits5 []byte if bits5, err = bech32.ConvertBits(b, 8, 5, true); chk.D(err) { return } return bech32.Encode(NoteHRP, bits5) } func EncodeProfile(publicKeyHex []byte, relays [][]byte) (s []byte, err error) { buf := &bytes.Buffer{} pb := []byte{:schnorr.PubKeyBytesLen} if _, err = hex.DecBytes(pb, publicKeyHex); chk.D(err) { return } tlv.WriteEntry(buf, tlv.Default, pb) for _, url := range relays { tlv.WriteEntry(buf, tlv.Relay, url) } var bits5 []byte if bits5, err = bech32.ConvertBits(buf.Bytes(), 8, 5, true); chk.D(err) { return } return bech32.Encode(NprofileHRP, bits5) } func EncodeEvent(eventIDHex []byte, relays [][]byte, author []byte) (s []byte, err error) { buf := &bytes.Buffer{} id := []byte{:sha256.Size} if _, err = hex.DecBytes(id, eventIDHex); chk.D(err) || len(id) != 32 { return nil, errorf.E([]byte("invalid id %d '%s': %v"), len(id), eventIDHex, err) } tlv.WriteEntry(buf, tlv.Default, id) for _, url := range relays { tlv.WriteEntry(buf, tlv.Relay, url) } pubkey := []byte{:schnorr.PubKeyBytesLen} if _, err = hex.DecBytes(pubkey, author); len(pubkey) == 32 { tlv.WriteEntry(buf, tlv.Author, pubkey) } var bits5 []byte if bits5, err = bech32.ConvertBits(buf.Bytes(), 8, 5, true); chk.D(err) { return } return bech32.Encode(NeventHRP, bits5) } func EncodeEntity(pk []byte, k *kind.K, id []byte, relays [][]byte) (s []byte, err error) { buf := &bytes.Buffer{} tlv.WriteEntry(buf, tlv.Default, id) for _, url := range relays { tlv.WriteEntry(buf, tlv.Relay, url) } pb := []byte{:schnorr.PubKeyBytesLen} if _, err = hex.DecBytes(pb, pk); chk.D(err) { return nil, errorf.E([]byte("invalid pubkey '%s': %w"), pb, err) } tlv.WriteEntry(buf, tlv.Author, pb) kindBytes := []byte{:4} binary.BigEndian.PutUint32(kindBytes, uint32(k.K)) tlv.WriteEntry(buf, tlv.Kind, kindBytes) var bits5 []byte if bits5, err = bech32.ConvertBits(buf.Bytes(), 8, 5, true); chk.D(err) { return nil, errorf.E([]byte("failed to convert bits: %w"), err) } return bech32.Encode(NentityHRP, bits5) }