Move MobileCoin SDK to SignalUI.

This commit is contained in:
Matthew Chen 2021-10-26 16:53:42 -03:00
parent 60593ee26f
commit 079bb08067
17 changed files with 504 additions and 524 deletions

View File

@ -331,7 +331,7 @@
3465F4D827287677001663AF /* PaymentsImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3465F4D327287675001663AF /* PaymentsImpl.swift */; };
3465F4D927287677001663AF /* MobileCoinAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3465F4D427287675001663AF /* MobileCoinAPI.swift */; };
3465F4DB27287859001663AF /* PaymentsHelperImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3465F4DA27287858001663AF /* PaymentsHelperImpl.swift */; };
3465F4DD2728812B001663AF /* Payments+SignalUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3465F4DC2728812A001663AF /* Payments+SignalUI.swift */; };
3465F4DD2728812B001663AF /* Payments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3465F4DC2728812A001663AF /* Payments.swift */; };
34661FB820C1C0D60056EDD6 /* message_sent.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 34661FB720C1C0D60056EDD6 /* message_sent.aiff */; };
34684DAF25C2228D00C5A93C /* determinate_spinner_white.json in Resources */ = {isa = PBXBuildFile; fileRef = 34684DAB25C2228C00C5A93C /* determinate_spinner_white.json */; };
34684DB025C2228D00C5A93C /* determinate_spinner_blue.json in Resources */ = {isa = PBXBuildFile; fileRef = 34684DAC25C2228D00C5A93C /* determinate_spinner_blue.json */; };
@ -1325,7 +1325,7 @@
3465F4D327287675001663AF /* PaymentsImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaymentsImpl.swift; sourceTree = "<group>"; };
3465F4D427287675001663AF /* MobileCoinAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MobileCoinAPI.swift; sourceTree = "<group>"; };
3465F4DA27287858001663AF /* PaymentsHelperImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaymentsHelperImpl.swift; sourceTree = "<group>"; };
3465F4DC2728812A001663AF /* Payments+SignalUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Payments+SignalUI.swift"; sourceTree = "<group>"; };
3465F4DC2728812A001663AF /* Payments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Payments.swift; sourceTree = "<group>"; };
34661FB720C1C0D60056EDD6 /* message_sent.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; name = message_sent.aiff; path = Signal/AudioFiles/message_sent.aiff; sourceTree = SOURCE_ROOT; };
34684DAB25C2228C00C5A93C /* determinate_spinner_white.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = determinate_spinner_white.json; sourceTree = "<group>"; };
34684DAC25C2228D00C5A93C /* determinate_spinner_blue.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = determinate_spinner_blue.json; sourceTree = "<group>"; };
@ -3297,7 +3297,7 @@
children = (
3465F4D427287675001663AF /* MobileCoinAPI.swift */,
3465F4D227287675001663AF /* MobileCoinAPI+Configuration.swift */,
3465F4DC2728812A001663AF /* Payments+SignalUI.swift */,
3465F4DC2728812A001663AF /* Payments.swift */,
34A955B8271B553D00B05242 /* PaymentsFormat.swift */,
3465F4D327287675001663AF /* PaymentsImpl.swift */,
3465F4D127287674001663AF /* PaymentsProcessor.swift */,
@ -5587,7 +5587,7 @@
3402AA4B271D9DCD0084CBAE /* AttachmentApprovalToolbar.swift in Sources */,
3402AA9A271D9E180084CBAE /* ImageEditorBrushViewController.swift in Sources */,
34A954CB271B4BE600B05242 /* ShareViewDelegate.swift in Sources */,
3465F4DD2728812B001663AF /* Payments+SignalUI.swift in Sources */,
3465F4DD2728812B001663AF /* Payments.swift in Sources */,
3402AA7A271D9E180084CBAE /* CVUtils.swift in Sources */,
3465F4D527287677001663AF /* PaymentsReconciliation.swift in Sources */,
3402AAAB271D9E180084CBAE /* OWSActionSheets.swift in Sources */,

View File

@ -112,8 +112,6 @@ class InternalSettingsViewController: OWSTableViewController2 {
infoSection.add(.label(withText: "Database WAL size: \(byteCountFormatter.string(for: databaseStorage.databaseWALFileSize) ?? "Unknown")"))
infoSection.add(.label(withText: "Database SHM size: \(byteCountFormatter.string(for: databaseStorage.databaseSHMFileSize) ?? "Unknown")"))
infoSection.add(.label(withText: "dataStoreForUI: \(NSStringForDataStore(StorageCoordinator.dataStoreForUI))"))
infoSection.add(.label(withText: "hasGrdbFile: \(StorageCoordinator.hasGrdbFile)"))
infoSection.add(.label(withText: "didEverUseYdb: \(SSKPreferences.didEverUseYdb())"))
infoSection.add(.label(withText: "Core count: \(LocalDevice.allCoreCount) (active: \(LocalDevice.activeCoreCount))"))

View File

@ -222,10 +222,6 @@ class MediaGallery: Dependencies {
}
func setupDatabaseObservation() {
guard StorageCoordinator.dataStoreForUI == .grdb else {
owsFailDebug("Invalid data store.")
return
}
databaseStorage.appendDatabaseChangeDelegate(self)
}

View File

@ -7,7 +7,375 @@ import MobileCoin
@objc
public class PaymentsHelperImpl: NSObject, PaymentsHelperSwift {
// MARK: - PaymentsState
private static let arePaymentsEnabledKey = "isPaymentEnabled"
private static let paymentsEntropyKey = "paymentsEntropy"
private let paymentStateCache = AtomicOptional<PaymentsState>(nil)
@objc
public static let arePaymentsEnabledDidChange = Notification.Name("arePaymentsEnabledDidChange")
public func warmCaches() {
owsAssertDebug(GRDBSchemaMigrator.areMigrationsComplete)
Self.databaseStorage.read { transaction in
self.paymentStateCache.set(Self.loadPaymentsState(transaction: transaction))
}
}
public var paymentsState: PaymentsState {
paymentStateCache.get() ?? .disabled
}
public var arePaymentsEnabled: Bool {
paymentsState.isEnabled
}
public var paymentsEntropy: Data? {
paymentsState.paymentsEntropy
}
// MARK: - Incoming Messages
func processIncomingPaymentRequest(thread: TSThread,
paymentRequest: TSPaymentRequest,
transaction: SDSAnyWriteTransaction) {
// TODO: Handle requests.
owsFailDebug("Not yet implemented.")
}
func processIncomingPaymentNotification(thread: TSThread,
paymentNotification: TSPaymentNotification,
senderAddress: SignalServiceAddress,
transaction: SDSAnyWriteTransaction) {
guard paymentNotification.isValid else {
owsFailDebug("Invalid paymentNotification.")
return
}
guard senderAddress.isValid else {
owsFailDebug("Invalid senderAddress.")
return
}
upsertPaymentModelForIncomingPaymentNotification(paymentNotification,
thread: thread,
senderAddress: senderAddress,
transaction: transaction)
}
func processIncomingPaymentCancellation(thread: TSThread,
paymentCancellation: TSPaymentCancellation,
transaction: SDSAnyWriteTransaction) {
guard paymentCancellation.isValid else {
owsFailDebug("Invalid paymentNotification.")
return
}
let requestUuidString = paymentCancellation.requestUuidString
guard let paymentRequestModel = Self.findPaymentRequestModel(forRequestUuidString: requestUuidString,
expectedIsIncomingRequest: nil,
transaction: transaction) else {
// This isn't necessarily an error; we might receive multiple
// cancellation messages for a given request.
owsFailDebug("Missing paymentRequestModel.")
return
}
paymentRequestModel.anyRemove(transaction: transaction)
}
func processReceivedTranscriptPaymentRequest(thread: TSThread,
paymentRequest: TSPaymentRequest,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
do {
guard let contactThread = thread as? TSContactThread else {
throw OWSAssertionError("Invalid thread.")
}
guard let contactUuid = contactThread.contactAddress.uuid else {
throw OWSAssertionError("Missing contactUuid.")
}
let paymentRequestModel = TSPaymentRequestModel(requestUuidString: paymentRequest.requestUuidString,
addressUuidString: contactUuid.uuidString,
isIncomingRequest: false,
paymentAmount: paymentRequest.paymentAmount,
memoMessage: paymentRequest.memoMessage,
createdDate: NSDate.ows_date(withMillisecondsSince1970: messageTimestamp))
guard paymentRequestModel.isValid else {
throw OWSAssertionError("Invalid paymentRequestModel.")
}
paymentRequestModel.anyInsert(transaction: transaction)
} catch {
owsFailDebug("Error: \(error)")
return
}
}
func processReceivedTranscriptPaymentNotification(thread: TSThread,
paymentNotification: TSPaymentNotification,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
Logger.info("Ignoring payment notification from sync transcript.")
}
func processReceivedTranscriptPaymentCancellation(thread: TSThread,
paymentCancellation: TSPaymentCancellation,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
let requestUuidString = paymentCancellation.requestUuidString
if let paymentRequestModel = Self.findPaymentRequestModel(forRequestUuidString: requestUuidString,
expectedIsIncomingRequest: nil,
transaction: transaction) {
paymentRequestModel.anyRemove(transaction: transaction)
}
}
func processIncomingPaymentSyncMessage(_ paymentProto: SSKProtoSyncMessageOutgoingPayment,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
Logger.verbose("")
do {
guard let mobileCoinProto = paymentProto.mobileCoin else {
throw OWSAssertionError("Invalid payment sync message: Missing mobileCoinProto.")
}
var recipientUuid: UUID?
if let recipientUuidString = paymentProto.recipientUuid {
guard let uuid = UUID(uuidString: recipientUuidString) else {
throw OWSAssertionError("Invalid payment sync message: Missing recipientUuid.")
}
recipientUuid = uuid
}
let paymentAmount = TSPaymentAmount(currency: .mobileCoin, picoMob: mobileCoinProto.amountPicoMob)
guard paymentAmount.isValidAmount(canBeEmpty: true) else {
throw OWSAssertionError("Invalid payment sync message: invalid paymentAmount.")
}
let feeAmount = TSPaymentAmount(currency: .mobileCoin, picoMob: mobileCoinProto.feePicoMob)
guard feeAmount.isValidAmount(canBeEmpty: false) else {
throw OWSAssertionError("Invalid payment sync message: invalid feeAmount.")
}
let recipientPublicAddressData = mobileCoinProto.recipientAddress
let memoMessage = paymentProto.note?.nilIfEmpty
let spentKeyImages = Array(Set(mobileCoinProto.spentKeyImages))
owsAssertDebug(spentKeyImages.count == mobileCoinProto.spentKeyImages.count)
guard !spentKeyImages.isEmpty else {
throw OWSAssertionError("Invalid payment sync message: Missing spentKeyImages.")
}
let outputPublicKeys = Array(Set(mobileCoinProto.outputPublicKeys))
owsAssertDebug(outputPublicKeys.count == mobileCoinProto.outputPublicKeys.count)
guard !outputPublicKeys.isEmpty else {
throw OWSAssertionError("Invalid payment sync message: Missing outputPublicKeys.")
}
guard let mcReceiptData = mobileCoinProto.receipt,
!mcReceiptData.isEmpty,
nil != MobileCoin.Receipt(serializedData: mcReceiptData) else {
throw OWSAssertionError("Invalid payment sync message: Missing or invalid receipt.")
}
let ledgerBlockIndex = mobileCoinProto.ledgerBlockIndex
guard ledgerBlockIndex > 0 else {
throw OWSAssertionError("Invalid payment sync message: Invalid ledgerBlockIndex.")
}
let ledgerBlockTimestamp = mobileCoinProto.ledgerBlockTimestamp
// TODO: Support requests.
let requestUuidString: String? = nil
// We use .outgoingComplete. We can safely assume that the device which
// sent the payment has verified and notified.
let paymentState: TSPaymentState = .outgoingComplete
let paymentType: TSPaymentType
if recipientPublicAddressData == nil {
// Possible defragmentation.
guard recipientUuid == nil else {
throw OWSAssertionError("Invalid payment sync message: unexpected recipientUuid.")
}
guard recipientPublicAddressData == nil else {
throw OWSAssertionError("Invalid payment sync message: unexpected recipientPublicAddressData.")
}
guard paymentAmount.isValidAmount(canBeEmpty: true),
paymentAmount.picoMob == 0 else {
throw OWSAssertionError("Invalid payment sync message: invalid paymentAmount.")
}
guard memoMessage == nil else {
throw OWSAssertionError("Invalid payment sync message: unexpected memoMessage.")
}
paymentType = .outgoingDefragmentationFromLinkedDevice
} else {
// Possible outgoing payment.
guard recipientUuid != nil else {
throw OWSAssertionError("Invalid payment sync message: missing recipientUuid.")
}
guard paymentAmount.isValidAmount(canBeEmpty: false) else {
throw OWSAssertionError("Invalid payment sync message: invalid paymentAmount.")
}
paymentType = .outgoingPaymentFromLinkedDevice
}
let mobileCoin = MobileCoinPayment(recipientPublicAddressData: recipientPublicAddressData,
transactionData: nil,
receiptData: mcReceiptData,
incomingTransactionPublicKeys: nil,
spentKeyImages: spentKeyImages,
outputPublicKeys: outputPublicKeys,
ledgerBlockTimestamp: ledgerBlockTimestamp,
ledgerBlockIndex: ledgerBlockIndex,
feeAmount: feeAmount)
let paymentModel = TSPaymentModel(paymentType: paymentType,
paymentState: paymentState,
paymentAmount: paymentAmount,
createdDate: NSDate.ows_date(withMillisecondsSince1970: messageTimestamp),
addressUuidString: recipientUuid?.uuidString,
memoMessage: memoMessage,
requestUuidString: requestUuidString,
isUnread: false,
mobileCoin: mobileCoin)
try tryToInsertPaymentModel(paymentModel, transaction: transaction)
} catch {
owsFailDebug("Error: \(error)")
return
}
}
// This method enforces invariants around TSPaymentModel.
func tryToInsertPaymentModel(_ paymentModel: TSPaymentModel,
transaction: SDSAnyWriteTransaction) throws {
Logger.info("Trying to insert: \(paymentModel.descriptionForLogs)")
guard paymentModel.isValid else {
throw OWSAssertionError("Invalid paymentModel.")
}
let isRedundant = try isProposedPaymentModelRedundant(paymentModel,
transaction: transaction)
guard !isRedundant else {
throw OWSAssertionError("Duplicate paymentModel.")
}
paymentModel.anyInsert(transaction: transaction)
if paymentModel.isOutgoing,
paymentModel.isIdentifiedPayment,
let requestUuidString = paymentModel.requestUuidString,
let paymentRequestModel = Self.findPaymentRequestModel(forRequestUuidString: requestUuidString,
expectedIsIncomingRequest: true,
transaction: transaction) {
paymentRequestModel.anyRemove(transaction: transaction)
}
}
// This method enforces invariants around TSPaymentModel.
private func isProposedPaymentModelRedundant(_ paymentModel: TSPaymentModel,
transaction: SDSAnyWriteTransaction) throws -> Bool {
guard paymentModel.isValid else {
throw OWSAssertionError("Invalid paymentModel.")
}
// Only one model in the database should have a given transaction.
if paymentModel.canHaveMCTransaction {
if let transactionData = paymentModel.mobileCoin?.transactionData {
let existingPaymentModels = PaymentFinder.paymentModels(forMcTransactionData: transactionData,
transaction: transaction)
if existingPaymentModels.count > 1 {
owsFailDebug("More than one conflict.")
}
if !existingPaymentModels.isEmpty {
owsFailDebug("Transaction conflict.")
return true
}
} else if paymentModel.shouldHaveMCTransaction {
throw OWSAssertionError("Missing transactionData.")
}
}
// Only one model in the database should have a given receipt.
if paymentModel.shouldHaveMCReceipt {
if let receiptData = paymentModel.mobileCoin?.receiptData {
let existingPaymentModels = PaymentFinder.paymentModels(forMcReceiptData: receiptData,
transaction: transaction)
if existingPaymentModels.count > 1 {
owsFailDebug("More than one conflict.")
}
if !existingPaymentModels.isEmpty {
owsFailDebug("Receipt conflict.")
return true
}
} else {
throw OWSAssertionError("Missing receiptData.")
}
}
// Only one _identified_ payment model in the database should correspond to any given
// spentKeyImage or outputPublicKey.
//
// We don't need to worry about conflicts with unidentified payment models;
// PaymentsReconciliation will avoid / clean those up.
let mcLedgerBlockIndex = paymentModel.mobileCoin?.ledgerBlockIndex ?? 0
let spentKeyImages = Set(paymentModel.mobileCoin?.spentKeyImages ?? [])
let outputPublicKeys = Set(paymentModel.mobileCoin?.outputPublicKeys ?? [])
if !paymentModel.isUnidentified,
mcLedgerBlockIndex > 0 {
let otherPaymentModels = PaymentFinder.paymentModels(forMcLedgerBlockIndex: mcLedgerBlockIndex,
transaction: transaction)
for otherPaymentModel in otherPaymentModels {
guard !otherPaymentModel.isUnidentified else {
continue
}
guard paymentModel.uniqueId != otherPaymentModel.uniqueId else {
owsFailDebug("Duplicate paymentModel.")
return true
}
let otherSpentKeyImages = Set(otherPaymentModel.mobileCoin?.spentKeyImages ?? [])
let otherOutputPublicKeys = Set(otherPaymentModel.mobileCoin?.outputPublicKeys ?? [])
if !spentKeyImages.intersection(otherSpentKeyImages).isEmpty {
for value in spentKeyImages {
Logger.verbose("spentKeyImage: \(value.hexadecimalString)")
}
for value in otherSpentKeyImages {
Logger.verbose("otherSpentKeyImage: \(value.hexadecimalString)")
}
owsFailDebug("spentKeyImage conflict.")
return true
}
if !outputPublicKeys.intersection(otherOutputPublicKeys).isEmpty {
for value in outputPublicKeys {
Logger.verbose("outputPublicKey: \(value.hexadecimalString)")
}
for value in otherOutputPublicKeys {
Logger.verbose("otherOutputPublicKey: \(value.hexadecimalString)")
}
owsFailDebug("outputPublicKey conflict.")
return true
}
}
}
return false
}
// var arePaymentsEnabled: Bool { get }
// func setArePaymentsEnabled(for address: SignalServiceAddress, hasPaymentsEnabled: Bool, transaction: SDSAnyWriteTransaction)
// func arePaymentsEnabled(for address: SignalServiceAddress, transaction: SDSAnyReadTransaction) -> Bool
}
// MARK: -
public struct PaymentsPassphrase: Equatable, Dependencies {
public let words: [String]
public init(words: [String]) throws {
guard words.count == PaymentsConstants.passphraseWordCount else {
owsFailDebug("words.count \(words.count) != \(PaymentsConstants.passphraseWordCount)")
throw PaymentsError.invalidPassphrase
}
self.words = words
}
public var wordCount: Int { words.count }
public var asPassphrase: String { words.joined(separator: " ") }
public var debugDescription: String { asPassphrase }
}

View File

@ -219,9 +219,7 @@ NS_ASSUME_NONNULL_BEGIN
[DeviceSleepManager.shared removeBlockWithBlockObject:sleepBlockObject];
if (StorageCoordinator.dataStoreForUI == DataStoreGrdb) {
[SSKEnvironment.shared warmCaches];
}
[SSKEnvironment.shared warmCaches];
migrationCompletion(nil);
OWSAssertDebug(backgroundTask);
@ -243,9 +241,6 @@ NS_ASSUME_NONNULL_BEGIN
+ (BOOL)shouldTruncateGrdbWal
{
if (StorageCoordinator.dataStoreForUI != DataStoreGrdb) {
return NO;
}
if (!CurrentAppContext().isMainApp) {
return NO;
}

View File

@ -85,24 +85,24 @@ NS_ASSUME_NONNULL_BEGIN
if (transcript.paymentRequest != nil) {
OWSLogInfo(@"Processing payment request from sync transcript.");
[self.payments processReceivedTranscriptPaymentRequestWithThread:transcript.thread
paymentRequest:transcript.paymentRequest
messageTimestamp:messageTimestamp
transaction:transaction];
[self.paymentsHelper processReceivedTranscriptPaymentRequestWithThread:transcript.thread
paymentRequest:transcript.paymentRequest
messageTimestamp:messageTimestamp
transaction:transaction];
return;
} else if (transcript.paymentNotification != nil) {
OWSLogInfo(@"Processing payment notification from sync transcript.");
[self.payments processReceivedTranscriptPaymentNotificationWithThread:transcript.thread
paymentNotification:transcript.paymentNotification
messageTimestamp:messageTimestamp
transaction:transaction];
[self.paymentsHelper processReceivedTranscriptPaymentNotificationWithThread:transcript.thread
paymentNotification:transcript.paymentNotification
messageTimestamp:messageTimestamp
transaction:transaction];
return;
} else if (transcript.paymentCancellation != nil) {
OWSLogInfo(@"Processing payment cancellation from sync transcript.");
[self.payments processReceivedTranscriptPaymentCancellationWithThread:transcript.thread
paymentCancellation:transcript.paymentCancellation
messageTimestamp:messageTimestamp
transaction:transaction];
[self.paymentsHelper processReceivedTranscriptPaymentCancellationWithThread:transcript.thread
paymentCancellation:transcript.paymentCancellation
messageTimestamp:messageTimestamp
transaction:transaction];
return;
}

View File

@ -1756,9 +1756,9 @@ NS_ASSUME_NONNULL_BEGIN
transaction:transaction];
} else if (syncMessage.outgoingPayment) {
// An "incoming" sync message notifies us of an "outgoing" payment.
[self.payments processIncomingPaymentSyncMessage:syncMessage.outgoingPayment
messageTimestamp:serverDeliveryTimestamp
transaction:transaction];
[self.paymentsHelper processIncomingPaymentSyncMessage:syncMessage.outgoingPayment
messageTimestamp:serverDeliveryTimestamp
transaction:transaction];
} else {
OWSLogWarn(@"Ignoring unsupported sync message.");
}
@ -2025,22 +2025,22 @@ NS_ASSUME_NONNULL_BEGIN
thread:thread];
if (paymentModels.request != nil) {
OWSLogInfo(@"Processing payment request.");
[self.payments processIncomingPaymentRequestWithThread:thread
paymentRequest:paymentModels.request
transaction:transaction];
[self.paymentsHelper processIncomingPaymentRequestWithThread:thread
paymentRequest:paymentModels.request
transaction:transaction];
return nil;
} else if (paymentModels.notification != nil) {
OWSLogInfo(@"Processing payment notification.");
[self.payments processIncomingPaymentNotificationWithThread:thread
paymentNotification:paymentModels.notification
senderAddress:envelope.sourceAddress
transaction:transaction];
[self.paymentsHelper processIncomingPaymentNotificationWithThread:thread
paymentNotification:paymentModels.notification
senderAddress:envelope.sourceAddress
transaction:transaction];
return nil;
} else if (paymentModels.cancellation != nil) {
OWSLogInfo(@"Processing payment cancellation.");
[self.payments processIncomingPaymentCancellationWithThread:thread
paymentCancellation:paymentModels.cancellation
transaction:transaction];
[self.paymentsHelper processIncomingPaymentCancellationWithThread:thread
paymentCancellation:paymentModels.cancellation
transaction:transaction];
return nil;
} else if (paymentModels != nil) {
OWSFailDebug(@"Unexpected payment model.");

View File

@ -47,6 +47,39 @@ public protocol PaymentsHelper: AnyObject {
var arePaymentsEnabled: Bool { get }
func setArePaymentsEnabled(for address: SignalServiceAddress, hasPaymentsEnabled: Bool, transaction: SDSAnyWriteTransaction)
func arePaymentsEnabled(for address: SignalServiceAddress, transaction: SDSAnyReadTransaction) -> Bool
@objc(processIncomingPaymentSyncMessage:messageTimestamp:transaction:)
func processIncomingPaymentSyncMessage(_ paymentProto: SSKProtoSyncMessageOutgoingPayment,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction)
func processIncomingPaymentRequest(thread: TSThread,
paymentRequest: TSPaymentRequest,
transaction: SDSAnyWriteTransaction)
func processIncomingPaymentNotification(thread: TSThread,
paymentNotification: TSPaymentNotification,
senderAddress: SignalServiceAddress,
transaction: SDSAnyWriteTransaction)
func processIncomingPaymentCancellation(thread: TSThread,
paymentCancellation: TSPaymentCancellation,
transaction: SDSAnyWriteTransaction)
func processReceivedTranscriptPaymentRequest(thread: TSThread,
paymentRequest: TSPaymentRequest,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction)
func processReceivedTranscriptPaymentNotification(thread: TSThread,
paymentNotification: TSPaymentNotification,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction)
func processReceivedTranscriptPaymentCancellation(thread: TSThread,
paymentCancellation: TSPaymentCancellation,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction)
}
// MARK: -
@ -156,6 +189,52 @@ extension MockPaymentsHelper: PaymentsHelperSwift {
public func arePaymentsEnabled(for address: SignalServiceAddress, transaction: SDSAnyReadTransaction) -> Bool {
owsFail("Not implemented.")
}
public func processIncomingPaymentRequest(thread: TSThread,
paymentRequest: TSPaymentRequest,
transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}
public func processIncomingPaymentNotification(thread: TSThread,
paymentNotification: TSPaymentNotification,
senderAddress: SignalServiceAddress,
transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}
public func processIncomingPaymentCancellation(thread: TSThread,
paymentCancellation: TSPaymentCancellation,
transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}
public func processReceivedTranscriptPaymentRequest(thread: TSThread,
paymentRequest: TSPaymentRequest,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}
public func processReceivedTranscriptPaymentNotification(thread: TSThread,
paymentNotification: TSPaymentNotification,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}
public func processReceivedTranscriptPaymentCancellation(thread: TSThread,
paymentCancellation: TSPaymentCancellation,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}
public func processIncomingPaymentSyncMessage(_ paymentProto: SSKProtoSyncMessageOutgoingPayment,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}
}
// MARK: -

View File

@ -52,20 +52,6 @@ NS_ASSUME_NONNULL_BEGIN
@implementation TSPaymentModel
#pragma mark - Dependencies
+ (id<Payments>)payments
{
return SSKEnvironment.shared.payments;
}
- (id<Payments>)payments
{
return SSKEnvironment.shared.payments;
}
#pragma mark -
- (instancetype)initWithPaymentType:(TSPaymentType)paymentType
paymentState:(TSPaymentState)paymentState
paymentAmount:(nullable TSPaymentAmount *)paymentAmount

View File

@ -84,7 +84,7 @@ extension TSPaymentAddress: TSPaymentBaseModel {
owsFailDebug("Unexpected currency.")
return false
}
return SSKEnvironment.shared.payments.isValidMobileCoinPublicAddress(mobileCoinPublicAddressData)
return true
}
public func buildProto() throws -> SSKProtoPaymentAddress {
@ -331,7 +331,7 @@ public class TSPaymentModels: NSObject {
guard !CurrentAppContext().isRunningTests else {
return nil
}
guard payments.arePaymentsEnabled else {
guard paymentsHelper.arePaymentsEnabled else {
return nil
}
guard let paymentProto = dataMessage.payment else {

View File

@ -50,7 +50,6 @@ public class SDSDatabaseStorage: SDSTransactable {
guard !CurrentAppContext().isRunningTests else {
return
}
owsAssertDebug(StorageCoordinator.dataStoreForUI == .grdb)
// Cross process writes
crossProcess.callback = { [weak self] in
DispatchQueue.main.async {

View File

@ -33,10 +33,6 @@ NSString *NSStringFromStorageCoordinatorState(StorageCoordinatorState value);
@property (class, nonatomic, readonly) BOOL hasInvalidDatabaseVersion;
// The data store that will be used once the app is ready.
// This data store may not be available before then.
@property (class, nonatomic, readonly) DataStore dataStoreForUI;
- (BOOL)isDatabasePasswordAccessible;
- (void)markStorageSetupAsComplete;

View File

@ -37,8 +37,6 @@ NSString *NSStringForDataStore(DataStore value)
@property (atomic) BOOL isStorageSetupComplete;
@property (nonatomic, readonly) DataStore dataStoreForUI;
@end
#pragma mark -
@ -54,10 +52,6 @@ NSString *NSStringForDataStore(DataStore value)
OWSSingletonAssert();
_dataStoreForUI = [StorageCoordinator computeDataStoreForUI];
OWSLogInfo(@"dataStoreForUI: %@", NSStringForDataStore(self.dataStoreForUI));
_databaseStorage = [[SDSDatabaseStorage alloc] initWithDelegate:self];
[self configure];
@ -97,25 +91,6 @@ NSString *NSStringForDataStore(DataStore value)
return self.state;
}
+ (DataStore)dataStoreForUI
{
// Computing dataStoreForUI is slightly expensive.
// Once SSKEnvironment is configured, we use the cached value
// that hangs on StorageCoordinator. Until then, we compute every
// time.
if (!SSKEnvironment.hasShared) {
return self.computeDataStoreForUI;
}
return SSKEnvironment.shared.storageCoordinator.dataStoreForUI;
}
+ (DataStore)computeDataStoreForUI
{
// TODO:
return DataStoreGrdb;
}
- (void)configure
{
OWSLogInfo(@"storageMode: %@", SSKFeatureFlags.storageModeDescription);

View File

@ -85,7 +85,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSMessagePipelineSupervisor *messagePipelineSupervisor = [OWSMessagePipelineSupervisor createStandardSupervisor];
AppExpiry *appExpiry = [AppExpiry new];
MessageProcessor *messageProcessor = [MessageProcessor new];
id<PaymentsHelper> payments = [MockPaymentsHelper new];
id<PaymentsHelper> paymentsHelper = [MockPaymentsHelper new];
id<PaymentsCurrencies> paymentsCurrencies = [MockPaymentsCurrencies new];
SpamChallengeResolver *spamChallengeResolver = [SpamChallengeResolver new];
SenderKeyStore *senderKeyStore = [SenderKeyStore new];

View File

@ -37,7 +37,7 @@ public class SUIEnvironment: NSObject {
@objc
public var paymentsRef: Payments = PaymentsImpl()
paymentsSwift
private override init() {
super.init()

View File

@ -4,8 +4,6 @@
import Foundation
// TODO: Rename this source file.
@objc
public protocol Payments: AnyObject {
@ -21,42 +19,9 @@ public protocol Payments: AnyObject {
func isValidMobileCoinPublicAddress(_ publicAddressData: Data) -> Bool
func processIncomingPaymentRequest(thread: TSThread,
paymentRequest: TSPaymentRequest,
transaction: SDSAnyWriteTransaction)
func processIncomingPaymentNotification(thread: TSThread,
paymentNotification: TSPaymentNotification,
senderAddress: SignalServiceAddress,
transaction: SDSAnyWriteTransaction)
func processIncomingPaymentCancellation(thread: TSThread,
paymentCancellation: TSPaymentCancellation,
transaction: SDSAnyWriteTransaction)
func processReceivedTranscriptPaymentRequest(thread: TSThread,
paymentRequest: TSPaymentRequest,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction)
func processReceivedTranscriptPaymentNotification(thread: TSThread,
paymentNotification: TSPaymentNotification,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction)
func processReceivedTranscriptPaymentCancellation(thread: TSThread,
paymentCancellation: TSPaymentCancellation,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction)
func willInsertPayment(_ paymentModel: TSPaymentModel, transaction: SDSAnyWriteTransaction)
func willUpdatePayment(_ paymentModel: TSPaymentModel, transaction: SDSAnyWriteTransaction)
@objc(processIncomingPaymentSyncMessage:messageTimestamp:transaction:)
func processIncomingPaymentSyncMessage(_ paymentProto: SSKProtoSyncMessageOutgoingPayment,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction)
func scheduleReconciliationNow(transaction: SDSAnyWriteTransaction)
func replaceAsUnidentified(paymentModel oldPaymentModel: TSPaymentModel,
@ -120,24 +85,7 @@ public protocol PaymentsSwift: Payments {
// MARK: -
public struct PaymentsPassphrase: Equatable, Dependencies {
public let words: [String]
public init(words: [String]) throws {
guard words.count == PaymentsConstants.passphraseWordCount else {
owsFailDebug("words.count \(words.count) != \(PaymentsConstants.passphraseWordCount)")
throw PaymentsError.invalidPassphrase
}
self.words = words
}
public var wordCount: Int { words.count }
public var asPassphrase: String { words.joined(separator: " ") }
public var debugDescription: String { asPassphrase }
extension PaymentsPassphrase {
public static func parse(passphrase: String,
validateWords: Bool) throws -> PaymentsPassphrase {
@ -249,52 +197,6 @@ extension MockPayments: PaymentsSwift {
owsFail("Not implemented.")
}
public func processIncomingPaymentRequest(thread: TSThread,
paymentRequest: TSPaymentRequest,
transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}
public func processIncomingPaymentNotification(thread: TSThread,
paymentNotification: TSPaymentNotification,
senderAddress: SignalServiceAddress,
transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}
public func processIncomingPaymentCancellation(thread: TSThread,
paymentCancellation: TSPaymentCancellation,
transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}
public func processReceivedTranscriptPaymentRequest(thread: TSThread,
paymentRequest: TSPaymentRequest,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}
public func processReceivedTranscriptPaymentNotification(thread: TSThread,
paymentNotification: TSPaymentNotification,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}
public func processReceivedTranscriptPaymentCancellation(thread: TSThread,
paymentCancellation: TSPaymentCancellation,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}
public func processIncomingPaymentSyncMessage(_ paymentProto: SSKProtoSyncMessageOutgoingPayment,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}
public func willInsertPayment(_ paymentModel: TSPaymentModel, transaction: SDSAnyWriteTransaction) {
owsFail("Not implemented.")
}

View File

@ -4,6 +4,7 @@
import Foundation
import MobileCoin
import SignalServiceKit
@objc
public class PaymentsImpl: NSObject, PaymentsSwift {
@ -160,32 +161,32 @@ public class PaymentsImpl: NSObject, PaymentsSwift {
// MARK: - PaymentsState
private static let arePaymentsEnabledKey = "isPaymentEnabled"
private static let paymentsEntropyKey = "paymentsEntropy"
private let paymentStateCache = AtomicOptional<PaymentsState>(nil)
@objc
public static let arePaymentsEnabledDidChange = Notification.Name("arePaymentsEnabledDidChange")
public func warmCaches() {
owsAssertDebug(GRDBSchemaMigrator.areMigrationsComplete)
Self.databaseStorage.read { transaction in
self.paymentStateCache.set(Self.loadPaymentsState(transaction: transaction))
}
}
public var paymentsState: PaymentsState {
paymentStateCache.get() ?? .disabled
}
// private static let arePaymentsEnabledKey = "isPaymentEnabled"
// private static let paymentsEntropyKey = "paymentsEntropy"
//
// private let paymentStateCache = AtomicOptional<PaymentsState>(nil)
//
// @objc
// public static let arePaymentsEnabledDidChange = Notification.Name("arePaymentsEnabledDidChange")
//
// public func warmCaches() {
// owsAssertDebug(GRDBSchemaMigrator.areMigrationsComplete)
//
// Self.databaseStorage.read { transaction in
// self.paymentStateCache.set(Self.loadPaymentsState(transaction: transaction))
// }
// }
//
// public var paymentsState: PaymentsState {
// paymentStateCache.get() ?? .disabled
// }
public var arePaymentsEnabled: Bool {
paymentsState.isEnabled
paymentsHelper.arePaymentsEnabled
}
public var paymentsEntropy: Data? {
paymentsState.paymentsEntropy
paymentsHelper.paymentsEntropy
}
public var passphrase: PaymentsPassphrase? {
@ -1329,321 +1330,6 @@ public extension PaymentsImpl {
Self.messageSenderJobQueue.add(message: message.asPreparer, transaction: transaction)
return message
}
// MARK: - Incoming Messages
func processIncomingPaymentRequest(thread: TSThread,
paymentRequest: TSPaymentRequest,
transaction: SDSAnyWriteTransaction) {
// TODO: Handle requests.
owsFailDebug("Not yet implemented.")
}
func processIncomingPaymentNotification(thread: TSThread,
paymentNotification: TSPaymentNotification,
senderAddress: SignalServiceAddress,
transaction: SDSAnyWriteTransaction) {
guard paymentNotification.isValid else {
owsFailDebug("Invalid paymentNotification.")
return
}
guard senderAddress.isValid else {
owsFailDebug("Invalid senderAddress.")
return
}
upsertPaymentModelForIncomingPaymentNotification(paymentNotification,
thread: thread,
senderAddress: senderAddress,
transaction: transaction)
}
func processIncomingPaymentCancellation(thread: TSThread,
paymentCancellation: TSPaymentCancellation,
transaction: SDSAnyWriteTransaction) {
guard paymentCancellation.isValid else {
owsFailDebug("Invalid paymentNotification.")
return
}
let requestUuidString = paymentCancellation.requestUuidString
guard let paymentRequestModel = Self.findPaymentRequestModel(forRequestUuidString: requestUuidString,
expectedIsIncomingRequest: nil,
transaction: transaction) else {
// This isn't necessarily an error; we might receive multiple
// cancellation messages for a given request.
owsFailDebug("Missing paymentRequestModel.")
return
}
paymentRequestModel.anyRemove(transaction: transaction)
}
func processReceivedTranscriptPaymentRequest(thread: TSThread,
paymentRequest: TSPaymentRequest,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
do {
guard let contactThread = thread as? TSContactThread else {
throw OWSAssertionError("Invalid thread.")
}
guard let contactUuid = contactThread.contactAddress.uuid else {
throw OWSAssertionError("Missing contactUuid.")
}
let paymentRequestModel = TSPaymentRequestModel(requestUuidString: paymentRequest.requestUuidString,
addressUuidString: contactUuid.uuidString,
isIncomingRequest: false,
paymentAmount: paymentRequest.paymentAmount,
memoMessage: paymentRequest.memoMessage,
createdDate: NSDate.ows_date(withMillisecondsSince1970: messageTimestamp))
guard paymentRequestModel.isValid else {
throw OWSAssertionError("Invalid paymentRequestModel.")
}
paymentRequestModel.anyInsert(transaction: transaction)
} catch {
owsFailDebug("Error: \(error)")
return
}
}
func processReceivedTranscriptPaymentNotification(thread: TSThread,
paymentNotification: TSPaymentNotification,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
Logger.info("Ignoring payment notification from sync transcript.")
}
func processIncomingPaymentSyncMessage(_ paymentProto: SSKProtoSyncMessageOutgoingPayment,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
Logger.verbose("")
do {
guard let mobileCoinProto = paymentProto.mobileCoin else {
throw OWSAssertionError("Invalid payment sync message: Missing mobileCoinProto.")
}
var recipientUuid: UUID?
if let recipientUuidString = paymentProto.recipientUuid {
guard let uuid = UUID(uuidString: recipientUuidString) else {
throw OWSAssertionError("Invalid payment sync message: Missing recipientUuid.")
}
recipientUuid = uuid
}
let paymentAmount = TSPaymentAmount(currency: .mobileCoin, picoMob: mobileCoinProto.amountPicoMob)
guard paymentAmount.isValidAmount(canBeEmpty: true) else {
throw OWSAssertionError("Invalid payment sync message: invalid paymentAmount.")
}
let feeAmount = TSPaymentAmount(currency: .mobileCoin, picoMob: mobileCoinProto.feePicoMob)
guard feeAmount.isValidAmount(canBeEmpty: false) else {
throw OWSAssertionError("Invalid payment sync message: invalid feeAmount.")
}
let recipientPublicAddressData = mobileCoinProto.recipientAddress
let memoMessage = paymentProto.note?.nilIfEmpty
let spentKeyImages = Array(Set(mobileCoinProto.spentKeyImages))
owsAssertDebug(spentKeyImages.count == mobileCoinProto.spentKeyImages.count)
guard !spentKeyImages.isEmpty else {
throw OWSAssertionError("Invalid payment sync message: Missing spentKeyImages.")
}
let outputPublicKeys = Array(Set(mobileCoinProto.outputPublicKeys))
owsAssertDebug(outputPublicKeys.count == mobileCoinProto.outputPublicKeys.count)
guard !outputPublicKeys.isEmpty else {
throw OWSAssertionError("Invalid payment sync message: Missing outputPublicKeys.")
}
guard let mcReceiptData = mobileCoinProto.receipt,
!mcReceiptData.isEmpty,
nil != MobileCoin.Receipt(serializedData: mcReceiptData) else {
throw OWSAssertionError("Invalid payment sync message: Missing or invalid receipt.")
}
let ledgerBlockIndex = mobileCoinProto.ledgerBlockIndex
guard ledgerBlockIndex > 0 else {
throw OWSAssertionError("Invalid payment sync message: Invalid ledgerBlockIndex.")
}
let ledgerBlockTimestamp = mobileCoinProto.ledgerBlockTimestamp
// TODO: Support requests.
let requestUuidString: String? = nil
// We use .outgoingComplete. We can safely assume that the device which
// sent the payment has verified and notified.
let paymentState: TSPaymentState = .outgoingComplete
let paymentType: TSPaymentType
if recipientPublicAddressData == nil {
// Possible defragmentation.
guard recipientUuid == nil else {
throw OWSAssertionError("Invalid payment sync message: unexpected recipientUuid.")
}
guard recipientPublicAddressData == nil else {
throw OWSAssertionError("Invalid payment sync message: unexpected recipientPublicAddressData.")
}
guard paymentAmount.isValidAmount(canBeEmpty: true),
paymentAmount.picoMob == 0 else {
throw OWSAssertionError("Invalid payment sync message: invalid paymentAmount.")
}
guard memoMessage == nil else {
throw OWSAssertionError("Invalid payment sync message: unexpected memoMessage.")
}
paymentType = .outgoingDefragmentationFromLinkedDevice
} else {
// Possible outgoing payment.
guard recipientUuid != nil else {
throw OWSAssertionError("Invalid payment sync message: missing recipientUuid.")
}
guard paymentAmount.isValidAmount(canBeEmpty: false) else {
throw OWSAssertionError("Invalid payment sync message: invalid paymentAmount.")
}
paymentType = .outgoingPaymentFromLinkedDevice
}
let mobileCoin = MobileCoinPayment(recipientPublicAddressData: recipientPublicAddressData,
transactionData: nil,
receiptData: mcReceiptData,
incomingTransactionPublicKeys: nil,
spentKeyImages: spentKeyImages,
outputPublicKeys: outputPublicKeys,
ledgerBlockTimestamp: ledgerBlockTimestamp,
ledgerBlockIndex: ledgerBlockIndex,
feeAmount: feeAmount)
let paymentModel = TSPaymentModel(paymentType: paymentType,
paymentState: paymentState,
paymentAmount: paymentAmount,
createdDate: NSDate.ows_date(withMillisecondsSince1970: messageTimestamp),
addressUuidString: recipientUuid?.uuidString,
memoMessage: memoMessage,
requestUuidString: requestUuidString,
isUnread: false,
mobileCoin: mobileCoin)
try tryToInsertPaymentModel(paymentModel, transaction: transaction)
} catch {
owsFailDebug("Error: \(error)")
return
}
}
// This method enforces invariants around TSPaymentModel.
func tryToInsertPaymentModel(_ paymentModel: TSPaymentModel,
transaction: SDSAnyWriteTransaction) throws {
Logger.info("Trying to insert: \(paymentModel.descriptionForLogs)")
guard paymentModel.isValid else {
throw OWSAssertionError("Invalid paymentModel.")
}
let isRedundant = try isProposedPaymentModelRedundant(paymentModel,
transaction: transaction)
guard !isRedundant else {
throw OWSAssertionError("Duplicate paymentModel.")
}
paymentModel.anyInsert(transaction: transaction)
if paymentModel.isOutgoing,
paymentModel.isIdentifiedPayment,
let requestUuidString = paymentModel.requestUuidString,
let paymentRequestModel = Self.findPaymentRequestModel(forRequestUuidString: requestUuidString,
expectedIsIncomingRequest: true,
transaction: transaction) {
paymentRequestModel.anyRemove(transaction: transaction)
}
}
// This method enforces invariants around TSPaymentModel.
private func isProposedPaymentModelRedundant(_ paymentModel: TSPaymentModel,
transaction: SDSAnyWriteTransaction) throws -> Bool {
guard paymentModel.isValid else {
throw OWSAssertionError("Invalid paymentModel.")
}
// Only one model in the database should have a given transaction.
if paymentModel.canHaveMCTransaction {
if let transactionData = paymentModel.mobileCoin?.transactionData {
let existingPaymentModels = PaymentFinder.paymentModels(forMcTransactionData: transactionData,
transaction: transaction)
if existingPaymentModels.count > 1 {
owsFailDebug("More than one conflict.")
}
if !existingPaymentModels.isEmpty {
owsFailDebug("Transaction conflict.")
return true
}
} else if paymentModel.shouldHaveMCTransaction {
throw OWSAssertionError("Missing transactionData.")
}
}
// Only one model in the database should have a given receipt.
if paymentModel.shouldHaveMCReceipt {
if let receiptData = paymentModel.mobileCoin?.receiptData {
let existingPaymentModels = PaymentFinder.paymentModels(forMcReceiptData: receiptData,
transaction: transaction)
if existingPaymentModels.count > 1 {
owsFailDebug("More than one conflict.")
}
if !existingPaymentModels.isEmpty {
owsFailDebug("Receipt conflict.")
return true
}
} else {
throw OWSAssertionError("Missing receiptData.")
}
}
// Only one _identified_ payment model in the database should correspond to any given
// spentKeyImage or outputPublicKey.
//
// We don't need to worry about conflicts with unidentified payment models;
// PaymentsReconciliation will avoid / clean those up.
let mcLedgerBlockIndex = paymentModel.mobileCoin?.ledgerBlockIndex ?? 0
let spentKeyImages = Set(paymentModel.mobileCoin?.spentKeyImages ?? [])
let outputPublicKeys = Set(paymentModel.mobileCoin?.outputPublicKeys ?? [])
if !paymentModel.isUnidentified,
mcLedgerBlockIndex > 0 {
let otherPaymentModels = PaymentFinder.paymentModels(forMcLedgerBlockIndex: mcLedgerBlockIndex,
transaction: transaction)
for otherPaymentModel in otherPaymentModels {
guard !otherPaymentModel.isUnidentified else {
continue
}
guard paymentModel.uniqueId != otherPaymentModel.uniqueId else {
owsFailDebug("Duplicate paymentModel.")
return true
}
let otherSpentKeyImages = Set(otherPaymentModel.mobileCoin?.spentKeyImages ?? [])
let otherOutputPublicKeys = Set(otherPaymentModel.mobileCoin?.outputPublicKeys ?? [])
if !spentKeyImages.intersection(otherSpentKeyImages).isEmpty {
for value in spentKeyImages {
Logger.verbose("spentKeyImage: \(value.hexadecimalString)")
}
for value in otherSpentKeyImages {
Logger.verbose("otherSpentKeyImage: \(value.hexadecimalString)")
}
owsFailDebug("spentKeyImage conflict.")
return true
}
if !outputPublicKeys.intersection(otherOutputPublicKeys).isEmpty {
for value in outputPublicKeys {
Logger.verbose("outputPublicKey: \(value.hexadecimalString)")
}
for value in otherOutputPublicKeys {
Logger.verbose("otherOutputPublicKey: \(value.hexadecimalString)")
}
owsFailDebug("outputPublicKey conflict.")
return true
}
}
}
return false
}
func processReceivedTranscriptPaymentCancellation(thread: TSThread,
paymentCancellation: TSPaymentCancellation,
messageTimestamp: UInt64,
transaction: SDSAnyWriteTransaction) {
let requestUuidString = paymentCancellation.requestUuidString
if let paymentRequestModel = Self.findPaymentRequestModel(forRequestUuidString: requestUuidString,
expectedIsIncomingRequest: nil,
transaction: transaction) {
paymentRequestModel.anyRemove(transaction: transaction)
}
}
}
// MARK: - Formatting