You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
110 lines
3.5 KiB
110 lines
3.5 KiB
// Package neo4j provides hex utilities for normalizing pubkeys and event IDs. |
|
// |
|
// The nostr library applies binary optimization to e/p tags, storing 64-character |
|
// hex strings as 33-byte binary (32 bytes + null terminator). This file provides |
|
// utilities to ensure all pubkeys and event IDs stored in Neo4j are in consistent |
|
// lowercase hex format. |
|
package neo4j |
|
|
|
import ( |
|
"strings" |
|
|
|
"git.mleku.dev/mleku/nostr/encoders/hex" |
|
"git.mleku.dev/mleku/nostr/encoders/tag" |
|
) |
|
|
|
// Tag binary encoding constants (matching the nostr library) |
|
const ( |
|
// BinaryEncodedLen is the length of a binary-encoded 32-byte hash with null terminator |
|
BinaryEncodedLen = 33 |
|
// HexEncodedLen is the length of a hex-encoded 32-byte hash (pubkey or event ID) |
|
HexEncodedLen = 64 |
|
// HashLen is the raw length of a hash (pubkey/event ID) |
|
HashLen = 32 |
|
) |
|
|
|
// IsBinaryEncoded checks if a value is stored in the nostr library's binary-optimized format |
|
func IsBinaryEncoded(val []byte) bool { |
|
return len(val) == BinaryEncodedLen && val[HashLen] == 0 |
|
} |
|
|
|
// NormalizePubkeyHex ensures a pubkey/event ID is in lowercase hex format. |
|
// It handles: |
|
// - Binary-encoded values (33 bytes with null terminator) -> converts to lowercase hex |
|
// - Raw binary values (32 bytes) -> converts to lowercase hex |
|
// - Uppercase hex strings -> converts to lowercase |
|
// - Already lowercase hex -> returns as-is |
|
// |
|
// This should be used for all pubkeys and event IDs before storing in Neo4j |
|
// to prevent duplicate nodes due to case differences. |
|
func NormalizePubkeyHex(val []byte) string { |
|
// Handle binary-encoded values from the nostr library (33 bytes with null terminator) |
|
if IsBinaryEncoded(val) { |
|
// Convert binary to lowercase hex |
|
return hex.Enc(val[:HashLen]) |
|
} |
|
|
|
// Handle raw binary values (32 bytes) - common when passing ev.ID or ev.Pubkey directly |
|
if len(val) == HashLen { |
|
// Convert binary to lowercase hex |
|
return hex.Enc(val) |
|
} |
|
|
|
// Handle hex strings (may be uppercase from external sources) |
|
if len(val) == HexEncodedLen { |
|
return strings.ToLower(string(val)) |
|
} |
|
|
|
// For other lengths (possibly prefixes), lowercase the hex |
|
return strings.ToLower(string(val)) |
|
} |
|
|
|
// ExtractPTagValue extracts a pubkey from a p-tag, handling binary encoding. |
|
// Returns lowercase hex string suitable for Neo4j storage. |
|
// Returns empty string if the tag doesn't have a valid value. |
|
func ExtractPTagValue(t *tag.T) string { |
|
if t == nil || len(t.T) < 2 { |
|
return "" |
|
} |
|
|
|
// Use ValueHex() which properly handles both binary and hex formats |
|
hexVal := t.ValueHex() |
|
if len(hexVal) == 0 { |
|
return "" |
|
} |
|
|
|
// Ensure lowercase (ValueHex returns the library's encoding which is lowercase, |
|
// but we normalize anyway for safety with external data) |
|
return strings.ToLower(string(hexVal)) |
|
} |
|
|
|
// ExtractETagValue extracts an event ID from an e-tag, handling binary encoding. |
|
// Returns lowercase hex string suitable for Neo4j storage. |
|
// Returns empty string if the tag doesn't have a valid value. |
|
func ExtractETagValue(t *tag.T) string { |
|
if t == nil || len(t.T) < 2 { |
|
return "" |
|
} |
|
|
|
// Use ValueHex() which properly handles both binary and hex formats |
|
hexVal := t.ValueHex() |
|
if len(hexVal) == 0 { |
|
return "" |
|
} |
|
|
|
// Ensure lowercase |
|
return strings.ToLower(string(hexVal)) |
|
} |
|
|
|
// IsValidHexPubkey checks if a string is a valid 64-character hex pubkey |
|
func IsValidHexPubkey(s string) bool { |
|
if len(s) != HexEncodedLen { |
|
return false |
|
} |
|
for _, c := range s { |
|
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { |
|
return false |
|
} |
|
} |
|
return true |
|
}
|
|
|