Merge pull request #1361 from Electric-Coin-Company/feature-2.0.7

Feature branch for release 2.0.7
This commit is contained in:
Kris Nuttycombe 2024-01-30 10:01:12 -07:00 committed by GitHub
commit d7ac804135
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 1152 additions and 414 deletions

View File

@ -1,4 +1,4 @@
# This SwiftLint file is based on this great guideline.
# This SwiftLint file is based on this great guideline.
# https://github.com/raywenderlich/swift-style-guide
included:
@ -11,6 +11,7 @@ excluded:
- Sources/ZcashLightClientKit/Modules/Service/GRPC/ProtoBuf/compact_formats.pb.swift
- Sources/ZcashLightClientKit/Modules/Service/GRPC/ProtoBuf/service.pb.swift
- Sources/ZcashLightClientKit/Modules/Service/GRPC/ProtoBuf/service.grpc.swift
- Sources/ZcashLightClientKit/Modules/Service/GRPC/ProtoBuf/proposal.pb.swift
- Tests/
- ZcashLightClientKitTests/Constants.generated.swift
- build/
@ -25,7 +26,7 @@ disabled_rules:
- todo
- unused_capture_list
- nesting # allow for types to be nested, common pattern in Swift
- multiple_closures_with_trailing_closure
- multiple_closures_with_trailing_closure
- generic_type_name # allow for arbitrarily long generic type names
- redundant_void_return
- empty_parentheses_with_trailing_closure
@ -168,7 +169,7 @@ file_length:
multiline_arguments:
first_argument_location: next_line
only_enforce_after_first_closure_on_first_line: true
private_over_fileprivate:
validate_extensions: true

View File

@ -4,6 +4,22 @@ All notable changes to this library will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this library adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
# 2.0.7 - 2024-01-29
## Added
- `Model.ScanSummary`
- `Model.WalletSummary.{PoolBalance, AccountBalance, WalletSummary}`
-
## Changed
- The `ZcashError` type has changed.
- Added variant `rustGetWalletSummary`
- Removed variants:
- `rustGetVerifiedBalance` (expect `rustGetWalletSummary` instead)
- `rustGetScanProgress` (expect `rustGetWalletSummary` instead)
- `rustGetBalance` (expect `rustGetWalletSummary` instead)
- The performance of `getWalletSummary` and `scanBlocks` have been improved.
# 2.0.6 - 2024-01-28
## Changed
@ -12,7 +28,7 @@ and this library adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
We focused on performance of the synchronization and found out a root cause in progress reporting. Simple change reduced the synchronization significantly by reporting less frequently. This affect the UX a bit because the % of the sync is updated only every 500 scanned blocks instead of every 100. Proper solution is going to be handled in #1353.
### [#1351] Recover from block stream issues
Async block stream grpc calls sometimes fail with unknown error 14, most of the times represented as `Transport became inactive` or `NIOHTTP2.StreamClosed`. Unless the service is truly down, these errors are usually false positive ones. The SDK was able to recover from this error with the next sync triggered but it takes 10-30s to happen. This delay is unnecessary so we made 2 changes. When these errors are caught the next sync is triggered immediately (at most 3 times) + the error state is not passed to the clients.
Async block stream grpc calls sometimes fail with unknown error 14, most of the times represented as `Transport became inactive` or `NIOHTTP2.StreamClosed`. Unless the service is truly down, these errors are usually false positive ones. The SDK was able to recover from this error with the next sync triggered but it takes 10-30s to happen. This delay is unnecessary so we made 2 changes. When these errors are caught the next sync is triggered immediately (at most 3 times) + the error state is not passed to the clients.
## Checkpoints
@ -38,7 +54,7 @@ Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2690000.json
## Added
### [#1336] Tweaks for sdk metrics
Shielded verified and total balances are logged for every sync of `SDKMetrics`.
Shielded verified and total balances are logged for every sync of `SDKMetrics`.
## Checkpoints
@ -51,12 +67,12 @@ Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2630000.json
# 2.0.4 - 2023-12-12
## Changed
The `SDKMetrics` logs data using os_log. The public API `enableMetrics()` and `disableMetrics()` no longer exist. All metrics are automatically logged for every sync run. Extraction of the metrics is up to the client/dev - done by using `OSLogStore`.
The `SDKMetrics` logs data using os_log. The public API `enableMetrics()` and `disableMetrics()` no longer exist. All metrics are automatically logged for every sync run. Extraction of the metrics is up to the client/dev - done by using `OSLogStore`.
## Added
### [#1325] Log metrics
The sync process is measured and detailed metrics are logged for every sync run. The data are logged using os_log so any client can export it. Verbose logs are under `sdkLogs_default` category, `default` level. Sync specific logs use `error` level.
The sync process is measured and detailed metrics are logged for every sync run. The data are logged using os_log so any client can export it. Verbose logs are under `sdkLogs_default` category, `default` level. Sync specific logs use `error` level.
## Checkpoints
@ -85,7 +101,7 @@ Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2620000.json
The enhancing of the transactions now processes all the blocks suggested by scan ranges. The issue was that when new scan ranges were suggested the value that drives the enhancing range computation wasn't reset, so when higher ranges were processed, the lower ranges were skipped. This fix ensures all transaction data are properly set, as well as fixing eventStream `.foundTransaction` reporting.
### Fix incorrect note deduplication in v_transactions (librustzcash)
This is a fix in the rust layer. The amount sent in the transaction was incorrectly reported even though the actual amount was sent properly. Now clients should see the amount they expect to see in the UI.
This is a fix in the rust layer. The amount sent in the transaction was incorrectly reported even though the actual amount was sent properly. Now clients should see the amount they expect to see in the UI.
## Checkpoints
@ -111,12 +127,12 @@ Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2550000.json
## Changed
### [#1303] Don't invalidate the timer with the error
The SDK has some simple logic of retrying when some erros occurs. There were 5 attempts of retry until the SDK stopped the synchronization process completely. (The timer is not restarted after those). That approach led to some annoying UX issue of manually starting the SDKSynchronizer from the client, shifting the responsibility to the devs/clients. This has been changed, the SDK never stops the timer unless `synchronizer.stop()` is called.
The SDK has some simple logic of retrying when some erros occurs. There were 5 attempts of retry until the SDK stopped the synchronization process completely. (The timer is not restarted after those). That approach led to some annoying UX issue of manually starting the SDKSynchronizer from the client, shifting the responsibility to the devs/clients. This has been changed, the SDK never stops the timer unless `synchronizer.stop()` is called.
## Fixed
### [#1301] foundTransactions don't emit after rewind
The `.foundTransactions` observed on eventStream worked well during the sync until the rewind was called. That API missed reset of the ActionContext in the CompactBlockProcesser and that led to never observing the same transactions again. This ticket fixed the problem, reset is called in the rewind and new sync passes the transactions to the stream.
The `.foundTransactions` observed on eventStream worked well during the sync until the rewind was called. That API missed reset of the ActionContext in the CompactBlockProcesser and that led to never observing the same transactions again. This ticket fixed the problem, reset is called in the rewind and new sync passes the transactions to the stream.
# 2.0.1 - 2023-10-03
@ -124,9 +140,9 @@ The `.foundTransactions` observed on eventStream worked well during the sync unt
### [#1294] Remove all uses of the incorrect 1000-ZAT fee
The 1000 Zatoshi fee proposed in ZIP-313 is deprecated now and so the minimum is 10k Zatoshi, defined in ZIP-317.
The SDK has been cleaned up from deprecated fee but note, real fee is handled in a rust layer.
The SDK has been cleaned up from deprecated fee but note, real fee is handled in a rust layer.
The public API `NetworkConstants.defaultFee(for: BlockHeight)` has been refactored to `NetworkConstants.defaultFee()`.
# 2.0.0 - 2023-09-25
## Notable Changes
@ -136,7 +152,7 @@ synchronization algorithm.
## Changed
Updated dependencies:
Updated dependencies:
- `zcash-light-client-ffi 0.4.0`
`CompactBlockProcessor` now processes compact blocks from the lightwalletd server with Spend-before-Sync algorithm (i.e. non-linear order). This feature shortens the time after which a wallet's spendable balance can be used.
@ -170,8 +186,8 @@ so both Height and Time don't make sense anymore.
### [#1230] Remove linear sync from the SDK
- `latestScannedHeight` and `latestScannedTime` have been removed from the
SynchronizerState.
- The concept of pending transaction has changed: `func allPendingTransactions()`
SynchronizerState.
- The concept of pending transaction has changed: `func allPendingTransactions()`
is no longer available. Use `public func allTransactions()` instead.
# 0.22.0-beta

View File

@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/grpc/grpc-swift.git",
"state" : {
"revision" : "84bac657e9930d26e9124bac082f26586dc2d209",
"version" : "1.19.1"
"revision" : "6ade19f0b57f5fc436dfecfced83f3c84d1095b9",
"version" : "1.21.0"
}
},
{
@ -68,8 +68,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "6c89474e62719ddcc1e9614989fff2f68208fe10",
"version" : "1.1.0"
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
"version" : "1.2.0"
}
},
{
@ -77,8 +77,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2",
"version" : "1.0.4"
"revision" : "d029d9d39c87bed85b1c50adee7c41795261a192",
"version" : "1.0.6"
}
},
{
@ -90,13 +90,22 @@
"version" : "2.6.0"
}
},
{
"identity" : "swift-http-types",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-http-types",
"state" : {
"revision" : "12358d55a3824bd5fed310b999ea8cf83a9a1a65",
"version" : "1.0.3"
}
},
{
"identity" : "swift-log",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "532d8b529501fb73a2455b179e0bbb6d49b652ed",
"version" : "1.5.3"
"revision" : "e97a6fcb1ab07462881ac165fdbb37f067e205d5",
"version" : "1.5.4"
}
},
{
@ -104,8 +113,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio.git",
"state" : {
"revision" : "3db5c4aeee8100d2db6f1eaf3864afdad5dc68fd",
"version" : "2.59.0"
"revision" : "635b2589494c97e48c62514bc8b37ced762e0a62",
"version" : "2.63.0"
}
},
{
@ -113,8 +122,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-extras.git",
"state" : {
"revision" : "fb70a0f5e984f23be48b11b4f1909f3bee016178",
"version" : "1.19.1"
"revision" : "363da63c1966405764f380c627409b2f9d9e710b",
"version" : "1.21.0"
}
},
{
@ -122,8 +131,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-http2.git",
"state" : {
"revision" : "a8ccf13fa62775277a5d56844878c828bbb3be1a",
"version" : "1.27.0"
"revision" : "0904bf0feb5122b7e5c3f15db7df0eabe623dd87",
"version" : "1.30.0"
}
},
{
@ -131,8 +140,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-ssl.git",
"state" : {
"revision" : "320bd978cceb8e88c125dcbb774943a92f6286e9",
"version" : "2.25.0"
"revision" : "7c381eb6083542b124a6c18fae742f55001dc2b5",
"version" : "2.26.0"
}
},
{
@ -140,8 +149,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-transport-services.git",
"state" : {
"revision" : "e7403c35ca6bb539a7ca353b91cc2d8ec0362d58",
"version" : "1.19.0"
"revision" : "6cbe0ed2b394f21ab0d46b9f0c50c6be964968ce",
"version" : "1.20.1"
}
},
{
@ -149,8 +158,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "cf62cdaea48b77f1a631e5cb3aeda6047c2cba1d",
"version" : "1.23.0"
"revision" : "65e8f29b2d63c4e38e736b25c27b83e012159be8",
"version" : "1.25.2"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system.git",
"state" : {
"revision" : "025bcb1165deab2e20d4eaba79967ce73013f496",
"version" : "1.2.1"
}
},
{
@ -158,8 +176,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : {
"revision" : "514dcd7e6fbfa252bf36d9f00d6b98f465a70704",
"version" : "0.4.1"
"revision" : "9a987c24240992a5eb3ff447e93f581d49a23acd",
"version" : "0.5.0"
}
}
],

View File

@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/grpc/grpc-swift.git",
"state" : {
"revision" : "84bac657e9930d26e9124bac082f26586dc2d209",
"version" : "1.19.1"
"revision" : "6ade19f0b57f5fc436dfecfced83f3c84d1095b9",
"version" : "1.21.0"
}
},
{
@ -23,8 +23,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "6c89474e62719ddcc1e9614989fff2f68208fe10",
"version" : "1.1.0"
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
"version" : "1.2.0"
}
},
{
@ -32,8 +32,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2",
"version" : "1.0.4"
"revision" : "d029d9d39c87bed85b1c50adee7c41795261a192",
"version" : "1.0.6"
}
},
{
"identity" : "swift-http-types",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-http-types",
"state" : {
"revision" : "12358d55a3824bd5fed310b999ea8cf83a9a1a65",
"version" : "1.0.3"
}
},
{
@ -41,8 +50,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "532d8b529501fb73a2455b179e0bbb6d49b652ed",
"version" : "1.5.3"
"revision" : "e97a6fcb1ab07462881ac165fdbb37f067e205d5",
"version" : "1.5.4"
}
},
{
@ -50,8 +59,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio.git",
"state" : {
"revision" : "3db5c4aeee8100d2db6f1eaf3864afdad5dc68fd",
"version" : "2.59.0"
"revision" : "635b2589494c97e48c62514bc8b37ced762e0a62",
"version" : "2.63.0"
}
},
{
@ -59,8 +68,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-extras.git",
"state" : {
"revision" : "fb70a0f5e984f23be48b11b4f1909f3bee016178",
"version" : "1.19.1"
"revision" : "363da63c1966405764f380c627409b2f9d9e710b",
"version" : "1.21.0"
}
},
{
@ -68,8 +77,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-http2.git",
"state" : {
"revision" : "a8ccf13fa62775277a5d56844878c828bbb3be1a",
"version" : "1.27.0"
"revision" : "0904bf0feb5122b7e5c3f15db7df0eabe623dd87",
"version" : "1.30.0"
}
},
{
@ -77,8 +86,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-ssl.git",
"state" : {
"revision" : "320bd978cceb8e88c125dcbb774943a92f6286e9",
"version" : "2.25.0"
"revision" : "7c381eb6083542b124a6c18fae742f55001dc2b5",
"version" : "2.26.0"
}
},
{
@ -86,8 +95,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-transport-services.git",
"state" : {
"revision" : "e7403c35ca6bb539a7ca353b91cc2d8ec0362d58",
"version" : "1.19.0"
"revision" : "6cbe0ed2b394f21ab0d46b9f0c50c6be964968ce",
"version" : "1.20.1"
}
},
{
@ -95,8 +104,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "cf62cdaea48b77f1a631e5cb3aeda6047c2cba1d",
"version" : "1.23.0"
"revision" : "65e8f29b2d63c4e38e736b25c27b83e012159be8",
"version" : "1.25.2"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system.git",
"state" : {
"revision" : "025bcb1165deab2e20d4eaba79967ce73013f496",
"version" : "1.2.1"
}
},
{
@ -104,8 +122,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : {
"revision" : "9bc5877ef6302e877922f79ebead52e50bce94fd",
"version" : "0.4.0"
"revision" : "9a987c24240992a5eb3ff447e93f581d49a23acd",
"version" : "0.5.0"
}
}
],

View File

@ -16,7 +16,7 @@ let package = Package(
dependencies: [
.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/zcash-hackworks/zcash-light-client-ffi", from: "0.4.1")
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", exact: "0.5.0")
],
targets: [
.target(
@ -28,6 +28,7 @@ let package = Package(
],
exclude: [
"Modules/Service/GRPC/ProtoBuf/proto/compact_formats.proto",
"Modules/Service/GRPC/ProtoBuf/proto/proposal.proto",
"Modules/Service/GRPC/ProtoBuf/proto/service.proto",
"Error/Sourcery/"
],

View File

@ -63,7 +63,7 @@ extension ScanAction: Action {
let incrementedProcessedHeight = processedHeight + BlockHeight(increment)
await context.update(processedHeight: incrementedProcessedHeight)
await self?.latestBlocksDataProvider.updateScannedData()
// ScanAction is controlled locally so it must report back the updated scanned height
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
if progressReportReducer == 0 {
// 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()
logger.debug("progress: \(progress)")
await didUpdate(.syncProgress(progress))

View File

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

View File

@ -1,6 +1,6 @@
//
// HandleSaplingParametersIfNeeded.swift
//
// SaplingParametersHandler.swift
//
//
// Created by Lukáš Korba on 23.11.2022.
//
@ -28,11 +28,13 @@ extension SaplingParametersHandlerImpl: SaplingParametersHandler {
try Task.checkCancellation()
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))
// Download Sapling parameters only if sapling funds are detected.
guard totalShieldedBalance > 0 || totalTransparentBalance > 0 else { return }
guard totalSaplingBalance > 0 || totalTransparentBalance > 0 else { return }
} catch {
// 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

View File

@ -51,9 +51,12 @@ extension BlockScannerImpl: BlockScanner {
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()
do {
try await self.rustBackend.scanBlocks(fromHeight: Int32(startHeight), limit: batchSize)
scanSummary = try await self.rustBackend.scanBlocks(fromHeight: Int32(startHeight), limit: batchSize)
} catch {
logger.debug("block scanning failed with error: \(String(describing: error))")
throw error
@ -61,10 +64,8 @@ extension BlockScannerImpl: BlockScanner {
let scanFinishTime = Date()
// TODO: [#1259] potential bug when rustBackend.scanBlocks scan less blocks than batchSize,
// https://github.com/zcash/ZcashLightClientKit/issues/1259
lastScannedHeight = startHeight + Int(batchSize) - 1
lastScannedHeight = scanSummary.scannedRange.upperBound - 1
scannedNewBlocks = previousScannedHeight != lastScannedHeight
if scannedNewBlocks {
try await didScan(lastScannedHeight, batchSize)

View File

@ -121,11 +121,6 @@ public enum ZcashError: Equatable, Error {
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0003
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
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0005
@ -153,11 +148,6 @@ public enum ZcashError: Equatable, Error {
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0011
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` is account passed to ZcashRustBackend.getVerifiedTransparentBalance.
/// ZRUST0013
@ -297,10 +287,6 @@ public enum ZcashError: Equatable, Error {
/// Invalid transaction ID length when calling ZcashRustBackend.getMemo. txId must be 32 bytes.
/// ZRUST0050
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
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0052
@ -317,6 +303,10 @@ public enum ZcashError: Equatable, Error {
/// - `progress` value reported
/// ZRUST0055
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.
/// - `sqliteError` is error produced by SQLite library.
/// ZADAO0001
@ -614,7 +604,6 @@ public enum ZcashError: Equatable, Error {
case .rustCreateAccount: return "Error from rust layer when calling ZcashRustBackend.createAccount"
case .rustCreateToAddress: return "Error from rust layer when calling ZcashRustBackend.createToAddress"
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 .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"
@ -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 .rustGetTransparentBalanceNegativeAccount: return "account parameter is lower than 0 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 .rustGetVerifiedTransparentBalance: return "Error from rust layer when calling ZcashRustBackend.getVerifiedTransparentBalance"
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 .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 .rustGetScanProgress: return "Error from rust layer when calling ZcashRustBackend.getScanProgress"
case .rustFullyScannedHeight: return "Error from rust layer when calling ZcashRustBackend.fullyScannedHeight"
case .rustMaxScannedHeight: return "Error from rust layer when calling ZcashRustBackend.maxScannedHeight"
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 .rustGetWalletSummary: return "Error from rust layer when calling ZcashRustBackend.getWalletSummary"
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 .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 .rustCreateToAddress: return .rustCreateToAddress
case .rustDecryptAndStoreTransaction: return .rustDecryptAndStoreTransaction
case .rustGetBalance: return .rustGetBalance
case .rustGetCurrentAddress: return .rustGetCurrentAddress
case .rustGetCurrentAddressInvalidAddress: return .rustGetCurrentAddressInvalidAddress
case .rustGetNearestRewindHeight: return .rustGetNearestRewindHeight
@ -793,7 +780,6 @@ public enum ZcashError: Equatable, Error {
case .rustGetNextAvailableAddressInvalidAddress: return .rustGetNextAvailableAddressInvalidAddress
case .rustGetTransparentBalanceNegativeAccount: return .rustGetTransparentBalanceNegativeAccount
case .rustGetTransparentBalance: return .rustGetTransparentBalance
case .rustGetVerifiedBalance: return .rustGetVerifiedBalance
case .rustGetVerifiedTransparentBalanceNegativeAccount: return .rustGetVerifiedTransparentBalanceNegativeAccount
case .rustGetVerifiedTransparentBalance: return .rustGetVerifiedTransparentBalance
case .rustInitDataDb: return .rustInitDataDb
@ -831,11 +817,11 @@ public enum ZcashError: Equatable, Error {
case .rustUpdateChainTip: return .rustUpdateChainTip
case .rustSuggestScanRanges: return .rustSuggestScanRanges
case .rustGetMemoInvalidTxIdLength: return .rustGetMemoInvalidTxIdLength
case .rustGetScanProgress: return .rustGetScanProgress
case .rustFullyScannedHeight: return .rustFullyScannedHeight
case .rustMaxScannedHeight: return .rustMaxScannedHeight
case .rustLatestCachedBlockHeight: return .rustLatestCachedBlockHeight
case .rustScanProgressOutOfRange: return .rustScanProgressOutOfRange
case .rustGetWalletSummary: return .rustGetWalletSummary
case .accountDAOGetAll: return .accountDAOGetAll
case .accountDAOGetAllCantDecode: return .accountDAOGetAllCantDecode
case .accountDAOFindBy: return .accountDAOFindBy

View File

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

View File

@ -12,7 +12,7 @@ protocol SDKMetrics {
func actionStart(_ action: CBPState)
func actionDetail(_ detail: String, `for` action: CBPState)
func actionStop()
func logCBPOverviewReport(_ logger: Logger, shieldedBalance: WalletBalance) async
func logCBPOverviewReport(_ logger: Logger, walletSummary: WalletSummary?) async
}
final class SDKMetricsImpl: SDKMetrics {
@ -102,15 +102,16 @@ final class SDKMetricsImpl: SDKMetrics {
}
// swiftlint:disable string_concatenation
func logCBPOverviewReport(_ logger: Logger, shieldedBalance: WalletBalance) async {
func logCBPOverviewReport(_ logger: Logger, walletSummary: WalletSummary?) async {
actionStop()
let accountBalance = walletSummary?.accountBalances[0]
logger.sync(
"""
SYNC (\(syncs)) REPORT
finished in: \(Date().timeIntervalSince1970 - cbpStartTime)
verified balance: \(shieldedBalance.verified.amount)
total balance: \(shieldedBalance.total.amount)
verified balance: \(accountBalance?.saplingBalance.spendableValue.amount ?? 0)
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.
//
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 {
let numerator: UInt64
let denominator: UInt64
@ -27,3 +42,11 @@ struct ScanProgress: Equatable {
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)
}
func createToAddress(
usk: UnifiedSpendingKey,
func proposeTransfer(
account: Int32,
to address: String,
value: Int64,
memo: MemoBytes?
) async throws -> Data {
var contiguousTxIdBytes = ContiguousArray<UInt8>([UInt8](repeating: 0x0, count: 32))
) async throws -> FfiProposal {
globalDBLock.lock()
let success = contiguousTxIdBytes.withUnsafeMutableBufferPointer { txIdBytePtr in
usk.bytes.withUnsafeBufferPointer { uskPtr in
zcashlc_create_to_address(
dbData.0,
dbData.1,
uskPtr.baseAddress,
UInt(usk.bytes.count),
[CChar](address.utf8CString),
value,
memo?.bytes,
spendParamsPath.0,
spendParamsPath.1,
outputParamsPath.0,
outputParamsPath.1,
networkType.networkId,
minimumConfirmations,
useZIP317Fees,
txIdBytePtr.baseAddress
)
}
}
let proposal = zcashlc_propose_transfer(
dbData.0,
dbData.1,
account,
[CChar](address.utf8CString),
value,
memo?.bytes,
networkType.networkId,
minimumConfirmations,
useZIP317Fees
)
globalDBLock.unlock()
guard success else {
throw ZcashError.rustCreateToAddress(lastErrorMessage(fallback: "`createToAddress` failed with unknown error"))
guard let proposal else {
throw ZcashError.rustCreateToAddress(lastErrorMessage(fallback: "`proposeTransfer` failed with unknown error"))
}
return contiguousTxIdBytes.withUnsafeBufferPointer { txIdBytePtr in
Data(txIdBytePtr)
}
defer { zcashlc_free_boxed_slice(proposal) }
return try FfiProposal(contiguousBytes: Data(
bytes: proposal.pointee.ptr,
count: Int(proposal.pointee.len)
))
}
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 {
globalDBLock.lock()
let addressCStr = zcashlc_get_current_address(
@ -256,27 +235,6 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
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 {
guard account >= 0 else {
throw ZcashError.rustGetVerifiedTransparentBalanceNegativeAccount(Int(account))
@ -576,21 +534,35 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
}
func getScanProgress() async throws -> ScanProgress? {
func getWalletSummary() async throws -> WalletSummary? {
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()
if result.denominator == 0 {
switch result.numerator {
case 0:
return nil
default:
throw ZcashError.rustGetScanProgress(lastErrorMessage(fallback: "`getScanProgress` failed with unknown error"))
}
} else {
return ScanProgress(numerator: result.numerator, denominator: result.denominator)
guard let summaryPtr else {
throw ZcashError.rustGetWalletSummary(lastErrorMessage(fallback: "`getWalletSummary` failed with unknown error"))
}
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] {
@ -623,48 +595,90 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
return scanRanges
}
func scanBlocks(fromHeight: Int32, limit: UInt32 = 0) async throws {
func scanBlocks(fromHeight: Int32, limit: UInt32 = 0) async throws -> ScanSummary {
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()
guard result != 0 else {
guard let summaryPtr else {
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(
usk: UnifiedSpendingKey,
func proposeShielding(
account: Int32,
memo: MemoBytes?,
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 {
var contiguousTxIdBytes = ContiguousArray<UInt8>([UInt8](repeating: 0x0, count: 32))
let proposalBytes = try proposal.serializedData(partial: false).bytes
globalDBLock.lock()
let success = contiguousTxIdBytes.withUnsafeMutableBufferPointer { txIdBytePtr in
usk.bytes.withUnsafeBufferPointer { uskBuffer in
zcashlc_shield_funds(
dbData.0,
dbData.1,
uskBuffer.baseAddress,
UInt(usk.bytes.count),
memo?.bytes,
UInt64(shieldingThreshold.amount),
spendParamsPath.0,
spendParamsPath.1,
outputParamsPath.0,
outputParamsPath.1,
networkType.networkId,
minimumConfirmations,
useZIP317Fees,
txIdBytePtr.baseAddress
)
proposalBytes.withUnsafeBufferPointer { proposalPtr in
usk.bytes.withUnsafeBufferPointer { uskPtr in
zcashlc_create_proposed_transaction(
dbData.0,
dbData.1,
proposalPtr.baseAddress,
UInt(proposalBytes.count),
uskPtr.baseAddress,
UInt(usk.bytes.count),
spendParamsPath.0,
spendParamsPath.1,
outputParamsPath.0,
outputParamsPath.1,
networkType.networkId,
txIdBytePtr.baseAddress
)
}
}
}
globalDBLock.unlock()
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
@ -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`.
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.
/// - parameter tx: the transaction to decrypt
/// - parameter minedHeight: height on which this transaction was mined. this is used to fetch the consensus branch ID.
/// - Throws: `rustDecryptAndStoreTransaction`.
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.
/// - parameter account: index of the given account
/// - Throws:
@ -120,11 +102,6 @@ protocol ZcashRustBackendWelding {
/// - `rustListTransparentReceiversInvalidAddress` if transarent received generated by rust is invalid.
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
/// - parameter account: account index to query the balance for.
/// - Throws:
@ -167,8 +144,8 @@ protocol ZcashRustBackendWelding {
/// height due to the fact that out-of-order scanning can leave gaps.
func maxScannedHeight() async throws -> BlockHeight?
/// Returns the scan progress derived from the current wallet state.
func getScanProgress() async throws -> ScanProgress?
/// Returns the account balances and sync status of the wallet.
func getWalletSummary() async throws -> WalletSummary?
/// 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 limit: scan up to limit blocks.
/// - 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
/// - parameter txid: the txid bytes for the UTXO
@ -215,14 +192,42 @@ protocol ZcashRustBackendWelding {
height: BlockHeight
) async throws
/// Creates a transaction to shield all found UTXOs in data db for the account the provided `UnifiedSpendingKey` has spend authority for.
/// - Parameter usk: `UnifiedSpendingKey` that spend transparent funds and where the funds will be shielded to.
/// Select transaction inputs, compute fees, and construct a proposal for a transaction
/// 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
/// - Throws: `rustShieldFunds` if rust layer returns error.
func shieldFunds(
usk: UnifiedSpendingKey,
func proposeShielding(
account: Int32,
memo: MemoBytes?,
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
/// 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 {
let balance = try await initializer.rustBackend.getBalance(account: Int32(accountIndex))
return Zatoshi(balance)
try await initializer.rustBackend.getWalletSummary()?.accountBalances[UInt32(accountIndex)]?
.saplingBalance.total() ?? Zatoshi.zero
}
public func getShieldedVerifiedBalance(accountIndex: Int = 0) async throws -> Zatoshi {
let balance = try await initializer.rustBackend.getVerifiedBalance(account: Int32(accountIndex))
return Zatoshi(balance)
try await initializer.rustBackend.getWalletSummary()?.accountBalances[UInt32(accountIndex)]?
.saplingBalance.spendableValue ?? Zatoshi.zero
}
public func getUnifiedAddress(accountIndex: Int) async throws -> UnifiedAddress {

View File

@ -85,13 +85,19 @@ class WalletTransactionEncoder: TransactionEncoder {
throw ZcashError.walletTransEncoderCreateTransactionMissingSaplingParams
}
let txId = try await rustBackend.createToAddress(
usk: spendingKey,
// TODO: Expose the proposal in a way that enables querying its fee.
let proposal = try await rustBackend.proposeTransfer(
account: Int32(spendingKey.account),
to: address,
value: zatoshi.amount,
memo: memoBytes
)
let txId = try await rustBackend.createProposedTransaction(
proposal: proposal,
usk: spendingKey
)
return txId
}
@ -121,13 +127,19 @@ class WalletTransactionEncoder: TransactionEncoder {
guard ensureParams(spend: self.spendParamsURL, output: self.outputParamsURL) else {
throw ZcashError.walletTransEncoderShieldFundsMissingSaplingParams
}
let txId = try await rustBackend.shieldFunds(
usk: spendingKey,
// TODO: Expose the proposal in a way that enables querying its fee.
let proposal = try await rustBackend.proposeShielding(
account: Int32(spendingKey.account),
memo: memo,
shieldingThreshold: shieldingThreshold
)
let txId = try await rustBackend.createProposedTransaction(
proposal: proposal,
usk: spendingKey
)
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
import Combine
@testable import ZcashLightClientKit
@ -1191,17 +1191,17 @@ class SDKMetricsMock: SDKMetrics {
// MARK: - logCBPOverviewReport
var logCBPOverviewReportShieldedBalanceCallsCount = 0
var logCBPOverviewReportShieldedBalanceCalled: Bool {
return logCBPOverviewReportShieldedBalanceCallsCount > 0
var logCBPOverviewReportWalletSummaryCallsCount = 0
var logCBPOverviewReportWalletSummaryCalled: Bool {
return logCBPOverviewReportWalletSummaryCallsCount > 0
}
var logCBPOverviewReportShieldedBalanceReceivedArguments: (logger: Logger, shieldedBalance: WalletBalance)?
var logCBPOverviewReportShieldedBalanceClosure: ((Logger, WalletBalance) async -> Void)?
var logCBPOverviewReportWalletSummaryReceivedArguments: (logger: Logger, walletSummary: WalletSummary?)?
var logCBPOverviewReportWalletSummaryClosure: ((Logger, WalletSummary?) async -> Void)?
func logCBPOverviewReport(_ logger: Logger, shieldedBalance: WalletBalance) async {
logCBPOverviewReportShieldedBalanceCallsCount += 1
logCBPOverviewReportShieldedBalanceReceivedArguments = (logger: logger, shieldedBalance: shieldedBalance)
await logCBPOverviewReportShieldedBalanceClosure!(logger, shieldedBalance)
func logCBPOverviewReport(_ logger: Logger, walletSummary: WalletSummary?) async {
logCBPOverviewReportWalletSummaryCallsCount += 1
logCBPOverviewReportWalletSummaryReceivedArguments = (logger: logger, walletSummary: walletSummary)
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
var decryptAndStoreTransactionTxBytesMinedHeightThrowableError: Error?
@ -2237,39 +2204,6 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
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
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
var getVerifiedTransparentBalanceAccountThrowableError: Error?
@ -2729,34 +2630,34 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
}
}
// MARK: - getScanProgress
// MARK: - getWalletSummary
var getScanProgressThrowableError: Error?
func setGetScanProgressThrowableError(_ param: Error?) async {
getScanProgressThrowableError = param
var getWalletSummaryThrowableError: Error?
func setGetWalletSummaryThrowableError(_ param: Error?) async {
getWalletSummaryThrowableError = param
}
var getScanProgressCallsCount = 0
var getScanProgressCalled: Bool {
return getScanProgressCallsCount > 0
var getWalletSummaryCallsCount = 0
var getWalletSummaryCalled: Bool {
return getWalletSummaryCallsCount > 0
}
var getScanProgressReturnValue: ScanProgress?
func setGetScanProgressReturnValue(_ param: ScanProgress?) async {
getScanProgressReturnValue = param
var getWalletSummaryReturnValue: WalletSummary?
func setGetWalletSummaryReturnValue(_ param: WalletSummary?) async {
getWalletSummaryReturnValue = param
}
var getScanProgressClosure: (() async throws -> ScanProgress?)?
func setGetScanProgressClosure(_ param: (() async throws -> ScanProgress?)?) async {
getScanProgressClosure = param
var getWalletSummaryClosure: (() async throws -> WalletSummary?)?
func setGetWalletSummaryClosure(_ param: (() async throws -> WalletSummary?)?) async {
getWalletSummaryClosure = param
}
func getScanProgress() async throws -> ScanProgress? {
if let error = getScanProgressThrowableError {
func getWalletSummary() async throws -> WalletSummary? {
if let error = getWalletSummaryThrowableError {
throw error
}
getScanProgressCallsCount += 1
if let closure = getScanProgressClosure {
getWalletSummaryCallsCount += 1
if let closure = getWalletSummaryClosure {
return try await closure()
} else {
return getScanProgressReturnValue
return getWalletSummaryReturnValue
}
}
@ -2802,18 +2703,26 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
return scanBlocksFromHeightLimitCallsCount > 0
}
var scanBlocksFromHeightLimitReceivedArguments: (fromHeight: Int32, limit: UInt32)?
var scanBlocksFromHeightLimitClosure: ((Int32, UInt32) async throws -> Void)?
func setScanBlocksFromHeightLimitClosure(_ param: ((Int32, UInt32) async throws -> Void)?) async {
var scanBlocksFromHeightLimitReturnValue: ScanSummary!
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
}
func scanBlocks(fromHeight: Int32, limit: UInt32) async throws {
func scanBlocks(fromHeight: Int32, limit: UInt32) async throws -> ScanSummary {
if let error = scanBlocksFromHeightLimitThrowableError {
throw error
}
scanBlocksFromHeightLimitCallsCount += 1
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
@ -2841,36 +2750,102 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
try await putUnspentTransparentOutputTxidIndexScriptValueHeightClosure!(txid, index, script, value, height)
}
// MARK: - shieldFunds
// MARK: - proposeTransfer
var shieldFundsUskMemoShieldingThresholdThrowableError: Error?
func setShieldFundsUskMemoShieldingThresholdThrowableError(_ param: Error?) async {
shieldFundsUskMemoShieldingThresholdThrowableError = param
var proposeTransferAccountToValueMemoThrowableError: Error?
func setProposeTransferAccountToValueMemoThrowableError(_ param: Error?) async {
proposeTransferAccountToValueMemoThrowableError = param
}
var shieldFundsUskMemoShieldingThresholdCallsCount = 0
var shieldFundsUskMemoShieldingThresholdCalled: Bool {
return shieldFundsUskMemoShieldingThresholdCallsCount > 0
var proposeTransferAccountToValueMemoCallsCount = 0
var proposeTransferAccountToValueMemoCalled: Bool {
return proposeTransferAccountToValueMemoCallsCount > 0
}
var shieldFundsUskMemoShieldingThresholdReceivedArguments: (usk: UnifiedSpendingKey, memo: MemoBytes?, shieldingThreshold: Zatoshi)?
var shieldFundsUskMemoShieldingThresholdReturnValue: Data!
func setShieldFundsUskMemoShieldingThresholdReturnValue(_ param: Data) async {
shieldFundsUskMemoShieldingThresholdReturnValue = param
var proposeTransferAccountToValueMemoReceivedArguments: (account: Int32, address: String, value: Int64, memo: MemoBytes?)?
var proposeTransferAccountToValueMemoReturnValue: FfiProposal!
func setProposeTransferAccountToValueMemoReturnValue(_ param: FfiProposal) async {
proposeTransferAccountToValueMemoReturnValue = param
}
var shieldFundsUskMemoShieldingThresholdClosure: ((UnifiedSpendingKey, MemoBytes?, Zatoshi) async throws -> Data)?
func setShieldFundsUskMemoShieldingThresholdClosure(_ param: ((UnifiedSpendingKey, MemoBytes?, Zatoshi) async throws -> Data)?) async {
shieldFundsUskMemoShieldingThresholdClosure = param
var proposeTransferAccountToValueMemoClosure: ((Int32, String, Int64, MemoBytes?) async throws -> FfiProposal)?
func setProposeTransferAccountToValueMemoClosure(_ param: ((Int32, String, Int64, MemoBytes?) async throws -> FfiProposal)?) async {
proposeTransferAccountToValueMemoClosure = param
}
func shieldFunds(usk: UnifiedSpendingKey, memo: MemoBytes?, shieldingThreshold: Zatoshi) async throws -> Data {
if let error = shieldFundsUskMemoShieldingThresholdThrowableError {
func proposeTransfer(account: Int32, to address: String, value: Int64, memo: MemoBytes?) async throws -> FfiProposal {
if let error = proposeTransferAccountToValueMemoThrowableError {
throw error
}
shieldFundsUskMemoShieldingThresholdCallsCount += 1
shieldFundsUskMemoShieldingThresholdReceivedArguments = (usk: usk, memo: memo, shieldingThreshold: shieldingThreshold)
if let closure = shieldFundsUskMemoShieldingThresholdClosure {
return try await closure(usk, memo, shieldingThreshold)
proposeTransferAccountToValueMemoCallsCount += 1
proposeTransferAccountToValueMemoReceivedArguments = (account: account, address: address, value: value, memo: memo)
if let closure = proposeTransferAccountToValueMemoClosure {
return try await closure(account, address, value, memo)
} 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}
cd "${scriptDir}"
sourcery_version=2.0.3
sourcery_version=2.1.4
if which sourcery >/dev/null; then
if [[ $(sourcery --version) != $sourcery_version ]]; then

View File

@ -75,7 +75,6 @@ class RustBackendMockHelper {
await rustBackendMock.setInitBlockMetadataDbClosure() { }
await rustBackendMock.setWriteBlocksMetadataBlocksClosure() { _ in }
await rustBackendMock.setGetTransparentBalanceAccountReturnValue(0)
await rustBackendMock.setGetVerifiedBalanceAccountReturnValue(0)
await rustBackendMock.setListTransparentReceiversAccountReturnValue([])
await rustBackendMock.setGetCurrentAddressAccountThrowableError(ZcashError.rustGetCurrentAddress("mocked error"))
await rustBackendMock.setGetNextAvailableAddressAccountThrowableError(ZcashError.rustGetNextAvailableAddress("mocked error"))
@ -84,22 +83,15 @@ class RustBackendMockHelper {
await rustBackendMock.setInitDataDbSeedReturnValue(.seedRequired)
await rustBackendMock.setGetNearestRewindHeightHeightReturnValue(-1)
await rustBackendMock.setPutUnspentTransparentOutputTxidIndexScriptValueHeightClosure() { _, _, _, _, _ in }
await rustBackendMock.setCreateToAddressUskToValueMemoThrowableError(ZcashError.rustCreateToAddress("mocked error"))
await rustBackendMock.setShieldFundsUskMemoShieldingThresholdThrowableError(ZcashError.rustShieldFunds("mocked error"))
await rustBackendMock.setProposeTransferAccountToValueMemoThrowableError(ZcashError.rustCreateToAddress("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.setInitDataDbSeedClosure() { seed in
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
try await rustBackend.rewindToHeight(height: height)
}