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",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : {
"revision" : "514dcd7e6fbfa252bf36d9f00d6b98f465a70704",
"version" : "0.4.1"
"revision" : "5f58e32ffae1b285cae1c01bbf350eaae31ffeb5"
}
}
],

View File

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

View File

@ -16,7 +16,8 @@ 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")
// Compiled from revision `d5bd88138610b15f9585fcba696bd1023e716fd8`.
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", revision: "5f58e32ffae1b285cae1c01bbf350eaae31ffeb5")
],
targets: [
.target(
@ -28,6 +29,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)
}