1
0
mirror of git://jb55.com/damus synced 2024-10-01 17:30:44 +00:00

nip19: add high level bech32 encoding method

Add encode method for converting Bech32Object to a bech32 encoded
string. This will be useful for generalized conversion of Bech32Objects
to its string representation.

Lightning-url: LNURL1DP68GURN8GHJ7EM9W3SKCCNE9E3K7MF0D3H82UNVWQHKWUN9V4HXGCTHDC6RZVGR8SW3G
Signed-off-by: kernelkind <kernelkind@gmail.com>
Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
kernelkind 2024-01-13 14:19:45 -05:00 committed by William Casarin
parent cb4adf06f1
commit a9e9701243
2 changed files with 198 additions and 0 deletions

View File

@ -89,6 +89,24 @@ enum Bech32Object : Equatable {
return decodeCBech32(b)
}
static func encode(_ obj: Bech32Object) -> String {
switch(obj) {
case .note(let noteid):
return bech32_encode(hrp: "note", noteid.bytes)
case .nevent(let nevent): return bech32EncodeNevent(nevent)
case .nprofile(let nprofile): return bech32EncodeNprofile(nprofile)
case .nrelay(let relayURL): return bech32EncodeNrelay(relayURL: relayURL)
case .naddr(let naddr): return bech32EncodeNaddr(naddr)
case .npub(let pubkey):
return bech32_encode(hrp: "npub", pubkey.bytes)
case .nsec(let privkey):
guard let pubkey = privkey_to_pubkey(privkey: privkey) else { return "" }
return bech32_encode(hrp: "npub", pubkey.bytes)
case .nscript(let data):
return bech32_encode(hrp: "nscript", data)
}
}
}
func decodeCBech32(_ b: nostr_bech32_t) -> Bech32Object? {
@ -162,3 +180,86 @@ private func getRelayStrings(from relays: relays) -> [String] {
return result
}
private enum TLVType: UInt8 {
case SPECIAL
case RELAY
case AUTHOR
case KIND
}
private func writeBytesList(bytesList: inout [UInt8], tlvType: TLVType, data: [UInt8]){
bytesList.append(tlvType.rawValue)
bytesList.append(UInt8(data.bytes.count))
bytesList.append(contentsOf: data.bytes)
}
private func writeBytesRelays(bytesList: inout [UInt8], relays: [String]) {
for relay in relays where !relay.isEmpty {
guard let relayData = relay.data(using: .utf8) else {
continue // skip relay if can't read data
}
writeBytesList(bytesList: &bytesList, tlvType: .RELAY, data: relayData.bytes)
}
}
private func writeBytesKind(bytesList: inout [UInt8], kind: UInt32) {
bytesList.append(TLVType.KIND.rawValue)
bytesList.append(UInt8(4))
var bigEndianBytes = kind.bigEndian
let data = Data(bytes: &bigEndianBytes, count: MemoryLayout<UInt32>.size)
bytesList.append(contentsOf: data)
}
private func bech32EncodeNevent(_ nevent: NEvent) -> String {
var neventBytes = [UInt8]();
writeBytesList(bytesList: &neventBytes, tlvType: .SPECIAL, data: nevent.noteid.bytes)
writeBytesRelays(bytesList: &neventBytes, relays: nevent.relays)
if let eventPubkey = nevent.author {
writeBytesList(bytesList: &neventBytes, tlvType: .AUTHOR, data: eventPubkey.bytes)
}
if let kind = nevent.kind {
writeBytesKind(bytesList: &neventBytes, kind: kind)
}
return bech32_encode(hrp: "nevent", neventBytes.bytes)
}
private func bech32EncodeNprofile(_ nprofile: NProfile) -> String {
var nprofileBytes = [UInt8]();
writeBytesList(bytesList: &nprofileBytes, tlvType: .SPECIAL, data: nprofile.author.bytes)
writeBytesRelays(bytesList: &nprofileBytes, relays: nprofile.relays)
return bech32_encode(hrp: "nprofile", nprofileBytes.bytes)
}
private func bech32EncodeNrelay(relayURL: String) -> String {
var nrelayBytes = [UInt8]();
guard let relayURLBytes = relayURL.data(using: .ascii) else {
return ""
}
writeBytesList(bytesList: &nrelayBytes, tlvType: .SPECIAL, data: relayURLBytes.bytes)
return bech32_encode(hrp: "nrelay", nrelayBytes.bytes)
}
private func bech32EncodeNaddr(_ naddr: NAddr) -> String {
var naddrBytes = [UInt8]();
guard let identifierBytes = naddr.identifier.data(using: .utf8) else {
return ""
}
writeBytesList(bytesList: &naddrBytes, tlvType: .SPECIAL, data: identifierBytes.bytes)
writeBytesRelays(bytesList: &naddrBytes, relays: naddr.relays)
writeBytesList(bytesList: &naddrBytes, tlvType: .AUTHOR, data: naddr.author.bytes)
writeBytesKind(bytesList: &naddrBytes, kind: naddr.kind)
return bech32_encode(hrp: "naddr", naddrBytes.bytes)
}

View File

@ -115,4 +115,101 @@ class Bech32ObjectTests: XCTestCase {
XCTAssertEqual(expectedObject, actualObject)
}
func testTLVEncoding_NeventHasRelaysNoAuthorNoKind_ValidContent() throws {
guard let noteid = hex_decode_noteid("b9f5441e45ca39179320e0031cfb18e34078673dcc3d3e3a3b3a981760aa5696") else {
XCTFail("Parsing note ID failed")
return
}
let relays = ["wss://nostr-relay.untethr.me", "wss://nostr-pub.wellorder.net"]
let expectedEncoding = "nevent1qqstna2yrezu5wghjvswqqculvvwxsrcvu7uc0f78gan4xqhvz49d9spr3mhxue69uhkummnw3ez6un9d3shjtn4de6x2argwghx6egpr4mhxue69uhkummnw3ez6ur4vgh8wetvd3hhyer9wghxuet5nxnepm"
let actualEncoding = Bech32Object.encode(.nevent(NEvent(noteid: noteid, relays: relays)))
XCTAssertEqual(expectedEncoding, actualEncoding)
}
func testTLVEncoding_NeventHasRelaysNoAuthorHasKind_ValidContent() throws {
guard let noteid = hex_decode_noteid("b9f5441e45ca39179320e0031cfb18e34078673dcc3d3e3a3b3a981760aa5696") else {
XCTFail()
return
}
let relays = [
"wss://nostr-relay.untethr.me",
"wss://nostr-pub.wellorder.net"
]
let expectedEncoding = "nevent1qqstna2yrezu5wghjvswqqculvvwxsrcvu7uc0f78gan4xqhvz49d9spr3mhxue69uhkummnw3ez6un9d3shjtn4de6x2argwghx6egpr4mhxue69uhkummnw3ez6ur4vgh8wetvd3hhyer9wghxuet5qvzqqqqqqyjyqz7d"
let actualEncoding = Bech32Object.encode(.nevent(NEvent(noteid: noteid, relays: relays, kind: 1)))
XCTAssertEqual(expectedEncoding, actualEncoding)
}
func testTLVEncoding_NeventHasRelaysHasAuthorHasKind_ValidContent() throws {
guard let noteid = hex_decode_noteid("b9f5441e45ca39179320e0031cfb18e34078673dcc3d3e3a3b3a981760aa5696") else {
XCTFail("Parsing note ID failed")
return
}
guard let author = try bech32_decode("npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6") else {
XCTFail()
return
}
let relays = ["wss://nostr-relay.untethr.me", "wss://nostr-pub.wellorder.net"]
let expectedEncoding = "nevent1qqstna2yrezu5wghjvswqqculvvwxsrcvu7uc0f78gan4xqhvz49d9spr3mhxue69uhkummnw3ez6un9d3shjtn4de6x2argwghx6egpr4mhxue69uhkummnw3ez6ur4vgh8wetvd3hhyer9wghxuet5qgsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8grqsqqqqqpw4032x"
let actualEncoding = Bech32Object.encode(.nevent(NEvent(noteid: noteid, relays: relays, author: Pubkey(author.data), kind: 1)))
XCTAssertEqual(expectedEncoding, actualEncoding)
}
func testTLVEncoding_NProfileExample_ValidContent() throws {
guard let author = try bech32_decode("npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6") else {
XCTFail()
return
}
let relays = [
"wss://r.x.com",
"wss://djbas.sadkb.com"
]
let expectedEncoding = "nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p"
let actualEncoding = Bech32Object.encode(.nprofile(NProfile(author: Pubkey(author.data), relays: relays)))
XCTAssertEqual(expectedEncoding, actualEncoding)
}
func testTLVEncoding_NRelayExample_ValidContent() throws {
let relay = "wss://relay.nostr.band"
let expectedEncoding = "nrelay1qqt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueq4r295t"
let actualEncoding = Bech32Object.encode(.nrelay(relay))
XCTAssertEqual(expectedEncoding, actualEncoding)
}
func testTLVEncoding_NaddrExample_ValidContent() throws {
guard let author = try bech32_decode("npub1tx0k0a7lw62vvqax6p3ku90tccgdka7ul4radews2wrdsg0m865szf9fw6") else {
XCTFail()
return
}
let relays = ["wss://relay.nostr.band"]
let identifier = "1700730909108"
let kind: UInt32 = 30023
let expectedEncoding = "naddr1qqxnzdesxqmnxvpexqunzvpcqyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqzypve7elhmamff3sr5mgxxms4a0rppkmhmn7504h96pfcdkpplvl2jqcyqqq823cnmhuld"
let actualEncoding = Bech32Object.encode(.naddr(NAddr(identifier: identifier, author: Pubkey(author.data), relays: relays, kind: kind)))
XCTAssertEqual(expectedEncoding, actualEncoding)
}
}