e-tag processing for replies is working correctly.

This commit is contained in:
Robert C. Martin 2022-05-03 15:46:44 -05:00
parent 139e3b1f0d
commit 55a2eafa43
4 changed files with 138 additions and 48 deletions

View File

@ -3,6 +3,9 @@
[more-speech.nostr.events :refer :all]
[more-speech.nostr.elliptic-signature :as ecc]))
(defn hexify [n]
(->> n (ecc/num->bytes 32) ecc/bytes->hex-string))
(defrecord event-handler-dummy []
event-handler
(handle-text-event [_ _event-id])
@ -88,10 +91,11 @@
(describe "Composing outgoing events"
(it "composes an original message."
(let [private-key (ecc/num->bytes 64 42)
(let [private-key (ecc/num->bytes 64 314159)
event-state {:keys {:private-key (ecc/bytes->hex-string private-key)}}
public-key (ecc/get-pub-key private-key)
text "message text"
event (compose-text-event private-key text)
event (compose-text-event event-state text)
{:keys [pubkey created_at kind tags content id sig]} (second event)
now (quot (System/currentTimeMillis) 1000)]
(should= "EVENT" (first event))
@ -104,19 +108,46 @@
public-key
(ecc/hex-string->bytes sig)))))
(it "composes a reply."
(it "composes a reply to a root article."
(let [private-key (ecc/num->bytes 64 42)
reply-to 7734
reply-to-hex (->> reply-to (ecc/num->bytes 32) ecc/bytes->hex-string)
event-state {:keys {:private-key (ecc/bytes->hex-string private-key)}
:text-event-map {reply-to {:tags []}}}
public-key (ecc/get-pub-key private-key)
reply-to (ecc/num->bytes 32 7734)
text "message text"
event (compose-text-event private-key text reply-to)
event (compose-text-event event-state text reply-to)
{:keys [pubkey created_at kind tags content id sig]} (second event)
now (quot (System/currentTimeMillis) 1000)]
(should= "EVENT" (first event))
(should= (ecc/bytes->hex-string public-key) pubkey)
(should (<= 0 (- now created_at) 1)) ;within one second.
(should= 1 kind)
(should= [[:e (ecc/bytes->hex-string reply-to)]] tags)
(should= [[:e reply-to-hex]] tags)
(should= text content)
(should (ecc/do-verify (ecc/hex-string->bytes id)
public-key
(ecc/hex-string->bytes sig)))))
(it "composes a reply to a non-root article."
(let [private-key (ecc/num->bytes 64 42)
reply-to-child 7734
reply-to-hex (->> reply-to-child (ecc/num->bytes 32) ecc/bytes->hex-string)
root 1952
root-hex (->> root (ecc/num->bytes 32) ecc/bytes->hex-string)
event-state {:keys {:private-key (ecc/bytes->hex-string private-key)}
:text-event-map {reply-to-child {:tags [[:e root-hex]]}
root {:tags []}}}
public-key (ecc/get-pub-key private-key)
text "message text"
event (compose-text-event event-state text reply-to-child)
{:keys [pubkey created_at kind tags content id sig]} (second event)
now (quot (System/currentTimeMillis) 1000)]
(should= "EVENT" (first event))
(should= (ecc/bytes->hex-string public-key) pubkey)
(should (<= 0 (- now created_at) 1)) ;within one second.
(should= 1 kind)
(should= [[:e root-hex] [:e reply-to-hex]] tags)
(should= text content)
(should (ecc/do-verify (ecc/hex-string->bytes id)
public-key
@ -124,10 +155,10 @@
(it "composes a message with a slash."
(let [private-key (ecc/num->bytes 64 42)
event-state {:keys {:private-key (ecc/bytes->hex-string private-key)}}
public-key (ecc/get-pub-key private-key)
reply-to nil
text "message/text"
event (compose-text-event private-key text reply-to)
event (compose-text-event event-state text)
{:keys [pubkey created_at kind tags content id sig]} (second event)
now (quot (System/currentTimeMillis) 1000)]
(should= "EVENT" (first event))
@ -141,6 +172,41 @@
(ecc/hex-string->bytes sig)))))
)
(describe "get references"
(it "given no tags, finds no references"
(let [event {:tags []}
[root mentions referent] (get-references event)]
(should-be-nil root)
(should= [] mentions)
(should-be-nil referent)))
(it "given one tag, finds only the referent"
(let [event {:tags [[:e (hexify 1)]]}
[root mentions referent] (get-references event)]
(should-be-nil root)
(should= [] mentions)
(should= 1 referent)))
(it "given two tags, finds root and referent"
(let [event {:tags [[:e (hexify 1)]
[:e (hexify 2)]]}
[root mentions referent] (get-references event)]
(should= 1 root)
(should= [] mentions)
(should= 2 referent)))
(it "given n>2 tags, finds root and referent"
(let [event {:tags [[:e (hexify 1)]
[:e (hexify 2)]
[:e (hexify 3)]
[:e (hexify 4)]
[:e (hexify 5)]]}
[root mentions referent] (get-references event)]
(should= 1 root)
(should= [2 3 4] mentions)
(should= 5 referent)))
)
(describe "json"
(it "does not escape slashes"
(should= "\"/\"" (to-json "/")))

View File

@ -1,5 +1,5 @@
;;Stories
;; - e and p tags for text events
;; - p tags for text events
;; - validate incoming messages.
;; - Add author/date, etc. to replies.
;; - Start checking sdefs in update.
@ -12,7 +12,6 @@
;;Notes:
;; Nice debug site: https://nostr-army-knife.netlify.app
(ns more-speech.core
(:require [clojure.core.async :as async]
[more-speech.nostr.protocol :as protocol]

View File

@ -78,24 +78,24 @@
(defn get-references [event]
(let [tags (:tags event)
e-tags (filter #(= :e (first %)) tags)
refs (map second e-tags)
refs (map hex-string->num refs)]
refs))
refs (map second e-tags)
refs (map hex-string->num refs)
root (if (< (count refs) 2) nil (first refs))
referent (last refs)
mentions (drop-last (rest refs))]
[root mentions referent]))
(defn process-references [state event]
(let [refs (take 1 (get-references event))] ;; Hack. Only the first reference is counted.
(loop [refs refs
state state]
(if (empty? refs)
state
(let [referent-path [:text-event-map (first refs)]]
(if (nil? (get-in state referent-path))
(recur (rest refs) state)
(recur (rest refs)
(update-in
state
(concat referent-path [:references])
conj (:id event)))))))))
(let [[_ _ referent] (get-references event)]
(if (nil? referent)
state
(let [referent-path [:text-event-map referent]]
(if (nil? (get-in state referent-path))
state
(update-in
state
(concat referent-path [:references])
conj (:id event)))))))
(defn translate-text-event [event]
(let [id (hex-string->num (get event "id"))
@ -141,17 +141,18 @@
id)
)
(declare make-reply-tag)
(declare make-reply-tags get-reply-root)
(defn compose-text-event
([private-key text]
(compose-text-event private-key text nil))
([event-state text]
(compose-text-event event-state text nil))
([private-key text reply-to]
(let [pubkey (ecc/get-pub-key private-key)
tags (if (some? reply-to)
[(make-reply-tag reply-to)]
[])
([event-state text reply-to-or-nil]
(let [private-key (get-in event-state [:keys :private-key])
private-key (ecc/hex-string->bytes private-key)
pubkey (ecc/get-pub-key private-key)
root (get-reply-root event-state reply-to-or-nil)
tags (make-reply-tags reply-to-or-nil root)
content text
now (quot (System/currentTimeMillis) 1000)
body {:pubkey (ecc/bytes->hex-string pubkey)
@ -167,16 +168,36 @@
]
["EVENT" event])))
(defn make-reply-tag [reply-to]
(let [reply-to (ecc/num->bytes 32 reply-to)
reply-to (ecc/bytes->hex-string reply-to)]
[:e reply-to])
(defn get-reply-root [event-state reply-to-or-nil]
(if (nil? reply-to-or-nil)
nil
(loop [parent-id reply-to-or-nil
event-map (:text-event-map event-state)]
(let [event (get event-map parent-id)
[_ _ referent] (get-references event)]
(if (nil? referent)
parent-id
(recur referent event-map)))))
)
(defn send-msg [event-state event message]
(let [private-key (get-in event-state [:keys :private-key])
private-key (ecc/hex-string->bytes private-key)
reply-to (:id event)
event (compose-text-event private-key message reply-to)
(defn make-reply-tags
([reply-to root]
(if (or (nil? root) (= root reply-to))
(make-reply-tags reply-to)
(let [root (->> root (ecc/num->bytes 32) (ecc/bytes->hex-string))
reply-to (->> reply-to (ecc/num->bytes 32) (ecc/bytes->hex-string))]
[[:e root] [:e reply-to]]))
)
([reply-to]
(if (nil? reply-to)
[]
(let [reply-to (->> reply-to (ecc/num->bytes 32) (ecc/bytes->hex-string))]
[[:e reply-to]]))
)
)
(defn send-msg [event-state source-event-or-nil message]
(let [reply-to-or-nil (:id source-event-or-nil)
outgoing-event (compose-text-event event-state message reply-to-or-nil)
send-chan (:send-chan event-state)]
(async/>!! send-chan [:event event])))
(async/>!! send-chan [:event outgoing-event])))

View File

@ -35,6 +35,11 @@
(add-references event)
))
;; at the moment an event can appear in several places in the tree.
;; it can be in the reply chain of an event, and it can stand alone.
;; The node-map holds the list of nodes that correspond to the id of
;; an event.
(defn add-reference [reference id]
(loop [nodes (get-in @ui-context [:node-map reference])]
(if (empty? nodes)
@ -46,11 +51,10 @@
(recur (rest nodes))))))
(defn add-references [event]
(loop [references (events/get-references event)]
(if (empty? references)
(let [[_ _ referent] (events/get-references event)]
(if (nil? referent)
nil
(do (add-reference (first references) (:id event))
(recur (rest references))))))
(add-reference referent (:id event)))))
(defn find-header-node [root id]
(loop [children (enumeration-seq (.children root))]