@ -60,11 +60,6 @@ public final class SnodeAPI : NSObject {
}
}
// MARK: T y p e A l i a s e s
public typealias MessageListPromise = Promise < [ JSON ] >
public typealias RawResponse = Any
public typealias RawResponsePromise = Promise < RawResponse >
// MARK: S n o d e P o o l I n t e r a c t i o n
private static func loadSnodePoolIfNeeded ( ) {
guard ! hasLoadedSnodePool else { return }
@ -129,30 +124,26 @@ public final class SnodeAPI : NSObject {
}
// MARK: I n t e r n a l A P I
internal static func invoke ( _ method : Snode . Method , on snode : Snode , associatedWith publicKey : String ? = nil , parameters : JSON ) -> RawResponse Promise {
internal static func invoke ( _ method : Snode . Method , on snode : Snode , associatedWith publicKey : String ? = nil , parameters : JSON ) -> Promise< Data > {
if Features . useOnionRequests {
return OnionRequestAPI . sendOnionRequest ( to : snode , invoking : method , with : parameters , using : . v3 , associatedWith : publicKey )
. map2 { responseData in
guard let responseJson : JSON = try ? JSONSerialization . jsonObject ( with : responseData , options : [ . fragmentsAllowed ] ) as ? JSON else {
throw Error . generic
}
// FIXME: W o u l d b e n i c e t o c h a n g e t h i s t o n o t s e n d ' A n y '
return responseJson as Any
}
} else {
}
else {
let url = " \( snode . address ) : \( snode . port ) /storage_rpc/v1 "
return HTTP . execute ( . post , url , parameters : parameters ) . map2 { $0 as Any } . recover2 { error -> Promise < Any > in
guard case HTTP . Error . httpRequestFailed ( let statusCode , let json ) = error else { throw error }
throw SnodeAPI . handleError ( withStatusCode : statusCode , json : json , forSnode : snode , associatedWith : publicKey ) ? ? error
}
return HTTP . execute ( . post , url , parameters : parameters )
. recover2 { error -> Promise < Data > in
guard case HTTP . Error . httpRequestFailed ( let statusCode , let data ) = error else { throw error }
throw SnodeAPI . handleError ( withStatusCode : statusCode , data : data , forSnode : snode , associatedWith : publicKey ) ? ? error
}
}
}
private static func getNetworkTime ( from snode : Snode ) -> Promise < UInt64 > {
return invoke ( . getInfo , on : snode , parameters : [ : ] ) . map2 { rawResponse in
guard let json = rawResponse as ? JSON ,
let timestamp = json [ " timestamp " ] as ? UInt64 else { throw HTTP . Error . invalidJSON }
return invoke ( . getInfo , on : snode , parameters : [ : ] ) . map2 { responseData in
guard let responseJson : JSON = try ? JSONSerialization . jsonObject ( with : responseData , options : [ . fragmentsAllowed ] ) as ? JSON else {
throw HTTP . Error . invalidJSON
}
guard let timestamp = responseJson [ " timestamp " ] as ? UInt64 else { throw HTTP . Error . invalidJSON }
return timestamp
}
}
@ -179,21 +170,27 @@ public final class SnodeAPI : NSObject {
let ( promise , seal ) = Promise < Set < Snode > > . pending ( )
Threading . workQueue . async {
attempt ( maxRetryCount : 4 , recoveringOn : Threading . workQueue ) {
HTTP . execute ( . post , url , parameters : parameters , useSeedNodeURLSession : true ) . map2 { json -> Set < Snode > in
guard let intermediate = json [ " result " ] as ? JSON , let rawSnodes = intermediate [ " service_node_states " ] as ? [ JSON ] else { throw Error . snodePoolUpdatingFailed }
return Set ( rawSnodes . compactMap { rawSnode in
guard let address = rawSnode [ " public_ip " ] as ? String , let port = rawSnode [ " storage_port " ] as ? Int ,
let ed25519PublicKey = rawSnode [ " pubkey_ed25519 " ] as ? String , let x25519PublicKey = rawSnode [ " pubkey_x25519 " ] as ? String , address != " 0.0.0.0 " else {
SNLog ( " Failed to parse snode from: \( rawSnode ) . " )
return nil
HTTP . execute ( . post , url , parameters : parameters , useSeedNodeURLSession : true )
. map2 { responseData -> Set < Snode > in
guard let responseJson : JSON = try ? JSONSerialization . jsonObject ( with : responseData , options : [ . fragmentsAllowed ] ) as ? JSON else {
throw HTTP . Error . invalidJSON
}
return Snode ( address : " https:// \( address ) " , port : UInt16 ( port ) , publicKeySet : Snode . KeySet ( ed25519Key : ed25519PublicKey , x25519Key : x25519PublicKey ) )
} )
}
} . done2 { snodePool in
guard let intermediate = responseJson [ " result " ] as ? JSON , let rawSnodes = intermediate [ " service_node_states " ] as ? [ JSON ] else { throw Error . snodePoolUpdatingFailed }
return Set ( rawSnodes . compactMap { rawSnode in
guard let address = rawSnode [ " public_ip " ] as ? String , let port = rawSnode [ " storage_port " ] as ? Int ,
let ed25519PublicKey = rawSnode [ " pubkey_ed25519 " ] as ? String , let x25519PublicKey = rawSnode [ " pubkey_x25519 " ] as ? String , address != " 0.0.0.0 " else {
SNLog ( " Failed to parse snode from: \( rawSnode ) . " )
return nil
}
return Snode ( address : " https:// \( address ) " , port : UInt16 ( port ) , publicKeySet : Snode . KeySet ( ed25519Key : ed25519PublicKey , x25519Key : x25519PublicKey ) )
} )
}
}
. done2 { snodePool in
SNLog ( " Got snode pool from seed node: \( target ) . " )
seal . fulfill ( snodePool )
} . catch2 { error in
}
. catch2 { error in
SNLog ( " Failed to contact seed node at: \( target ) . " )
seal . reject ( error )
}
@ -223,20 +220,24 @@ public final class SnodeAPI : NSObject {
]
]
]
return invoke ( . oxenDaemonRPCCall , on : snode , parameters : parameters ) . map2 { rawResponse in
guard let json = rawResponse as ? JSON , let intermediate = json [ " result " ] as ? JSON ,
let rawSnodes = intermediate [ " service_node_states " ] as ? [ JSON ] else {
throw Error . snodePoolUpdatingFailed
}
return Set ( rawSnodes . compactMap { rawSnode in
guard let address = rawSnode [ " public_ip " ] as ? String , let port = rawSnode [ " storage_port " ] as ? Int ,
let ed25519PublicKey = rawSnode [ " pubkey_ed25519 " ] as ? String , let x25519PublicKey = rawSnode [ " pubkey_x25519 " ] as ? String , address != " 0.0.0.0 " else {
SNLog ( " Failed to parse snode from: \( rawSnode ) . " )
return nil
return invoke ( . oxenDaemonRPCCall , on : snode , parameters : parameters )
. map2 { responseData in
guard let responseJson : JSON = try ? JSONSerialization . jsonObject ( with : responseData , options : [ . fragmentsAllowed ] ) as ? JSON else {
throw HTTP . Error . invalidJSON
}
return Snode ( address : " https:// \( address ) " , port : UInt16 ( port ) , publicKeySet : Snode . KeySet ( ed25519Key : ed25519PublicKey , x25519Key : x25519PublicKey ) )
} )
}
guard let intermediate = responseJson [ " result " ] as ? JSON ,
let rawSnodes = intermediate [ " service_node_states " ] as ? [ JSON ] else {
throw Error . snodePoolUpdatingFailed
}
return Set ( rawSnodes . compactMap { rawSnode in
guard let address = rawSnode [ " public_ip " ] as ? String , let port = rawSnode [ " storage_port " ] as ? Int ,
let ed25519PublicKey = rawSnode [ " pubkey_ed25519 " ] as ? String , let x25519PublicKey = rawSnode [ " pubkey_x25519 " ] as ? String , address != " 0.0.0.0 " else {
SNLog ( " Failed to parse snode from: \( rawSnode ) . " )
return nil
}
return Snode ( address : " https:// \( address ) " , port : UInt16 ( port ) , publicKeySet : Snode . KeySet ( ed25519Key : ed25519PublicKey , x25519Key : x25519PublicKey ) )
} )
}
}
}
let promise = when ( fulfilled : snodePoolPromises ) . map2 { results -> Set < Snode > in
@ -336,8 +337,11 @@ public final class SnodeAPI : NSObject {
for result in results {
switch result {
case . rejected ( let error ) : return seal . reject ( error )
case . fulfilled ( let rawResponse ) :
guard let json = rawResponse as ? JSON , let intermediate = json [ " result " ] as ? JSON ,
case . fulfilled ( let responseData ) :
guard let responseJson : JSON = try ? JSONSerialization . jsonObject ( with : responseData , options : [ . fragmentsAllowed ] ) as ? JSON else {
throw HTTP . Error . invalidJSON
}
guard let intermediate = responseJson [ " result " ] as ? JSON ,
let hexEncodedCiphertext = intermediate [ " encrypted_value " ] as ? String else { return seal . reject ( HTTP . Error . invalidJSON ) }
let ciphertext = [ UInt8 ] ( Data ( hex : hexEncodedCiphertext ) )
let isArgon2Based = ( intermediate [ " nonce " ] = = nil )
@ -390,37 +394,23 @@ public final class SnodeAPI : NSObject {
attempt ( maxRetryCount : 4 , recoveringOn : Threading . workQueue ) {
invoke ( . getSwarm , on : snode , associatedWith : publicKey , parameters : parameters )
}
} . map2 { r awSnodes in
let swarm = parseSnodes ( from : r awSnodes )
} . map2 { r esponseData in
let swarm = parseSnodes ( from : r esponseData )
setSwarm ( to : swarm , for : publicKey )
return swarm
}
}
}
public static func getRawMessages ( from snode : Snode , associatedWith publicKey : String ) -> RawResponse Promise {
let ( promise , seal ) = RawResponse Promise. pending ( )
public static func getRawMessages ( from snode : Snode , associatedWith publicKey : String ) -> Promise< Data > {
let ( promise , seal ) = Promise< Data > . pending ( )
Threading . workQueue . async {
getMessagesInternal ( from : snode , associatedWith : publicKey ) . done2 { seal . fulfill ( $0 ) } . catch2 { seal . reject ( $0 ) }
}
return promise
}
public static func getMessages ( for publicKey : String ) -> Promise < Set < MessageListPromise > > {
let ( promise , seal ) = Promise < Set < MessageListPromise > > . pending ( )
Threading . workQueue . async {
attempt ( maxRetryCount : maxRetryCount , recoveringOn : Threading . workQueue ) {
getTargetSnodes ( for : publicKey ) . mapValues2 { targetSnode in
return getMessagesInternal ( from : targetSnode , associatedWith : publicKey ) . map2 { rawResponse in
parseRawMessagesResponse ( rawResponse , from : targetSnode , associatedWith : publicKey )
}
} . map2 { Set ( $0 ) }
} . done2 { seal . fulfill ( $0 ) } . catch2 { seal . reject ( $0 ) }
}
return promise
}
private static func getMessagesInternal ( from snode : Snode , associatedWith publicKey : String ) -> RawResponsePromise {
private static func getMessagesInternal ( from snode : Snode , associatedWith publicKey : String ) -> Promise < Data > {
let storage = SNSnodeKitConfiguration . shared . storage
// N O T E : A l l a u t h e n t i c a t i o n l o g i c i s c u r r e n t l y c o m m e n t e d o u t , t h e r e a s o n b e i n g t h a t w e c a n ' t c u r r e n t l y s u p p o r t
@ -447,18 +437,21 @@ public final class SnodeAPI : NSObject {
return invoke ( . getMessages , on : snode , associatedWith : publicKey , parameters : parameters )
}
public static func sendMessage ( _ message : SnodeMessage ) -> Promise < Set < RawResponse Promise > > {
let ( promise , seal ) = Promise < Set < RawResponse Promise > >. pending ( )
public static func sendMessage ( _ message : SnodeMessage ) -> Promise < Set < Promise<Data >> > {
let ( promise , seal ) = Promise < Set < Promise<Data >> >. pending ( )
let publicKey = Features . useTestnet ? message . recipient . removingIdPrefixIfNeeded ( ) : message . recipient
Threading . workQueue . async {
getTargetSnodes ( for : publicKey ) . map2 { targetSnodes in
let parameters = message . toJSON ( )
return Set ( targetSnodes . map { targetSnode in
attempt ( maxRetryCount : maxRetryCount , recoveringOn : Threading . workQueue ) {
invoke ( . sendMessage , on : targetSnode , associatedWith : publicKey , parameters : parameters )
}
} )
} . done2 { seal . fulfill ( $0 ) } . catch2 { seal . reject ( $0 ) }
getTargetSnodes ( for : publicKey )
. map2 { targetSnodes in
let parameters = message . toJSON ( )
return Set ( targetSnodes . map { targetSnode in
attempt ( maxRetryCount : maxRetryCount , recoveringOn : Threading . workQueue ) {
invoke ( . sendMessage , on : targetSnode , associatedWith : publicKey , parameters : parameters )
}
} )
}
. done2 { seal . fulfill ( $0 ) }
. catch2 { seal . reject ( $0 ) }
}
return promise
}
@ -485,29 +478,34 @@ public final class SnodeAPI : NSObject {
" signature " : signature . toBase64 ( )
]
return attempt ( maxRetryCount : maxRetryCount , recoveringOn : Threading . workQueue ) {
invoke ( . deleteMessage , on : snode , associatedWith : publicKey , parameters : parameters ) . map2 { rawResponse -> [ String : Bool ] in
guard let json = rawResponse as ? JSON , let swarm = json [ " swarm " ] as ? JSON else { throw HTTP . Error . invalidJSON }
var result : [ String : Bool ] = [ : ]
for ( snodePublicKey , rawJSON ) in swarm {
guard let json = rawJSON as ? JSON else { throw HTTP . Error . invalidJSON }
let isFailed = json [ " failed " ] as ? Bool ? ? false
if ! isFailed {
guard let hashes = json [ " deleted " ] as ? [ String ] , let signature = json [ " signature " ] as ? String else { throw HTTP . Error . invalidJSON }
// T h e s i g n a t u r e f o r m a t i s ( P U B K E Y _ H E X | | R M S G [ 0 ] | | . . . | | R M S G [ N ] | | D M S G [ 0 ] | | . . . | | D M S G [ M ] )
let verificationData = ( userX25519PublicKey + serverHashes . joined ( separator : " " ) + hashes . joined ( separator : " " ) ) . data ( using : String . Encoding . utf8 ) !
let isValid = sodium . sign . verify ( message : Bytes ( verificationData ) , publicKey : Bytes ( Data ( hex : snodePublicKey ) ) , signature : Bytes ( Data ( base64Encoded : signature ) ! ) )
result [ snodePublicKey ] = isValid
} else {
if let reason = json [ " reason " ] as ? String , let statusCode = json [ " code " ] as ? String {
SNLog ( " Couldn't delete data from: \( snodePublicKey ) due to error: \( reason ) ( \( statusCode ) ). " )
invoke ( . deleteMessage , on : snode , associatedWith : publicKey , parameters : parameters )
. map2 { responseData -> [ String : Bool ] in
guard let responseJson : JSON = try ? JSONSerialization . jsonObject ( with : responseData , options : [ . fragmentsAllowed ] ) as ? JSON else {
throw HTTP . Error . invalidJSON
}
guard let swarm = responseJson [ " swarm " ] as ? JSON else { throw HTTP . Error . invalidJSON }
var result : [ String : Bool ] = [ : ]
for ( snodePublicKey , rawJSON ) in swarm {
guard let json = rawJSON as ? JSON else { throw HTTP . Error . invalidJSON }
let isFailed = json [ " failed " ] as ? Bool ? ? false
if ! isFailed {
guard let hashes = json [ " deleted " ] as ? [ String ] , let signature = json [ " signature " ] as ? String else { throw HTTP . Error . invalidJSON }
// T h e s i g n a t u r e f o r m a t i s ( P U B K E Y _ H E X | | R M S G [ 0 ] | | . . . | | R M S G [ N ] | | D M S G [ 0 ] | | . . . | | D M S G [ M ] )
let verificationData = ( userX25519PublicKey + serverHashes . joined ( separator : " " ) + hashes . joined ( separator : " " ) ) . data ( using : String . Encoding . utf8 ) !
let isValid = sodium . sign . verify ( message : Bytes ( verificationData ) , publicKey : Bytes ( Data ( hex : snodePublicKey ) ) , signature : Bytes ( Data ( base64Encoded : signature ) ! ) )
result [ snodePublicKey ] = isValid
} else {
SNLog ( " Couldn't delete data from: \( snodePublicKey ) . " )
if let reason = json [ " reason " ] as ? String , let statusCode = json [ " code " ] as ? String {
SNLog ( " Couldn't delete data from: \( snodePublicKey ) due to error: \( reason ) ( \( statusCode ) ). " )
} else {
SNLog ( " Couldn't delete data from: \( snodePublicKey ) . " )
}
result [ snodePublicKey ] = false
}
result [ snodePublicKey ] = false
}
return result
}
return result
}
}
}
}
@ -532,29 +530,36 @@ public final class SnodeAPI : NSObject {
" signature " : signature . toBase64 ( )
]
return attempt ( maxRetryCount : maxRetryCount , recoveringOn : Threading . workQueue ) {
invoke ( . clearAllData , on : snode , parameters : parameters ) . map2 { rawResponse -> [ String : Bool ] in
guard let json = rawResponse as ? JSON , let swarm = json [ " swarm " ] as ? JSON else { throw HTTP . Error . invalidJSON }
var result : [ String : Bool ] = [ : ]
for ( snodePublicKey , rawJSON ) in swarm {
guard let json = rawJSON as ? JSON else { throw HTTP . Error . invalidJSON }
let isFailed = json [ " failed " ] as ? Bool ? ? false
if ! isFailed {
guard let hashes = json [ " deleted " ] as ? [ String ] , let signature = json [ " signature " ] as ? String else { throw HTTP . Error . invalidJSON }
// T h e s i g n a t u r e f o r m a t i s ( P U B K E Y _ H E X | | T I M E S T A M P | | D E L E T E D H A S H [ 0 ] | | . . . | | D E L E T E D H A S H [ N ] )
let verificationData = ( userX25519PublicKey + String ( timestamp ) + hashes . joined ( separator : " " ) ) . data ( using : String . Encoding . utf8 ) !
let isValid = sodium . sign . verify ( message : Bytes ( verificationData ) , publicKey : Bytes ( Data ( hex : snodePublicKey ) ) , signature : Bytes ( Data ( base64Encoded : signature ) ! ) )
result [ snodePublicKey ] = isValid
} else {
if let reason = json [ " reason " ] as ? String , let statusCode = json [ " code " ] as ? String {
SNLog ( " Couldn't delete data from: \( snodePublicKey ) due to error: \( reason ) ( \( statusCode ) ). " )
invoke ( . clearAllData , on : snode , parameters : parameters )
. map2 { responseData -> [ String : Bool ] in
guard let responseJson : JSON = try ? JSONSerialization . jsonObject ( with : responseData , options : [ . fragmentsAllowed ] ) as ? JSON else {
throw HTTP . Error . invalidJSON
}
guard let swarm = responseJson [ " swarm " ] as ? JSON else { throw HTTP . Error . invalidJSON }
var result : [ String : Bool ] = [ : ]
for ( snodePublicKey , rawJSON ) in swarm {
guard let json = rawJSON as ? JSON else { throw HTTP . Error . invalidJSON }
let isFailed = json [ " failed " ] as ? Bool ? ? false
if ! isFailed {
guard let hashes = json [ " deleted " ] as ? [ String ] , let signature = json [ " signature " ] as ? String else { throw HTTP . Error . invalidJSON }
// T h e s i g n a t u r e f o r m a t i s ( P U B K E Y _ H E X | | T I M E S T A M P | | D E L E T E D H A S H [ 0 ] | | . . . | | D E L E T E D H A S H [ N ] )
let verificationData = ( userX25519PublicKey + String ( timestamp ) + hashes . joined ( separator : " " ) ) . data ( using : String . Encoding . utf8 ) !
let isValid = sodium . sign . verify ( message : Bytes ( verificationData ) , publicKey : Bytes ( Data ( hex : snodePublicKey ) ) , signature : Bytes ( Data ( base64Encoded : signature ) ! ) )
result [ snodePublicKey ] = isValid
} else {
SNLog ( " Couldn't delete data from: \( snodePublicKey ) . " )
if let reason = json [ " reason " ] as ? String , let statusCode = json [ " code " ] as ? String {
SNLog ( " Couldn't delete data from: \( snodePublicKey ) due to error: \( reason ) ( \( statusCode ) ). " )
} else {
SNLog ( " Couldn't delete data from: \( snodePublicKey ) . " )
}
result [ snodePublicKey ] = false
}
result [ snodePublicKey ] = false
}
return result
}
return result
}
}
}
}
@ -566,9 +571,13 @@ public final class SnodeAPI : NSObject {
// T h e p a r s i n g u t i l i t i e s b e l o w u s e a b e s t a t t e m p t a p p r o a c h t o p a r s i n g ; t h e y w a r n f o r p a r s i n g f a i l u r e s b u t d o n ' t t h r o w e x c e p t i o n s .
private static func parseSnodes ( from rawResponse : Any ) -> Set < Snode > {
guard let json = rawResponse as ? JSON , let rawSnodes = json [ " snodes " ] as ? [ JSON ] else {
SNLog ( " Failed to parse snodes from: \( rawResponse ) . " )
private static func parseSnodes ( from responseData : Data ) -> Set < Snode > {
guard let responseJson : JSON = try ? JSONSerialization . jsonObject ( with : responseData , options : [ . fragmentsAllowed ] ) as ? JSON else {
SNLog ( " Failed to parse snodes from response data. " )
return [ ]
}
guard let rawSnodes = responseJson [ " snodes " ] as ? [ JSON ] else {
SNLog ( " Failed to parse snodes from: \( responseJson ) . " )
return [ ]
}
return Set ( rawSnodes . compactMap { rawSnode in
@ -581,8 +590,11 @@ public final class SnodeAPI : NSObject {
} )
}
public static func parseRawMessagesResponse ( _ rawResponse : Any , from snode : Snode , associatedWith publicKey : String ) -> [ JSON ] {
guard let json = rawResponse as ? JSON , let rawMessages = json [ " messages " ] as ? [ JSON ] else { return [ ] }
public static func parseRawMessagesResponse ( _ responseData : Data , from snode : Snode , associatedWith publicKey : String ) -> [ JSON ] {
guard let responseJson : JSON = try ? JSONSerialization . jsonObject ( with : responseData , options : [ . fragmentsAllowed ] ) as ? JSON else {
return [ ]
}
guard let rawMessages = responseJson [ " messages " ] as ? [ JSON ] else { return [ ] }
updateLastMessageHashValueIfPossible ( for : snode , associatedWith : publicKey , from : rawMessages )
return removeDuplicates ( from : rawMessages , associatedWith : publicKey )
}
@ -622,7 +634,7 @@ public final class SnodeAPI : NSObject {
// MARK: E r r o r H a n d l i n g
// / - N o t e : S h o u l d o n l y b e i n v o k e d f r o m ` T h r e a d i n g . w o r k Q u e u e ` t o a v o i d r a c e c o n d i t i o n s .
@ discardableResult
internal static func handleError ( withStatusCode statusCode : UInt , json: JSON ? , forSnode snode : Snode , associatedWith publicKey : String ? = nil ) -> Error ? {
internal static func handleError ( withStatusCode statusCode : UInt , data: Data ? , forSnode snode : Snode , associatedWith publicKey : String ? = nil ) -> Error ? {
#if DEBUG
dispatchPrecondition ( condition : . onQueue ( Threading . workQueue ) )
#endif
@ -641,37 +653,47 @@ public final class SnodeAPI : NSObject {
SnodeAPI . snodeFailureCount [ snode ] = 0
}
}
switch statusCode {
case 500 , 502 , 503 :
// T h e s n o d e i s u n r e a c h a b l e
handleBadSnode ( )
case 406 :
SNLog ( " The user's clock is out of sync with the service node network. " )
return Error . clockOutOfSync
case 421 :
// T h e s n o d e i s n ' t a s s o c i a t e d w i t h t h e g i v e n p u b l i c k e y a n y m o r e
if let publicKey = publicKey {
func invalidateSwarm ( ) {
SNLog ( " Invalidating swarm for: \( publicKey ) . " )
SnodeAPI . dropSnodeFromSwarmIfNeeded ( snode , publicKey : publicKey )
}
if let json = json {
let snodes = parseSnodes ( from : json )
if ! snodes . isEmpty {
setSwarm ( to : snodes , for : publicKey )
} else {
case 500 , 502 , 503 :
// T h e s n o d e i s u n r e a c h a b l e
handleBadSnode ( )
case 406 :
SNLog ( " The user's clock is out of sync with the service node network. " )
return Error . clockOutOfSync
case 421 :
// T h e s n o d e i s n ' t a s s o c i a t e d w i t h t h e g i v e n p u b l i c k e y a n y m o r e
if let publicKey = publicKey {
func invalidateSwarm ( ) {
SNLog ( " Invalidating swarm for: \( publicKey ) . " )
SnodeAPI . dropSnodeFromSwarmIfNeeded ( snode , publicKey : publicKey )
}
if let data : Data = data {
let snodes = parseSnodes ( from : data )
if ! snodes . isEmpty {
setSwarm ( to : snodes , for : publicKey )
}
else {
invalidateSwarm ( )
}
}
else {
invalidateSwarm ( )
}
} else {
invalidateSwarm ( )
}
} else {
SNLog ( " Got a 421 without an associated public key. " )
}
default :
handleBadSnode ( )
SNLog ( " Unhandled response code: \( statusCode ) . " )
else {
SNLog ( " Got a 421 without an associated public key. " )
}
default :
handleBadSnode ( )
SNLog ( " Unhandled response code: \( statusCode ) . " )
}
return nil
}
}