Bech32Object.swift (11970B)
1 // 2 // Bech32Object.swift 3 // damus 4 // 5 // Created by William Casarin on 2023-01-28. 6 // 7 8 import Foundation 9 10 fileprivate extension String { 11 /// Failable initializer to build a Swift.String from a C-backed `str_block_t`. 12 init?(_ s: str_block_t) { 13 let bytes = Data(bytes: s.str, count: Int(s.len)) 14 self.init(bytes: bytes, encoding: .utf8) 15 } 16 } 17 18 struct NEvent : Equatable, Hashable { 19 let noteid: NoteId 20 let relays: [RelayURL] 21 let author: Pubkey? 22 let kind: UInt32? 23 24 init(noteid: NoteId, relays: [RelayURL], author: Pubkey? = nil, kind: UInt32? = nil) { 25 self.noteid = noteid 26 self.relays = relays 27 self.author = author 28 self.kind = kind 29 } 30 31 init(event: NostrEvent, relays: [RelayURL]) { 32 self.init(noteid: event.id, relays: relays, author: event.pubkey, kind: event.kind) 33 } 34 } 35 36 struct NProfile : Equatable, Hashable { 37 let author: Pubkey 38 let relays: [RelayURL] 39 } 40 41 struct NAddr : Equatable, Hashable { 42 let identifier: String 43 let author: Pubkey 44 let relays: [RelayURL] 45 let kind: UInt32 46 } 47 48 extension ndb_relays { 49 func as_urls() -> [RelayURL] { 50 var urls = [RelayURL]() 51 52 // 53 // This is so incredibly dumb but it's just what the Swift <-> C bridge 54 // does and I don't have a better way that doesn't involve complicated 55 // and slow stuff like reflection 56 // 57 let (r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16,r17,r18,r19,r20,r21,r22,r23) = self.relays 58 59 for i in 0..<self.num_relays { 60 switch i { 61 case 0: if let relay = RelayURL(r0.as_str()) { urls.append(relay) } 62 case 1: if let relay = RelayURL(r1.as_str()) { urls.append(relay) } 63 case 2: if let relay = RelayURL(r2.as_str()) { urls.append(relay) } 64 case 3: if let relay = RelayURL(r3.as_str()) { urls.append(relay) } 65 case 4: if let relay = RelayURL(r4.as_str()) { urls.append(relay) } 66 case 5: if let relay = RelayURL(r5.as_str()) { urls.append(relay) } 67 case 6: if let relay = RelayURL(r6.as_str()) { urls.append(relay) } 68 case 7: if let relay = RelayURL(r7.as_str()) { urls.append(relay) } 69 case 8: if let relay = RelayURL(r8.as_str()) { urls.append(relay) } 70 case 9: if let relay = RelayURL(r9.as_str()) { urls.append(relay) } 71 case 10: if let relay = RelayURL(r10.as_str()) { urls.append(relay) } 72 case 11: if let relay = RelayURL(r11.as_str()) { urls.append(relay) } 73 case 12: if let relay = RelayURL(r12.as_str()) { urls.append(relay) } 74 case 13: if let relay = RelayURL(r13.as_str()) { urls.append(relay) } 75 case 14: if let relay = RelayURL(r14.as_str()) { urls.append(relay) } 76 case 15: if let relay = RelayURL(r15.as_str()) { urls.append(relay) } 77 case 16: if let relay = RelayURL(r16.as_str()) { urls.append(relay) } 78 case 17: if let relay = RelayURL(r17.as_str()) { urls.append(relay) } 79 case 18: if let relay = RelayURL(r18.as_str()) { urls.append(relay) } 80 case 19: if let relay = RelayURL(r19.as_str()) { urls.append(relay) } 81 case 20: if let relay = RelayURL(r20.as_str()) { urls.append(relay) } 82 case 21: if let relay = RelayURL(r21.as_str()) { urls.append(relay) } 83 case 22: if let relay = RelayURL(r22.as_str()) { urls.append(relay) } 84 case 23: if let relay = RelayURL(r23.as_str()) { urls.append(relay) } 85 default: 86 break 87 } 88 } 89 90 return urls 91 } 92 93 } 94 95 enum Bech32Object : Equatable, Hashable { 96 case nsec(Privkey) 97 case npub(Pubkey) 98 case note(NoteId) 99 case nscript([UInt8]) 100 case nevent(NEvent) 101 case nprofile(NProfile) 102 case nrelay(String) 103 case naddr(NAddr) 104 105 func pubkey() -> Pubkey? { 106 switch self { 107 case .nprofile(let nprofile): return nprofile.author 108 case .npub(let pubkey): return pubkey 109 case .nevent(let ev): return ev.author 110 case .naddr(let naddr): return naddr.author 111 case .nscript: return nil 112 case .nsec: return nil // TODO privkey_to_pubkey ? 113 case .note: return nil 114 case .nrelay: return nil 115 } 116 } 117 118 init?(block: ndb_mention_bech32_block) { 119 let b32 = block.bech32 120 switch block.bech32_type { 121 case .note: 122 let data = b32.note.event_id.as_data(size: 32) 123 self = .note(NoteId(data)) 124 case .npub: 125 let data = b32.npub.pubkey.as_data(size: 32) 126 self = .npub(Pubkey(data)) 127 case .nprofile: 128 let pk = b32.nprofile.pubkey.as_data(size: 32) 129 let relays = b32.nprofile.relays.as_urls() 130 self = .nprofile(NProfile(author: Pubkey(pk), relays: relays)) 131 case .nevent: 132 let nevent = b32.nevent 133 let note_id = NoteId(nevent.event_id.as_data(size: 32)) 134 let relays = nevent.relays.as_urls() 135 var author: Pubkey? = nil 136 if nevent.pubkey != nil { 137 author = Pubkey(nevent.pubkey.as_data(size: 32)) 138 } 139 var kind: UInt32? = nil 140 if nevent.has_kind { 141 kind = nevent.kind 142 } 143 144 self = .nevent(NEvent(noteid: note_id, relays: relays, author: author, kind: kind)) 145 case .nrelay: 146 self = .nrelay(b32.nrelay.relay.as_str()) 147 case .naddr: 148 let identifier = b32.naddr.identifier.as_str() 149 let author = Pubkey(b32.naddr.pubkey.as_data(size: 32)) 150 let relays = b32.naddr.relays.as_urls() 151 self = .naddr(NAddr(identifier: identifier, author: author, relays: relays, kind: b32.naddr.kind)) 152 case .nsec: 153 return nil 154 case .none: 155 return nil 156 } 157 } 158 159 static func parse(_ str: String) -> Bech32Object? { 160 if str.starts(with: "nscript"), let decoded = try? bech32_decode(str) { 161 return .nscript(decoded.data.bytes) 162 } 163 164 var b: nostr_bech32_t = nostr_bech32() 165 var bytes = Data(capacity: str.utf8.count) 166 167 let ok = str.withCString { cstr in 168 let ok = bytes.withUnsafeMutableBytes { buffer -> Int32 in 169 guard let addr = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { 170 return 0 171 } 172 return parse_nostr_bech32(addr, Int32(buffer.count), cstr, str.utf8.count, &b) 173 } 174 175 return ok != 0 176 } 177 178 guard ok else { return nil } 179 180 return decodeCBech32(b) 181 } 182 183 static func encode(_ obj: Bech32Object) -> String { 184 switch(obj) { 185 case .note(let noteid): 186 return bech32_encode(hrp: "note", noteid.bytes) 187 case .nevent(let nevent): return bech32EncodeNevent(nevent) 188 case .nprofile(let nprofile): return bech32EncodeNprofile(nprofile) 189 case .nrelay(let relayURL): return bech32EncodeNrelay(relayURL: relayURL) 190 case .naddr(let naddr): return bech32EncodeNaddr(naddr) 191 case .npub(let pubkey): 192 return bech32_encode(hrp: "npub", pubkey.bytes) 193 case .nsec(let privkey): 194 guard let pubkey = privkey_to_pubkey(privkey: privkey) else { return "" } 195 return bech32_encode(hrp: "npub", pubkey.bytes) 196 case .nscript(let data): 197 return bech32_encode(hrp: "nscript", data) 198 } 199 } 200 201 func toMentionRef() -> MentionRef? { 202 MentionRef(nip19: self) 203 } 204 205 } 206 207 func decodeCBech32(_ b: nostr_bech32_t) -> Bech32Object? { 208 switch b.type { 209 case NOSTR_BECH32_NOTE: 210 let note_id = NoteId(Data(bytes: b.note.event_id, count: 32)) 211 return .note(note_id) 212 case NOSTR_BECH32_NEVENT: 213 let note_id = NoteId(Data(bytes: b.nevent.event_id, count: 32)) 214 let pubkey = b.nevent.pubkey != nil ? Pubkey(Data(bytes: b.nevent.pubkey, count: 32)) : nil 215 let kind: UInt32? = !b.nevent.has_kind ? nil : b.nevent.kind 216 let relays = b.nevent.relays.as_urls() 217 return .nevent(NEvent(noteid: note_id, relays: relays, author: pubkey, kind: kind)) 218 case NOSTR_BECH32_NPUB: 219 let pubkey = Pubkey(Data(bytes: b.npub.pubkey, count: 32)) 220 return .npub(pubkey) 221 case NOSTR_BECH32_NSEC: 222 let privkey = Privkey(Data(bytes: b.nsec.nsec, count: 32)) 223 guard let pubkey = privkey_to_pubkey(privkey: privkey) else { return nil } 224 return .npub(pubkey) 225 case NOSTR_BECH32_NPROFILE: 226 let pubkey = Pubkey(Data(bytes: b.nprofile.pubkey, count: 32)) 227 return .nprofile(NProfile(author: pubkey, relays: b.nprofile.relays.as_urls())) 228 case NOSTR_BECH32_NRELAY: 229 return .nrelay(b.nrelay.relay.as_str()) 230 case NOSTR_BECH32_NADDR: 231 let pubkey = Pubkey(Data(bytes: b.naddr.pubkey, count: 32)) 232 let kind = b.naddr.kind 233 let identifier = b.naddr.identifier.as_str() 234 235 return .naddr(NAddr(identifier: identifier, author: pubkey, relays: b.naddr.relays.as_urls(), kind: kind)) 236 default: 237 return nil 238 } 239 } 240 241 private enum TLVType: UInt8 { 242 case SPECIAL 243 case RELAY 244 case AUTHOR 245 case KIND 246 } 247 248 private func writeBytesList(bytesList: inout [UInt8], tlvType: TLVType, data: [UInt8]){ 249 bytesList.append(tlvType.rawValue) 250 bytesList.append(UInt8(data.bytes.count)) 251 bytesList.append(contentsOf: data.bytes) 252 } 253 254 private func writeBytesRelays(bytesList: inout [UInt8], relays: [RelayURL]) { 255 for relay in relays { 256 guard let relayData = relay.url.absoluteString.data(using: .utf8) else { 257 continue // skip relay if can't read data 258 } 259 writeBytesList(bytesList: &bytesList, tlvType: .RELAY, data: relayData.bytes) 260 } 261 } 262 263 private func writeBytesKind(bytesList: inout [UInt8], kind: UInt32) { 264 bytesList.append(TLVType.KIND.rawValue) 265 bytesList.append(UInt8(4)) 266 267 var bigEndianBytes = kind.bigEndian 268 let data = Data(bytes: &bigEndianBytes, count: MemoryLayout<UInt32>.size) 269 270 bytesList.append(contentsOf: data) 271 } 272 273 private func bech32EncodeNevent(_ nevent: NEvent) -> String { 274 var neventBytes = [UInt8](); 275 writeBytesList(bytesList: &neventBytes, tlvType: .SPECIAL, data: nevent.noteid.bytes) 276 277 writeBytesRelays(bytesList: &neventBytes, relays: nevent.relays) 278 279 if let eventPubkey = nevent.author { 280 writeBytesList(bytesList: &neventBytes, tlvType: .AUTHOR, data: eventPubkey.bytes) 281 } 282 283 if let kind = nevent.kind { 284 writeBytesKind(bytesList: &neventBytes, kind: kind) 285 } 286 287 return bech32_encode(hrp: "nevent", neventBytes.bytes) 288 } 289 290 private func bech32EncodeNprofile(_ nprofile: NProfile) -> String { 291 var nprofileBytes = [UInt8](); 292 293 writeBytesList(bytesList: &nprofileBytes, tlvType: .SPECIAL, data: nprofile.author.bytes) 294 writeBytesRelays(bytesList: &nprofileBytes, relays: nprofile.relays) 295 296 return bech32_encode(hrp: "nprofile", nprofileBytes.bytes) 297 } 298 299 private func bech32EncodeNrelay(relayURL: String) -> String { 300 var nrelayBytes = [UInt8](); 301 302 guard let relayURLBytes = relayURL.data(using: .ascii) else { 303 return "" 304 } 305 306 writeBytesList(bytesList: &nrelayBytes, tlvType: .SPECIAL, data: relayURLBytes.bytes) 307 return bech32_encode(hrp: "nrelay", nrelayBytes.bytes) 308 } 309 310 private func bech32EncodeNaddr(_ naddr: NAddr) -> String { 311 var naddrBytes = [UInt8](); 312 313 guard let identifierBytes = naddr.identifier.data(using: .utf8) else { 314 return "" 315 } 316 317 writeBytesList(bytesList: &naddrBytes, tlvType: .SPECIAL, data: identifierBytes.bytes) 318 writeBytesRelays(bytesList: &naddrBytes, relays: naddr.relays) 319 writeBytesList(bytesList: &naddrBytes, tlvType: .AUTHOR, data: naddr.author.bytes) 320 writeBytesKind(bytesList: &naddrBytes, kind: naddr.kind) 321 return bech32_encode(hrp: "naddr", naddrBytes.bytes) 322 }