Refactor code to build on top of Proposal API

This commit is contained in:
Francisco Gindre 2024-02-22 21:00:16 -03:00
parent 41841c9458
commit a9e8a40267
No known key found for this signature in database
GPG Key ID: 6B61CD8DAA2862B4
8 changed files with 108 additions and 90 deletions

View File

@ -35,19 +35,39 @@ public protocol CombineSynchronizer {
func getUnifiedAddress(accountIndex: Int) -> SinglePublisher<UnifiedAddress, Error>
func getTransparentAddress(accountIndex: Int) -> SinglePublisher<TransparentAddress, Error>
@available(*, deprecated, message: "Upcoming SDK 2.1 will create multiple transactions at once for some recipients. use `proposeTransfer` instead")
func sendToAddress(
spendingKey: UnifiedSpendingKey,
zatoshi: Zatoshi,
toAddress: Recipient,
memo: Memo?
) -> SinglePublisher<ZcashTransaction.Overview, Error>
@available(*, deprecated, message: "Upcoming SDK 2.1 will create multiple transactions at once for some recipients. use `proposeShielding:` instead")
func shieldFunds(
spendingKey: UnifiedSpendingKey,
memo: Memo,
shieldingThreshold: Zatoshi
) -> SinglePublisher<ZcashTransaction.Overview, Error>
func proposeTransfer(
accountIndex: Int,
recipient: Recipient,
amount: Zatoshi,
memo: Memo?
) -> SinglePublisher<Proposal, Error>
func proposeShielding(
accountIndex: Int,
shieldingThreshold: Zatoshi,
memo: Memo
) -> SinglePublisher<Proposal, Error>
func proposefulfillingPaymentURI(
_ uri: String,
accountIndex: Int
) -> SinglePublisher<Proposal, Error>
var allTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
var sentTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
var receivedTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }

View File

@ -225,16 +225,16 @@ public protocol Synchronizer: AnyObject {
memo: Memo?
) async throws -> ZcashTransaction.Overview
/// Attempts to fulfill a [ZIP-321](https://zips.z.cash/zip-0321) payment URI using the given `UnifiedSpendingKey`
/// Attempts to propose fulfilling a [ZIP-321](https://zips.z.cash/zip-0321) payment URI using the given `accountIndex`
/// - Parameter uri: a valid ZIP-321 payment URI
/// - Parameter spendingKey: the `UnifiedSpendingKey` that allows spends to occur.
/// - Parameter accountIndex: the account index that allows spends to occur.
///
/// - NOTE: If `prepare()` hasn't already been called since creating of synchronizer instance or since the last wipe then this method throws
/// `SynchronizerErrors.notPrepared`.
func fulfillPaymentURI(
func proposefulfillingPaymentURI(
_ uri: String,
spendingKey: UnifiedSpendingKey
) async throws -> ZcashTransaction.Overview
accountIndex: Int
) async throws -> Proposal
/// Shields transparent funds from the given private key into the best shielded pool of the account associated to the given `UnifiedSpendingKey`.
/// - Parameter spendingKey: the `UnifiedSpendingKey` that allows to spend transparent funds

View File

@ -90,6 +90,18 @@ extension CombineSDKSynchronizer: CombineSynchronizer {
}
}
public func proposefulfillingPaymentURI(
_ uri: String,
accountIndex: Int
) -> SinglePublisher<Proposal, Error> {
AsyncToCombineGateway.executeThrowingAction() {
try await self.synchronizer.proposefulfillingPaymentURI(
uri,
accountIndex: accountIndex
)
}
}
public func createProposedTransactions(
proposal: Proposal,
spendingKey: UnifiedSpendingKey

View File

@ -291,14 +291,29 @@ public class SDKSynchronizer: Synchronizer {
) async throws -> Proposal? {
try throwIfUnprepared()
let proposal = try await transactionEncoder.proposeShielding(
return try await transactionEncoder.proposeShielding(
accountIndex: accountIndex,
shieldingThreshold: shieldingThreshold,
memoBytes: memo.asMemoBytes(),
transparentReceiver: transparentReceiver?.stringEncoded
)
}
return proposal
public func proposefulfillingPaymentURI(
_ uri: String,
accountIndex: Int
) async throws -> Proposal {
do {
try throwIfUnprepared()
return try await transactionEncoder.proposeFulfillingPaymentFromURI(
uri,
accountIndex: accountIndex
)
} catch ZcashError.rustCreateToAddress(let e) {
throw ZcashError.rustProposeTransferFromURI(e)
} catch {
throw error
}
}
public func createProposedTransactions(
@ -344,25 +359,6 @@ public class SDKSynchronizer: Synchronizer {
}
}
public func fulfillPaymentURI(_ uri: String, spendingKey: UnifiedSpendingKey) async throws -> ZcashTransaction.Overview {
do {
let transaction = try await transactionEncoder.createTransactionFromPaymentURI(
uri,
spendingKey: spendingKey
)
let encodedTransaction = try transaction.encodedTransaction()
try await transactionEncoder.submit(transaction: encodedTransaction)
return transaction
} catch ZcashError.rustCreateToAddress(let e) {
throw ZcashError.rustProposeTransferFromURI(e)
} catch {
throw error
}
}
public func sendToAddress(
spendingKey: UnifiedSpendingKey,
zatoshi: Zatoshi,

View File

@ -2,7 +2,7 @@
// TransactionEncoder.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 11/20/19.
// Created by Francisco Gindre on 2019-11-20.
//
import Foundation
@ -72,20 +72,20 @@ protocol TransactionEncoder {
spendingKey: UnifiedSpendingKey
) async throws -> [ZcashTransaction.Overview]
/// Creates a transaction to fulfill a [ZIP-321](https://zips.z.cash/zip-0321), throwing an exception whenever things are missing. When the provided wallet implementation
/// Creates a transaction proposal to fulfill a [ZIP-321](https://zips.z.cash/zip-0321), throwing an exception whenever things are missing. When the provided wallet implementation
/// doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using
/// double-bangs for things).
///
/// - Parameters:
/// - Parameter uri: a valid ZIP-321 payment URI.
/// - Parameter spendingKey: a `UnifiedSpendingKey` containing the spending key
/// - Parameter accountIndex: the index of the account the proposal should be made from.
/// - Throws:
/// - `walletTransEncoderCreateTransactionMissingSaplingParams` if the sapling parameters aren't downloaded.
/// - Some `ZcashError.rust*` if the creation of transaction fails.
func createTransactionFromPaymentURI(
func proposeFulfillingPaymentFromURI(
_ uri: String,
spendingKey: UnifiedSpendingKey
) async throws -> ZcashTransaction.Overview
accountIndex: Int
) async throws -> Proposal
/// submits a transaction to the Zcash peer-to-peer network.
/// - Parameter transaction: a transaction overview

View File

@ -87,39 +87,15 @@ class WalletTransactionEncoder: TransactionEncoder {
return Proposal(inner: proposal)
}
func createTransactionFromPaymentURI(
func proposeFulfillingPaymentFromURI(
_ uri: String,
spendingKey: UnifiedSpendingKey
) async throws -> ZcashTransaction.Overview {
let txId = try await createSpendFromPaymentURI(
uri,
spendingKey: spendingKey
)
logger.debug("transaction id: \(txId)")
return try await repository.find(rawID: txId)
}
func createSpendFromPaymentURI(
_ uri: String,
spendingKey: UnifiedSpendingKey
) async throws -> Data {
guard ensureParams(spend: self.spendParamsURL, output: self.outputParamsURL) else {
throw ZcashError.walletTransEncoderCreateTransactionMissingSaplingParams
}
// TODO: Expose the proposal in a way that enables querying its fee.
accountIndex: Int
) async throws -> Proposal {
let proposal = try await rustBackend.proposeTransferFromURI(
uri,
account: Int32(spendingKey.account)
account: Int32(accountIndex)
)
let txId = try await rustBackend.createProposedTransaction(
proposal: proposal,
usk: spendingKey
)
return txId
return Proposal(inner: proposal)
}
func createProposedTransactions(

View File

@ -101,7 +101,7 @@ class PaymentURIFulfillmentTests: ZcashTestCase {
sleep(1)
let sendExpectation = XCTestExpectation(description: "send expectation")
var pendingEntity: ZcashTransaction.Overview?
var proposal: ZcashTransaction.Overview?
/*
2. send transaction to recipient address
@ -111,24 +111,38 @@ class PaymentURIFulfillmentTests: ZcashTestCase {
let paymentURI = "zcash:\(Environment.testRecipientAddress)?amount=0.0002&memo=\(memo)&message=Thank%20you%20for%20your%20purchase&label=Your%20Purchase"
do {
let pendingTx = try await coordinator.synchronizer.fulfillPaymentURI(
let proposal = try await coordinator.synchronizer.proposefulfillingPaymentURI(
paymentURI,
spendingKey: self.coordinator.spendingKey
accountIndex: 0
)
pendingEntity = pendingTx
let transactions = try await coordinator.synchronizer.createProposedTransactions(
proposal: proposal,
spendingKey: coordinator.spendingKey
)
for try await tx in transactions {
switch tx {
case .grpcFailure(_, let error):
XCTFail("transaction failed to submit with error:\(error.localizedDescription)")
return
case .success(txId: let txId):
continue
case .submitFailure(txId: let txId, code: let code, description: let description):
XCTFail("transaction failed to submit with code: \(code) - description: \(description)")
return
case .notAttempted(txId: let txId):
XCTFail("transaction not attempted")
return
}
}
sendExpectation.fulfill()
} catch {
await handleError(error)
}
await fulfillment(of: [sendExpectation], timeout: 11)
await fulfillment(of: [sendExpectation], timeout: 13)
guard pendingEntity != nil else {
XCTFail("no pending transaction after sending")
try await coordinator.stop()
return
}
/**
3. getIncomingTransaction
@ -304,13 +318,13 @@ class PaymentURIFulfillmentTests: ZcashTestCase {
let paymentURI = "zcash:zecIsGreat17mg40levjezevuhdp5pqrd52zere7r7vrjgdwn5sj4xsqtm20euwahv9anxmwr3y3kmwuz8k55a?amount=0.0002&memo=\(memo)&message=Thank%20you%20for%20your%20purchase&label=Your%20Purchase"
do {
let _ = try await coordinator.synchronizer.fulfillPaymentURI(
let _ = try await coordinator.synchronizer.proposefulfillingPaymentURI(
paymentURI,
spendingKey: self.coordinator.spendingKey
accountIndex: 0
)
XCTFail("`fulfillPaymentURI` should have failed")
} catch ZcashError.rustCreateToAddress {
} catch ZcashError.rustProposeTransferFromURI {
XCTAssertTrue(true)
} catch {
XCTFail("Expected ZcashError.rustCreateToAddress but got \(error.localizedDescription)")

View File

@ -1493,27 +1493,27 @@ class SynchronizerMock: Synchronizer {
}
}
// MARK: - fulfillPaymentURI
// MARK: - proposefulfillingPaymentURI
var fulfillPaymentURISpendingKeyThrowableError: Error?
var fulfillPaymentURISpendingKeyCallsCount = 0
var fulfillPaymentURISpendingKeyCalled: Bool {
return fulfillPaymentURISpendingKeyCallsCount > 0
var proposefulfillingPaymentURIAccountIndexThrowableError: Error?
var proposefulfillingPaymentURIAccountIndexCallsCount = 0
var proposefulfillingPaymentURIAccountIndexCalled: Bool {
return proposefulfillingPaymentURIAccountIndexCallsCount > 0
}
var fulfillPaymentURISpendingKeyReceivedArguments: (uri: String, spendingKey: UnifiedSpendingKey)?
var fulfillPaymentURISpendingKeyReturnValue: ZcashTransaction.Overview!
var fulfillPaymentURISpendingKeyClosure: ((String, UnifiedSpendingKey) async throws -> ZcashTransaction.Overview)?
var proposefulfillingPaymentURIAccountIndexReceivedArguments: (uri: String, accountIndex: Int)?
var proposefulfillingPaymentURIAccountIndexReturnValue: Proposal!
var proposefulfillingPaymentURIAccountIndexClosure: ((String, Int) async throws -> Proposal)?
func fulfillPaymentURI(_ uri: String, spendingKey: UnifiedSpendingKey) async throws -> ZcashTransaction.Overview {
if let error = fulfillPaymentURISpendingKeyThrowableError {
func proposefulfillingPaymentURI(_ uri: String, accountIndex: Int) async throws -> Proposal {
if let error = proposefulfillingPaymentURIAccountIndexThrowableError {
throw error
}
fulfillPaymentURISpendingKeyCallsCount += 1
fulfillPaymentURISpendingKeyReceivedArguments = (uri: uri, spendingKey: spendingKey)
if let closure = fulfillPaymentURISpendingKeyClosure {
return try await closure(uri, spendingKey)
proposefulfillingPaymentURIAccountIndexCallsCount += 1
proposefulfillingPaymentURIAccountIndexReceivedArguments = (uri: uri, accountIndex: accountIndex)
if let closure = proposefulfillingPaymentURIAccountIndexClosure {
return try await closure(uri, accountIndex)
} else {
return fulfillPaymentURISpendingKeyReturnValue
return proposefulfillingPaymentURIAccountIndexReturnValue
}
}