field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

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 }