// Package index defines key prefix types and encoding for the store's index system. // Each key type has a 3-byte prefix followed by fixed-width fields. package index import ( "smesh.lol/pkg/store/serial" ) // Prefix is a 3-byte index prefix. type Prefix [3]byte var ( Eid = Prefix{'e', 'i', 'd'} // event ID hash -> serial Fpc = Prefix{'f', 'p', 'c'} // serial -> full ID + pubkey hash + timestamp Sei = Prefix{'s', 'e', 'i'} // serial -> full 32-byte event ID CA = Prefix{'c', '-', '-'} // created_at -> serial Exp = Prefix{'e', 'x', 'p'} // expiration timestamp -> serial KC = Prefix{'k', 'c', '-'} // kind + created_at -> serial PC = Prefix{'p', 'c', '-'} // pubkey hash + created_at -> serial KPC = Prefix{'k', 'p', 'c'} // kind + pubkey hash + created_at -> serial TC = Prefix{'t', 'c', '-'} // tag key + value hash + created_at -> serial TKC = Prefix{'t', 'k', 'c'} // tag kind + key + value hash + created_at -> serial TPC = Prefix{'t', 'p', 'c'} // tag pubkey + key + value hash + created_at -> serial TKP = Prefix{'t', 'k', 'p'} // tag kind + pubkey + key + value hash + created_at -> serial Wrd = Prefix{'w', 'r', 'd'} // word hash -> serial Pks = Prefix{'p', 'k', 's'} // pubkey hash -> pubkey serial Spk = Prefix{'s', 'p', 'k'} // pubkey serial -> full pubkey (32 bytes in value) Epg = Prefix{'e', 'p', 'g'} // event_serial|pubkey_serial|kind|direction Peg = Prefix{'p', 'e', 'g'} // pubkey_serial|kind|direction|event_serial Eeg = Prefix{'e', 'e', 'g'} // source_event_serial|target_event_serial|kind|direction Gee = Prefix{'g', 'e', 'e'} // target_event_serial|kind|direction|source_event_serial Ppg = Prefix{'p', 'p', 'g'} // source_pk_serial|target_pk_serial|kind|direction|event_serial Gpp = Prefix{'g', 'p', 'p'} // target_pk_serial|kind|direction|source_pk_serial|event_serial ) // Edge direction constants (match orly.dev exactly). const ( // Event-pubkey edge directions (epg/peg). DirAuthor byte = 0 // pubkey is the event author DirPTagOut byte = 1 // event references pubkey via p-tag (outbound) DirPTagIn byte = 2 // pubkey is referenced by event's p-tag (inbound) // Event-event edge directions (eeg/gee). DirETagOut byte = 0 // this event references target event via e-tag DirETagIn byte = 1 // this event is referenced by source event via e-tag // Pubkey-pubkey edge directions (ppg/gpp). DirPKOut byte = 0 // source pubkey references target pubkey DirPKIn byte = 1 // target pubkey is referenced by source pubkey ) // Key sizes (prefix + fields). These are the total key lengths for each index type. const ( EidKeyLen = 3 + serial.HashLen + serial.Len // 16 FpcKeyLen = 3 + serial.Len + 32 + serial.HashLen + 8 // 56 SeiKeyLen = 3 + serial.Len // 8 (value: 32 bytes) CAKeyLen = 3 + 8 + serial.Len // 16 ExpKeyLen = 3 + 8 + serial.Len // 16 KCKeyLen = 3 + 2 + 8 + serial.Len // 18 PCKeyLen = 3 + serial.HashLen + 8 + serial.Len // 24 KPCKeyLen = 3 + 2 + serial.HashLen + 8 + serial.Len // 26 TCKeyLen = 3 + 1 + serial.HashLen + 8 + serial.Len // 25 TKCKeyLen = 3 + 2 + 1 + serial.HashLen + 8 + serial.Len // 27 TPCKeyLen = 3 + serial.HashLen + 1 + serial.HashLen + 8 + serial.Len // 33 TKPKeyLen = 3 + 2 + serial.HashLen + 1 + serial.HashLen + 8 + serial.Len // 35 WrdKeyLen = 3 + serial.HashLen + serial.Len // 16 PksKeyLen = 3 + serial.HashLen + serial.Len // 16 SpkKeyLen = 3 + serial.Len // 8 (value: 32 bytes) // Graph index key lengths. EpgKeyLen = 3 + serial.Len + serial.Len + 2 + 1 // 16: prefix|event_ser|pk_ser|kind|dir PegKeyLen = 3 + serial.Len + 2 + 1 + serial.Len // 16: prefix|pk_ser|kind|dir|event_ser EegKeyLen = 3 + serial.Len + serial.Len + 2 + 1 // 16: prefix|src_ser|tgt_ser|kind|dir GeeKeyLen = 3 + serial.Len + 2 + 1 + serial.Len // 16: prefix|tgt_ser|kind|dir|src_ser PpgKeyLen = 3 + serial.Len + serial.Len + 2 + 1 + serial.Len // 21: prefix|src_pk|tgt_pk|kind|dir|ev_ser GppKeyLen = 3 + serial.Len + 2 + 1 + serial.Len + serial.Len // 21: prefix|tgt_pk|kind|dir|src_pk|ev_ser // Record lengths for indexes that carry values after the key. SeiRecLen = SeiKeyLen + 32 // 40 (key + 32-byte event ID) SpkRecLen = SpkKeyLen + 32 // 40 (key + 32-byte pubkey) FpcCmpLen = 3 + serial.Len // 8 (prefix + serial, for search) ) // MakeEid builds an eid key: prefix|id_hash(8)|serial(5). func MakeEid(idHash []byte, ser uint64) []byte { k := []byte{:EidKeyLen} copy(k, Eid[:]) copy(k[3:], idHash[:serial.HashLen]) serial.Put(k[3+serial.HashLen:], ser) return k } // MakeSei builds a sei key: prefix|serial(5). Value is full 32-byte event ID. func MakeSei(ser uint64) []byte { k := []byte{:SeiKeyLen} copy(k, Sei[:]) serial.Put(k[3:], ser) return k } // MakeCA builds a created_at key: prefix|timestamp(8)|serial(5). func MakeCA(ts int64, ser uint64) []byte { k := []byte{:CAKeyLen} copy(k, CA[:]) serial.PutTimestamp(k[3:], ts) serial.Put(k[3+8:], ser) return k } // MakeKC builds a kind+created_at key: prefix|kind(2)|timestamp(8)|serial(5). func MakeKC(kind uint16, ts int64, ser uint64) []byte { k := []byte{:KCKeyLen} copy(k, KC[:]) serial.PutKind(k[3:], kind) serial.PutTimestamp(k[5:], ts) serial.Put(k[13:], ser) return k } // MakePC builds a pubkey+created_at key: prefix|pubhash(8)|timestamp(8)|serial(5). func MakePC(pubHash []byte, ts int64, ser uint64) []byte { k := []byte{:PCKeyLen} copy(k, PC[:]) copy(k[3:], pubHash[:serial.HashLen]) serial.PutTimestamp(k[3+serial.HashLen:], ts) serial.Put(k[3+serial.HashLen+8:], ser) return k } // MakeKPC builds a kind+pubkey+created_at key. func MakeKPC(kind uint16, pubHash []byte, ts int64, ser uint64) []byte { k := []byte{:KPCKeyLen} copy(k, KPC[:]) serial.PutKind(k[3:], kind) copy(k[5:], pubHash[:serial.HashLen]) serial.PutTimestamp(k[5+serial.HashLen:], ts) serial.Put(k[5+serial.HashLen+8:], ser) return k } // MakeTC builds a tag+created_at key: prefix|tagkey(1)|valuehash(8)|timestamp(8)|serial(5). func MakeTC(tagKey byte, valHash []byte, ts int64, ser uint64) []byte { k := []byte{:TCKeyLen} copy(k, TC[:]) k[3] = tagKey copy(k[4:], valHash[:serial.HashLen]) serial.PutTimestamp(k[4+serial.HashLen:], ts) serial.Put(k[4+serial.HashLen+8:], ser) return k } // MakeTKC builds a tag+kind+created_at key. func MakeTKC(kind uint16, tagKey byte, valHash []byte, ts int64, ser uint64) []byte { k := []byte{:TKCKeyLen} copy(k, TKC[:]) serial.PutKind(k[3:], kind) k[5] = tagKey copy(k[6:], valHash[:serial.HashLen]) serial.PutTimestamp(k[6+serial.HashLen:], ts) serial.Put(k[6+serial.HashLen+8:], ser) return k } // MakeTPC builds a tag+pubkey+created_at key. func MakeTPC(pubHash []byte, tagKey byte, valHash []byte, ts int64, ser uint64) []byte { k := []byte{:TPCKeyLen} copy(k, TPC[:]) copy(k[3:], pubHash[:serial.HashLen]) k[3+serial.HashLen] = tagKey copy(k[3+serial.HashLen+1:], valHash[:serial.HashLen]) serial.PutTimestamp(k[3+serial.HashLen+1+serial.HashLen:], ts) serial.Put(k[3+serial.HashLen+1+serial.HashLen+8:], ser) return k } // MakeTKP builds a tag+kind+pubkey+created_at key. func MakeTKP(kind uint16, pubHash []byte, tagKey byte, valHash []byte, ts int64, ser uint64) []byte { k := []byte{:TKPKeyLen} copy(k, TKP[:]) serial.PutKind(k[3:], kind) copy(k[5:], pubHash[:serial.HashLen]) k[5+serial.HashLen] = tagKey copy(k[5+serial.HashLen+1:], valHash[:serial.HashLen]) serial.PutTimestamp(k[5+serial.HashLen+1+serial.HashLen:], ts) serial.Put(k[5+serial.HashLen+1+serial.HashLen+8:], ser) return k } // MakeWrd builds a word key: prefix|wordhash(8)|serial(5). func MakeWrd(wordHash []byte, ser uint64) []byte { k := []byte{:WrdKeyLen} copy(k, Wrd[:]) copy(k[3:], wordHash[:serial.HashLen]) serial.Put(k[3+serial.HashLen:], ser) return k } // MakePks builds a pubkey serial key: prefix|pubhash(8)|serial(5). func MakePks(pubHash []byte, ser uint64) []byte { k := []byte{:PksKeyLen} copy(k, Pks[:]) copy(k[3:], pubHash[:serial.HashLen]) serial.Put(k[3+serial.HashLen:], ser) return k } // MakeSpk builds a serial->pubkey key: prefix|serial(5). Value: 32-byte pubkey. func MakeSpk(ser uint64) []byte { k := []byte{:SpkKeyLen} copy(k, Spk[:]) serial.Put(k[3:], ser) return k } // MakeExp builds an expiration key: prefix|timestamp(8)|serial(5). func MakeExp(ts int64, ser uint64) []byte { k := []byte{:ExpKeyLen} copy(k, Exp[:]) serial.PutTimestamp(k[3:], ts) serial.Put(k[3+8:], ser) return k } // MakeFpc builds an fpc key: prefix|serial(5)|full_id(32)|pubhash(8)|timestamp(8). func MakeFpc(ser uint64, fullID []byte, pubHash []byte, ts int64) []byte { k := []byte{:FpcKeyLen} copy(k, Fpc[:]) serial.Put(k[3:], ser) copy(k[3+serial.Len:], fullID[:32]) copy(k[3+serial.Len+32:], pubHash[:serial.HashLen]) serial.PutTimestamp(k[3+serial.Len+32+serial.HashLen:], ts) return k } // MakeSeiRec builds a sei record: prefix|serial(5)|event_id(32). func MakeSeiRec(ser uint64, eventID []byte) []byte { rec := []byte{:SeiRecLen} copy(rec, Sei[:]) serial.Put(rec[3:], ser) copy(rec[SeiKeyLen:], eventID[:32]) return rec } // MakeSpkRec builds a spk record: prefix|serial(5)|pubkey(32). func MakeSpkRec(ser uint64, pubkey []byte) []byte { rec := []byte{:SpkRecLen} copy(rec, Spk[:]) serial.Put(rec[3:], ser) copy(rec[SpkKeyLen:], pubkey[:32]) return rec } // --- Graph index key builders --- // MakeEpg: prefix|event_serial(5)|pubkey_serial(5)|kind(2)|direction(1). func MakeEpg(evSer, pkSer uint64, kind uint16, dir byte) []byte { k := []byte{:EpgKeyLen} copy(k, Epg[:]) serial.Put(k[3:], evSer) serial.Put(k[8:], pkSer) serial.PutKind(k[13:], kind) k[15] = dir return k } // MakePeg: prefix|pubkey_serial(5)|kind(2)|direction(1)|event_serial(5). func MakePeg(pkSer uint64, kind uint16, dir byte, evSer uint64) []byte { k := []byte{:PegKeyLen} copy(k, Peg[:]) serial.Put(k[3:], pkSer) serial.PutKind(k[8:], kind) k[10] = dir serial.Put(k[11:], evSer) return k } // MakeEeg: prefix|source_serial(5)|target_serial(5)|kind(2)|direction(1). func MakeEeg(srcSer, tgtSer uint64, kind uint16, dir byte) []byte { k := []byte{:EegKeyLen} copy(k, Eeg[:]) serial.Put(k[3:], srcSer) serial.Put(k[8:], tgtSer) serial.PutKind(k[13:], kind) k[15] = dir return k } // MakeGee: prefix|target_serial(5)|kind(2)|direction(1)|source_serial(5). func MakeGee(tgtSer uint64, kind uint16, dir byte, srcSer uint64) []byte { k := []byte{:GeeKeyLen} copy(k, Gee[:]) serial.Put(k[3:], tgtSer) serial.PutKind(k[8:], kind) k[10] = dir serial.Put(k[11:], srcSer) return k } // MakePpg: prefix|src_pk(5)|tgt_pk(5)|kind(2)|direction(1)|event_serial(5). func MakePpg(srcPK, tgtPK uint64, kind uint16, dir byte, evSer uint64) []byte { k := []byte{:PpgKeyLen} copy(k, Ppg[:]) serial.Put(k[3:], srcPK) serial.Put(k[8:], tgtPK) serial.PutKind(k[13:], kind) k[15] = dir serial.Put(k[16:], evSer) return k } // MakeGpp: prefix|tgt_pk(5)|kind(2)|direction(1)|src_pk(5)|event_serial(5). func MakeGpp(tgtPK uint64, kind uint16, dir byte, srcPK, evSer uint64) []byte { k := []byte{:GppKeyLen} copy(k, Gpp[:]) serial.Put(k[3:], tgtPK) serial.PutKind(k[8:], kind) k[10] = dir serial.Put(k[11:], srcPK) serial.Put(k[16:], evSer) return k }