tinyrelayfile
tinyrelay/internal/syncprotocol/hll.go/

internal/syncprotocol/hll.go raw
1package syncprotocol
2
3import (
4 "crypto/sha256"
5 "encoding/hex"
6 "fmt"
7 "strconv"
8 "strings"
9
10 "github.com/FelineStateMachine/tinyrelay/internal/event"
11)
12
13const hllRegisters = 256
14
15// HLL is the NIP-45 256-register sketch used to merge counts from relays.
16type HLL struct {
17 offset int
18 regs [hllRegisters]uint8
19}
20
21func NewHLL(offset int) *HLL {
22 if offset < 0 || offset >= 32 {
23 return &HLL{}
24 }
25 return &HLL{offset: offset}
26}
27
28// HLLFilterOffset derives the register byte offset from the single tag value
29// used by the relay's count filter. Hashed values and address-style values are
30// both accepted.
31func HLLFilterOffset(f event.Filter) (int, bool) {
32 if len(f.Tags) != 1 {
33 return 0, false
34 }
35 for _, values := range f.Tags {
36 if len(values) == 0 {
37 return 0, false
38 }
39 value := values[0]
40 parts := strings.Split(value, ":")
41 if len(parts) == 3 && len(parts[1]) == 64 {
42 value = parts[1]
43 }
44 if len(value) != 64 || !isLowerHex(value) {
45 sum := sha256.Sum256([]byte(value))
46 value = hex.EncodeToString(sum[:])
47 }
48 digit, err := strconv.ParseUint(string(value[32]), 16, 4)
49 if err != nil {
50 return 0, false
51 }
52 return int(digit) + 8, true
53 }
54 return 0, false
55}
56
57// Add incorporates one 32-byte hexadecimal public key into the sketch.
58func (h *HLL) Add(pubkeyHex string) error {
59 if len(pubkeyHex) != 64 || !isHexCharacters(pubkeyHex) {
60 return fmt.Errorf("invalid pubkey: expected 32-byte hex")
61 }
62 key, err := hex.DecodeString(pubkeyHex)
63 if err != nil {
64 return fmt.Errorf("decode pubkey: %w", err)
65 }
66 register := key[h.offset]
67 zeros := 0
68 for i := h.offset + 1; i < len(key); i++ {
69 if key[i] == 0 {
70 zeros += 8
71 continue
72 }
73 zeros += leadingZeros8(key[i])
74 break
75 }
76 if value := uint8(zeros + 1); value > h.regs[register] {
77 h.regs[register] = value
78 }
79 return nil
80}
81
82// Hex returns the 256 registers in their wire hexadecimal form.
83func (h *HLL) Hex() string { return hex.EncodeToString(h.regs[:]) }
84
85// MergeHex unions another 256-register sketch into h.
86func (h *HLL) MergeHex(encoded string) error {
87 if len(encoded) != hllRegisters*2 {
88 return fmt.Errorf("invalid HLL: expected %d hex characters", hllRegisters*2)
89 }
90 registers, err := hex.DecodeString(encoded)
91 if err != nil || len(registers) != hllRegisters {
92 return fmt.Errorf("invalid HLL registers")
93 }
94 for i, value := range registers {
95 if value > h.regs[i] {
96 h.regs[i] = value
97 }
98 }
99 return nil
100}
101
102func leadingZeros8(value byte) int {
103 zeros := 0
104 for bit := byte(0x80); value&bit == 0; bit >>= 1 {
105 zeros++
106 }
107 return zeros
108}
109
110func isLowerHex(value string) bool {
111 for _, char := range value {
112 if !(char >= '0' && char <= '9' || char >= 'a' && char <= 'f') {
113 return false
114 }
115 }
116 return true
117}
118
119func isHexCharacters(value string) bool {
120 for _, char := range value {
121 if !(char >= '0' && char <= '9' || char >= 'a' && char <= 'f' || char >= 'A' && char <= 'F') {
122 return false
123 }
124 }
125 return true
126}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs