Move MobileCoin SDK to SignalUI.
This commit is contained in:
parent
60593ee26f
commit
079bb08067
@ -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 */,
|
||||
|
||||
@ -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))"))
|
||||
|
||||
@ -222,10 +222,6 @@ class MediaGallery: Dependencies {
|
||||
}
|
||||
|
||||
func setupDatabaseObservation() {
|
||||
guard StorageCoordinator.dataStoreForUI == .grdb else {
|
||||
owsFailDebug("Invalid data store.")
|
||||
return
|
||||
}
|
||||
databaseStorage.appendDatabaseChangeDelegate(self)
|
||||
}
|
||||
|
||||
|
||||
@ -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 }
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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.");
|
||||
|
||||
@ -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: -
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -37,7 +37,7 @@ public class SUIEnvironment: NSObject {
|
||||
|
||||
@objc
|
||||
public var paymentsRef: Payments = PaymentsImpl()
|
||||
paymentsSwift
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
|
||||
|
||||
@ -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.")
|
||||
}
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user