TeraNativeUploadReconciliation.swift (2262B)
1 import Foundation 2 import RadrootsKit 3 4 /// Rust has already confirmed the exact attempt. Native settlement cannot 5 /// create or advance that completion, including after a lost native reply. 6 enum TeraNativeUploadReconciliation { 7 static func settle(_ expected: RadrootsBackgroundTransferSnapshot, input: TeraRecoveryUploadReceipt, 8 receipt: TeraRecoveryCompletionReceipt, transfer: any RadrootsBackgroundTransfer) async throws 9 { 10 try receipt.confirm(input) 11 guard input.response.identifier == expected.identifier.rawValue, 12 input.uploadURL == expected.request.remoteURL.absoluteString, 13 input.media.sha256 == expected.request.expectedSourceSHA256 else { throw TeraComposerAcknowledgment.unconfirmed } 14 let current = try await matchingSnapshot(expected, transfer: transfer) 15 try Task.checkCancellation() 16 if current.state == .completed { 17 return 18 } 19 guard current.state == .awaitingVerification else { throw TeraComposerAcknowledgment.unconfirmed } 20 do { 21 try await transfer.settle(expected.identifier, verification: .accepted) 22 } catch { 23 try Task.checkCancellation() 24 // A storage or transport reply can be lost after its state is committed. 25 // Only matching durable native success makes this success-equivalent. 26 let recovered = try await matchingSnapshot(expected, transfer: transfer) 27 guard recovered.state == .completed else { throw error } 28 return 29 } 30 let confirmed = try await matchingSnapshot(expected, transfer: transfer) 31 guard confirmed.state == .completed else { throw TeraComposerAcknowledgment.unconfirmed } 32 } 33 34 private static func matchingSnapshot(_ expected: RadrootsBackgroundTransferSnapshot, 35 transfer: any RadrootsBackgroundTransfer) async throws -> RadrootsBackgroundTransferSnapshot 36 { 37 guard let current = try await transfer.snapshot(for: expected.identifier), 38 current.identifier == expected.identifier, current.request == expected.request, 39 current.response == expected.response, current.executionID == expected.executionID, 40 current.uploadLease == expected.uploadLease else { throw TeraComposerAcknowledgment.unconfirmed } 41 return current 42 } 43 }