NIP05 Verification
Changelog-Added: Added NIP05 Verification
This commit is contained in:
@@ -544,7 +544,9 @@ func process_metadata_event(profiles: Profiles, ev: NostrEvent) {
|
||||
return
|
||||
}
|
||||
|
||||
var old_nip05: String? = nil
|
||||
if let mprof = profiles.lookup_with_timestamp(id: ev.pubkey) {
|
||||
old_nip05 = mprof.profile.nip05
|
||||
if mprof.timestamp > ev.created_at {
|
||||
// skip if we already have an newer profile
|
||||
return
|
||||
@@ -554,6 +556,20 @@ func process_metadata_event(profiles: Profiles, ev: NostrEvent) {
|
||||
let tprof = TimestampedProfile(profile: profile, timestamp: ev.created_at)
|
||||
profiles.add(id: ev.pubkey, profile: tprof)
|
||||
|
||||
if let nip05 = profile.nip05, old_nip05 != profile.nip05 {
|
||||
Task.detached(priority: .background) {
|
||||
let validated = await validate_nip05(pubkey: ev.pubkey, nip05_str: nip05)
|
||||
if validated != nil {
|
||||
print("validated nip05 for '\(nip05)'")
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
profiles.validated[ev.pubkey] = validated
|
||||
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load pfps asap
|
||||
let picture = tprof.profile.picture ?? robohash(ev.pubkey)
|
||||
if let _ = URL(string: picture) {
|
||||
|
||||
@@ -11,6 +11,11 @@ import UIKit
|
||||
|
||||
class Profiles {
|
||||
var profiles: [String: TimestampedProfile] = [:]
|
||||
var validated: [String: NIP05] = [:]
|
||||
|
||||
func is_validated(_ pk: String) -> NIP05? {
|
||||
return validated[pk]
|
||||
}
|
||||
|
||||
func add(id: String, profile: TimestampedProfile) {
|
||||
profiles[id] = profile
|
||||
|
||||
65
damus/Util/NIP05.swift
Normal file
65
damus/Util/NIP05.swift
Normal file
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// NIP05.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-01-04.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct NIP05 {
|
||||
let username: String
|
||||
let host: String
|
||||
|
||||
var url: URL? {
|
||||
URL(string: "https://\(host)/.well-known/nostr.json?name=\(username)")
|
||||
}
|
||||
|
||||
static func parse(_ nip05: String) -> NIP05? {
|
||||
let parts = nip05.split(separator: "@")
|
||||
guard parts.count == 2 else {
|
||||
return nil
|
||||
}
|
||||
return NIP05(username: String(parts[0]), host: String(parts[1]))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct NIP05Response: Decodable {
|
||||
let names: [String: String]
|
||||
let relays: [String: [String]]?
|
||||
}
|
||||
|
||||
enum NIP05Validation {
|
||||
case invalid
|
||||
case valid
|
||||
}
|
||||
|
||||
func validate_nip05(pubkey: String, nip05_str: String) async -> NIP05? {
|
||||
guard let nip05 = NIP05.parse(nip05_str) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let url = nip05.url else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let ret = try? await URLSession.shared.data(from: url) else {
|
||||
return nil
|
||||
}
|
||||
let dat = ret.0
|
||||
|
||||
guard let decoded = try? JSONDecoder().decode(NIP05Response.self, from: dat) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let stored_pk = decoded.names[nip05.username] else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard stored_pk == pubkey else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nip05
|
||||
}
|
||||
@@ -86,7 +86,7 @@ struct ChatView: View {
|
||||
VStack(alignment: .leading) {
|
||||
if just_started {
|
||||
HStack {
|
||||
ProfileName(pubkey: event.pubkey, profile: damus_state.profiles.lookup(id: event.pubkey), contacts: damus_state.contacts, show_friend_confirmed: true)
|
||||
ProfileName(pubkey: event.pubkey, profile: damus_state.profiles.lookup(id: event.pubkey), contacts: damus_state.contacts, show_friend_confirmed: true, profiles: damus_state.profiles)
|
||||
.foregroundColor(colorScheme == .dark ? id_to_color(event.pubkey) : Color.black)
|
||||
//.shadow(color: Color.black, radius: 2)
|
||||
Text("\(format_relative_time(event.created_at))")
|
||||
|
||||
@@ -40,7 +40,7 @@ struct DMChatView: View {
|
||||
HStack {
|
||||
ProfilePicView(pubkey: pubkey, size: 24, highlight: .none, profiles: damus_state.profiles)
|
||||
|
||||
ProfileName(pubkey: pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: true)
|
||||
ProfileName(pubkey: pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: true, profiles: damus_state.profiles)
|
||||
}
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
|
||||
@@ -15,19 +15,6 @@ func isHttpsUrl(_ string: String) -> Bool {
|
||||
return urlTest.evaluate(with: string)
|
||||
}
|
||||
|
||||
struct NIP05 {
|
||||
let username: String
|
||||
let host: String
|
||||
|
||||
static func parse(_ nip05: String) -> NIP05? {
|
||||
let parts = nip05.split(separator: "@")
|
||||
guard parts.count == 2 else {
|
||||
return nil
|
||||
}
|
||||
return NIP05(username: String(parts[0]), host: String(parts[1]))
|
||||
}
|
||||
}
|
||||
|
||||
func isImage(_ urlString: String) -> Bool {
|
||||
let imageTypes = ["image/jpg", "image/jpeg", "image/png", "image/gif", "image/tiff", "image/bmp", "image/webp"]
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ struct EventView: View {
|
||||
Image(systemName: "arrow.2.squarepath")
|
||||
.font(.footnote.weight(.bold))
|
||||
.foregroundColor(Color.gray)
|
||||
ProfileName(pubkey: event.pubkey, profile: prof, contacts: damus.contacts, show_friend_confirmed: true)
|
||||
ProfileName(pubkey: event.pubkey, profile: prof, contacts: damus.contacts, show_friend_confirmed: true, profiles: damus.profiles)
|
||||
.font(.footnote.weight(.bold))
|
||||
.foregroundColor(Color.gray)
|
||||
Text("Boosted")
|
||||
@@ -227,7 +227,7 @@ struct EventView: View {
|
||||
}
|
||||
}
|
||||
|
||||
EventProfileName(pubkey: pubkey, profile: profile, contacts: damus.contacts, show_friend_confirmed: show_friend_icon, size: size)
|
||||
EventProfileName(pubkey: pubkey, profile: profile, contacts: damus.contacts, show_friend_confirmed: show_friend_icon, profiles: damus.profiles, size: size)
|
||||
if size != .selected {
|
||||
Text("\(format_relative_time(event.created_at))")
|
||||
.font(eventviewsize_to_font(size))
|
||||
|
||||
@@ -24,7 +24,7 @@ struct FollowUserView: View {
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
let profile = damus_state.profiles.lookup(id: target.pubkey)
|
||||
ProfileName(pubkey: target.pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: false)
|
||||
ProfileName(pubkey: target.pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: false, profiles: damus_state.profiles)
|
||||
if let about = profile?.about {
|
||||
Text(FollowUserView.markdown.process(about))
|
||||
.lineLimit(3)
|
||||
|
||||
@@ -7,28 +7,6 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ProfileFullName: View {
|
||||
let pubkey: String
|
||||
let profile: Profile?
|
||||
let contacts: Contacts
|
||||
|
||||
@State var display_name: String?
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
if let real_name = profile?.display_name {
|
||||
Text(real_name)
|
||||
.bold()
|
||||
ProfileName(pubkey: pubkey, profile: profile, prefix: "@", contacts: contacts, show_friend_confirmed: true)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
} else {
|
||||
// ProfileName(pubkey: pubkey, profile: profile, contacts: contacts, show_friend_confirmed: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ProfileName: View {
|
||||
let pubkey: String
|
||||
let profile: Profile?
|
||||
@@ -36,23 +14,27 @@ struct ProfileName: View {
|
||||
let prefix: String
|
||||
|
||||
let show_friend_confirmed: Bool
|
||||
let profiles: Profiles
|
||||
|
||||
@State var display_name: String?
|
||||
@State var nip05: NIP05?
|
||||
|
||||
init(pubkey: String, profile: Profile?, contacts: Contacts, show_friend_confirmed: Bool) {
|
||||
init(pubkey: String, profile: Profile?, contacts: Contacts, show_friend_confirmed: Bool, profiles: Profiles) {
|
||||
self.pubkey = pubkey
|
||||
self.profile = profile
|
||||
self.prefix = ""
|
||||
self.contacts = contacts
|
||||
self.show_friend_confirmed = show_friend_confirmed
|
||||
self.profiles = profiles
|
||||
}
|
||||
|
||||
init(pubkey: String, profile: Profile?, prefix: String, contacts: Contacts, show_friend_confirmed: Bool) {
|
||||
init(pubkey: String, profile: Profile?, prefix: String, contacts: Contacts, show_friend_confirmed: Bool, profiles: Profiles) {
|
||||
self.pubkey = pubkey
|
||||
self.profile = profile
|
||||
self.prefix = prefix
|
||||
self.contacts = contacts
|
||||
self.show_friend_confirmed = show_friend_confirmed
|
||||
self.profiles = profiles
|
||||
}
|
||||
|
||||
var friend_icon: String? {
|
||||
@@ -71,12 +53,26 @@ struct ProfileName: View {
|
||||
return nil
|
||||
}
|
||||
|
||||
var nip05_color: Color {
|
||||
contacts.is_friend(pubkey) ? .blue : .yellow
|
||||
}
|
||||
|
||||
var current_nip05: NIP05? {
|
||||
nip05 ?? profiles.is_validated(pubkey)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
HStack(spacing: 2) {
|
||||
Text(prefix + String(display_name ?? Profile.displayName(profile: profile, pubkey: pubkey)))
|
||||
.font(.body)
|
||||
.fontWeight(prefix == "@" ? .none : .bold)
|
||||
if let friend = friend_icon {
|
||||
if let nip05 = current_nip05 {
|
||||
Image(systemName: "checkmark.seal.fill")
|
||||
.foregroundColor(nip05_color)
|
||||
Text(nip05.host)
|
||||
.foregroundColor(nip05_color)
|
||||
}
|
||||
if let friend = friend_icon, current_nip05 == nil {
|
||||
Image(systemName: friend)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
@@ -87,6 +83,7 @@ struct ProfileName: View {
|
||||
return
|
||||
}
|
||||
display_name = Profile.displayName(profile: update.profile, pubkey: pubkey)
|
||||
nip05 = profiles.is_validated(pubkey)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,27 +96,31 @@ struct EventProfileName: View {
|
||||
let prefix: String
|
||||
|
||||
let show_friend_confirmed: Bool
|
||||
let profiles: Profiles
|
||||
|
||||
@State var display_name: String?
|
||||
@State var nip05: NIP05?
|
||||
|
||||
let size: EventViewKind
|
||||
|
||||
init(pubkey: String, profile: Profile?, contacts: Contacts, show_friend_confirmed: Bool, size: EventViewKind = .normal) {
|
||||
init(pubkey: String, profile: Profile?, contacts: Contacts, show_friend_confirmed: Bool, profiles: Profiles, size: EventViewKind = .normal) {
|
||||
self.pubkey = pubkey
|
||||
self.profile = profile
|
||||
self.prefix = ""
|
||||
self.contacts = contacts
|
||||
self.show_friend_confirmed = show_friend_confirmed
|
||||
self.size = size
|
||||
self.profiles = profiles
|
||||
}
|
||||
|
||||
init(pubkey: String, profile: Profile?, prefix: String, contacts: Contacts, show_friend_confirmed: Bool, size: EventViewKind = .normal) {
|
||||
init(pubkey: String, profile: Profile?, prefix: String, contacts: Contacts, show_friend_confirmed: Bool, profiles: Profiles, size: EventViewKind = .normal) {
|
||||
self.pubkey = pubkey
|
||||
self.profile = profile
|
||||
self.prefix = prefix
|
||||
self.contacts = contacts
|
||||
self.show_friend_confirmed = show_friend_confirmed
|
||||
self.size = size
|
||||
self.profiles = profiles
|
||||
}
|
||||
|
||||
var friend_icon: String? {
|
||||
@@ -137,12 +138,21 @@ struct EventProfileName: View {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var nip05_color: Color {
|
||||
contacts.is_friend(pubkey) ? .blue : .yellow
|
||||
}
|
||||
|
||||
var current_nip05: NIP05? {
|
||||
nip05 ?? profiles.is_validated(pubkey)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
HStack(spacing: 2) {
|
||||
if let real_name = profile?.display_name {
|
||||
Text(real_name)
|
||||
.font(.body.weight(.bold))
|
||||
.padding([.trailing], 4)
|
||||
|
||||
Text("@" + String(display_name ?? Profile.displayName(profile: profile, pubkey: pubkey)))
|
||||
.foregroundColor(.gray)
|
||||
@@ -153,7 +163,16 @@ struct EventProfileName: View {
|
||||
.fontWeight(.bold)
|
||||
}
|
||||
|
||||
if let frend = friend_icon {
|
||||
if let nip05 = current_nip05 {
|
||||
Image(systemName: "checkmark.seal.fill")
|
||||
.foregroundColor(nip05_color)
|
||||
if !contacts.is_friend(pubkey) {
|
||||
Text(nip05.host)
|
||||
.foregroundColor(nip05_color)
|
||||
}
|
||||
}
|
||||
|
||||
if let frend = friend_icon, current_nip05 == nil {
|
||||
Label("", systemImage: frend)
|
||||
.foregroundColor(.gray)
|
||||
.font(.footnote)
|
||||
@@ -165,6 +184,7 @@ struct EventProfileName: View {
|
||||
return
|
||||
}
|
||||
display_name = Profile.displayName(profile: update.profile, pubkey: pubkey)
|
||||
nip05 = profiles.is_validated(pubkey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ struct ProfileNameView: View {
|
||||
let pubkey: String
|
||||
let profile: Profile?
|
||||
let contacts: Contacts
|
||||
let profiles: Profiles
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
@@ -56,7 +57,7 @@ struct ProfileNameView: View {
|
||||
VStack(alignment: .leading) {
|
||||
Text(real_name)
|
||||
.font(.title3.weight(.bold))
|
||||
ProfileName(pubkey: pubkey, profile: profile, prefix: "@", contacts: contacts, show_friend_confirmed: true)
|
||||
ProfileName(pubkey: pubkey, profile: profile, prefix: "@", contacts: contacts, show_friend_confirmed: true, profiles: profiles)
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
KeyView(pubkey: pubkey)
|
||||
@@ -64,7 +65,7 @@ struct ProfileNameView: View {
|
||||
}
|
||||
} else {
|
||||
VStack(alignment: .leading) {
|
||||
ProfileName(pubkey: pubkey, profile: profile, contacts: contacts, show_friend_confirmed: true)
|
||||
ProfileName(pubkey: pubkey, profile: profile, contacts: contacts, show_friend_confirmed: true, profiles: profiles)
|
||||
.font(.title3.weight(.bold))
|
||||
KeyView(pubkey: pubkey)
|
||||
.pubkey_context_menu(bech32_pubkey: pubkey)
|
||||
@@ -227,7 +228,7 @@ struct ProfileView: View {
|
||||
.offset(y: -15.0) // Increase if set a frame
|
||||
}
|
||||
|
||||
ProfileNameView(pubkey: profile.pubkey, profile: data, contacts: damus_state.contacts)
|
||||
ProfileNameView(pubkey: profile.pubkey, profile: data, contacts: damus_state.contacts, profiles: damus_state.profiles)
|
||||
//.padding(.bottom)
|
||||
.padding(.top,-(pfp_size/2.0))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user