TeraSessionRuntime.swift (5390B)
1 import Foundation 2 import RadrootsKit 3 4 extension TeraSessionStore { 5 func startRuntime( 6 configuration: TeraAppConfiguration, 7 identity: TeraAppIdentity, 8 generation requestedGeneration: TeraSessionGeneration, 9 forceReconfiguration: Bool, 10 adoptBootstrapSettings: Bool 11 ) async throws -> TeraSessionPhase { 12 guard let publicKeyHex = identity.publicKeyHex, 13 let signerGeneration = identity.signerGeneration 14 else { 15 throw TeraIdentityStoreError.unavailable 16 } 17 let mobileStore = try RadrootsAppleMobileStore.prepare( 18 roots: roots, 19 publicKeyHex: publicKeyHex, 20 protectedDataAvailability: protectedData.isAvailable() ? .available : .unavailable 21 ) 22 let sourceGeneration = try await configurationStore.sourceGeneration() 23 try ensureCurrent(requestedGeneration) 24 let signer = try await identityStore.signer(for: identity) 25 guard generation == requestedGeneration else { throw TeraRuntimeClientError.superseded } 26 let launchConfiguration = TeraRuntimeLaunchConfiguration( 27 applicationSupportDirectory: mobileStore.applicationSupportDirectory.path, 28 publicKeyHex: publicKeyHex, 29 sourceGenerationHex: sourceGeneration.generationHex, 30 sourceGenerationCreatedAtUnixMilliseconds: sourceGeneration.createdAtUnixMilliseconds, 31 protectedData: protectedData.isAvailable() ? .available : .unavailable, 32 networkProfile: configuration.profile.runtimeValue, 33 writableRelays: configuration.writableRelays, 34 blossom: configuration.blossom, 35 app: configuration.appMetadata, 36 signerGeneration: signerGeneration, 37 signer: signer, 38 adoptBootstrapSettings: adoptBootstrapSettings 39 ) 40 let snapshot = 41 if forceReconfiguration { 42 try await runtimeClient.reconfigure(configuration: launchConfiguration) 43 } else { 44 try await runtimeClient.start(configuration: launchConfiguration) 45 } 46 guard generation == requestedGeneration else { 47 throw TeraRuntimeClientError.superseded 48 } 49 try await reconcileIdentity(identity, generation: requestedGeneration) 50 try ensureCurrent(requestedGeneration) 51 if configuration.activationState == .reconfigurationRequired, 52 adoptBootstrapSettings 53 { 54 try await configurationStore.confirmBootstrapActivation( 55 expectedGeneration: configuration.generation 56 ) 57 } 58 guard generation == requestedGeneration else { 59 throw TeraRuntimeClientError.superseded 60 } 61 return .running(snapshot) 62 } 63 64 private func reconcileIdentity( 65 _ identity: TeraAppIdentity, generation requestedGeneration: TeraSessionGeneration 66 ) async throws { 67 guard let identityID = identity.identityHandle, 68 let publicKeyHex = identity.publicKeyHex 69 else { 70 throw TeraIdentityStoreError.unavailable 71 } 72 var settings = try await runtimeClient.mobileSettings() 73 try ensureCurrent(requestedGeneration) 74 if let pending = settings.identity.pendingImportOperationID { 75 settings = try await runtimeClient.applyIdentityCommand( 76 expectedRevision: settings.revision, 77 command: TeraIdentityCommand( 78 kind: .cancelImport, 79 operationID: pending, 80 identityID: nil, 81 publicKeyHex: nil 82 ) 83 ).settings 84 try ensureCurrent(requestedGeneration) 85 } 86 settings = try await adoptIdentity( 87 identityID: identityID, publicKeyHex: publicKeyHex, settings: settings, generation: requestedGeneration 88 ) 89 try ensureCurrent(requestedGeneration) 90 _ = try await runtimeClient.applyIdentityCommand( 91 expectedRevision: settings.revision, 92 command: TeraIdentityCommand( 93 kind: .unlock, 94 operationID: nil, 95 identityID: nil, 96 publicKeyHex: nil 97 ) 98 ) 99 try ensureCurrent(requestedGeneration) 100 } 101 102 private func adoptIdentity( 103 identityID: String, publicKeyHex: String, settings initial: TeraMobileSettings, 104 generation requestedGeneration: TeraSessionGeneration 105 ) async throws -> TeraMobileSettings { 106 var settings = initial 107 if let existing = settings.identity.identities.first(where: { 108 $0.publicKeyHex == publicKeyHex 109 }) { 110 if settings.identity.activeIdentityID != existing.id { 111 settings = try await runtimeClient.applyIdentityCommand( 112 expectedRevision: settings.revision, 113 command: TeraIdentityCommand( 114 kind: .select, 115 operationID: nil, 116 identityID: existing.id, 117 publicKeyHex: nil 118 ) 119 ).settings 120 try ensureCurrent(requestedGeneration) 121 } 122 } else { 123 let operationID = UUID().uuidString.lowercased() 124 settings = try await runtimeClient.applyIdentityCommand( 125 expectedRevision: settings.revision, 126 command: TeraIdentityCommand( 127 kind: .beginImport, 128 operationID: operationID, 129 identityID: nil, 130 publicKeyHex: nil 131 ) 132 ).settings 133 try ensureCurrent(requestedGeneration) 134 settings = try await runtimeClient.applyIdentityCommand( 135 expectedRevision: settings.revision, 136 command: TeraIdentityCommand( 137 kind: .completeImport, 138 operationID: operationID, 139 identityID: identityID, 140 publicKeyHex: publicKeyHex 141 ) 142 ).settings 143 try ensureCurrent(requestedGeneration) 144 } 145 return settings 146 } 147 }