damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

Relay.swift (5169B)


      1 //
      2 //  Relay.swift
      3 //  damus
      4 //
      5 //  Created by William Casarin on 2022-04-11.
      6 //
      7 
      8 import Foundation
      9 
     10 public struct LegacyKind3RelayRWConfiguration: Codable, Sendable {
     11     public let read: Bool?
     12     public let write: Bool?
     13     
     14     init(read: Bool, write: Bool) {
     15         self.read = read
     16         self.write = write
     17     }
     18 
     19     static let rw = LegacyKind3RelayRWConfiguration(read: true, write: true)
     20     
     21     func toNIP65RWConfiguration() -> NIP65.RelayList.RelayItem.RWConfiguration? {
     22         switch (self.read, self.write) {
     23         case (false, true): return .write
     24         case (true, false): return .read
     25         case (true, true): return .readWrite
     26         default: return nil
     27         }
     28     }
     29 }
     30 
     31 enum RelayVariant {
     32     case regular
     33     case ephemeral
     34     case nwc
     35 }
     36 
     37 extension RelayPool {
     38     /// Describes a relay for use in `RelayPool`
     39     public struct RelayDescriptor {
     40         let url: RelayURL
     41         var info: NIP65.RelayList.RelayItem.RWConfiguration
     42         let variant: RelayVariant
     43         
     44         init(url: RelayURL, info: NIP65.RelayList.RelayItem.RWConfiguration, variant: RelayVariant = .regular) {
     45             self.url = url
     46             self.info = info
     47             self.variant = variant
     48         }
     49         
     50         var ephemeral: Bool {
     51             switch variant {
     52             case .regular:
     53                 return false
     54             case .ephemeral:
     55                 return true
     56             case .nwc:
     57                 return true
     58             }
     59         }
     60         
     61         static func nwc(url: RelayURL) -> RelayDescriptor {
     62             return RelayDescriptor(url: url, info: .readWrite, variant: .nwc)
     63         }
     64     }
     65 }
     66 
     67 enum RelayFlags: Int {
     68     case none = 0
     69     case broken = 1
     70 }
     71 
     72 enum RelayAuthenticationError {
     73     /// Only a public key was provided in keypair to sign challenge.
     74     ///
     75     /// A private key is required to sign `auth` challenge.
     76     case no_private_key
     77     /// No keypair was provided to sign challenge.
     78     case no_key
     79 }
     80 enum RelayAuthenticationState: Equatable {
     81     /// No `auth` request has been made from this relay
     82     case none
     83     /// We have received an `auth` challenge, but have not yet replied to the challenge
     84     case pending
     85     /// We have received an `auth` challenge and replied with an `auth` event
     86     case verified
     87     /// We received an `auth` challenge but failed to reply to the challenge
     88     case error(RelayAuthenticationError)
     89 }
     90 
     91 struct Limitations: Codable {
     92     let payment_required: Bool?
     93     
     94     static var empty: Limitations {
     95         Limitations(payment_required: nil)
     96     }
     97 }
     98 
     99 struct Admission: Codable {
    100     let amount: Int64
    101     let unit: String
    102 }
    103 
    104 struct Subscription: Codable {
    105     let amount: Int64
    106     let unit: String
    107     let period: Int
    108 }
    109 
    110 struct Publication: Codable {
    111     let kinds: [Int]
    112     let amount: Int64
    113     let unit: String
    114 }
    115 
    116 struct Fees: Codable {
    117     let admission: [Admission]?
    118     let subscription: [Subscription]?
    119     let publication: [Publication]?
    120     
    121     static var empty: Fees {
    122         Fees(admission: nil, subscription: nil, publication: nil)
    123     }
    124 }
    125 
    126 struct RelayMetadata: Codable {
    127     let name: String?
    128     let description: String?
    129     let pubkey: Pubkey?
    130     let contact: String?
    131     let supported_nips: [Int]?
    132     let software: String?
    133     let version: String?
    134     let limitation: Limitations?
    135     let payments_url: String?
    136     let icon: String?
    137     let fees: Fees?
    138     
    139     var is_paid: Bool {
    140         return limitation?.payment_required ?? false
    141     }
    142 }
    143 
    144 extension RelayPool {
    145     class Relay: Identifiable {
    146         var descriptor: RelayDescriptor
    147         let connection: RelayConnection
    148         var authentication_state: RelayAuthenticationState
    149         
    150         var flags: Int
    151         
    152         init(descriptor: RelayDescriptor, connection: RelayConnection) {
    153             self.flags = 0
    154             self.descriptor = descriptor
    155             self.connection = connection
    156             self.authentication_state = RelayAuthenticationState.none
    157         }
    158         
    159         var is_broken: Bool {
    160             return (flags & RelayFlags.broken.rawValue) == RelayFlags.broken.rawValue
    161         }
    162         
    163         var id: RelayURL {
    164             return descriptor.url
    165         }
    166     }
    167 }
    168 
    169 extension RelayPool {
    170     enum RelayError: Error {
    171         case RelayAlreadyExists
    172     }
    173 }
    174 
    175 
    176 // MARK: - Extension to bridge NIP-65 relay list structs with app-native objects
    177 
    178 extension NIP65.RelayList {
    179     static func fromLegacyContactList(_ contactList: NdbNote) throws(BridgeError) -> Self {
    180         guard let relayListInfo = decode_json_relays(contactList.content) else { throw .couldNotDecodeRelayListInfo }
    181         let relayItems = relayListInfo.map({ url, rwConfiguration in
    182             return RelayItem(url: url, rwConfiguration: rwConfiguration.toNIP65RWConfiguration() ?? .readWrite)
    183         })
    184         return NIP65.RelayList(relays: relayItems)
    185     }
    186     
    187     static func fromLegacyContactList(_ contactList: NdbNote?) throws(BridgeError) -> Self? {
    188         guard let contactList = contactList else { return nil }
    189         return try fromLegacyContactList(contactList)
    190     }
    191     
    192     enum BridgeError: Error {
    193         case couldNotDecodeRelayListInfo
    194     }
    195 }
    196