Migrate to in-progress version of FFI backend 0.5.0

Includes:
- Exposed `WalletSummary`.
- Exposed transaction proposals.
- `ScanSummary` returned from `ZcashRustBackend.scanBlocks`.

Closes Electric-Coin-Company/zcash-swift-wallet-sdk#1259.
Closes Electric-Coin-Company/zcash-swift-wallet-sdk#1299.
This commit is contained in:
Jack Grigg 2024-01-27 03:07:18 +00:00
parent 06d2b6986f
commit ded20fe7b0
21 changed files with 1043 additions and 359 deletions

View File

@ -158,8 +158,7 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi", "location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : { "state" : {
"revision" : "514dcd7e6fbfa252bf36d9f00d6b98f465a70704", "revision" : "5f58e32ffae1b285cae1c01bbf350eaae31ffeb5"
"version" : "0.4.1"
} }
} }
], ],

View File

@ -104,8 +104,7 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi", "location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : { "state" : {
"revision" : "9bc5877ef6302e877922f79ebead52e50bce94fd", "revision" : "5f58e32ffae1b285cae1c01bbf350eaae31ffeb5"
"version" : "0.4.0"
} }
} }
], ],

View File

@ -16,7 +16,8 @@ let package = Package(
dependencies: [ dependencies: [
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.19.1"), .package(url: "https://github.com/grpc/grpc-swift.git", from: "1.19.1"),
.package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1"), .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1"),
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", from: "0.4.1") // Compiled from revision `d5bd88138610b15f9585fcba696bd1023e716fd8`.
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", revision: "5f58e32ffae1b285cae1c01bbf350eaae31ffeb5")
], ],
targets: [ targets: [
.target( .target(
@ -28,6 +29,7 @@ let package = Package(
], ],
exclude: [ exclude: [
"Modules/Service/GRPC/ProtoBuf/proto/compact_formats.proto", "Modules/Service/GRPC/ProtoBuf/proto/compact_formats.proto",
"Modules/Service/GRPC/ProtoBuf/proto/proposal.proto",
"Modules/Service/GRPC/ProtoBuf/proto/service.proto", "Modules/Service/GRPC/ProtoBuf/proto/service.proto",
"Error/Sourcery/" "Error/Sourcery/"
], ],

View File

@ -63,7 +63,7 @@ extension ScanAction: Action {
let incrementedProcessedHeight = processedHeight + BlockHeight(increment) let incrementedProcessedHeight = processedHeight + BlockHeight(increment)
await context.update(processedHeight: incrementedProcessedHeight) await context.update(processedHeight: incrementedProcessedHeight)
await self?.latestBlocksDataProvider.updateScannedData() await self?.latestBlocksDataProvider.updateScannedData()
// ScanAction is controlled locally so it must report back the updated scanned height // ScanAction is controlled locally so it must report back the updated scanned height
await context.update(lastScannedHeight: lastScannedHeight) await context.update(lastScannedHeight: lastScannedHeight)
} }
@ -74,7 +74,7 @@ extension ScanAction: Action {
// TODO: [#1353] Advanced progress reporting, https://github.com/Electric-Coin-Company/zcash-swift-wallet-sdk/issues/1353 // TODO: [#1353] Advanced progress reporting, https://github.com/Electric-Coin-Company/zcash-swift-wallet-sdk/issues/1353
if progressReportReducer == 0 { if progressReportReducer == 0 {
// report scan progress only if it's available // report scan progress only if it's available
if let scanProgress = try? await rustBackend.getScanProgress() { if let scanProgress = try? await rustBackend.getWalletSummary()?.scanProgress {
let progress = try scanProgress.progress() let progress = try scanProgress.progress()
logger.debug("progress: \(progress)") logger.debug("progress: \(progress)")
await didUpdate(.syncProgress(progress)) await didUpdate(.syncProgress(progress))

View File

@ -658,13 +658,8 @@ extension CompactBlockProcessor {
await send(event: .finished(lastScannedHeight)) await send(event: .finished(lastScannedHeight))
await context.update(state: .finished) await context.update(state: .finished)
let verifiedBalance = Zatoshi((try? await rustBackend.getVerifiedBalance(account: 0)) ?? 0) let walletSummary = try? await rustBackend.getWalletSummary()
let totalBalance = Zatoshi((try? await rustBackend.getBalance(account: 0)) ?? 0) await metrics.logCBPOverviewReport(logger, walletSummary: walletSummary)
let shieldedBalance = WalletBalance(
verified: verifiedBalance,
total: totalBalance
)
await metrics.logCBPOverviewReport(logger, shieldedBalance: shieldedBalance)
// If new blocks were mined during previous sync run the sync process again // If new blocks were mined during previous sync run the sync process again
if newerBlocksWereMinedDuringSync { if newerBlocksWereMinedDuringSync {

View File

@ -1,6 +1,6 @@
// //
// HandleSaplingParametersIfNeeded.swift // SaplingParametersHandler.swift
// //
// //
// Created by Lukáš Korba on 23.11.2022. // Created by Lukáš Korba on 23.11.2022.
// //
@ -28,11 +28,13 @@ extension SaplingParametersHandlerImpl: SaplingParametersHandler {
try Task.checkCancellation() try Task.checkCancellation()
do { do {
let totalShieldedBalance = try await rustBackend.getBalance(account: Int32(0)) let totalSaplingBalance =
try await rustBackend.getWalletSummary()?.accountBalances[0]?.saplingBalance.total().amount
?? 0
let totalTransparentBalance = try await rustBackend.getTransparentBalance(account: Int32(0)) let totalTransparentBalance = try await rustBackend.getTransparentBalance(account: Int32(0))
// Download Sapling parameters only if sapling funds are detected. // Download Sapling parameters only if sapling funds are detected.
guard totalShieldedBalance > 0 || totalTransparentBalance > 0 else { return } guard totalSaplingBalance > 0 || totalTransparentBalance > 0 else { return }
} catch { } catch {
// if sapling balance can't be detected of we fail to obtain the balance // if sapling balance can't be detected of we fail to obtain the balance
// for some reason we shall not proceed to download the parameters and // for some reason we shall not proceed to download the parameters and

View File

@ -51,9 +51,12 @@ extension BlockScannerImpl: BlockScanner {
let batchSize = UInt32(config.scanningBatchSize) let batchSize = UInt32(config.scanningBatchSize)
// TODO: [#1355] Do more with ScanSummary
// https://github.com/Electric-Coin-Company/zcash-swift-wallet-sdk/issues/1355
let scanSummary: ScanSummary
let scanStartTime = Date() let scanStartTime = Date()
do { do {
try await self.rustBackend.scanBlocks(fromHeight: Int32(startHeight), limit: batchSize) scanSummary = try await self.rustBackend.scanBlocks(fromHeight: Int32(startHeight), limit: batchSize)
} catch { } catch {
logger.debug("block scanning failed with error: \(String(describing: error))") logger.debug("block scanning failed with error: \(String(describing: error))")
throw error throw error
@ -61,10 +64,8 @@ extension BlockScannerImpl: BlockScanner {
let scanFinishTime = Date() let scanFinishTime = Date()
// TODO: [#1259] potential bug when rustBackend.scanBlocks scan less blocks than batchSize, lastScannedHeight = scanSummary.scannedRange.upperBound - 1
// https://github.com/zcash/ZcashLightClientKit/issues/1259
lastScannedHeight = startHeight + Int(batchSize) - 1
scannedNewBlocks = previousScannedHeight != lastScannedHeight scannedNewBlocks = previousScannedHeight != lastScannedHeight
if scannedNewBlocks { if scannedNewBlocks {
try await didScan(lastScannedHeight, batchSize) try await didScan(lastScannedHeight, batchSize)

View File

@ -121,11 +121,6 @@ public enum ZcashError: Equatable, Error {
/// - `rustError` contains error generated by the rust layer. /// - `rustError` contains error generated by the rust layer.
/// ZRUST0003 /// ZRUST0003
case rustDecryptAndStoreTransaction(_ rustError: String) case rustDecryptAndStoreTransaction(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.getBalance
/// - `account` is account passed to ZcashRustBackend.getBalance.
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0004
case rustGetBalance(_ account: Int, _ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.getCurrentAddress /// Error from rust layer when calling ZcashRustBackend.getCurrentAddress
/// - `rustError` contains error generated by the rust layer. /// - `rustError` contains error generated by the rust layer.
/// ZRUST0005 /// ZRUST0005
@ -153,11 +148,6 @@ public enum ZcashError: Equatable, Error {
/// - `rustError` contains error generated by the rust layer. /// - `rustError` contains error generated by the rust layer.
/// ZRUST0011 /// ZRUST0011
case rustGetTransparentBalance(_ account: Int, _ rustError: String) case rustGetTransparentBalance(_ account: Int, _ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.getVerifiedBalance
/// - `account` is account passed to ZcashRustBackend.getVerifiedBalance.
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0012
case rustGetVerifiedBalance(_ account: Int, _ rustError: String)
/// account parameter is lower than 0 when calling ZcashRustBackend.getVerifiedTransparentBalance /// account parameter is lower than 0 when calling ZcashRustBackend.getVerifiedTransparentBalance
/// - `account` is account passed to ZcashRustBackend.getVerifiedTransparentBalance. /// - `account` is account passed to ZcashRustBackend.getVerifiedTransparentBalance.
/// ZRUST0013 /// ZRUST0013
@ -297,10 +287,6 @@ public enum ZcashError: Equatable, Error {
/// Invalid transaction ID length when calling ZcashRustBackend.getMemo. txId must be 32 bytes. /// Invalid transaction ID length when calling ZcashRustBackend.getMemo. txId must be 32 bytes.
/// ZRUST0050 /// ZRUST0050
case rustGetMemoInvalidTxIdLength case rustGetMemoInvalidTxIdLength
/// Error from rust layer when calling ZcashRustBackend.getScanProgress
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0051
case rustGetScanProgress(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.fullyScannedHeight /// Error from rust layer when calling ZcashRustBackend.fullyScannedHeight
/// - `rustError` contains error generated by the rust layer. /// - `rustError` contains error generated by the rust layer.
/// ZRUST0052 /// ZRUST0052
@ -317,6 +303,10 @@ public enum ZcashError: Equatable, Error {
/// - `progress` value reported /// - `progress` value reported
/// ZRUST0055 /// ZRUST0055
case rustScanProgressOutOfRange(_ progress: String) case rustScanProgressOutOfRange(_ progress: String)
/// Error from rust layer when calling ZcashRustBackend.getWalletSummary
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0056
case rustGetWalletSummary(_ rustError: String)
/// SQLite query failed when fetching all accounts from the database. /// SQLite query failed when fetching all accounts from the database.
/// - `sqliteError` is error produced by SQLite library. /// - `sqliteError` is error produced by SQLite library.
/// ZADAO0001 /// ZADAO0001
@ -614,7 +604,6 @@ public enum ZcashError: Equatable, Error {
case .rustCreateAccount: return "Error from rust layer when calling ZcashRustBackend.createAccount" case .rustCreateAccount: return "Error from rust layer when calling ZcashRustBackend.createAccount"
case .rustCreateToAddress: return "Error from rust layer when calling ZcashRustBackend.createToAddress" case .rustCreateToAddress: return "Error from rust layer when calling ZcashRustBackend.createToAddress"
case .rustDecryptAndStoreTransaction: return "Error from rust layer when calling ZcashRustBackend.decryptAndStoreTransaction" case .rustDecryptAndStoreTransaction: return "Error from rust layer when calling ZcashRustBackend.decryptAndStoreTransaction"
case .rustGetBalance: return "Error from rust layer when calling ZcashRustBackend.getBalance"
case .rustGetCurrentAddress: return "Error from rust layer when calling ZcashRustBackend.getCurrentAddress" case .rustGetCurrentAddress: return "Error from rust layer when calling ZcashRustBackend.getCurrentAddress"
case .rustGetCurrentAddressInvalidAddress: return "Unified address generated by rust layer is invalid when calling ZcashRustBackend.getCurrentAddress" case .rustGetCurrentAddressInvalidAddress: return "Unified address generated by rust layer is invalid when calling ZcashRustBackend.getCurrentAddress"
case .rustGetNearestRewindHeight: return "Error from rust layer when calling ZcashRustBackend.getNearestRewindHeight" case .rustGetNearestRewindHeight: return "Error from rust layer when calling ZcashRustBackend.getNearestRewindHeight"
@ -622,7 +611,6 @@ public enum ZcashError: Equatable, Error {
case .rustGetNextAvailableAddressInvalidAddress: return "Unified address generated by rust layer is invalid when calling ZcashRustBackend.getNextAvailableAddress" case .rustGetNextAvailableAddressInvalidAddress: return "Unified address generated by rust layer is invalid when calling ZcashRustBackend.getNextAvailableAddress"
case .rustGetTransparentBalanceNegativeAccount: return "account parameter is lower than 0 when calling ZcashRustBackend.getTransparentBalance" case .rustGetTransparentBalanceNegativeAccount: return "account parameter is lower than 0 when calling ZcashRustBackend.getTransparentBalance"
case .rustGetTransparentBalance: return "Error from rust layer when calling ZcashRustBackend.getTransparentBalance" case .rustGetTransparentBalance: return "Error from rust layer when calling ZcashRustBackend.getTransparentBalance"
case .rustGetVerifiedBalance: return "Error from rust layer when calling ZcashRustBackend.getVerifiedBalance"
case .rustGetVerifiedTransparentBalanceNegativeAccount: return "account parameter is lower than 0 when calling ZcashRustBackend.getVerifiedTransparentBalance" case .rustGetVerifiedTransparentBalanceNegativeAccount: return "account parameter is lower than 0 when calling ZcashRustBackend.getVerifiedTransparentBalance"
case .rustGetVerifiedTransparentBalance: return "Error from rust layer when calling ZcashRustBackend.getVerifiedTransparentBalance" case .rustGetVerifiedTransparentBalance: return "Error from rust layer when calling ZcashRustBackend.getVerifiedTransparentBalance"
case .rustInitDataDb: return "Error from rust layer when calling ZcashRustBackend.initDataDb" case .rustInitDataDb: return "Error from rust layer when calling ZcashRustBackend.initDataDb"
@ -660,11 +648,11 @@ public enum ZcashError: Equatable, Error {
case .rustUpdateChainTip: return "Error from rust layer when calling ZcashRustBackend.updateChainTip" case .rustUpdateChainTip: return "Error from rust layer when calling ZcashRustBackend.updateChainTip"
case .rustSuggestScanRanges: return "Error from rust layer when calling ZcashRustBackend.suggestScanRanges" case .rustSuggestScanRanges: return "Error from rust layer when calling ZcashRustBackend.suggestScanRanges"
case .rustGetMemoInvalidTxIdLength: return "Invalid transaction ID length when calling ZcashRustBackend.getMemo. txId must be 32 bytes." case .rustGetMemoInvalidTxIdLength: return "Invalid transaction ID length when calling ZcashRustBackend.getMemo. txId must be 32 bytes."
case .rustGetScanProgress: return "Error from rust layer when calling ZcashRustBackend.getScanProgress"
case .rustFullyScannedHeight: return "Error from rust layer when calling ZcashRustBackend.fullyScannedHeight" case .rustFullyScannedHeight: return "Error from rust layer when calling ZcashRustBackend.fullyScannedHeight"
case .rustMaxScannedHeight: return "Error from rust layer when calling ZcashRustBackend.maxScannedHeight" case .rustMaxScannedHeight: return "Error from rust layer when calling ZcashRustBackend.maxScannedHeight"
case .rustLatestCachedBlockHeight: return "Error from rust layer when calling ZcashRustBackend.latestCachedBlockHeight" case .rustLatestCachedBlockHeight: return "Error from rust layer when calling ZcashRustBackend.latestCachedBlockHeight"
case .rustScanProgressOutOfRange: return "Rust layer's call ZcashRustBackend.getScanProgress returned values that after computation are outside of allowed range 0-100%." case .rustScanProgressOutOfRange: return "Rust layer's call ZcashRustBackend.getScanProgress returned values that after computation are outside of allowed range 0-100%."
case .rustGetWalletSummary: return "Error from rust layer when calling ZcashRustBackend.getWalletSummary"
case .accountDAOGetAll: return "SQLite query failed when fetching all accounts from the database." case .accountDAOGetAll: return "SQLite query failed when fetching all accounts from the database."
case .accountDAOGetAllCantDecode: return "Fetched accounts from SQLite but can't decode them." case .accountDAOGetAllCantDecode: return "Fetched accounts from SQLite but can't decode them."
case .accountDAOFindBy: return "SQLite query failed when seaching for accounts in the database." case .accountDAOFindBy: return "SQLite query failed when seaching for accounts in the database."
@ -785,7 +773,6 @@ public enum ZcashError: Equatable, Error {
case .rustCreateAccount: return .rustCreateAccount case .rustCreateAccount: return .rustCreateAccount
case .rustCreateToAddress: return .rustCreateToAddress case .rustCreateToAddress: return .rustCreateToAddress
case .rustDecryptAndStoreTransaction: return .rustDecryptAndStoreTransaction case .rustDecryptAndStoreTransaction: return .rustDecryptAndStoreTransaction
case .rustGetBalance: return .rustGetBalance
case .rustGetCurrentAddress: return .rustGetCurrentAddress case .rustGetCurrentAddress: return .rustGetCurrentAddress
case .rustGetCurrentAddressInvalidAddress: return .rustGetCurrentAddressInvalidAddress case .rustGetCurrentAddressInvalidAddress: return .rustGetCurrentAddressInvalidAddress
case .rustGetNearestRewindHeight: return .rustGetNearestRewindHeight case .rustGetNearestRewindHeight: return .rustGetNearestRewindHeight
@ -793,7 +780,6 @@ public enum ZcashError: Equatable, Error {
case .rustGetNextAvailableAddressInvalidAddress: return .rustGetNextAvailableAddressInvalidAddress case .rustGetNextAvailableAddressInvalidAddress: return .rustGetNextAvailableAddressInvalidAddress
case .rustGetTransparentBalanceNegativeAccount: return .rustGetTransparentBalanceNegativeAccount case .rustGetTransparentBalanceNegativeAccount: return .rustGetTransparentBalanceNegativeAccount
case .rustGetTransparentBalance: return .rustGetTransparentBalance case .rustGetTransparentBalance: return .rustGetTransparentBalance
case .rustGetVerifiedBalance: return .rustGetVerifiedBalance
case .rustGetVerifiedTransparentBalanceNegativeAccount: return .rustGetVerifiedTransparentBalanceNegativeAccount case .rustGetVerifiedTransparentBalanceNegativeAccount: return .rustGetVerifiedTransparentBalanceNegativeAccount
case .rustGetVerifiedTransparentBalance: return .rustGetVerifiedTransparentBalance case .rustGetVerifiedTransparentBalance: return .rustGetVerifiedTransparentBalance
case .rustInitDataDb: return .rustInitDataDb case .rustInitDataDb: return .rustInitDataDb
@ -831,11 +817,11 @@ public enum ZcashError: Equatable, Error {
case .rustUpdateChainTip: return .rustUpdateChainTip case .rustUpdateChainTip: return .rustUpdateChainTip
case .rustSuggestScanRanges: return .rustSuggestScanRanges case .rustSuggestScanRanges: return .rustSuggestScanRanges
case .rustGetMemoInvalidTxIdLength: return .rustGetMemoInvalidTxIdLength case .rustGetMemoInvalidTxIdLength: return .rustGetMemoInvalidTxIdLength
case .rustGetScanProgress: return .rustGetScanProgress
case .rustFullyScannedHeight: return .rustFullyScannedHeight case .rustFullyScannedHeight: return .rustFullyScannedHeight
case .rustMaxScannedHeight: return .rustMaxScannedHeight case .rustMaxScannedHeight: return .rustMaxScannedHeight
case .rustLatestCachedBlockHeight: return .rustLatestCachedBlockHeight case .rustLatestCachedBlockHeight: return .rustLatestCachedBlockHeight
case .rustScanProgressOutOfRange: return .rustScanProgressOutOfRange case .rustScanProgressOutOfRange: return .rustScanProgressOutOfRange
case .rustGetWalletSummary: return .rustGetWalletSummary
case .accountDAOGetAll: return .accountDAOGetAll case .accountDAOGetAll: return .accountDAOGetAll
case .accountDAOGetAllCantDecode: return .accountDAOGetAllCantDecode case .accountDAOGetAllCantDecode: return .accountDAOGetAllCantDecode
case .accountDAOFindBy: return .accountDAOFindBy case .accountDAOFindBy: return .accountDAOFindBy

View File

@ -71,8 +71,6 @@ public enum ZcashErrorCode: String {
case rustCreateToAddress = "ZRUST0002" case rustCreateToAddress = "ZRUST0002"
/// Error from rust layer when calling ZcashRustBackend.decryptAndStoreTransaction /// Error from rust layer when calling ZcashRustBackend.decryptAndStoreTransaction
case rustDecryptAndStoreTransaction = "ZRUST0003" case rustDecryptAndStoreTransaction = "ZRUST0003"
/// Error from rust layer when calling ZcashRustBackend.getBalance
case rustGetBalance = "ZRUST0004"
/// Error from rust layer when calling ZcashRustBackend.getCurrentAddress /// Error from rust layer when calling ZcashRustBackend.getCurrentAddress
case rustGetCurrentAddress = "ZRUST0005" case rustGetCurrentAddress = "ZRUST0005"
/// Unified address generated by rust layer is invalid when calling ZcashRustBackend.getCurrentAddress /// Unified address generated by rust layer is invalid when calling ZcashRustBackend.getCurrentAddress
@ -87,8 +85,6 @@ public enum ZcashErrorCode: String {
case rustGetTransparentBalanceNegativeAccount = "ZRUST0010" case rustGetTransparentBalanceNegativeAccount = "ZRUST0010"
/// Error from rust layer when calling ZcashRustBackend.getTransparentBalance /// Error from rust layer when calling ZcashRustBackend.getTransparentBalance
case rustGetTransparentBalance = "ZRUST0011" case rustGetTransparentBalance = "ZRUST0011"
/// Error from rust layer when calling ZcashRustBackend.getVerifiedBalance
case rustGetVerifiedBalance = "ZRUST0012"
/// account parameter is lower than 0 when calling ZcashRustBackend.getVerifiedTransparentBalance /// account parameter is lower than 0 when calling ZcashRustBackend.getVerifiedTransparentBalance
case rustGetVerifiedTransparentBalanceNegativeAccount = "ZRUST0013" case rustGetVerifiedTransparentBalanceNegativeAccount = "ZRUST0013"
/// Error from rust layer when calling ZcashRustBackend.getVerifiedTransparentBalance /// Error from rust layer when calling ZcashRustBackend.getVerifiedTransparentBalance
@ -163,8 +159,6 @@ public enum ZcashErrorCode: String {
case rustSuggestScanRanges = "ZRUST0049" case rustSuggestScanRanges = "ZRUST0049"
/// Invalid transaction ID length when calling ZcashRustBackend.getMemo. txId must be 32 bytes. /// Invalid transaction ID length when calling ZcashRustBackend.getMemo. txId must be 32 bytes.
case rustGetMemoInvalidTxIdLength = "ZRUST0050" case rustGetMemoInvalidTxIdLength = "ZRUST0050"
/// Error from rust layer when calling ZcashRustBackend.getScanProgress
case rustGetScanProgress = "ZRUST0051"
/// Error from rust layer when calling ZcashRustBackend.fullyScannedHeight /// Error from rust layer when calling ZcashRustBackend.fullyScannedHeight
case rustFullyScannedHeight = "ZRUST0052" case rustFullyScannedHeight = "ZRUST0052"
/// Error from rust layer when calling ZcashRustBackend.maxScannedHeight /// Error from rust layer when calling ZcashRustBackend.maxScannedHeight
@ -173,6 +167,8 @@ public enum ZcashErrorCode: String {
case rustLatestCachedBlockHeight = "ZRUST0054" case rustLatestCachedBlockHeight = "ZRUST0054"
/// Rust layer's call ZcashRustBackend.getScanProgress returned values that after computation are outside of allowed range 0-100%. /// Rust layer's call ZcashRustBackend.getScanProgress returned values that after computation are outside of allowed range 0-100%.
case rustScanProgressOutOfRange = "ZRUST0055" case rustScanProgressOutOfRange = "ZRUST0055"
/// Error from rust layer when calling ZcashRustBackend.getWalletSummary
case rustGetWalletSummary = "ZRUST0056"
/// SQLite query failed when fetching all accounts from the database. /// SQLite query failed when fetching all accounts from the database.
case accountDAOGetAll = "ZADAO0001" case accountDAOGetAll = "ZADAO0001"
/// Fetched accounts from SQLite but can't decode them. /// Fetched accounts from SQLite but can't decode them.

View File

@ -12,7 +12,7 @@ protocol SDKMetrics {
func actionStart(_ action: CBPState) func actionStart(_ action: CBPState)
func actionDetail(_ detail: String, `for` action: CBPState) func actionDetail(_ detail: String, `for` action: CBPState)
func actionStop() func actionStop()
func logCBPOverviewReport(_ logger: Logger, shieldedBalance: WalletBalance) async func logCBPOverviewReport(_ logger: Logger, walletSummary: WalletSummary?) async
} }
final class SDKMetricsImpl: SDKMetrics { final class SDKMetricsImpl: SDKMetrics {
@ -102,15 +102,16 @@ final class SDKMetricsImpl: SDKMetrics {
} }
// swiftlint:disable string_concatenation // swiftlint:disable string_concatenation
func logCBPOverviewReport(_ logger: Logger, shieldedBalance: WalletBalance) async { func logCBPOverviewReport(_ logger: Logger, walletSummary: WalletSummary?) async {
actionStop() actionStop()
let accountBalance = walletSummary?.accountBalances[0]
logger.sync( logger.sync(
""" """
SYNC (\(syncs)) REPORT SYNC (\(syncs)) REPORT
finished in: \(Date().timeIntervalSince1970 - cbpStartTime) finished in: \(Date().timeIntervalSince1970 - cbpStartTime)
verified balance: \(shieldedBalance.verified.amount) verified balance: \(accountBalance?.saplingBalance.spendableValue.amount ?? 0)
total balance: \(shieldedBalance.total.amount) total balance: \(accountBalance?.saplingBalance.total().amount ?? 0)
""" """
) )

View File

@ -0,0 +1,14 @@
//
// ScanSummary.swift
//
//
// Created by Jack Grigg on 26/01/2024.
//
import Foundation
struct ScanSummary: Equatable {
let scannedRange: Range<BlockHeight>
let spentSaplingNoteCount: UInt64
let receivedSaplingNoteCount: UInt64
}

View File

@ -1,12 +1,27 @@
// //
// ScanProgress.swift // WalletSummary.swift
// //
// //
// Created by Jack Grigg on 06/09/2023. // Created by Jack Grigg on 06/09/2023.
// //
import Foundation import Foundation
struct PoolBalance: Equatable {
let spendableValue: Zatoshi
let changePendingConfirmation: Zatoshi
let valuePendingSpendability: Zatoshi
func total() -> Zatoshi {
self.spendableValue + self.changePendingConfirmation + self.valuePendingSpendability
}
}
struct AccountBalance: Equatable {
let saplingBalance: PoolBalance
let unshielded: Zatoshi
}
struct ScanProgress: Equatable { struct ScanProgress: Equatable {
let numerator: UInt64 let numerator: UInt64
let denominator: UInt64 let denominator: UInt64
@ -27,3 +42,11 @@ struct ScanProgress: Equatable {
return value return value
} }
} }
struct WalletSummary: Equatable {
let accountBalances: [UInt32: AccountBalance]
let chainTipHeight: BlockHeight
let fullyScannedHeight: BlockHeight
let scanProgress: ScanProgress?
let nextSaplingSubtreeIndex: UInt32
}

View File

@ -0,0 +1,548 @@
// DO NOT EDIT.
// swift-format-ignore-file
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: proto/proposal.proto
//
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
// Copyright (c) 2023 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
enum FfiValuePool: SwiftProtobuf.Enum {
typealias RawValue = Int
/// Protobuf requires that enums have a zero discriminant as the default
/// value. However, we need to require that a known value pool is selected,
/// and we do not want to fall back to any default, so sending the
/// PoolNotSpecified value will be treated as an error.
case poolNotSpecified // = 0
/// The transparent value pool (P2SH is not distinguished from P2PKH)
case transparent // = 1
/// The Sapling value pool
case sapling // = 2
/// The Orchard value pool
case orchard // = 3
case UNRECOGNIZED(Int)
init() {
self = .poolNotSpecified
}
init?(rawValue: Int) {
switch rawValue {
case 0: self = .poolNotSpecified
case 1: self = .transparent
case 2: self = .sapling
case 3: self = .orchard
default: self = .UNRECOGNIZED(rawValue)
}
}
var rawValue: Int {
switch self {
case .poolNotSpecified: return 0
case .transparent: return 1
case .sapling: return 2
case .orchard: return 3
case .UNRECOGNIZED(let i): return i
}
}
}
#if swift(>=4.2)
extension FfiValuePool: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
static let allCases: [FfiValuePool] = [
.poolNotSpecified,
.transparent,
.sapling,
.orchard,
]
}
#endif // swift(>=4.2)
/// The fee rule used in constructing a Proposal
enum FfiFeeRule: SwiftProtobuf.Enum {
typealias RawValue = Int
/// Protobuf requires that enums have a zero discriminant as the default
/// value. However, we need to require that a known fee rule is selected,
/// and we do not want to fall back to any default, so sending the
/// FeeRuleNotSpecified value will be treated as an error.
case notSpecified // = 0
/// 10000 ZAT
case preZip313 // = 1
/// 1000 ZAT
case zip313 // = 2
/// MAX(10000, 5000 * logical_actions) ZAT
case zip317 // = 3
case UNRECOGNIZED(Int)
init() {
self = .notSpecified
}
init?(rawValue: Int) {
switch rawValue {
case 0: self = .notSpecified
case 1: self = .preZip313
case 2: self = .zip313
case 3: self = .zip317
default: self = .UNRECOGNIZED(rawValue)
}
}
var rawValue: Int {
switch self {
case .notSpecified: return 0
case .preZip313: return 1
case .zip313: return 2
case .zip317: return 3
case .UNRECOGNIZED(let i): return i
}
}
}
#if swift(>=4.2)
extension FfiFeeRule: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
static let allCases: [FfiFeeRule] = [
.notSpecified,
.preZip313,
.zip313,
.zip317,
]
}
#endif // swift(>=4.2)
/// A data structure that describes the inputs to be consumed and outputs to
/// be produced in a proposed transaction.
struct FfiProposal {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var protoVersion: UInt32 = 0
/// ZIP 321 serialized transaction request
var transactionRequest: String = String()
/// The anchor height to be used in creating the transaction, if any.
/// Setting the anchor height to zero will disallow the use of any shielded
/// inputs.
var anchorHeight: UInt32 = 0
/// The inputs to be used in creating the transaction.
var inputs: [FfiProposedInput] = []
/// The total value, fee value, and change outputs of the proposed
/// transaction
var balance: FfiTransactionBalance {
get {return _balance ?? FfiTransactionBalance()}
set {_balance = newValue}
}
/// Returns true if `balance` has been explicitly set.
var hasBalance: Bool {return self._balance != nil}
/// Clears the value of `balance`. Subsequent reads from it will return its default value.
mutating func clearBalance() {self._balance = nil}
/// The fee rule used in constructing this proposal
var feeRule: FfiFeeRule = .notSpecified
/// The target height for which the proposal was constructed
///
/// The chain must contain at least this many blocks in order for the proposal to
/// be executed.
var minTargetHeight: UInt32 = 0
/// A flag indicating whether the proposal is for a shielding transaction,
/// used for determining which OVK to select for wallet-internal outputs.
var isShielding: Bool = false
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _balance: FfiTransactionBalance? = nil
}
/// The unique identifier and value for each proposed input.
struct FfiProposedInput {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var txid: Data = Data()
var valuePool: FfiValuePool = .poolNotSpecified
var index: UInt32 = 0
var value: UInt64 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
/// The proposed change outputs and fee value.
struct FfiTransactionBalance {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var proposedChange: [FfiChangeValue] = []
var feeRequired: UInt64 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
/// A proposed change output. If the transparent value pool is selected,
/// the `memo` field must be null.
struct FfiChangeValue {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var value: UInt64 = 0
var valuePool: FfiValuePool = .poolNotSpecified
var memo: FfiMemoBytes {
get {return _memo ?? FfiMemoBytes()}
set {_memo = newValue}
}
/// Returns true if `memo` has been explicitly set.
var hasMemo: Bool {return self._memo != nil}
/// Clears the value of `memo`. Subsequent reads from it will return its default value.
mutating func clearMemo() {self._memo = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _memo: FfiMemoBytes? = nil
}
/// An object wrapper for memo bytes, to facilitate representing the
/// `change_memo == None` case.
struct FfiMemoBytes {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var value: Data = Data()
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension FfiValuePool: @unchecked Sendable {}
extension FfiFeeRule: @unchecked Sendable {}
extension FfiProposal: @unchecked Sendable {}
extension FfiProposedInput: @unchecked Sendable {}
extension FfiTransactionBalance: @unchecked Sendable {}
extension FfiChangeValue: @unchecked Sendable {}
extension FfiMemoBytes: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "cash.z.wallet.sdk.ffi"
extension FfiValuePool: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "PoolNotSpecified"),
1: .same(proto: "Transparent"),
2: .same(proto: "Sapling"),
3: .same(proto: "Orchard"),
]
}
extension FfiFeeRule: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "FeeRuleNotSpecified"),
1: .same(proto: "PreZip313"),
2: .same(proto: "Zip313"),
3: .same(proto: "Zip317"),
]
}
extension FfiProposal: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".Proposal"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "protoVersion"),
2: .same(proto: "transactionRequest"),
3: .same(proto: "anchorHeight"),
4: .same(proto: "inputs"),
5: .same(proto: "balance"),
6: .same(proto: "feeRule"),
7: .same(proto: "minTargetHeight"),
8: .same(proto: "isShielding"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularUInt32Field(value: &self.protoVersion) }()
case 2: try { try decoder.decodeSingularStringField(value: &self.transactionRequest) }()
case 3: try { try decoder.decodeSingularUInt32Field(value: &self.anchorHeight) }()
case 4: try { try decoder.decodeRepeatedMessageField(value: &self.inputs) }()
case 5: try { try decoder.decodeSingularMessageField(value: &self._balance) }()
case 6: try { try decoder.decodeSingularEnumField(value: &self.feeRule) }()
case 7: try { try decoder.decodeSingularUInt32Field(value: &self.minTargetHeight) }()
case 8: try { try decoder.decodeSingularBoolField(value: &self.isShielding) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if self.protoVersion != 0 {
try visitor.visitSingularUInt32Field(value: self.protoVersion, fieldNumber: 1)
}
if !self.transactionRequest.isEmpty {
try visitor.visitSingularStringField(value: self.transactionRequest, fieldNumber: 2)
}
if self.anchorHeight != 0 {
try visitor.visitSingularUInt32Field(value: self.anchorHeight, fieldNumber: 3)
}
if !self.inputs.isEmpty {
try visitor.visitRepeatedMessageField(value: self.inputs, fieldNumber: 4)
}
try { if let v = self._balance {
try visitor.visitSingularMessageField(value: v, fieldNumber: 5)
} }()
if self.feeRule != .notSpecified {
try visitor.visitSingularEnumField(value: self.feeRule, fieldNumber: 6)
}
if self.minTargetHeight != 0 {
try visitor.visitSingularUInt32Field(value: self.minTargetHeight, fieldNumber: 7)
}
if self.isShielding != false {
try visitor.visitSingularBoolField(value: self.isShielding, fieldNumber: 8)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: FfiProposal, rhs: FfiProposal) -> Bool {
if lhs.protoVersion != rhs.protoVersion {return false}
if lhs.transactionRequest != rhs.transactionRequest {return false}
if lhs.anchorHeight != rhs.anchorHeight {return false}
if lhs.inputs != rhs.inputs {return false}
if lhs._balance != rhs._balance {return false}
if lhs.feeRule != rhs.feeRule {return false}
if lhs.minTargetHeight != rhs.minTargetHeight {return false}
if lhs.isShielding != rhs.isShielding {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension FfiProposedInput: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".ProposedInput"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "txid"),
2: .same(proto: "valuePool"),
3: .same(proto: "index"),
4: .same(proto: "value"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularBytesField(value: &self.txid) }()
case 2: try { try decoder.decodeSingularEnumField(value: &self.valuePool) }()
case 3: try { try decoder.decodeSingularUInt32Field(value: &self.index) }()
case 4: try { try decoder.decodeSingularUInt64Field(value: &self.value) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.txid.isEmpty {
try visitor.visitSingularBytesField(value: self.txid, fieldNumber: 1)
}
if self.valuePool != .poolNotSpecified {
try visitor.visitSingularEnumField(value: self.valuePool, fieldNumber: 2)
}
if self.index != 0 {
try visitor.visitSingularUInt32Field(value: self.index, fieldNumber: 3)
}
if self.value != 0 {
try visitor.visitSingularUInt64Field(value: self.value, fieldNumber: 4)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: FfiProposedInput, rhs: FfiProposedInput) -> Bool {
if lhs.txid != rhs.txid {return false}
if lhs.valuePool != rhs.valuePool {return false}
if lhs.index != rhs.index {return false}
if lhs.value != rhs.value {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension FfiTransactionBalance: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".TransactionBalance"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "proposedChange"),
2: .same(proto: "feeRequired"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeRepeatedMessageField(value: &self.proposedChange) }()
case 2: try { try decoder.decodeSingularUInt64Field(value: &self.feeRequired) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.proposedChange.isEmpty {
try visitor.visitRepeatedMessageField(value: self.proposedChange, fieldNumber: 1)
}
if self.feeRequired != 0 {
try visitor.visitSingularUInt64Field(value: self.feeRequired, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: FfiTransactionBalance, rhs: FfiTransactionBalance) -> Bool {
if lhs.proposedChange != rhs.proposedChange {return false}
if lhs.feeRequired != rhs.feeRequired {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension FfiChangeValue: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".ChangeValue"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "value"),
2: .same(proto: "valuePool"),
3: .same(proto: "memo"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularUInt64Field(value: &self.value) }()
case 2: try { try decoder.decodeSingularEnumField(value: &self.valuePool) }()
case 3: try { try decoder.decodeSingularMessageField(value: &self._memo) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if self.value != 0 {
try visitor.visitSingularUInt64Field(value: self.value, fieldNumber: 1)
}
if self.valuePool != .poolNotSpecified {
try visitor.visitSingularEnumField(value: self.valuePool, fieldNumber: 2)
}
try { if let v = self._memo {
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
} }()
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: FfiChangeValue, rhs: FfiChangeValue) -> Bool {
if lhs.value != rhs.value {return false}
if lhs.valuePool != rhs.valuePool {return false}
if lhs._memo != rhs._memo {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension FfiMemoBytes: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".MemoBytes"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "value"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularBytesField(value: &self.value) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.value.isEmpty {
try visitor.visitSingularBytesField(value: self.value, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: FfiMemoBytes, rhs: FfiMemoBytes) -> Bool {
if lhs.value != rhs.value {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}

View File

@ -0,0 +1,91 @@
// Copyright (c) 2023 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
syntax = "proto3";
package cash.z.wallet.sdk.ffi;
option swift_prefix = "Ffi";
// A data structure that describes the inputs to be consumed and outputs to
// be produced in a proposed transaction.
message Proposal {
uint32 protoVersion = 1;
// ZIP 321 serialized transaction request
string transactionRequest = 2;
// The anchor height to be used in creating the transaction, if any.
// Setting the anchor height to zero will disallow the use of any shielded
// inputs.
uint32 anchorHeight = 3;
// The inputs to be used in creating the transaction.
repeated ProposedInput inputs = 4;
// The total value, fee value, and change outputs of the proposed
// transaction
TransactionBalance balance = 5;
// The fee rule used in constructing this proposal
FeeRule feeRule = 6;
// The target height for which the proposal was constructed
//
// The chain must contain at least this many blocks in order for the proposal to
// be executed.
uint32 minTargetHeight = 7;
// A flag indicating whether the proposal is for a shielding transaction,
// used for determining which OVK to select for wallet-internal outputs.
bool isShielding = 8;
}
enum ValuePool {
// Protobuf requires that enums have a zero discriminant as the default
// value. However, we need to require that a known value pool is selected,
// and we do not want to fall back to any default, so sending the
// PoolNotSpecified value will be treated as an error.
PoolNotSpecified = 0;
// The transparent value pool (P2SH is not distinguished from P2PKH)
Transparent = 1;
// The Sapling value pool
Sapling = 2;
// The Orchard value pool
Orchard = 3;
}
// The unique identifier and value for each proposed input.
message ProposedInput {
bytes txid = 1;
ValuePool valuePool = 2;
uint32 index = 3;
uint64 value = 4;
}
// The fee rule used in constructing a Proposal
enum FeeRule {
// Protobuf requires that enums have a zero discriminant as the default
// value. However, we need to require that a known fee rule is selected,
// and we do not want to fall back to any default, so sending the
// FeeRuleNotSpecified value will be treated as an error.
FeeRuleNotSpecified = 0;
// 10000 ZAT
PreZip313 = 1;
// 1000 ZAT
Zip313 = 2;
// MAX(10000, 5000 * logical_actions) ZAT
Zip317 = 3;
}
// The proposed change outputs and fee value.
message TransactionBalance {
repeated ChangeValue proposedChange = 1;
uint64 feeRequired = 2;
}
// A proposed change output. If the transparent value pool is selected,
// the `memo` field must be null.
message ChangeValue {
uint64 value = 1;
ValuePool valuePool = 2;
MemoBytes memo = 3;
}
// An object wrapper for memo bytes, to facilitate representing the
// `change_memo == None` case.
message MemoBytes {
bytes value = 1;
}

View File

@ -80,45 +80,36 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
return ffiBinaryKeyPtr.pointee.unsafeToUnifiedSpendingKey(network: networkType) return ffiBinaryKeyPtr.pointee.unsafeToUnifiedSpendingKey(network: networkType)
} }
func createToAddress( func proposeTransfer(
usk: UnifiedSpendingKey, account: Int32,
to address: String, to address: String,
value: Int64, value: Int64,
memo: MemoBytes? memo: MemoBytes?
) async throws -> Data { ) async throws -> FfiProposal {
var contiguousTxIdBytes = ContiguousArray<UInt8>([UInt8](repeating: 0x0, count: 32))
globalDBLock.lock() globalDBLock.lock()
let success = contiguousTxIdBytes.withUnsafeMutableBufferPointer { txIdBytePtr in let proposal = zcashlc_propose_transfer(
usk.bytes.withUnsafeBufferPointer { uskPtr in dbData.0,
zcashlc_create_to_address( dbData.1,
dbData.0, account,
dbData.1, [CChar](address.utf8CString),
uskPtr.baseAddress, value,
UInt(usk.bytes.count), memo?.bytes,
[CChar](address.utf8CString), networkType.networkId,
value, minimumConfirmations,
memo?.bytes, useZIP317Fees
spendParamsPath.0, )
spendParamsPath.1,
outputParamsPath.0,
outputParamsPath.1,
networkType.networkId,
minimumConfirmations,
useZIP317Fees,
txIdBytePtr.baseAddress
)
}
}
globalDBLock.unlock() globalDBLock.unlock()
guard success else { guard let proposal else {
throw ZcashError.rustCreateToAddress(lastErrorMessage(fallback: "`createToAddress` failed with unknown error")) throw ZcashError.rustCreateToAddress(lastErrorMessage(fallback: "`proposeTransfer` failed with unknown error"))
} }
return contiguousTxIdBytes.withUnsafeBufferPointer { txIdBytePtr in defer { zcashlc_free_boxed_slice(proposal) }
Data(txIdBytePtr)
} return try FfiProposal(contiguousBytes: Data(
bytes: proposal.pointee.ptr,
count: Int(proposal.pointee.len)
))
} }
func decryptAndStoreTransaction(txBytes: [UInt8], minedHeight: Int32) async throws { func decryptAndStoreTransaction(txBytes: [UInt8], minedHeight: Int32) async throws {
@ -138,18 +129,6 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
} }
} }
func getBalance(account: Int32) async throws -> Int64 {
globalDBLock.lock()
let balance = zcashlc_get_balance(dbData.0, dbData.1, account, networkType.networkId)
globalDBLock.unlock()
guard balance >= 0 else {
throw ZcashError.rustGetBalance(Int(account), lastErrorMessage(fallback: "Error getting total balance from account \(account)"))
}
return balance
}
func getCurrentAddress(account: Int32) async throws -> UnifiedAddress { func getCurrentAddress(account: Int32) async throws -> UnifiedAddress {
globalDBLock.lock() globalDBLock.lock()
let addressCStr = zcashlc_get_current_address( let addressCStr = zcashlc_get_current_address(
@ -256,27 +235,6 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
return balance return balance
} }
func getVerifiedBalance(account: Int32) async throws -> Int64 {
globalDBLock.lock()
let balance = zcashlc_get_verified_balance(
dbData.0,
dbData.1,
account,
networkType.networkId,
minimumConfirmations
)
globalDBLock.unlock()
guard balance >= 0 else {
throw ZcashError.rustGetVerifiedBalance(
Int(account),
lastErrorMessage(fallback: "Error getting verified balance from account \(account)")
)
}
return balance
}
func getVerifiedTransparentBalance(account: Int32) async throws -> Int64 { func getVerifiedTransparentBalance(account: Int32) async throws -> Int64 {
guard account >= 0 else { guard account >= 0 else {
throw ZcashError.rustGetVerifiedTransparentBalanceNegativeAccount(Int(account)) throw ZcashError.rustGetVerifiedTransparentBalanceNegativeAccount(Int(account))
@ -576,21 +534,35 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
} }
} }
func getScanProgress() async throws -> ScanProgress? { func getWalletSummary() async throws -> WalletSummary? {
globalDBLock.lock() globalDBLock.lock()
let result = zcashlc_get_scan_progress(dbData.0, dbData.1, networkType.networkId) let summaryPtr = zcashlc_get_wallet_summary(dbData.0, dbData.1, networkType.networkId, minimumConfirmations)
globalDBLock.unlock() globalDBLock.unlock()
if result.denominator == 0 { guard let summaryPtr else {
switch result.numerator { throw ZcashError.rustGetWalletSummary(lastErrorMessage(fallback: "`getWalletSummary` failed with unknown error"))
case 0:
return nil
default:
throw ZcashError.rustGetScanProgress(lastErrorMessage(fallback: "`getScanProgress` failed with unknown error"))
}
} else {
return ScanProgress(numerator: result.numerator, denominator: result.denominator)
} }
defer { zcashlc_free_wallet_summary(summaryPtr) }
if summaryPtr.pointee.fully_scanned_height < 0 {
return nil
}
var accountBalances: [UInt32: AccountBalance] = [:]
for i in (0 ..< Int(summaryPtr.pointee.account_balances_len)) {
let accountBalance = summaryPtr.pointee.account_balances.advanced(by: i).pointee
accountBalances[accountBalance.account_id] = accountBalance.toAccountBalance()
}
return WalletSummary(
accountBalances: accountBalances,
chainTipHeight: BlockHeight(summaryPtr.pointee.chain_tip_height),
fullyScannedHeight: BlockHeight(summaryPtr.pointee.fully_scanned_height),
scanProgress: summaryPtr.pointee.scan_progress?.pointee.toScanProgress(),
nextSaplingSubtreeIndex: UInt32(summaryPtr.pointee.next_sapling_subtree_index)
)
} }
func suggestScanRanges() async throws -> [ScanRange] { func suggestScanRanges() async throws -> [ScanRange] {
@ -623,48 +595,90 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
return scanRanges return scanRanges
} }
func scanBlocks(fromHeight: Int32, limit: UInt32 = 0) async throws { func scanBlocks(fromHeight: Int32, limit: UInt32 = 0) async throws -> ScanSummary {
globalDBLock.lock() globalDBLock.lock()
let result = zcashlc_scan_blocks(fsBlockDbRoot.0, fsBlockDbRoot.1, dbData.0, dbData.1, fromHeight, limit, networkType.networkId) let summaryPtr = zcashlc_scan_blocks(fsBlockDbRoot.0, fsBlockDbRoot.1, dbData.0, dbData.1, fromHeight, limit, networkType.networkId)
globalDBLock.unlock() globalDBLock.unlock()
guard result != 0 else { guard let summaryPtr else {
throw ZcashError.rustScanBlocks(lastErrorMessage(fallback: "`scanBlocks` failed with unknown error")) throw ZcashError.rustScanBlocks(lastErrorMessage(fallback: "`scanBlocks` failed with unknown error"))
} }
defer { zcashlc_free_scan_summary(summaryPtr) }
return ScanSummary(
scannedRange: Range(uncheckedBounds: (
BlockHeight(summaryPtr.pointee.scanned_start),
BlockHeight(summaryPtr.pointee.scanned_end)
)),
spentSaplingNoteCount: summaryPtr.pointee.spent_sapling_note_count,
receivedSaplingNoteCount: summaryPtr.pointee.received_sapling_note_count
)
} }
func shieldFunds( func proposeShielding(
usk: UnifiedSpendingKey, account: Int32,
memo: MemoBytes?, memo: MemoBytes?,
shieldingThreshold: Zatoshi shieldingThreshold: Zatoshi
) async throws -> FfiProposal {
globalDBLock.lock()
let proposal = zcashlc_propose_shielding(
dbData.0,
dbData.1,
account,
memo?.bytes,
UInt64(shieldingThreshold.amount),
networkType.networkId,
minimumConfirmations,
useZIP317Fees
)
globalDBLock.unlock()
guard let proposal else {
throw ZcashError.rustShieldFunds(lastErrorMessage(fallback: "`proposeShielding` failed with unknown error"))
}
defer { zcashlc_free_boxed_slice(proposal) }
return try FfiProposal(contiguousBytes: Data(
bytes: proposal.pointee.ptr,
count: Int(proposal.pointee.len)
))
}
func createProposedTransaction(
proposal: FfiProposal,
usk: UnifiedSpendingKey
) async throws -> Data { ) async throws -> Data {
var contiguousTxIdBytes = ContiguousArray<UInt8>([UInt8](repeating: 0x0, count: 32)) var contiguousTxIdBytes = ContiguousArray<UInt8>([UInt8](repeating: 0x0, count: 32))
let proposalBytes = try proposal.serializedData(partial: false).bytes
globalDBLock.lock() globalDBLock.lock()
let success = contiguousTxIdBytes.withUnsafeMutableBufferPointer { txIdBytePtr in let success = contiguousTxIdBytes.withUnsafeMutableBufferPointer { txIdBytePtr in
usk.bytes.withUnsafeBufferPointer { uskBuffer in proposalBytes.withUnsafeBufferPointer { proposalPtr in
zcashlc_shield_funds( usk.bytes.withUnsafeBufferPointer { uskPtr in
dbData.0, zcashlc_create_proposed_transaction(
dbData.1, dbData.0,
uskBuffer.baseAddress, dbData.1,
UInt(usk.bytes.count), proposalPtr.baseAddress,
memo?.bytes, UInt(proposalBytes.count),
UInt64(shieldingThreshold.amount), uskPtr.baseAddress,
spendParamsPath.0, UInt(usk.bytes.count),
spendParamsPath.1, spendParamsPath.0,
outputParamsPath.0, spendParamsPath.1,
outputParamsPath.1, outputParamsPath.0,
networkType.networkId, outputParamsPath.1,
minimumConfirmations, networkType.networkId,
useZIP317Fees, txIdBytePtr.baseAddress
txIdBytePtr.baseAddress )
) }
} }
} }
globalDBLock.unlock() globalDBLock.unlock()
guard success else { guard success else {
throw ZcashError.rustShieldFunds(lastErrorMessage(fallback: "`shieldFunds` failed with unknown error")) throw ZcashError.rustCreateToAddress(lastErrorMessage(fallback: "`createToAddress` failed with unknown error"))
} }
return contiguousTxIdBytes.withUnsafeBufferPointer { txIdBytePtr in return contiguousTxIdBytes.withUnsafeBufferPointer { txIdBytePtr in
@ -778,3 +792,34 @@ extension Array where Element == FfiSubtreeRoot {
} }
} }
} }
extension FfiBalance {
/// Converts an [`FfiBalance`] into a [`PoolBalance`].
func toPoolBalance() -> PoolBalance {
.init(
spendableValue: Zatoshi(self.spendable_value),
changePendingConfirmation: Zatoshi(self.change_pending_confirmation),
valuePendingSpendability: Zatoshi(self.value_pending_spendability)
)
}
}
extension FfiAccountBalance {
/// Converts an [`FfiAccountBalance`] into a [`AccountBalance`].
func toAccountBalance() -> AccountBalance {
.init(
saplingBalance: self.sapling_balance.toPoolBalance(),
unshielded: Zatoshi(self.unshielded)
)
}
}
extension FfiScanProgress {
/// Converts an [`FfiScanProgress`] into a [`ScanProgress`].
func toScanProgress() -> ScanProgress {
.init(
numerator: self.numerator,
denominator: self.denominator
)
}
}

View File

@ -41,30 +41,12 @@ protocol ZcashRustBackendWelding {
/// - Throws: `rustCreateAccount`. /// - Throws: `rustCreateAccount`.
func createAccount(seed: [UInt8], treeState: TreeState, recoverUntil: UInt32?) async throws -> UnifiedSpendingKey func createAccount(seed: [UInt8], treeState: TreeState, recoverUntil: UInt32?) async throws -> UnifiedSpendingKey
/// Creates a transaction to the given address from the given account
/// - Parameter usk: `UnifiedSpendingKey` for the account that controls the funds to be spent.
/// - Parameter to: recipient address
/// - Parameter value: transaction amount in Zatoshi
/// - Parameter memo: the `MemoBytes` for this transaction. pass `nil` when sending to transparent receivers
/// - Throws: `rustCreateToAddress`.
func createToAddress(
usk: UnifiedSpendingKey,
to address: String,
value: Int64,
memo: MemoBytes?
) async throws -> Data
/// Scans a transaction for any information that can be decrypted by the accounts in the wallet, and saves it to the wallet. /// Scans a transaction for any information that can be decrypted by the accounts in the wallet, and saves it to the wallet.
/// - parameter tx: the transaction to decrypt /// - parameter tx: the transaction to decrypt
/// - parameter minedHeight: height on which this transaction was mined. this is used to fetch the consensus branch ID. /// - parameter minedHeight: height on which this transaction was mined. this is used to fetch the consensus branch ID.
/// - Throws: `rustDecryptAndStoreTransaction`. /// - Throws: `rustDecryptAndStoreTransaction`.
func decryptAndStoreTransaction(txBytes: [UInt8], minedHeight: Int32) async throws func decryptAndStoreTransaction(txBytes: [UInt8], minedHeight: Int32) async throws
/// Get the (unverified) balance from the given account.
/// - parameter account: index of the given account
/// - Throws: `rustGetBalance`.
func getBalance(account: Int32) async throws -> Int64
/// Returns the most-recently-generated unified payment address for the specified account. /// Returns the most-recently-generated unified payment address for the specified account.
/// - parameter account: index of the given account /// - parameter account: index of the given account
/// - Throws: /// - Throws:
@ -120,11 +102,6 @@ protocol ZcashRustBackendWelding {
/// - `rustListTransparentReceiversInvalidAddress` if transarent received generated by rust is invalid. /// - `rustListTransparentReceiversInvalidAddress` if transarent received generated by rust is invalid.
func listTransparentReceivers(account: Int32) async throws -> [TransparentAddress] func listTransparentReceivers(account: Int32) async throws -> [TransparentAddress]
/// Get the verified balance from the given account
/// - parameter account: index of the given account
/// - Throws: `rustGetVerifiedBalance` when rust layer throws error.
func getVerifiedBalance(account: Int32) async throws -> Int64
/// Get the verified cached transparent balance for the given account /// Get the verified cached transparent balance for the given account
/// - parameter account: account index to query the balance for. /// - parameter account: account index to query the balance for.
/// - Throws: /// - Throws:
@ -167,8 +144,8 @@ protocol ZcashRustBackendWelding {
/// height due to the fact that out-of-order scanning can leave gaps. /// height due to the fact that out-of-order scanning can leave gaps.
func maxScannedHeight() async throws -> BlockHeight? func maxScannedHeight() async throws -> BlockHeight?
/// Returns the scan progress derived from the current wallet state. /// Returns the account balances and sync status of the wallet.
func getScanProgress() async throws -> ScanProgress? func getWalletSummary() async throws -> WalletSummary?
/// Returns a list of suggested scan ranges based upon the current wallet state. /// Returns a list of suggested scan ranges based upon the current wallet state.
/// ///
@ -198,7 +175,7 @@ protocol ZcashRustBackendWelding {
/// - parameter fromHeight: scan starting from the given height. /// - parameter fromHeight: scan starting from the given height.
/// - parameter limit: scan up to limit blocks. /// - parameter limit: scan up to limit blocks.
/// - Throws: `rustScanBlocks` if rust layer returns error. /// - Throws: `rustScanBlocks` if rust layer returns error.
func scanBlocks(fromHeight: Int32, limit: UInt32) async throws func scanBlocks(fromHeight: Int32, limit: UInt32) async throws -> ScanSummary
/// Upserts a UTXO into the data db database /// Upserts a UTXO into the data db database
/// - parameter txid: the txid bytes for the UTXO /// - parameter txid: the txid bytes for the UTXO
@ -215,14 +192,42 @@ protocol ZcashRustBackendWelding {
height: BlockHeight height: BlockHeight
) async throws ) async throws
/// Creates a transaction to shield all found UTXOs in data db for the account the provided `UnifiedSpendingKey` has spend authority for. /// Select transaction inputs, compute fees, and construct a proposal for a transaction
/// - Parameter usk: `UnifiedSpendingKey` that spend transparent funds and where the funds will be shielded to. /// that can then be authorized and made ready for submission to the network with
/// `createProposedTransaction`.
///
/// - parameter account: index of the given account
/// - Parameter to: recipient address
/// - Parameter value: transaction amount in Zatoshi
/// - Parameter memo: the `MemoBytes` for this transaction. pass `nil` when sending to transparent receivers
/// - Throws: `rustCreateToAddress`.
func proposeTransfer(
account: Int32,
to address: String,
value: Int64,
memo: MemoBytes?
) async throws -> FfiProposal
/// Constructs a transaction proposal to shield all found UTXOs in data db for the given account,
/// that can then be authorized and made ready for submission to the network with
/// `createProposedTransaction`.
///
/// - parameter account: index of the given account
/// - Parameter memo: the `Memo` for this transaction /// - Parameter memo: the `Memo` for this transaction
/// - Throws: `rustShieldFunds` if rust layer returns error. /// - Throws: `rustShieldFunds` if rust layer returns error.
func shieldFunds( func proposeShielding(
usk: UnifiedSpendingKey, account: Int32,
memo: MemoBytes?, memo: MemoBytes?,
shieldingThreshold: Zatoshi shieldingThreshold: Zatoshi
) async throws -> FfiProposal
/// Creates a transaction from the given proposal.
/// - Parameter proposal: the transaction proposal.
/// - Parameter usk: `UnifiedSpendingKey` for the account that controls the funds to be spent.
/// - Throws: `rustCreateToAddress`.
func createProposedTransaction(
proposal: FfiProposal,
usk: UnifiedSpendingKey
) async throws -> Data ) async throws -> Data
/// Gets the consensus branch id for the given height /// Gets the consensus branch id for the given height

View File

@ -414,15 +414,13 @@ public class SDKSynchronizer: Synchronizer {
} }
public func getShieldedBalance(accountIndex: Int = 0) async throws -> Zatoshi { public func getShieldedBalance(accountIndex: Int = 0) async throws -> Zatoshi {
let balance = try await initializer.rustBackend.getBalance(account: Int32(accountIndex)) try await initializer.rustBackend.getWalletSummary()?.accountBalances[UInt32(accountIndex)]?
.saplingBalance.total() ?? Zatoshi.zero
return Zatoshi(balance)
} }
public func getShieldedVerifiedBalance(accountIndex: Int = 0) async throws -> Zatoshi { public func getShieldedVerifiedBalance(accountIndex: Int = 0) async throws -> Zatoshi {
let balance = try await initializer.rustBackend.getVerifiedBalance(account: Int32(accountIndex)) try await initializer.rustBackend.getWalletSummary()?.accountBalances[UInt32(accountIndex)]?
.saplingBalance.spendableValue ?? Zatoshi.zero
return Zatoshi(balance)
} }
public func getUnifiedAddress(accountIndex: Int) async throws -> UnifiedAddress { public func getUnifiedAddress(accountIndex: Int) async throws -> UnifiedAddress {

View File

@ -85,13 +85,19 @@ class WalletTransactionEncoder: TransactionEncoder {
throw ZcashError.walletTransEncoderCreateTransactionMissingSaplingParams throw ZcashError.walletTransEncoderCreateTransactionMissingSaplingParams
} }
let txId = try await rustBackend.createToAddress( // TODO: Expose the proposal in a way that enables querying its fee.
usk: spendingKey, let proposal = try await rustBackend.proposeTransfer(
account: Int32(spendingKey.account),
to: address, to: address,
value: zatoshi.amount, value: zatoshi.amount,
memo: memoBytes memo: memoBytes
) )
let txId = try await rustBackend.createProposedTransaction(
proposal: proposal,
usk: spendingKey
)
return txId return txId
} }
@ -121,13 +127,19 @@ class WalletTransactionEncoder: TransactionEncoder {
guard ensureParams(spend: self.spendParamsURL, output: self.outputParamsURL) else { guard ensureParams(spend: self.spendParamsURL, output: self.outputParamsURL) else {
throw ZcashError.walletTransEncoderShieldFundsMissingSaplingParams throw ZcashError.walletTransEncoderShieldFundsMissingSaplingParams
} }
let txId = try await rustBackend.shieldFunds( // TODO: Expose the proposal in a way that enables querying its fee.
usk: spendingKey, let proposal = try await rustBackend.proposeShielding(
account: Int32(spendingKey.account),
memo: memo, memo: memo,
shieldingThreshold: shieldingThreshold shieldingThreshold: shieldingThreshold
) )
let txId = try await rustBackend.createProposedTransaction(
proposal: proposal,
usk: spendingKey
)
return txId return txId
} }

View File

@ -1,4 +1,4 @@
// Generated using Sourcery 2.0.3 https://github.com/krzysztofzablocki/Sourcery // Generated using Sourcery 2.1.4 https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT // DO NOT EDIT
import Combine import Combine
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
@ -1191,17 +1191,17 @@ class SDKMetricsMock: SDKMetrics {
// MARK: - logCBPOverviewReport // MARK: - logCBPOverviewReport
var logCBPOverviewReportShieldedBalanceCallsCount = 0 var logCBPOverviewReportWalletSummaryCallsCount = 0
var logCBPOverviewReportShieldedBalanceCalled: Bool { var logCBPOverviewReportWalletSummaryCalled: Bool {
return logCBPOverviewReportShieldedBalanceCallsCount > 0 return logCBPOverviewReportWalletSummaryCallsCount > 0
} }
var logCBPOverviewReportShieldedBalanceReceivedArguments: (logger: Logger, shieldedBalance: WalletBalance)? var logCBPOverviewReportWalletSummaryReceivedArguments: (logger: Logger, walletSummary: WalletSummary?)?
var logCBPOverviewReportShieldedBalanceClosure: ((Logger, WalletBalance) async -> Void)? var logCBPOverviewReportWalletSummaryClosure: ((Logger, WalletSummary?) async -> Void)?
func logCBPOverviewReport(_ logger: Logger, shieldedBalance: WalletBalance) async { func logCBPOverviewReport(_ logger: Logger, walletSummary: WalletSummary?) async {
logCBPOverviewReportShieldedBalanceCallsCount += 1 logCBPOverviewReportWalletSummaryCallsCount += 1
logCBPOverviewReportShieldedBalanceReceivedArguments = (logger: logger, shieldedBalance: shieldedBalance) logCBPOverviewReportWalletSummaryReceivedArguments = (logger: logger, walletSummary: walletSummary)
await logCBPOverviewReportShieldedBalanceClosure!(logger, shieldedBalance) await logCBPOverviewReportWalletSummaryClosure!(logger, walletSummary)
} }
} }
@ -2179,39 +2179,6 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
} }
} }
// MARK: - createToAddress
var createToAddressUskToValueMemoThrowableError: Error?
func setCreateToAddressUskToValueMemoThrowableError(_ param: Error?) async {
createToAddressUskToValueMemoThrowableError = param
}
var createToAddressUskToValueMemoCallsCount = 0
var createToAddressUskToValueMemoCalled: Bool {
return createToAddressUskToValueMemoCallsCount > 0
}
var createToAddressUskToValueMemoReceivedArguments: (usk: UnifiedSpendingKey, address: String, value: Int64, memo: MemoBytes?)?
var createToAddressUskToValueMemoReturnValue: Data!
func setCreateToAddressUskToValueMemoReturnValue(_ param: Data) async {
createToAddressUskToValueMemoReturnValue = param
}
var createToAddressUskToValueMemoClosure: ((UnifiedSpendingKey, String, Int64, MemoBytes?) async throws -> Data)?
func setCreateToAddressUskToValueMemoClosure(_ param: ((UnifiedSpendingKey, String, Int64, MemoBytes?) async throws -> Data)?) async {
createToAddressUskToValueMemoClosure = param
}
func createToAddress(usk: UnifiedSpendingKey, to address: String, value: Int64, memo: MemoBytes?) async throws -> Data {
if let error = createToAddressUskToValueMemoThrowableError {
throw error
}
createToAddressUskToValueMemoCallsCount += 1
createToAddressUskToValueMemoReceivedArguments = (usk: usk, address: address, value: value, memo: memo)
if let closure = createToAddressUskToValueMemoClosure {
return try await closure(usk, address, value, memo)
} else {
return createToAddressUskToValueMemoReturnValue
}
}
// MARK: - decryptAndStoreTransaction // MARK: - decryptAndStoreTransaction
var decryptAndStoreTransactionTxBytesMinedHeightThrowableError: Error? var decryptAndStoreTransactionTxBytesMinedHeightThrowableError: Error?
@ -2237,39 +2204,6 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
try await decryptAndStoreTransactionTxBytesMinedHeightClosure!(txBytes, minedHeight) try await decryptAndStoreTransactionTxBytesMinedHeightClosure!(txBytes, minedHeight)
} }
// MARK: - getBalance
var getBalanceAccountThrowableError: Error?
func setGetBalanceAccountThrowableError(_ param: Error?) async {
getBalanceAccountThrowableError = param
}
var getBalanceAccountCallsCount = 0
var getBalanceAccountCalled: Bool {
return getBalanceAccountCallsCount > 0
}
var getBalanceAccountReceivedAccount: Int32?
var getBalanceAccountReturnValue: Int64!
func setGetBalanceAccountReturnValue(_ param: Int64) async {
getBalanceAccountReturnValue = param
}
var getBalanceAccountClosure: ((Int32) async throws -> Int64)?
func setGetBalanceAccountClosure(_ param: ((Int32) async throws -> Int64)?) async {
getBalanceAccountClosure = param
}
func getBalance(account: Int32) async throws -> Int64 {
if let error = getBalanceAccountThrowableError {
throw error
}
getBalanceAccountCallsCount += 1
getBalanceAccountReceivedAccount = account
if let closure = getBalanceAccountClosure {
return try await closure(account)
} else {
return getBalanceAccountReturnValue
}
}
// MARK: - getCurrentAddress // MARK: - getCurrentAddress
var getCurrentAddressAccountThrowableError: Error? var getCurrentAddressAccountThrowableError: Error?
@ -2501,39 +2435,6 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
} }
} }
// MARK: - getVerifiedBalance
var getVerifiedBalanceAccountThrowableError: Error?
func setGetVerifiedBalanceAccountThrowableError(_ param: Error?) async {
getVerifiedBalanceAccountThrowableError = param
}
var getVerifiedBalanceAccountCallsCount = 0
var getVerifiedBalanceAccountCalled: Bool {
return getVerifiedBalanceAccountCallsCount > 0
}
var getVerifiedBalanceAccountReceivedAccount: Int32?
var getVerifiedBalanceAccountReturnValue: Int64!
func setGetVerifiedBalanceAccountReturnValue(_ param: Int64) async {
getVerifiedBalanceAccountReturnValue = param
}
var getVerifiedBalanceAccountClosure: ((Int32) async throws -> Int64)?
func setGetVerifiedBalanceAccountClosure(_ param: ((Int32) async throws -> Int64)?) async {
getVerifiedBalanceAccountClosure = param
}
func getVerifiedBalance(account: Int32) async throws -> Int64 {
if let error = getVerifiedBalanceAccountThrowableError {
throw error
}
getVerifiedBalanceAccountCallsCount += 1
getVerifiedBalanceAccountReceivedAccount = account
if let closure = getVerifiedBalanceAccountClosure {
return try await closure(account)
} else {
return getVerifiedBalanceAccountReturnValue
}
}
// MARK: - getVerifiedTransparentBalance // MARK: - getVerifiedTransparentBalance
var getVerifiedTransparentBalanceAccountThrowableError: Error? var getVerifiedTransparentBalanceAccountThrowableError: Error?
@ -2729,34 +2630,34 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
} }
} }
// MARK: - getScanProgress // MARK: - getWalletSummary
var getScanProgressThrowableError: Error? var getWalletSummaryThrowableError: Error?
func setGetScanProgressThrowableError(_ param: Error?) async { func setGetWalletSummaryThrowableError(_ param: Error?) async {
getScanProgressThrowableError = param getWalletSummaryThrowableError = param
} }
var getScanProgressCallsCount = 0 var getWalletSummaryCallsCount = 0
var getScanProgressCalled: Bool { var getWalletSummaryCalled: Bool {
return getScanProgressCallsCount > 0 return getWalletSummaryCallsCount > 0
} }
var getScanProgressReturnValue: ScanProgress? var getWalletSummaryReturnValue: WalletSummary?
func setGetScanProgressReturnValue(_ param: ScanProgress?) async { func setGetWalletSummaryReturnValue(_ param: WalletSummary?) async {
getScanProgressReturnValue = param getWalletSummaryReturnValue = param
} }
var getScanProgressClosure: (() async throws -> ScanProgress?)? var getWalletSummaryClosure: (() async throws -> WalletSummary?)?
func setGetScanProgressClosure(_ param: (() async throws -> ScanProgress?)?) async { func setGetWalletSummaryClosure(_ param: (() async throws -> WalletSummary?)?) async {
getScanProgressClosure = param getWalletSummaryClosure = param
} }
func getScanProgress() async throws -> ScanProgress? { func getWalletSummary() async throws -> WalletSummary? {
if let error = getScanProgressThrowableError { if let error = getWalletSummaryThrowableError {
throw error throw error
} }
getScanProgressCallsCount += 1 getWalletSummaryCallsCount += 1
if let closure = getScanProgressClosure { if let closure = getWalletSummaryClosure {
return try await closure() return try await closure()
} else { } else {
return getScanProgressReturnValue return getWalletSummaryReturnValue
} }
} }
@ -2802,18 +2703,26 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
return scanBlocksFromHeightLimitCallsCount > 0 return scanBlocksFromHeightLimitCallsCount > 0
} }
var scanBlocksFromHeightLimitReceivedArguments: (fromHeight: Int32, limit: UInt32)? var scanBlocksFromHeightLimitReceivedArguments: (fromHeight: Int32, limit: UInt32)?
var scanBlocksFromHeightLimitClosure: ((Int32, UInt32) async throws -> Void)? var scanBlocksFromHeightLimitReturnValue: ScanSummary!
func setScanBlocksFromHeightLimitClosure(_ param: ((Int32, UInt32) async throws -> Void)?) async { func setScanBlocksFromHeightLimitReturnValue(_ param: ScanSummary) async {
scanBlocksFromHeightLimitReturnValue = param
}
var scanBlocksFromHeightLimitClosure: ((Int32, UInt32) async throws -> ScanSummary)?
func setScanBlocksFromHeightLimitClosure(_ param: ((Int32, UInt32) async throws -> ScanSummary)?) async {
scanBlocksFromHeightLimitClosure = param scanBlocksFromHeightLimitClosure = param
} }
func scanBlocks(fromHeight: Int32, limit: UInt32) async throws { func scanBlocks(fromHeight: Int32, limit: UInt32) async throws -> ScanSummary {
if let error = scanBlocksFromHeightLimitThrowableError { if let error = scanBlocksFromHeightLimitThrowableError {
throw error throw error
} }
scanBlocksFromHeightLimitCallsCount += 1 scanBlocksFromHeightLimitCallsCount += 1
scanBlocksFromHeightLimitReceivedArguments = (fromHeight: fromHeight, limit: limit) scanBlocksFromHeightLimitReceivedArguments = (fromHeight: fromHeight, limit: limit)
try await scanBlocksFromHeightLimitClosure!(fromHeight, limit) if let closure = scanBlocksFromHeightLimitClosure {
return try await closure(fromHeight, limit)
} else {
return scanBlocksFromHeightLimitReturnValue
}
} }
// MARK: - putUnspentTransparentOutput // MARK: - putUnspentTransparentOutput
@ -2841,36 +2750,102 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
try await putUnspentTransparentOutputTxidIndexScriptValueHeightClosure!(txid, index, script, value, height) try await putUnspentTransparentOutputTxidIndexScriptValueHeightClosure!(txid, index, script, value, height)
} }
// MARK: - shieldFunds // MARK: - proposeTransfer
var shieldFundsUskMemoShieldingThresholdThrowableError: Error? var proposeTransferAccountToValueMemoThrowableError: Error?
func setShieldFundsUskMemoShieldingThresholdThrowableError(_ param: Error?) async { func setProposeTransferAccountToValueMemoThrowableError(_ param: Error?) async {
shieldFundsUskMemoShieldingThresholdThrowableError = param proposeTransferAccountToValueMemoThrowableError = param
} }
var shieldFundsUskMemoShieldingThresholdCallsCount = 0 var proposeTransferAccountToValueMemoCallsCount = 0
var shieldFundsUskMemoShieldingThresholdCalled: Bool { var proposeTransferAccountToValueMemoCalled: Bool {
return shieldFundsUskMemoShieldingThresholdCallsCount > 0 return proposeTransferAccountToValueMemoCallsCount > 0
} }
var shieldFundsUskMemoShieldingThresholdReceivedArguments: (usk: UnifiedSpendingKey, memo: MemoBytes?, shieldingThreshold: Zatoshi)? var proposeTransferAccountToValueMemoReceivedArguments: (account: Int32, address: String, value: Int64, memo: MemoBytes?)?
var shieldFundsUskMemoShieldingThresholdReturnValue: Data! var proposeTransferAccountToValueMemoReturnValue: FfiProposal!
func setShieldFundsUskMemoShieldingThresholdReturnValue(_ param: Data) async { func setProposeTransferAccountToValueMemoReturnValue(_ param: FfiProposal) async {
shieldFundsUskMemoShieldingThresholdReturnValue = param proposeTransferAccountToValueMemoReturnValue = param
} }
var shieldFundsUskMemoShieldingThresholdClosure: ((UnifiedSpendingKey, MemoBytes?, Zatoshi) async throws -> Data)? var proposeTransferAccountToValueMemoClosure: ((Int32, String, Int64, MemoBytes?) async throws -> FfiProposal)?
func setShieldFundsUskMemoShieldingThresholdClosure(_ param: ((UnifiedSpendingKey, MemoBytes?, Zatoshi) async throws -> Data)?) async { func setProposeTransferAccountToValueMemoClosure(_ param: ((Int32, String, Int64, MemoBytes?) async throws -> FfiProposal)?) async {
shieldFundsUskMemoShieldingThresholdClosure = param proposeTransferAccountToValueMemoClosure = param
} }
func shieldFunds(usk: UnifiedSpendingKey, memo: MemoBytes?, shieldingThreshold: Zatoshi) async throws -> Data { func proposeTransfer(account: Int32, to address: String, value: Int64, memo: MemoBytes?) async throws -> FfiProposal {
if let error = shieldFundsUskMemoShieldingThresholdThrowableError { if let error = proposeTransferAccountToValueMemoThrowableError {
throw error throw error
} }
shieldFundsUskMemoShieldingThresholdCallsCount += 1 proposeTransferAccountToValueMemoCallsCount += 1
shieldFundsUskMemoShieldingThresholdReceivedArguments = (usk: usk, memo: memo, shieldingThreshold: shieldingThreshold) proposeTransferAccountToValueMemoReceivedArguments = (account: account, address: address, value: value, memo: memo)
if let closure = shieldFundsUskMemoShieldingThresholdClosure { if let closure = proposeTransferAccountToValueMemoClosure {
return try await closure(usk, memo, shieldingThreshold) return try await closure(account, address, value, memo)
} else { } else {
return shieldFundsUskMemoShieldingThresholdReturnValue return proposeTransferAccountToValueMemoReturnValue
}
}
// MARK: - proposeShielding
var proposeShieldingAccountMemoShieldingThresholdThrowableError: Error?
func setProposeShieldingAccountMemoShieldingThresholdThrowableError(_ param: Error?) async {
proposeShieldingAccountMemoShieldingThresholdThrowableError = param
}
var proposeShieldingAccountMemoShieldingThresholdCallsCount = 0
var proposeShieldingAccountMemoShieldingThresholdCalled: Bool {
return proposeShieldingAccountMemoShieldingThresholdCallsCount > 0
}
var proposeShieldingAccountMemoShieldingThresholdReceivedArguments: (account: Int32, memo: MemoBytes?, shieldingThreshold: Zatoshi)?
var proposeShieldingAccountMemoShieldingThresholdReturnValue: FfiProposal!
func setProposeShieldingAccountMemoShieldingThresholdReturnValue(_ param: FfiProposal) async {
proposeShieldingAccountMemoShieldingThresholdReturnValue = param
}
var proposeShieldingAccountMemoShieldingThresholdClosure: ((Int32, MemoBytes?, Zatoshi) async throws -> FfiProposal)?
func setProposeShieldingAccountMemoShieldingThresholdClosure(_ param: ((Int32, MemoBytes?, Zatoshi) async throws -> FfiProposal)?) async {
proposeShieldingAccountMemoShieldingThresholdClosure = param
}
func proposeShielding(account: Int32, memo: MemoBytes?, shieldingThreshold: Zatoshi) async throws -> FfiProposal {
if let error = proposeShieldingAccountMemoShieldingThresholdThrowableError {
throw error
}
proposeShieldingAccountMemoShieldingThresholdCallsCount += 1
proposeShieldingAccountMemoShieldingThresholdReceivedArguments = (account: account, memo: memo, shieldingThreshold: shieldingThreshold)
if let closure = proposeShieldingAccountMemoShieldingThresholdClosure {
return try await closure(account, memo, shieldingThreshold)
} else {
return proposeShieldingAccountMemoShieldingThresholdReturnValue
}
}
// MARK: - createProposedTransaction
var createProposedTransactionProposalUskThrowableError: Error?
func setCreateProposedTransactionProposalUskThrowableError(_ param: Error?) async {
createProposedTransactionProposalUskThrowableError = param
}
var createProposedTransactionProposalUskCallsCount = 0
var createProposedTransactionProposalUskCalled: Bool {
return createProposedTransactionProposalUskCallsCount > 0
}
var createProposedTransactionProposalUskReceivedArguments: (proposal: FfiProposal, usk: UnifiedSpendingKey)?
var createProposedTransactionProposalUskReturnValue: Data!
func setCreateProposedTransactionProposalUskReturnValue(_ param: Data) async {
createProposedTransactionProposalUskReturnValue = param
}
var createProposedTransactionProposalUskClosure: ((FfiProposal, UnifiedSpendingKey) async throws -> Data)?
func setCreateProposedTransactionProposalUskClosure(_ param: ((FfiProposal, UnifiedSpendingKey) async throws -> Data)?) async {
createProposedTransactionProposalUskClosure = param
}
func createProposedTransaction(proposal: FfiProposal, usk: UnifiedSpendingKey) async throws -> Data {
if let error = createProposedTransactionProposalUskThrowableError {
throw error
}
createProposedTransactionProposalUskCallsCount += 1
createProposedTransactionProposalUskReceivedArguments = (proposal: proposal, usk: usk)
if let closure = createProposedTransactionProposalUskClosure {
return try await closure(proposal, usk)
} else {
return createProposedTransactionProposalUskReturnValue
} }
} }

View File

@ -3,7 +3,7 @@
scriptDir=${0:a:h} scriptDir=${0:a:h}
cd "${scriptDir}" cd "${scriptDir}"
sourcery_version=2.0.3 sourcery_version=2.1.4
if which sourcery >/dev/null; then if which sourcery >/dev/null; then
if [[ $(sourcery --version) != $sourcery_version ]]; then if [[ $(sourcery --version) != $sourcery_version ]]; then

View File

@ -75,7 +75,6 @@ class RustBackendMockHelper {
await rustBackendMock.setInitBlockMetadataDbClosure() { } await rustBackendMock.setInitBlockMetadataDbClosure() { }
await rustBackendMock.setWriteBlocksMetadataBlocksClosure() { _ in } await rustBackendMock.setWriteBlocksMetadataBlocksClosure() { _ in }
await rustBackendMock.setGetTransparentBalanceAccountReturnValue(0) await rustBackendMock.setGetTransparentBalanceAccountReturnValue(0)
await rustBackendMock.setGetVerifiedBalanceAccountReturnValue(0)
await rustBackendMock.setListTransparentReceiversAccountReturnValue([]) await rustBackendMock.setListTransparentReceiversAccountReturnValue([])
await rustBackendMock.setGetCurrentAddressAccountThrowableError(ZcashError.rustGetCurrentAddress("mocked error")) await rustBackendMock.setGetCurrentAddressAccountThrowableError(ZcashError.rustGetCurrentAddress("mocked error"))
await rustBackendMock.setGetNextAvailableAddressAccountThrowableError(ZcashError.rustGetNextAvailableAddress("mocked error")) await rustBackendMock.setGetNextAvailableAddressAccountThrowableError(ZcashError.rustGetNextAvailableAddress("mocked error"))
@ -84,22 +83,15 @@ class RustBackendMockHelper {
await rustBackendMock.setInitDataDbSeedReturnValue(.seedRequired) await rustBackendMock.setInitDataDbSeedReturnValue(.seedRequired)
await rustBackendMock.setGetNearestRewindHeightHeightReturnValue(-1) await rustBackendMock.setGetNearestRewindHeightHeightReturnValue(-1)
await rustBackendMock.setPutUnspentTransparentOutputTxidIndexScriptValueHeightClosure() { _, _, _, _, _ in } await rustBackendMock.setPutUnspentTransparentOutputTxidIndexScriptValueHeightClosure() { _, _, _, _, _ in }
await rustBackendMock.setCreateToAddressUskToValueMemoThrowableError(ZcashError.rustCreateToAddress("mocked error")) await rustBackendMock.setProposeTransferAccountToValueMemoThrowableError(ZcashError.rustCreateToAddress("mocked error"))
await rustBackendMock.setShieldFundsUskMemoShieldingThresholdThrowableError(ZcashError.rustShieldFunds("mocked error")) await rustBackendMock.setProposeShieldingAccountMemoShieldingThresholdThrowableError(ZcashError.rustShieldFunds("mocked error"))
await rustBackendMock.setCreateProposedTransactionProposalUskThrowableError(ZcashError.rustCreateToAddress("mocked error"))
await rustBackendMock.setDecryptAndStoreTransactionTxBytesMinedHeightThrowableError(ZcashError.rustDecryptAndStoreTransaction("mock fail")) await rustBackendMock.setDecryptAndStoreTransactionTxBytesMinedHeightThrowableError(ZcashError.rustDecryptAndStoreTransaction("mock fail"))
await rustBackendMock.setInitDataDbSeedClosure() { seed in await rustBackendMock.setInitDataDbSeedClosure() { seed in
return try await rustBackend.initDataDb(seed: seed) return try await rustBackend.initDataDb(seed: seed)
} }
await rustBackendMock.setGetBalanceAccountClosure() { account in
return try await rustBackend.getBalance(account: account)
}
await rustBackendMock.setGetVerifiedBalanceAccountClosure() { account in
return try await rustBackend.getVerifiedBalance(account: account)
}
await rustBackendMock.setRewindToHeightHeightClosure() { height in await rustBackendMock.setRewindToHeightHeightClosure() { height in
try await rustBackend.rewindToHeight(height: height) try await rustBackend.rewindToHeight(height: height)
} }