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 }