commit adacdbb7648cd3b4a19b102eba036a7ecff07530
parent cd9bd1b8926c48df746cbde116c50ce1cb9a81bd
Author: William Casarin <jb55@jb55.com>
Date: Sat, 6 Aug 2022 23:01:56 -0700
switch to bech32 everywhere
You can now use @npub, @note or @nsec to reference notes and pubkeys
Changelog-Changed: use bech32 ids everywhere
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
5 files changed, 137 insertions(+), 7 deletions(-)
diff --git a/damus/Models/Post.swift b/damus/Models/Post.swift
@@ -32,15 +32,76 @@ func parse_post_reference(_ p: Parser) -> ReferencedId? {
return parse_nostr_ref_uri(p)
}
- guard let id = parse_hexstr(p, len: 64) else {
+ if let ref = parse_post_mention(p, mention_type: typ) {
+ return ref
+ }
+
+ p.pos = start
+
+ return nil
+}
+
+func is_bech32_char(_ c: Character) -> Bool {
+ let contains = "qpzry9x8gf2tvdw0s3jn54khce6mua7l".contains(c)
+ return contains
+}
+
+func parse_post_mention(_ p: Parser, mention_type: MentionType) -> ReferencedId? {
+ if let id = parse_hexstr(p, len: 64) {
+ return ReferencedId(ref_id: id, relay_id: nil, key: mention_type.ref)
+ } else if let bech32_ref = parse_post_bech32_mention(p) {
+ return bech32_ref
+ } else {
+ return nil
+ }
+}
+
+func parse_post_bech32_mention(_ p: Parser) -> ReferencedId? {
+ let start = p.pos
+ if parse_str(p, "note") {
+ } else if parse_str(p, "npub") {
+ } else if parse_str(p, "nsec") {
+ } else {
+ return nil
+ }
+
+ if !parse_char(p, "1") {
+ p.pos = start
+ return nil
+ }
+
+ var end = p.pos
+ if consume_until(p, match: { c in !is_bech32_char(c) }) {
+ end = p.pos
+ } else {
+ p.pos = start
+ return nil
+ }
+
+ let sliced = String(substring(p.str, start: start, end: end))
+ guard let decoded = try? bech32_decode(sliced) else {
p.pos = start
return nil
}
- return ReferencedId(ref_id: id, relay_id: nil, key: typ.ref)
+ let hex = hex_encode(decoded.data)
+ switch decoded.hrp {
+ case "note":
+ return ReferencedId(ref_id: hex, relay_id: nil, key: "e")
+ case "npub":
+ return ReferencedId(ref_id: hex, relay_id: nil, key: "p")
+ case "nsec":
+ guard let pubkey = privkey_to_pubkey(privkey: hex) else {
+ p.pos = start
+ return nil
+ }
+ return ReferencedId(ref_id: pubkey, relay_id: nil, key: "p")
+ default:
+ p.pos = start
+ return nil
+ }
}
-
/// Return a list of tags
func parse_post_blocks(content: String) -> [PostBlock] {
let p = Parser(pos: 0, str: content)
diff --git a/damus/Util/Keys.swift b/damus/Util/Keys.swift
@@ -58,6 +58,13 @@ func bech32_pubkey(_ pubkey: String) -> String? {
return bech32_encode(hrp: "npub", bytes)
}
+func bech32_note_id(_ evid: String) -> String? {
+ guard let bytes = hex_decode(evid) else {
+ return nil
+ }
+ return bech32_encode(hrp: "note", bytes)
+}
+
func generate_new_keypair() -> Keypair {
let key = try! secp256k1.Signing.PrivateKey()
let privkey = hex_encode(key.rawRepresentation)
diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift
@@ -148,6 +148,16 @@ struct EventView: View {
}
extension View {
+ func pubkey_context_menu(bech32_pubkey: String) -> some View {
+ return self.contextMenu {
+ Button {
+ UIPasteboard.general.string = bech32_pubkey
+ } label: {
+ Label("Copy Account ID", systemImage: "doc.on.doc")
+ }
+ }
+ }
+
func event_context_menu(_ event: NostrEvent, privkey: String?) -> some View {
return self.contextMenu {
Button {
@@ -157,13 +167,13 @@ extension View {
}
Button {
- UIPasteboard.general.string = "@" + event.pubkey
+ UIPasteboard.general.string = bech32_pubkey(event.pubkey) ?? event.pubkey
} label: {
Label("Copy User ID", systemImage: "tag")
}
Button {
- UIPasteboard.general.string = "&" + event.id
+ UIPasteboard.general.string = bech32_note_id(event.id) ?? event.id
} label: {
Label("Copy Note ID", systemImage: "tag")
}
diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift
@@ -104,6 +104,7 @@ struct ProfileView: View {
KeyView(pubkey: profile.pubkey)
.padding(.bottom, 10)
+ .pubkey_context_menu(bech32_pubkey: bech32_pubkey(profile.pubkey) ?? profile.pubkey)
Text(data?.about ?? "")
@@ -190,12 +191,14 @@ struct KeyView: View {
var body: some View {
let col = id_to_color(pubkey)
+ let bech32 = bech32_pubkey(pubkey) ?? pubkey
+ let half = bech32.count / 2
VStack {
- Text("\(String(pubkey.prefix(32)))")
+ Text("\(String(bech32.prefix(half)))")
.foregroundColor(colorScheme == .light ? .black : col)
.font(.footnote.monospaced())
- Text("\(String(pubkey.suffix(32)))")
+ Text("\(String(bech32.suffix(half)))")
.font(.footnote.monospaced())
.foregroundColor(colorScheme == .light ? .black : col)
}
diff --git a/damusTests/ReplyTests.swift b/damusTests/ReplyTests.swift
@@ -189,6 +189,55 @@ class ReplyTests: XCTestCase {
XCTAssertEqual(parsed.count, 0)
}
+ func testNpubMention() throws {
+ let evid = "0000000000000000000000000000000000000000000000000000000000000005"
+ let pk = "npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s"
+ let hex_pk = "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"
+ let content = "this is a @\(pk) mention"
+ let reply_ref = ReferencedId(ref_id: evid, relay_id: nil, key: "e")
+ let blocks = parse_post_blocks(content: content)
+ let post = NostrPost(content: content, references: [reply_ref])
+ let ev = post_to_event(post: post, privkey: evid, pubkey: pk)
+
+ XCTAssertEqual(ev.tags.count, 2)
+ XCTAssertEqual(blocks.count, 3)
+ XCTAssertEqual(blocks[1].is_ref, ReferencedId(ref_id: hex_pk, relay_id: nil, key: "p"))
+ XCTAssertEqual(ev.content, "this is a #[1] mention")
+ }
+
+ func testNoteMention() throws {
+ let evid = "0000000000000000000000000000000000000000000000000000000000000005"
+ let pk = "note154fwmp6hdxqnmqdzkt5jeay8l4kxdsrpn02vw9kp4gylkxxur5fsq3ckpy"
+ let hex_note_id = "a552ed875769813d81a2b2e92cf487fd6c66c0619bd4c716c1aa09fb18dc1d13"
+ let content = "this is a @\(pk) &\(pk) mention"
+ let reply_ref = ReferencedId(ref_id: evid, relay_id: nil, key: "e")
+ let blocks = parse_post_blocks(content: content)
+ let post = NostrPost(content: content, references: [reply_ref])
+ let ev = post_to_event(post: post, privkey: evid, pubkey: pk)
+
+ XCTAssertEqual(ev.tags.count, 3)
+ XCTAssertEqual(blocks.count, 5)
+ XCTAssertEqual(blocks[1].is_ref, ReferencedId(ref_id: hex_note_id, relay_id: nil, key: "e"))
+ XCTAssertEqual(blocks[3].is_ref, ReferencedId(ref_id: hex_note_id, relay_id: nil, key: "e"))
+ XCTAssertEqual(ev.content, "this is a #[1] #[2] mention")
+ }
+
+ func testNsecMention() throws {
+ let evid = "0000000000000000000000000000000000000000000000000000000000000005"
+ let pk = "nsec1jmzdz7d0ldqctdxwm5fzue277ttng2pk28n2u8wntc2r4a0w96ssnyukg7"
+ let hex_pk = "ccf95d668650178defca5ac503693b6668eb77895f610178ff8ed9fe5cf9482e"
+ let content = "this is a @\(pk) mention"
+ let reply_ref = ReferencedId(ref_id: evid, relay_id: nil, key: "e")
+ let blocks = parse_post_blocks(content: content)
+ let post = NostrPost(content: content, references: [reply_ref])
+ let ev = post_to_event(post: post, privkey: evid, pubkey: pk)
+
+ XCTAssertEqual(ev.tags.count, 2)
+ XCTAssertEqual(blocks.count, 3)
+ XCTAssertEqual(blocks[1].is_ref, ReferencedId(ref_id: hex_pk, relay_id: nil, key: "p"))
+ XCTAssertEqual(ev.content, "this is a #[1] mention")
+ }
+
func testPostWithMentions() throws {
let evid = "0000000000000000000000000000000000000000000000000000000000000005"
let pk = "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"