Update MC SDK.

This commit is contained in:
Matthew Chen 2021-03-12 09:59:22 -03:00
parent d8078132fe
commit e10e9ee9fb
6 changed files with 199 additions and 149 deletions

View File

@ -76,8 +76,8 @@ pod 'BonMot', inhibit_warnings: true
# For catalyst we need to be on master until 3.6.7 or later is released
pod 'ZXingObjC', git: 'https://github.com/zxingify/zxingify-objc.git', inhibit_warnings: true, binary: true
pod 'MobileCoin', git: 'https://github.com/mobilecoinofficial/MobileCoin-Swift.git'
# pod 'MobileCoin', path: '../MobileCoin-Swift'
# pod 'MobileCoin', git: 'https://github.com/mobilecoinofficial/MobileCoin-Swift.git'
pod 'MobileCoin', path: '../MobileCoin-Swift'
target 'Signal' do
project 'Signal.xcodeproj', 'Debug' => :debug, 'Release' => :release

View File

@ -234,7 +234,7 @@ DEPENDENCIES:
- libPhoneNumber-iOS (from `https://github.com/signalapp/libPhoneNumber-iOS`, branch `signal-master`)
- lottie-ios
- Mantle (from `https://github.com/signalapp/Mantle`, branch `signal-master`)
- MobileCoin (from `https://github.com/mobilecoinofficial/MobileCoin-Swift.git`)
- MobileCoin (from `../MobileCoin-Swift`)
- PromiseKit
- PureLayout
- Reachability
@ -313,7 +313,7 @@ EXTERNAL SOURCES:
:branch: signal-master
:git: https://github.com/signalapp/Mantle
MobileCoin:
:git: https://github.com/mobilecoinofficial/MobileCoin-Swift.git
:path: "../MobileCoin-Swift"
SignalArgon2:
:git: https://github.com/signalapp/Argon2.git
:submodules: true
@ -354,9 +354,16 @@ CHECKOUT OPTIONS:
Mantle:
:commit: e7e46253bb01ce39525d90aa69ed9e85e758bfc4
:git: https://github.com/signalapp/Mantle
<<<<<<< HEAD
MobileCoin:
:commit: deedb518af67817bfbf3c67e2cf3ab87c8463e85
:git: https://github.com/mobilecoinofficial/MobileCoin-Swift.git
||||||| parent of cfce6c84da... Update MC SDK.
MobileCoin:
:commit: 57b9da09203916b8bc67fc40a2b0dd06fb705df8
:git: https://github.com/mobilecoinofficial/MobileCoin-Swift.git
=======
>>>>>>> cfce6c84da... Update MC SDK.
SignalArgon2:
:commit: 6a37e32167500ff7830e337b726a1653061d86b2
:git: https://github.com/signalapp/Argon2.git
@ -435,6 +442,12 @@ SPEC CHECKSUMS:
ZKGroup: 15a08eed5870a44b9dd14af0824ac95f6eed7380
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
<<<<<<< HEAD
PODFILE CHECKSUM: b9765e2bc1ac90c71ecfb8a54c0afd8a1b7281ca
||||||| parent of cfce6c84da... Update MC SDK.
PODFILE CHECKSUM: 021a74040108d7fd42587934c29307c660065276
=======
PODFILE CHECKSUM: 932b6d2360c8f11321e233bd8238bcd05a3160f0
>>>>>>> cfce6c84da... Update MC SDK.
COCOAPODS: 1.9.2

View File

@ -162,8 +162,9 @@ class MobileCoinAPI {
let fogView: Attestation
let fogKeyImage: Attestation
let fogMerkleProof: Attestation
let fogIngest: Attestation
let fogReport: Attestation
// PAYMENTS TODO: What are the correct values?
static let CONSENSUS_PRODUCT_ID: UInt16 = 1
static let CONSENSUS_SECURITY_VERSION: UInt16 = 1
static let FOG_VIEW_PRODUCT_ID: UInt16 = 3
@ -172,9 +173,32 @@ class MobileCoinAPI {
static let FOG_LEDGER_SECURITY_VERSION: UInt16 = 1
static let FOG_INGEST_PRODUCT_ID: UInt16 = 4
static let FOG_INGEST_SECURITY_VERSION: UInt16 = 1
// PAYMENTS TODO: What are the correct values?
static let FOG_REPORT_PRODUCT_ID: UInt16 = 4
static let FOG_REPORT_SECURITY_VERSION: UInt16 = 1
static var allowedHardeningAdvisories: [String] { ["INTEL-SA-00334"] }
// PAYMENTS TODO: What are the correct values?
private static func buildAttestation(mrSigner: Data,
productId: UInt16,
minimumSecurityVersion: UInt16,
allowedConfigAdvisories: [String] = [],
allowedHardeningAdvisories: [String] = []) throws -> MobileCoin.Attestation {
let result = MobileCoin.Attestation.make(mrSigner: mrSigner,
productId: productId,
minimumSecurityVersion: minimumSecurityVersion,
allowedConfigAdvisories: allowedConfigAdvisories,
allowedHardeningAdvisories: allowedHardeningAdvisories)
switch result {
case .success(let attestation):
return attestation
case .failure(let error):
owsFailDebug("Error: \(error)")
throw error
}
}
static var signalStaging: OWSAttestationConfig {
// PAYMENTS TODO:
let phonyMrSigner = Data([
@ -182,32 +206,34 @@ class MobileCoinAPI {
121, 244, 252, 42, 166, 188, 185, 147, 1, 44, 60, 56, 108
])
// PAYMENTS TODO: What are the correct values?
do {
return OWSAttestationConfig(
consensus: try Attestation(
consensus: try buildAttestation(
mrSigner: phonyMrSigner,
productId: CONSENSUS_PRODUCT_ID,
minimumSecurityVersion: CONSENSUS_SECURITY_VERSION,
allowedHardeningAdvisories: allowedHardeningAdvisories),
fogView: try Attestation(
fogView: try buildAttestation(
mrSigner: phonyMrSigner,
productId: FOG_VIEW_PRODUCT_ID,
minimumSecurityVersion: CONSENSUS_SECURITY_VERSION,
minimumSecurityVersion: FOG_VIEW_SECURITY_VERSION,
allowedHardeningAdvisories: allowedHardeningAdvisories),
fogKeyImage: try Attestation(
fogKeyImage: try buildAttestation(
mrSigner: phonyMrSigner,
productId: FOG_LEDGER_PRODUCT_ID,
minimumSecurityVersion: CONSENSUS_SECURITY_VERSION,
minimumSecurityVersion: FOG_LEDGER_SECURITY_VERSION,
allowedHardeningAdvisories: allowedHardeningAdvisories),
fogMerkleProof: try Attestation(
fogMerkleProof: try buildAttestation(
mrSigner: phonyMrSigner,
productId: FOG_LEDGER_PRODUCT_ID,
minimumSecurityVersion: CONSENSUS_SECURITY_VERSION,
minimumSecurityVersion: FOG_INGEST_SECURITY_VERSION,
allowedHardeningAdvisories: allowedHardeningAdvisories),
fogIngest: try Attestation(
// TODO:
fogReport: try buildAttestation(
mrSigner: phonyMrSigner,
productId: FOG_INGEST_PRODUCT_ID,
minimumSecurityVersion: CONSENSUS_SECURITY_VERSION,
productId: FOG_REPORT_PRODUCT_ID,
minimumSecurityVersion: FOG_REPORT_SECURITY_VERSION,
allowedHardeningAdvisories: allowedHardeningAdvisories))
} catch {
owsFail("Invalid attestationConfig: \(error)")
@ -221,32 +247,34 @@ class MobileCoinAPI {
121, 244, 252, 42, 166, 188, 185, 147, 1, 44, 60, 56, 108
])
// PAYMENTS TODO: What are the correct values?
do {
return OWSAttestationConfig(
consensus: try Attestation(
consensus: try buildAttestation(
mrSigner: phonyMrSigner,
productId: CONSENSUS_PRODUCT_ID,
minimumSecurityVersion: CONSENSUS_SECURITY_VERSION,
allowedHardeningAdvisories: allowedHardeningAdvisories),
fogView: try Attestation(
fogView: try buildAttestation(
mrSigner: phonyMrSigner,
productId: FOG_VIEW_PRODUCT_ID,
minimumSecurityVersion: CONSENSUS_SECURITY_VERSION,
minimumSecurityVersion: FOG_VIEW_SECURITY_VERSION,
allowedHardeningAdvisories: allowedHardeningAdvisories),
fogKeyImage: try Attestation(
fogKeyImage: try buildAttestation(
mrSigner: phonyMrSigner,
productId: FOG_LEDGER_PRODUCT_ID,
minimumSecurityVersion: CONSENSUS_SECURITY_VERSION,
minimumSecurityVersion: FOG_LEDGER_SECURITY_VERSION,
allowedHardeningAdvisories: allowedHardeningAdvisories),
fogMerkleProof: try Attestation(
fogMerkleProof: try buildAttestation(
mrSigner: phonyMrSigner,
productId: FOG_LEDGER_PRODUCT_ID,
minimumSecurityVersion: CONSENSUS_SECURITY_VERSION,
minimumSecurityVersion: FOG_INGEST_SECURITY_VERSION,
allowedHardeningAdvisories: allowedHardeningAdvisories),
fogIngest: try Attestation(
// TODO:
fogReport: try buildAttestation(
mrSigner: phonyMrSigner,
productId: FOG_INGEST_PRODUCT_ID,
minimumSecurityVersion: CONSENSUS_SECURITY_VERSION,
productId: FOG_REPORT_PRODUCT_ID,
minimumSecurityVersion: FOG_REPORT_SECURITY_VERSION,
allowedHardeningAdvisories: allowedHardeningAdvisories))
} catch {
owsFail("Invalid attestationConfig: \(error)")
@ -289,37 +317,47 @@ class MobileCoinAPI {
fileprivate func buildClient(signalAuthorization: OWSAuthorization) throws -> MobileCoinClient {
let networkConfig = MobileCoinNetworkConfig.networkConfig(environment: environment)
let client: MobileCoinClient
let authorization: OWSAuthorization
let configResult: Swift.Result<MobileCoinClient.Config, InvalidInputError>
switch environment {
case .signalProduction, .signalStaging:
authorization = signalAuthorization
let attestationConfig = OWSAttestationConfig.attestationConfig(environment: environment)
let config = try MobileCoinClient.Config(consensusUrl: networkConfig.consensusUrl,
consensusAttestation: attestationConfig.consensus,
fogViewUrl: networkConfig.fogViewUrl,
fogViewAttestation: attestationConfig.fogView,
fogLedgerUrl: networkConfig.fogLedgerUrl,
fogKeyImageAttestation: attestationConfig.fogKeyImage,
fogMerkleProofAttestation: attestationConfig.fogMerkleProof,
fogIngestAttestation: attestationConfig.fogIngest)
client = try MobileCoinClient(accountKey: accountKey, config: config)
configResult = MobileCoinClient.Config.make(consensusUrl: networkConfig.consensusUrl,
consensusAttestation: attestationConfig.consensus,
fogViewUrl: networkConfig.fogViewUrl,
fogViewAttestation: attestationConfig.fogView,
fogLedgerUrl: networkConfig.fogLedgerUrl,
fogKeyImageAttestation: attestationConfig.fogKeyImage,
fogMerkleProofAttestation: attestationConfig.fogMerkleProof,
fogReportAttestation: attestationConfig.fogReport)
case .mobileCoinAlphaNet:
authorization = OWSAuthorization.mobileCoinAlpha
let config = try MobileCoinClient.Config(consensusUrl: networkConfig.consensusUrl,
fogViewUrl: networkConfig.fogViewUrl,
fogLedgerUrl: networkConfig.fogLedgerUrl)
client = try MobileCoinClient(accountKey: accountKey, config: config)
configResult = MobileCoinClient.Config.make(consensusUrl: networkConfig.consensusUrl,
fogViewUrl: networkConfig.fogViewUrl,
fogLedgerUrl: networkConfig.fogLedgerUrl)
case .mobileCoinMobileDev:
authorization = OWSAuthorization.mobileCoinMobileDev
let config = try MobileCoinClient.Config(consensusUrl: networkConfig.consensusUrl,
fogViewUrl: networkConfig.fogViewUrl,
fogLedgerUrl: networkConfig.fogLedgerUrl)
client = try MobileCoinClient(accountKey: accountKey, config: config)
configResult = MobileCoinClient.Config.make(consensusUrl: networkConfig.consensusUrl,
fogViewUrl: networkConfig.fogViewUrl,
fogLedgerUrl: networkConfig.fogLedgerUrl)
}
switch configResult {
case .success(let config):
let clientResult = MobileCoinClient.make(accountKey: accountKey, config: config)
switch clientResult {
case .success(let client):
client.setBasicAuthorization(username: authorization.username,
password: authorization.password)
return client
case .failure(let error):
owsFailDebug("Error: \(error)")
throw error
}
case .failure(let error):
owsFailDebug("Error: \(error)")
throw error
}
client.setBasicAuthorization(username: authorization.username,
password: authorization.password)
return client
}
}
@ -367,8 +405,8 @@ class MobileCoinAPI {
let fogReportId = ""
let result = MobileCoin.AccountKey.make(rootEntropy: rootEntropy,
fogReportUrl: networkConfig.fogReportUrl,
fogAuthoritySpki: fogAuthoritySpki,
fogReportId: fogReportId)
fogReportId: fogReportId,
fogAuthoritySpki: fogAuthoritySpki)
switch result {
case .success(let accountKey):
return accountKey
@ -420,65 +458,33 @@ class MobileCoinAPI {
}
}
func getMinimumFee(forPaymentAmount paymentAmount: TSPaymentAmount) -> Promise<TSPaymentAmount> {
func getEstimatedFee(forPaymentAmount paymentAmount: TSPaymentAmount) throws -> TSPaymentAmount {
Logger.verbose("")
let client = self.client
guard paymentAmount.isValidAmount(canBeEmpty: false) else {
throw OWSAssertionError("Invalid amount.")
}
return firstly(on: .global()) { () throws -> Promise<TSPaymentAmount> in
guard paymentAmount.currency == .mobileCoin else {
throw OWSAssertionError("Invalid currency.")
}
guard paymentAmount.picoMob > 0 else {
// We don't need to support amountPicoMobHigh.
//
// TODO: Are we always going to use _minimum_ fee?
let result = client.estimateTotalFee(toSendAmount: paymentAmount.picoMob,
feeLevel: .minimum)
switch result {
case .success(let feePicoMob):
let fee = TSPaymentAmount(currency: .mobileCoin, picoMob: feePicoMob)
guard fee.isValidAmount(canBeEmpty: false) else {
throw OWSAssertionError("Invalid amount.")
}
let (promise, resolver) = Promise<TSPaymentAmount>.pending()
if DebugFlags.paymentsNoRequestsComplete.get() {
// Never resolve.
return promise
}
// We don't need to support amountPicoMobHigh.
//
// TODO: Are we always going to use _minimum_ fee?
client.minimumFee(amount: paymentAmount.picoMob) { (result: Swift.Result<UInt64, ConnectionError>) in
switch result {
case .success(let feePicoMob):
let fee = TSPaymentAmount(currency: .mobileCoin, picoMob: feePicoMob)
guard paymentAmount.currency == .mobileCoin,
fee.currency == .mobileCoin else {
resolver.reject(OWSAssertionError("Invalid currency."))
return
}
guard paymentAmount.picoMob > 0,
fee.picoMob > 0 else {
resolver.reject(OWSAssertionError("Invalid amount."))
return
}
Logger.verbose("Success paymentAmount: \(paymentAmount), fee: \(fee), ")
resolver.fulfill(fee)
case .failure(let error):
let error = Self.convertMCError(error: error)
resolver.reject(error)
}
}
return promise
}.map(on: .global()) { (value: TSPaymentAmount) -> TSPaymentAmount in
Logger.verbose("Success: \(value)")
return value
}.recover(on: .global()) { (error: Error) -> Promise<TSPaymentAmount> in
Logger.verbose("Success paymentAmount: \(paymentAmount), fee: \(fee), ")
return fee
case .failure(let error):
let error = Self.convertMCError(error: error)
owsFailDebugUnlessMCNetworkFailure(error)
throw error
}.timeout(seconds: Self.timeoutDuration, description: "prepareTransaction") { () -> Error in
PaymentsError.timeout
}
}
func getEstimatedFee(forPaymentAmount paymentAmount: TSPaymentAmount) -> Promise<TSPaymentAmount> {
// TODO: Use proper SDK method when ready.
getMinimumFee(forPaymentAmount: paymentAmount)
}
struct PreparedTransaction {
let transaction: MobileCoin.Transaction
let receipt: MobileCoin.Receipt
@ -494,9 +500,9 @@ class MobileCoinAPI {
return firstly(on: .global()) { () throws -> Promise<TSPaymentAmount> in
// prepareTransaction() will fail if local balance is not yet known.
self.getLocalBalance()
}.then(on: .global()) { (balance: TSPaymentAmount) -> Promise<TSPaymentAmount> in
}.map(on: .global()) { (balance: TSPaymentAmount) -> TSPaymentAmount in
Logger.verbose("balance: \(balance.picoMob)")
return self.getMinimumFee(forPaymentAmount: paymentAmount)
return try self.getEstimatedFee(forPaymentAmount: paymentAmount)
}.then(on: .global()) { (feeAmount: TSPaymentAmount) -> Promise<PreparedTransaction> in
guard paymentAmount.isValidAmount(canBeEmpty: false) else {
throw OWSAssertionError("Invalid amount.")
@ -551,7 +557,7 @@ class MobileCoinAPI {
return promise
}
let client = self.client
client.submitTransaction(transaction) { (result: Swift.Result<Void, ConnectionError>) in
client.submitTransaction(transaction) { (result: Swift.Result<Void, TransactionSubmissionError>) in
switch result {
case .success:
resolver.fulfill(())
@ -581,7 +587,12 @@ class MobileCoinAPI {
}
let client = self.client
return firstly(on: .global()) { () throws -> Promise<MCOutgoingTransactionStatus> in
return firstly(on: .global()) { () throws -> Promise<TSPaymentAmount> in
// .status(of: transaction) requires an updated balance.
//
// TODO: We could improve perf when verifying multiple transactions by getting balance just once.
self.getLocalBalance()
}.then(on: .global()) { (_: TSPaymentAmount) -> Promise<MCOutgoingTransactionStatus> in
let (promise, resolver) = Promise<MCOutgoingTransactionStatus>.pending()
if DebugFlags.paymentsNoRequestsComplete.get() {
// Never resolve.
@ -638,38 +649,34 @@ class MobileCoinAPI {
let client = self.client
let localAccount = self.localAccount
return firstly(on: .global()) { () throws -> Promise<MCIncomingReceiptStatus> in
return firstly(on: .global()) { () throws -> Promise<TSPaymentAmount> in
// .status(of: receipt) requires an updated balance.
//
// TODO: We could improve perf when verifying multiple receipts by getting balance just once.
self.getLocalBalance()
}.map(on: .global()) { (_: TSPaymentAmount) -> MCIncomingReceiptStatus in
let paymentAmount: TSPaymentAmount
do {
paymentAmount = try Self.paymentAmount(forReceipt: receipt,
localAccount: localAccount)
} catch {
owsFailDebug("Error: \(error)")
return Promise.value(MCIncomingReceiptStatus(receiptStatus: .failed,
paymentAmount: .zeroMob,
txOutPublicKey: Data()))
return MCIncomingReceiptStatus(receiptStatus: .failed,
paymentAmount: .zeroMob,
txOutPublicKey: Data())
}
let txOutPublicKey: Data = receipt.txOutPublicKey
let (promise, resolver) = Promise<MCIncomingReceiptStatus>.pending()
if DebugFlags.paymentsNoRequestsComplete.get() {
// Never resolve.
return promise
let result = client.status(of: receipt)
switch result {
case .success(let receiptStatus):
return MCIncomingReceiptStatus(receiptStatus: receiptStatus,
paymentAmount: paymentAmount,
txOutPublicKey: txOutPublicKey)
case .failure(let error):
let error = Self.convertMCError(error: error)
throw error
}
client.status(of: receipt) { (result: Swift.Result<MobileCoin.ReceiptStatus, ReceiptStatusCheckError>) in
switch result {
case .success(let receiptStatus):
resolver.fulfill(MCIncomingReceiptStatus(receiptStatus: receiptStatus,
paymentAmount: paymentAmount,
txOutPublicKey: txOutPublicKey))
break
case .failure(let error):
let error = Self.convertMCError(error: error)
resolver.reject(error)
break
}
}
return promise
}.map(on: .global()) { (value: MCIncomingReceiptStatus) -> MCIncomingReceiptStatus in
Logger.verbose("Success: \(value)")
return value
@ -788,18 +795,31 @@ extension MobileCoinAPI {
case .insufficientBalance:
Logger.warn("Error: \(error)")
return PaymentsError.insufficientFunds
case .defragmentationRequired:
Logger.warn("Error: \(error)")
return PaymentsError.defragmentationRequired
case .connectionError(let connectionError):
// Recurse.
return convertMCError(error: connectionError)
}
case let error as MobileCoin.ReceiptStatusCheckError:
case let error as MobileCoin.TransactionSubmissionError:
switch error {
case .invalidReceipt(let invalidInputError):
// Recurse.
return convertMCError(error: invalidInputError)
case .connectionError(let connectionError):
// Recurse.
return convertMCError(error: connectionError)
case .invalidTransaction:
Logger.warn("Error: \(error)")
return PaymentsError.invalidTransaction
case .feeError:
Logger.warn("Error: \(error)")
return PaymentsError.invalidFee
case .tombstoneBlockTooFar:
Logger.warn("Error: \(error)")
// Map to .invalidTransaction
return PaymentsError.invalidTransaction
case .inputsAlreadySpent:
Logger.warn("Error: \(error)")
return PaymentsError.inputsAlreadySpent
}
default:
owsFailDebug("Unexpected error: \(error)")
@ -834,7 +854,10 @@ public extension PaymentsError {
.serializationError,
.verificationStatusUnknown,
.ledgerBlockTimestampUnknown,
.missingModel:
.missingModel,
.defragmentationRequired,
.invalidTransaction,
.inputsAlreadySpent:
return false
case .connectionFailure,
.timeout:

View File

@ -820,8 +820,8 @@ public extension PaymentsImpl {
return firstly(on: .global()) { () -> Promise<MobileCoinAPI> in
self.getMobileCoinAPI()
}.then(on: .global()) { (mobileCoinAPI: MobileCoinAPI) -> Promise<TSPaymentAmount> in
mobileCoinAPI.getEstimatedFee(forPaymentAmount: paymentAmount)
}.map(on: .global()) { (mobileCoinAPI: MobileCoinAPI) -> TSPaymentAmount in
try mobileCoinAPI.getEstimatedFee(forPaymentAmount: paymentAmount)
}
}

View File

@ -542,7 +542,9 @@ private class PaymentProcessingOperation: OWSOperation {
.serializationError,
.missingModel,
.connectionFailure,
.timeout:
.timeout,
.invalidTransaction,
.inputsAlreadySpent:
owsFailDebugUnlessMCNetworkFailure(error)
case .authorizationFailure:
owsFailDebugUnlessMCNetworkFailure(error)
@ -553,6 +555,9 @@ private class PaymentProcessingOperation: OWSOperation {
.ledgerBlockTimestampUnknown:
// These errors are expected.
Logger.info("Error: \(error)")
case .defragmentationRequired:
// These errors are expected but should be very rare.
owsFailDebugUnlessMCNetworkFailure(error)
}
default:
owsFailDebugUnlessMCNetworkFailure(error)
@ -590,7 +595,9 @@ private class PaymentProcessingOperation: OWSOperation {
.attestationVerificationFailed,
.outdatedClient,
.serializationError,
.missingModel:
.missingModel,
.invalidTransaction,
.inputsAlreadySpent:
// Do not retry these errors.
delegate?.endProcessing(paymentId: self.paymentId)
case .serverRateLimited:
@ -633,6 +640,13 @@ private class PaymentProcessingOperation: OWSOperation {
delegate?.scheduleRetryProcessing(paymentModel: paymentModel,
retryDelayInteral: retryDelayInteral,
nextRetryDelayInteral: nextRetryDelayInteral)
case .defragmentationRequired:
// Vanilla exponential backoff.
let retryDelayInteral = self.retryDelayInteral
let nextRetryDelayInteral = self.retryDelayInteral * 2
delegate?.scheduleRetryProcessing(paymentModel: paymentModel,
retryDelayInteral: retryDelayInteral,
nextRetryDelayInteral: nextRetryDelayInteral)
}
default:
// Do not retry assertion errors.
@ -757,19 +771,20 @@ private class PaymentProcessingOperation: OWSOperation {
Self.payments.getMobileCoinAPI()
}.then(on: .global()) { (mobileCoinAPI: MobileCoinAPI) -> Promise<Void> in
return mobileCoinAPI.submitTransaction(transaction: transaction)
}.map(on: .global()) { _ in
try Self.databaseStorage.write { transaction in
try paymentModel.updatePaymentModelState(fromState: .outgoingUnsubmitted,
toState: .outgoingUnverified,
transaction: transaction)
}
}.then(on: .global()) { _ in
Self.updatePaymentStatePromise(paymentModel: paymentModel,
fromState: .outgoingUnsubmitted,
toState: .outgoingUnverified)
}.recover(on: .global()) { (error: Error) -> Promise<Void> in
// Payments TODO: We need to handle certain errors here.
// e.g. if we double-submit a transaction, it should become unverified,
// not stuck in unsubmitted.
//
// Other errors might make this failed?
throw error
if case PaymentsError.inputsAlreadySpent = error {
// e.g. if we double-submit a transaction, it should become unverified,
// not stuck in unsubmitted.
return Self.updatePaymentStatePromise(paymentModel: paymentModel,
fromState: .outgoingUnsubmitted,
toState: .outgoingUnverified)
} else {
throw error
}
}
}
@ -797,7 +812,7 @@ private class PaymentProcessingOperation: OWSOperation {
try Self.databaseStorage.write { transaction in
switch transactionStatus.transactionStatus {
case .unknown, .pending:
case .unknown:
throw PaymentsError.verificationStatusUnknown
case .accepted(let block):
if !paymentModel.hasMCLedgerBlockIndex {
@ -1091,10 +1106,6 @@ private class PaymentProcessingOperation: OWSOperation {
// This should never happen, since we've already verified the transaction.
owsFailDebug("Unexpected receiptStatus for verified transaction: .unknown")
throw PaymentsError.verificationStatusUnknown
case .pending:
// This should never happen, since we've already verified the transaction.
owsFailDebug("Unexpected receiptStatus for verified transaction: .pending")
throw PaymentsError.verificationStatusUnknown
case .accepted(let block):
guard let ledgerBlockDate = block.timestamp else {
Logger.warn("Could not fill in ledger timestamp for transaction.")

View File

@ -30,6 +30,9 @@ public enum PaymentsError: Error {
case verificationStatusUnknown
case ledgerBlockTimestampUnknown
case missingModel
case defragmentationRequired
case invalidTransaction
case inputsAlreadySpent
}
// MARK: -