Defragmentation.
This commit is contained in:
parent
bac06335d4
commit
d1f6b56658
@ -312,6 +312,8 @@ class DebugUIPayments: DebugUIPage {
|
||||
memoMessage: "Tiny: \(count)",
|
||||
paymentRequestModel: nil,
|
||||
isOutgoingTransfer: false)
|
||||
}.then(on: .global()) { (paymentModel: TSPaymentModel) in
|
||||
Self.payments.blockOnOutgoingVerification(paymentModel: paymentModel)
|
||||
}.done(on: .global()) { _ in
|
||||
if count > 1 {
|
||||
Self.sendTinyPayments(contactThread: contactThread, count: count - 1)
|
||||
|
||||
@ -509,7 +509,7 @@ public class SendPaymentCompletionActionSheet: ActionSheetController {
|
||||
// TODO: Finalize this timeout duration with design.
|
||||
let blockInterval: TimeInterval = kSecondInterval * 30
|
||||
return firstly(on: .global()) { () -> Promise<Void> in
|
||||
Self.paymentsSwift.blockOnOutgoingVerification(paymentModel: paymentModel)
|
||||
Self.paymentsSwift.blockOnOutgoingVerification(paymentModel: paymentModel).asVoid()
|
||||
}.timeout(seconds: blockInterval, description: "Payments Verify Submission") {
|
||||
PaymentsError.timeout
|
||||
}.recover(on: .global()) { (error: Error) -> Guarantee<()> in
|
||||
|
||||
@ -596,78 +596,6 @@ class MobileCoinAPI {
|
||||
}
|
||||
}
|
||||
|
||||
// private func defragmentIfNecessary(forPaymentAmount paymentAmount: TSPaymentAmount) -> Promise<Void> {
|
||||
// Logger.verbose("")
|
||||
//
|
||||
// let client = self.client
|
||||
//
|
||||
// return firstly(on: .global()) { () throws -> Promise<Void> in
|
||||
// let result = client.requiresDefragmentation(toSendAmount: paymentAmount.picoMob, feeLevel: Self.feeLevel)
|
||||
// switch result {
|
||||
// case .success(let shouldDefragment):
|
||||
// guard shouldDefragment else {
|
||||
// return Promise.value(())
|
||||
// }
|
||||
// return self.defragment(forPaymentAmount: paymentAmount)
|
||||
// case .failure(let error):
|
||||
// let error = Self.convertMCError(error: error)
|
||||
// throw error
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private func defragment(forPaymentAmount paymentAmount: TSPaymentAmount) -> Promise<Void> {
|
||||
// Logger.verbose("")
|
||||
//
|
||||
// let client = self.client
|
||||
//
|
||||
// return firstly(on: .global()) { () throws -> Promise<[MobileCoin.Transaction]> in
|
||||
// let (promise, resolver) = Promise<[MobileCoin.Transaction]>.pending()
|
||||
// if DebugFlags.paymentsNoRequestsComplete.get() {
|
||||
// // Never resolve.
|
||||
// return promise
|
||||
// }
|
||||
// client.prepareDefragmentationStepTransactions(toSendAmount: paymentAmount.picoMob,
|
||||
// feeLevel: Self.feeLevel) { (result: Swift.Result<[MobileCoin.Transaction],
|
||||
// MobileCoin.TransactionPreparationError>) in
|
||||
// switch result {
|
||||
// case .success(let transactions):
|
||||
// resolver.fulfill(transactions)
|
||||
// break
|
||||
// case .failure(let error):
|
||||
// let error = Self.convertMCError(error: error)
|
||||
// resolver.reject(error)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// return promise
|
||||
// }.then(on: .global()) { (transactions: [MobileCoin.Transaction]) -> Promise<Void> in
|
||||
// // 1. Prepare defragmentation transactions.
|
||||
// // 2. Record defragmentation transactions in database.
|
||||
// // 3. Notify linked devices of possible defragmentation transactions.
|
||||
// // 4. Submit defragmentation transactions.
|
||||
// // 5. Verify defragmentation transactions.
|
||||
// return firstly(on: .global()) { () throws -> Promise<Void> in
|
||||
// let submissionPromises = transactions.map { transaction in
|
||||
// self.submitTransaction(transaction: transaction)
|
||||
// }
|
||||
// return when(fulfilled: submissionPromises)
|
||||
// }.then(on: .global()) { () -> Promise<Void> in
|
||||
// let submissionPromises = transactions.map { transaction in
|
||||
// self.getOutgoingTransactionStatus(transaction: transaction)
|
||||
// }
|
||||
// return when(fulfilled: submissionPromises)
|
||||
// }.then(on: .global()) { (transactionStatuses: [MCOutgoingTransactionStatus]) -> Promise<Void> in
|
||||
// }
|
||||
//// func getOutgoingTransactionStatus(transaction: MobileCoin.Transaction) -> Promise<MCOutgoingTransactionStatus> {
|
||||
// }
|
||||
// }
|
||||
// public func prepareDefragmentationStepTransactions(
|
||||
// toSendAmount amount: UInt64,
|
||||
// feeLevel: FeeLevel = .minimum,
|
||||
// completion: @escaping (Result<[Transaction], TransactionPreparationError>) -> Void
|
||||
// ) {
|
||||
|
||||
func submitTransaction(transaction: MobileCoin.Transaction) -> Promise<Void> {
|
||||
Logger.verbose("")
|
||||
|
||||
@ -982,7 +910,8 @@ public extension PaymentsError {
|
||||
.missingModel,
|
||||
.defragmentationRequired,
|
||||
.invalidTransaction,
|
||||
.inputsAlreadySpent:
|
||||
.inputsAlreadySpent,
|
||||
.defragmentationFailed:
|
||||
return false
|
||||
case .connectionFailure,
|
||||
.timeout:
|
||||
|
||||
@ -478,27 +478,27 @@ public class PaymentsImpl: NSObject, PaymentsSwift {
|
||||
return [
|
||||
// iPhone 11 Pro Max Simulator
|
||||
try! DevDevice(phoneNumber: "+441752395464",
|
||||
fakeRootEntropy: MobileCoinAPI.rootEntropy1),
|
||||
fakeRootEntropy: MobileCoinAPI.rootEntropy1)
|
||||
|
||||
// iPhone Xs Simulator
|
||||
try! DevDevice(phoneNumber: "+14503002620",
|
||||
fakeRootEntropy: MobileCoinAPI.rootEntropy2),
|
||||
|
||||
// iPhone Xr Simulator
|
||||
try! DevDevice(phoneNumber: "+13602090656",
|
||||
fakeRootEntropy: MobileCoinAPI.rootEntropy3),
|
||||
|
||||
// iPhone XS Max Device
|
||||
try! DevDevice(phoneNumber: "+12262864592",
|
||||
fakeRootEntropy: MobileCoinAPI.rootEntropy4),
|
||||
|
||||
// iPhone 12 mini Device
|
||||
try! DevDevice(phoneNumber: "+12534877762",
|
||||
fakeRootEntropy: MobileCoinAPI.rootEntropy5),
|
||||
|
||||
// iPhone 11 Pro Simulator
|
||||
try! DevDevice(phoneNumber: "+15092608677",
|
||||
fakeRootEntropy: MobileCoinAPI.rootEntropy6)
|
||||
// // iPhone Xs Simulator
|
||||
// try! DevDevice(phoneNumber: "+14503002620",
|
||||
// fakeRootEntropy: MobileCoinAPI.rootEntropy2),
|
||||
//
|
||||
// // iPhone Xr Simulator
|
||||
// try! DevDevice(phoneNumber: "+13602090656",
|
||||
// fakeRootEntropy: MobileCoinAPI.rootEntropy3),
|
||||
//
|
||||
// // iPhone XS Max Device
|
||||
// try! DevDevice(phoneNumber: "+12262864592",
|
||||
// fakeRootEntropy: MobileCoinAPI.rootEntropy4),
|
||||
//
|
||||
// // iPhone 12 mini Device
|
||||
// try! DevDevice(phoneNumber: "+12534877762",
|
||||
// fakeRootEntropy: MobileCoinAPI.rootEntropy5),
|
||||
//
|
||||
// // iPhone 11 Pro Simulator
|
||||
// try! DevDevice(phoneNumber: "+15092608677",
|
||||
// fakeRootEntropy: MobileCoinAPI.rootEntropy6)
|
||||
]
|
||||
}
|
||||
|
||||
@ -935,27 +935,6 @@ public extension PaymentsImpl {
|
||||
// 6. Block defragmentation transactions.
|
||||
return firstly(on: .global()) { () throws -> Promise<[MobileCoin.Transaction]> in
|
||||
mobileCoinAPI.prepareDefragmentationStepTransactions(forPaymentAmount: paymentAmount)
|
||||
// {
|
||||
//
|
||||
// let (promise, resolver) = Promise<[MobileCoin.Transaction]>.pending()
|
||||
// if DebugFlags.paymentsNoRequestsComplete.get() {
|
||||
// // Never resolve.
|
||||
// return promise
|
||||
// }
|
||||
// client.prepareDefragmentationStepTransactions(toSendAmount: paymentAmount.picoMob,
|
||||
// feeLevel: Self.feeLevel) { (result: Swift.Result<[MobileCoin.Transaction],
|
||||
// MobileCoin.TransactionPreparationError>) in
|
||||
// switch result {
|
||||
// case .success(let transactions):
|
||||
// resolver.fulfill(transactions)
|
||||
// break
|
||||
// case .failure(let error):
|
||||
// let error = Self.convertMCError(error: error)
|
||||
// resolver.reject(error)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// return promise
|
||||
}.map(on: .global()) { (mcTransactions: [MobileCoin.Transaction]) -> [TSPaymentModel] in
|
||||
// To initiate the defragmentation transactions, all we need to do
|
||||
// is save TSPaymentModels to the database. The PaymentsProcessor
|
||||
@ -1009,7 +988,13 @@ public extension PaymentsImpl {
|
||||
|
||||
return firstly(on: .global()) { () -> Promise<Void> in
|
||||
let promises = paymentModels.map { paymentModel in
|
||||
self.blockOnOutgoingVerification(paymentModel: paymentModel)
|
||||
firstly(on: .global()) { () -> Promise<Bool> in
|
||||
self.blockOnOutgoingVerification(paymentModel: paymentModel)
|
||||
}.map(on: .global()) { (didSucceed: Bool) -> Void in
|
||||
guard didSucceed else {
|
||||
throw PaymentsError.defragmentationFailed
|
||||
}
|
||||
}
|
||||
}
|
||||
return when(fulfilled: promises)
|
||||
}.timeout(seconds: maxBlockInterval, description: "blockOnVerificationOfDefragmentation") { () -> Error in
|
||||
@ -1017,8 +1002,8 @@ public extension PaymentsImpl {
|
||||
}
|
||||
}
|
||||
|
||||
func blockOnOutgoingVerification(paymentModel: TSPaymentModel) -> Promise<Void> {
|
||||
firstly(on: .global()) { () -> Promise<Void> in
|
||||
func blockOnOutgoingVerification(paymentModel: TSPaymentModel) -> Promise<Bool> {
|
||||
firstly(on: .global()) { () -> Promise<Bool> in
|
||||
let paymentModelLatest = Self.databaseStorage.read { transaction in
|
||||
TSPaymentModel.anyFetch(uniqueId: paymentModel.uniqueId,
|
||||
transaction: transaction)
|
||||
@ -1032,7 +1017,7 @@ public extension PaymentsImpl {
|
||||
// Not yet verified, wait then try again.
|
||||
return firstly(on: .global()) {
|
||||
after(seconds: 0.05)
|
||||
}.then(on: .global()) { () -> Promise<Void> in
|
||||
}.then(on: .global()) { () -> Promise<Bool> in
|
||||
// Recurse.
|
||||
self.blockOnOutgoingVerification(paymentModel: paymentModel)
|
||||
}
|
||||
@ -1040,10 +1025,12 @@ public extension PaymentsImpl {
|
||||
.outgoingSending,
|
||||
.outgoingSent,
|
||||
.outgoingMissingLedgerTimestamp,
|
||||
.outgoingComplete,
|
||||
.outgoingFailed:
|
||||
// Success: Verified or failed.
|
||||
return Promise.value(())
|
||||
.outgoingComplete:
|
||||
// Success: Verified.
|
||||
return Promise.value(true)
|
||||
case .outgoingFailed:
|
||||
// Success: Failed.
|
||||
return Promise.value(false)
|
||||
case .incomingUnverified,
|
||||
.incomingVerified,
|
||||
.incomingMissingLedgerTimestamp,
|
||||
|
||||
@ -542,7 +542,8 @@ private class PaymentProcessingOperation: OWSOperation {
|
||||
.connectionFailure,
|
||||
.timeout,
|
||||
.invalidTransaction,
|
||||
.inputsAlreadySpent:
|
||||
.inputsAlreadySpent,
|
||||
.defragmentationFailed:
|
||||
owsFailDebugUnlessMCNetworkFailure(error)
|
||||
case .authorizationFailure:
|
||||
owsFailDebugUnlessMCNetworkFailure(error)
|
||||
@ -595,7 +596,8 @@ private class PaymentProcessingOperation: OWSOperation {
|
||||
.serializationError,
|
||||
.missingModel,
|
||||
.invalidTransaction,
|
||||
.inputsAlreadySpent:
|
||||
.inputsAlreadySpent,
|
||||
.defragmentationFailed:
|
||||
// Do not retry these errors.
|
||||
delegate?.endProcessing(paymentId: self.paymentId)
|
||||
case .serverRateLimited:
|
||||
@ -828,9 +830,10 @@ private class PaymentProcessingOperation: OWSOperation {
|
||||
// If we've verified a payment, our balance may have changed.
|
||||
Self.payments.updateCurrentPaymentBalance()
|
||||
case .failed:
|
||||
try paymentModel.updatePaymentModelState(fromState: .outgoingUnverified,
|
||||
toState: .outgoingFailed,
|
||||
transaction: transaction)
|
||||
Self.markAsFailed(paymentModel: paymentModel,
|
||||
paymentFailure: .validationFailed,
|
||||
paymentState: .outgoingFailed,
|
||||
transaction: transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ public enum PaymentsError: Error {
|
||||
case defragmentationRequired
|
||||
case invalidTransaction
|
||||
case inputsAlreadySpent
|
||||
case defragmentationFailed
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
@ -150,7 +151,7 @@ public protocol PaymentsSwift: Payments {
|
||||
|
||||
var allPossiblePassphraseWords: [String] { get }
|
||||
|
||||
func blockOnOutgoingVerification(paymentModel: TSPaymentModel) -> Promise<Void>
|
||||
func blockOnOutgoingVerification(paymentModel: TSPaymentModel) -> Promise<Bool>
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
@ -476,7 +477,7 @@ extension MockPayments: PaymentsSwift {
|
||||
owsFail("Not implemented.")
|
||||
}
|
||||
|
||||
public func blockOnOutgoingVerification(paymentModel: TSPaymentModel) -> Promise<Void> {
|
||||
public func blockOnOutgoingVerification(paymentModel: TSPaymentModel) -> Promise<Bool> {
|
||||
owsFail("Not implemented.")
|
||||
}
|
||||
}
|
||||
|
||||
@ -437,12 +437,12 @@ extension TSPaymentModel: TSPaymentBaseModel {
|
||||
isValid = false
|
||||
}
|
||||
|
||||
let shouldHaveMCRecipientPublicAddressData = isOutgoing && (isIdentifiedPayment || isOutgoingTransfer)
|
||||
let shouldHaveMCRecipientPublicAddressData = isOutgoing && (isIdentifiedPayment || isOutgoingTransfer) && !isFailed
|
||||
if shouldHaveMCRecipientPublicAddressData, mcRecipientPublicAddressData == nil {
|
||||
owsFailDebug("Missing mcRecipientPublicAddressData: \(formattedState).")
|
||||
}
|
||||
|
||||
let shouldHaveMCTransaction = isOutgoing && !isUnidentified
|
||||
let shouldHaveMCTransaction = isOutgoing && !isUnidentified && !isFailed
|
||||
if shouldHaveMCTransaction, mcTransactionData == nil {
|
||||
owsFailDebug("Missing mcTransactionData: \(formattedState).")
|
||||
}
|
||||
@ -471,7 +471,7 @@ extension TSPaymentModel: TSPaymentBaseModel {
|
||||
isValid = false
|
||||
}
|
||||
|
||||
let shouldHaveMCSpentKeyImages = isOutgoing
|
||||
let shouldHaveMCSpentKeyImages = isOutgoing && !isFailed
|
||||
if shouldHaveMCSpentKeyImages,
|
||||
mcSpentKeyImages == nil {
|
||||
owsFailDebug("Missing mcSpentKeyImages: \(formattedState).")
|
||||
@ -482,8 +482,8 @@ extension TSPaymentModel: TSPaymentBaseModel {
|
||||
isValid = false
|
||||
}
|
||||
|
||||
let canHaveMCOutputPublicKeys = isOutgoing
|
||||
let shouldHaveMCOutputPublicKeys = isOutgoing && !isUnidentified
|
||||
let canHaveMCOutputPublicKeys = isOutgoing && !isFailed
|
||||
let shouldHaveMCOutputPublicKeys = isOutgoing && !isUnidentified && !isFailed
|
||||
if shouldHaveMCOutputPublicKeys,
|
||||
mcOutputPublicKeys == nil {
|
||||
owsFailDebug("Missing mcOutputPublicKeys: \(formattedState).")
|
||||
@ -494,20 +494,31 @@ extension TSPaymentModel: TSPaymentBaseModel {
|
||||
isValid = false
|
||||
}
|
||||
|
||||
let shouldHaveMCLedgerBlockTimestamp = isComplete && !isUnidentified
|
||||
let shouldHaveMCLedgerBlockTimestamp = isComplete && !isUnidentified && !isFailed
|
||||
if shouldHaveMCLedgerBlockTimestamp,
|
||||
!hasMCLedgerBlockTimestamp {
|
||||
// For some payments, we'll never be able to fill in the block timestamp.
|
||||
Logger.warn("Missing mcLedgerBlockTimestamp: \(formattedState).")
|
||||
}
|
||||
|
||||
let shouldHaveMCLedgerBlockIndex = isVerified || isUnidentified
|
||||
let shouldHaveMCLedgerBlockIndex = isVerified || isUnidentified && !isFailed
|
||||
if shouldHaveMCLedgerBlockIndex,
|
||||
!hasMCLedgerBlockIndex {
|
||||
owsFailDebug("Missing mcLedgerBlockIndex: \(formattedState).")
|
||||
isValid = false
|
||||
}
|
||||
|
||||
let shouldHaveMobileCoin = !isFailed
|
||||
if shouldHaveMobileCoin,
|
||||
mobileCoin == nil {
|
||||
owsFailDebug("Missing mobileCoin: \(formattedState).")
|
||||
isValid = false
|
||||
} else if !shouldHaveMobileCoin,
|
||||
mobileCoin != nil {
|
||||
owsFailDebug("Unexpected mobileCoin: \(formattedState).")
|
||||
isValid = false
|
||||
}
|
||||
|
||||
return isValid
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user