Update MC SDK.
This commit is contained in:
parent
d8078132fe
commit
e10e9ee9fb
4
Podfile
4
Podfile
@ -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
|
||||
|
||||
17
Podfile.lock
17
Podfile.lock
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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.")
|
||||
|
||||
@ -30,6 +30,9 @@ public enum PaymentsError: Error {
|
||||
case verificationStatusUnknown
|
||||
case ledgerBlockTimestampUnknown
|
||||
case missingModel
|
||||
case defragmentationRequired
|
||||
case invalidTransaction
|
||||
case inputsAlreadySpent
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
Loading…
Reference in New Issue
Block a user