field_ios

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

TeraGeneratedHostSigner.swift (3669B)


      1 import Foundation
      2 import TeraKitBindings
      3 
      4 final class TeraGeneratedHostSigner: TeraHostSigner, @unchecked Sendable {
      5   typealias QualificationEvidenceRecorder = @Sendable (HostSigningRequest, String) throws -> Void
      6 
      7   private let signer: any TeraRuntimeSigner
      8   private let clock: TeraClock
      9   private let qualificationEvidenceRecorder: QualificationEvidenceRecorder
     10 
     11   init(
     12     signer: any TeraRuntimeSigner,
     13     clock: TeraClock = .system,
     14     qualificationEvidenceRecorder: @escaping QualificationEvidenceRecorder =
     15       TeraRemoteQualificationEvidence.recordBlossomAuthorization
     16   ) {
     17     self.signer = signer
     18     self.clock = clock
     19     self.qualificationEvidenceRecorder = qualificationEvidenceRecorder
     20   }
     21 
     22   func signerStatus() async -> SignerStatusRecord {
     23     await SignerStatusRecord(
     24       schemaVersion: 1,
     25       availability: signer.availability().generatedValue
     26     )
     27   }
     28 
     29   func sign(request: HostSigningRequest) async -> HostSigningResult {
     30     guard (try? clock.unixMilliseconds()) != nil else {
     31       return failedSigningResult(for: request)
     32     }
     33     let purpose = request.purpose.appValue
     34     let outcome = await signer.sign(
     35       TeraRuntimeSigningRequest(
     36         operationID: request.operationId,
     37         signerRequestID: request.signerRequestId,
     38         publicKeyHex: request.publicKey,
     39         purpose: purpose,
     40         deadlineUnixMilliseconds: request.deadlineUnixMs,
     41         digest: request.eventIdDigest
     42       )
     43     )
     44     #if DEBUG
     45       if request.purpose == .blossomUpload,
     46          let signatureHex = outcome.signatureHex
     47       {
     48         do {
     49           try qualificationEvidenceRecorder(request, signatureHex)
     50         } catch {
     51           return failedSigningResult(for: request)
     52         }
     53       }
     54     #endif
     55     let completedAtUnixMilliseconds = try? clock.unixMilliseconds()
     56     guard completedAtUnixMilliseconds != nil || (request.purpose == .nostrEvent && outcome.signatureHex != nil) else {
     57       return failedSigningResult(for: request)
     58     }
     59     return HostSigningResult(
     60       schemaVersion: 1,
     61       outcome: outcome.generatedOutcome,
     62       operationId: request.operationId,
     63       signerRequestId: request.signerRequestId,
     64       publicKey: request.publicKey,
     65       purpose: request.purpose,
     66       signatureHex: outcome.signatureHex,
     67       completedAtUnixMs: completedAtUnixMilliseconds ?? 0
     68     )
     69   }
     70 
     71   private func failedSigningResult(for request: HostSigningRequest) -> HostSigningResult {
     72     HostSigningResult(
     73       schemaVersion: 1,
     74       outcome: .failed,
     75       operationId: request.operationId,
     76       signerRequestId: request.signerRequestId,
     77       publicKey: request.publicKey,
     78       purpose: request.purpose,
     79       signatureHex: nil,
     80       completedAtUnixMs: 0
     81     )
     82   }
     83 }
     84 
     85 extension TeraRuntimeSignerAvailability {
     86   fileprivate var generatedValue: SignerAvailabilityRecord {
     87     switch self {
     88     case .ready: .ready
     89     case .busy: .busy
     90     case .locked: .locked
     91     case .unavailable: .unavailable
     92     }
     93   }
     94 }
     95 
     96 extension HostSigningPurpose {
     97   fileprivate var appValue: TeraRuntimeSigningPurpose {
     98     switch self {
     99     case .nostrEvent: .nostrEvent
    100     case .blossomUpload: .blossomUpload
    101     }
    102   }
    103 }
    104 
    105 extension TeraRuntimeSigningOutcome {
    106   fileprivate var generatedOutcome: HostSigningOutcome {
    107     switch self {
    108     case .signed: .signed
    109     case .locked: .locked
    110     case .cancelled: .cancelled
    111     case .rejected: .rejected
    112     case .timedOut: .timedOut
    113     case .unavailable: .unavailable
    114     case .invalidated: .invalidated
    115     case .failed: .failed
    116     }
    117   }
    118 
    119   fileprivate var signatureHex: String? {
    120     if case let .signed(signatureHex) = self {
    121       return signatureHex
    122     }
    123     return nil
    124   }
    125 }