Merge pull request #1382 from Electric-Coin-Company/1204-expose-proposals
Expose APIs for working with transaction proposals
This commit is contained in:
commit
7fcf1fad02
22
CHANGELOG.md
22
CHANGELOG.md
|
@ -4,6 +4,28 @@ 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).
|
||||
|
||||
# Unreleased
|
||||
|
||||
## Changed
|
||||
- Migrated to `zcash-light-client-ffi 0.6.0`.
|
||||
|
||||
### [#1186] Enable ZIP 317 fees
|
||||
- The SDK now generates transactions using [ZIP 317](https://zips.z.cash/zip-0317) fees,
|
||||
instead of a fixed fee of 10,000 Zatoshi. Use `Proposal.totalFeeRequired` to check the
|
||||
total fee for a transfer before creating it.
|
||||
|
||||
## Added
|
||||
|
||||
### [#1204] Expose APIs for working with transaction proposals
|
||||
New `Synchronizer` APIs that enable constructing a proposal for transferring or
|
||||
shielding funds, and then creating transactions from a proposal. The intermediate
|
||||
proposal can be used to determine the required fee, before committing to producing
|
||||
transactions.
|
||||
|
||||
The old `Synchronizer.sendToAddress` and `Synchronizer.shieldFunds` APIs have been
|
||||
deprecated, and will be removed in 2.1.0 (which will create multiple transactions
|
||||
at once for some recipients).
|
||||
|
||||
# 2.0.10 - 2024-02-12
|
||||
|
||||
## Added
|
||||
|
|
|
@ -176,8 +176,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||
"state" : {
|
||||
"revision" : "c90afd6cc092468e71810bc715ddb49be8210b75",
|
||||
"version" : "0.5.1"
|
||||
"revision" : "7c801be1f445402a433b32835a50d832e8a50437",
|
||||
"version" : "0.6.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -122,8 +122,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||
"state" : {
|
||||
"revision" : "c90afd6cc092468e71810bc715ddb49be8210b75",
|
||||
"version" : "0.5.1"
|
||||
"revision" : "7c801be1f445402a433b32835a50d832e8a50437",
|
||||
"version" : "0.6.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -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", exact: "0.5.1")
|
||||
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", exact: "0.6.0")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
|
|
@ -36,6 +36,64 @@ public protocol ClosureSynchronizer {
|
|||
func getUnifiedAddress(accountIndex: Int, completion: @escaping (Result<UnifiedAddress, Error>) -> Void)
|
||||
func getTransparentAddress(accountIndex: Int, completion: @escaping (Result<TransparentAddress, Error>) -> Void)
|
||||
|
||||
/// Creates a proposal for transferring funds to the given recipient.
|
||||
///
|
||||
/// - Parameter accountIndex: the account from which to transfer funds.
|
||||
/// - Parameter recipient: the recipient's address.
|
||||
/// - Parameter amount: the amount to send in Zatoshi.
|
||||
/// - Parameter memo: an optional memo to include as part of the proposal's transactions. Use `nil` when sending to transparent receivers otherwise the function will throw an error.
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
func proposeTransfer(
|
||||
accountIndex: Int,
|
||||
recipient: Recipient,
|
||||
amount: Zatoshi,
|
||||
memo: Memo?,
|
||||
completion: @escaping (Result<Proposal, Error>) -> Void
|
||||
)
|
||||
|
||||
/// Creates a proposal for shielding any transparent funds received by the given account.
|
||||
///
|
||||
/// - Parameter accountIndex: the account for which to shield funds.
|
||||
/// - Parameter shieldingThreshold: the minimum transparent balance required before a proposal will be created.
|
||||
/// - Parameter memo: an optional memo to include as part of the proposal's transactions.
|
||||
/// - Parameter transparentReceiver: a specific transparent receiver within the account
|
||||
/// that should be the source of transparent funds. Default is `nil` which
|
||||
/// will select whichever of the account's transparent receivers has funds
|
||||
/// to shield.
|
||||
///
|
||||
/// Returns the proposal, or `nil` if the transparent balance that would be shielded
|
||||
/// is zero or below `shieldingThreshold`.
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
func proposeShielding(
|
||||
accountIndex: Int,
|
||||
shieldingThreshold: Zatoshi,
|
||||
memo: Memo,
|
||||
transparentReceiver: TransparentAddress?,
|
||||
completion: @escaping (Result<Proposal?, Error>) -> Void
|
||||
)
|
||||
|
||||
/// Creates the transactions in the given proposal.
|
||||
///
|
||||
/// - Parameter proposal: the proposal for which to create transactions.
|
||||
/// - Parameter spendingKey: the `UnifiedSpendingKey` associated with the account for which the proposal was created.
|
||||
///
|
||||
/// Returns a stream of objects for the transactions that were created as part of the
|
||||
/// proposal, indicating whether they were submitted to the network or if an error
|
||||
/// occurred.
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance
|
||||
/// or since the last wipe then this method throws `SynchronizerErrors.notPrepared`.
|
||||
func createProposedTransactions(
|
||||
proposal: Proposal,
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
completion: @escaping (Result<AsyncThrowingStream<TransactionSubmitResult, Error>, Error>) -> Void
|
||||
)
|
||||
|
||||
@available(*, deprecated, message: "Upcoming SDK 2.1 will create multiple transactions at once for some recipients.")
|
||||
func sendToAddress(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
|
@ -44,6 +102,7 @@ public protocol ClosureSynchronizer {
|
|||
completion: @escaping (Result<ZcashTransaction.Overview, Error>) -> Void
|
||||
)
|
||||
|
||||
@available(*, deprecated, message: "Upcoming SDK 2.1 will create multiple transactions at once for some recipients.")
|
||||
func shieldFunds(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
memo: Memo,
|
||||
|
|
|
@ -35,6 +35,61 @@ public protocol CombineSynchronizer {
|
|||
func getUnifiedAddress(accountIndex: Int) -> SinglePublisher<UnifiedAddress, Error>
|
||||
func getTransparentAddress(accountIndex: Int) -> SinglePublisher<TransparentAddress, Error>
|
||||
|
||||
/// Creates a proposal for transferring funds to the given recipient.
|
||||
///
|
||||
/// - Parameter accountIndex: the account from which to transfer funds.
|
||||
/// - Parameter recipient: the recipient's address.
|
||||
/// - Parameter amount: the amount to send in Zatoshi.
|
||||
/// - Parameter memo: an optional memo to include as part of the proposal's transactions. Use `nil` when sending to transparent receivers otherwise the function will throw an error.
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
func proposeTransfer(
|
||||
accountIndex: Int,
|
||||
recipient: Recipient,
|
||||
amount: Zatoshi,
|
||||
memo: Memo?
|
||||
) -> SinglePublisher<Proposal, Error>
|
||||
|
||||
/// Creates a proposal for shielding any transparent funds received by the given account.
|
||||
///
|
||||
/// - Parameter accountIndex: the account for which to shield funds.
|
||||
/// - Parameter shieldingThreshold: the minimum transparent balance required before a proposal will be created.
|
||||
/// - Parameter memo: an optional memo to include as part of the proposal's transactions.
|
||||
/// - Parameter transparentReceiver: a specific transparent receiver within the account
|
||||
/// that should be the source of transparent funds. Default is `nil` which
|
||||
/// will select whichever of the account's transparent receivers has funds
|
||||
/// to shield.
|
||||
///
|
||||
/// Returns the proposal, or `nil` if the transparent balance that would be shielded
|
||||
/// is zero or below `shieldingThreshold`.
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
func proposeShielding(
|
||||
accountIndex: Int,
|
||||
shieldingThreshold: Zatoshi,
|
||||
memo: Memo,
|
||||
transparentReceiver: TransparentAddress?
|
||||
) -> SinglePublisher<Proposal?, Error>
|
||||
|
||||
/// Creates the transactions in the given proposal.
|
||||
///
|
||||
/// - Parameter proposal: the proposal for which to create transactions.
|
||||
/// - Parameter spendingKey: the `UnifiedSpendingKey` associated with the account for which the proposal was created.
|
||||
///
|
||||
/// Returns a stream of objects for the transactions that were created as part of the
|
||||
/// proposal, indicating whether they were submitted to the network or if an error
|
||||
/// occurred.
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance
|
||||
/// or since the last wipe then this method throws `SynchronizerErrors.notPrepared`.
|
||||
func createProposedTransactions(
|
||||
proposal: Proposal,
|
||||
spendingKey: UnifiedSpendingKey
|
||||
) -> SinglePublisher<AsyncThrowingStream<TransactionSubmitResult, Error>, Error>
|
||||
|
||||
@available(*, deprecated, message: "Upcoming SDK 2.1 will create multiple transactions at once for some recipients.")
|
||||
func sendToAddress(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
|
@ -42,6 +97,7 @@ public protocol CombineSynchronizer {
|
|||
memo: Memo?
|
||||
) -> SinglePublisher<ZcashTransaction.Overview, Error>
|
||||
|
||||
@available(*, deprecated, message: "Upcoming SDK 2.1 will create multiple transactions at once for some recipients.")
|
||||
func shieldFunds(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
memo: Memo,
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Proposal.swift
|
||||
//
|
||||
//
|
||||
// Created by Jack Grigg on 20/02/2024.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// A data structure that describes a series of transactions to be created.
|
||||
public struct Proposal: Equatable {
|
||||
let inner: FfiProposal
|
||||
|
||||
/// Returns the number of transactions that this proposal will create.
|
||||
///
|
||||
/// This is equal to the number of `TransactionSubmitResult`s that will be returned
|
||||
/// from `Synchronizer.createProposedTransactions`.
|
||||
///
|
||||
/// Proposals always create at least one transaction.
|
||||
public func transactionCount() -> Int {
|
||||
inner.steps.count
|
||||
}
|
||||
|
||||
/// Returns the total fee to be paid across all proposed transactions, in zatoshis.
|
||||
public func totalFeeRequired() -> Zatoshi {
|
||||
inner.steps.reduce(Zatoshi.zero) { acc, step in
|
||||
acc + Zatoshi(Int64(step.balance.feeRequired))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Proposal {
|
||||
/// IMPORTANT: This function is for testing purposes only. It produces fake invalid
|
||||
/// data that can be used to check UI elements, but will always produce an error when
|
||||
/// passed to `Synchronizer.createProposedTransactions`. It should never be called in
|
||||
/// production code.
|
||||
static func testOnlyFakeProposal(totalFee: UInt64) -> Self {
|
||||
var ffiProposal = FfiProposal()
|
||||
var balance = FfiTransactionBalance()
|
||||
|
||||
balance.feeRequired = totalFee
|
||||
|
||||
return Self(inner: ffiProposal)
|
||||
}
|
||||
}
|
|
@ -143,18 +143,46 @@ extension FfiFeeRule: CaseIterable {
|
|||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
/// A data structure that describes the inputs to be consumed and outputs to
|
||||
/// be produced in a proposed transaction.
|
||||
/// A data structure that describes a series of transactions to be created.
|
||||
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.
|
||||
|
||||
/// The version of this serialization format.
|
||||
var protoVersion: UInt32 = 0
|
||||
|
||||
/// 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
|
||||
|
||||
/// The series of transactions to be created.
|
||||
var steps: [FfiProposalStep] = []
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
/// A data structure that describes the inputs to be consumed and outputs to
|
||||
/// be produced in a proposed transaction.
|
||||
struct FfiProposalStep {
|
||||
// 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.
|
||||
|
||||
/// ZIP 321 serialized transaction request
|
||||
var transactionRequest: String = String()
|
||||
|
||||
/// The vector of selected payment index / output pool mappings. Payment index
|
||||
/// 0 corresponds to the payment with no explicit index.
|
||||
var paymentOutputPools: [FfiPaymentOutputPool] = []
|
||||
|
||||
/// 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.
|
||||
|
@ -174,16 +202,7 @@ struct FfiProposal {
|
|||
/// 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,
|
||||
/// A flag indicating whether the step is for a shielding transaction,
|
||||
/// used for determining which OVK to select for wallet-internal outputs.
|
||||
var isShielding: Bool = false
|
||||
|
||||
|
@ -194,8 +213,26 @@ struct FfiProposal {
|
|||
fileprivate var _balance: FfiTransactionBalance? = nil
|
||||
}
|
||||
|
||||
/// The unique identifier and value for each proposed input.
|
||||
struct FfiProposedInput {
|
||||
/// A mapping from ZIP 321 payment index to the output pool that has been chosen
|
||||
/// for that payment, based upon the payment address and the selected inputs to
|
||||
/// the transaction.
|
||||
struct FfiPaymentOutputPool {
|
||||
// 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 paymentIndex: UInt32 = 0
|
||||
|
||||
var valuePool: FfiValuePool = .poolNotSpecified
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
/// The unique identifier and value for each proposed input that does not
|
||||
/// require a back-reference to a prior step of the proposal.
|
||||
struct FfiReceivedOutput {
|
||||
// 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.
|
||||
|
@ -213,14 +250,113 @@ struct FfiProposedInput {
|
|||
init() {}
|
||||
}
|
||||
|
||||
/// A reference a payment in a prior step of the proposal. This payment must
|
||||
/// belong to the wallet.
|
||||
struct FfiPriorStepOutput {
|
||||
// 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 stepIndex: UInt32 = 0
|
||||
|
||||
var paymentIndex: UInt32 = 0
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
/// A reference a change output from a prior step of the proposal.
|
||||
struct FfiPriorStepChange {
|
||||
// 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 stepIndex: UInt32 = 0
|
||||
|
||||
var changeIndex: UInt32 = 0
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
/// The unique identifier and value for an input to be used in the transaction.
|
||||
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 value: FfiProposedInput.OneOf_Value? = nil
|
||||
|
||||
var receivedOutput: FfiReceivedOutput {
|
||||
get {
|
||||
if case .receivedOutput(let v)? = value {return v}
|
||||
return FfiReceivedOutput()
|
||||
}
|
||||
set {value = .receivedOutput(newValue)}
|
||||
}
|
||||
|
||||
var priorStepOutput: FfiPriorStepOutput {
|
||||
get {
|
||||
if case .priorStepOutput(let v)? = value {return v}
|
||||
return FfiPriorStepOutput()
|
||||
}
|
||||
set {value = .priorStepOutput(newValue)}
|
||||
}
|
||||
|
||||
var priorStepChange: FfiPriorStepChange {
|
||||
get {
|
||||
if case .priorStepChange(let v)? = value {return v}
|
||||
return FfiPriorStepChange()
|
||||
}
|
||||
set {value = .priorStepChange(newValue)}
|
||||
}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
enum OneOf_Value: Equatable {
|
||||
case receivedOutput(FfiReceivedOutput)
|
||||
case priorStepOutput(FfiPriorStepOutput)
|
||||
case priorStepChange(FfiPriorStepChange)
|
||||
|
||||
#if !swift(>=4.1)
|
||||
static func ==(lhs: FfiProposedInput.OneOf_Value, rhs: FfiProposedInput.OneOf_Value) -> Bool {
|
||||
// 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 (lhs, rhs) {
|
||||
case (.receivedOutput, .receivedOutput): return {
|
||||
guard case .receivedOutput(let l) = lhs, case .receivedOutput(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.priorStepOutput, .priorStepOutput): return {
|
||||
guard case .priorStepOutput(let l) = lhs, case .priorStepOutput(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.priorStepChange, .priorStepChange): return {
|
||||
guard case .priorStepChange(let l) = lhs, case .priorStepChange(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
/// A list of change output values.
|
||||
var proposedChange: [FfiChangeValue] = []
|
||||
|
||||
/// The fee to be paid by the proposed transaction, in zatoshis.
|
||||
var feeRequired: UInt64 = 0
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
@ -235,10 +371,14 @@ struct FfiChangeValue {
|
|||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
/// The value of a change output to be created, in zatoshis.
|
||||
var value: UInt64 = 0
|
||||
|
||||
/// The value pool in which the change output should be created.
|
||||
var valuePool: FfiValuePool = .poolNotSpecified
|
||||
|
||||
/// The optional memo that should be associated with the newly created change output.
|
||||
/// Memos must not be present for transparent change outputs.
|
||||
var memo: FfiMemoBytes {
|
||||
get {return _memo ?? FfiMemoBytes()}
|
||||
set {_memo = newValue}
|
||||
|
@ -273,7 +413,13 @@ struct FfiMemoBytes {
|
|||
extension FfiValuePool: @unchecked Sendable {}
|
||||
extension FfiFeeRule: @unchecked Sendable {}
|
||||
extension FfiProposal: @unchecked Sendable {}
|
||||
extension FfiProposalStep: @unchecked Sendable {}
|
||||
extension FfiPaymentOutputPool: @unchecked Sendable {}
|
||||
extension FfiReceivedOutput: @unchecked Sendable {}
|
||||
extension FfiPriorStepOutput: @unchecked Sendable {}
|
||||
extension FfiPriorStepChange: @unchecked Sendable {}
|
||||
extension FfiProposedInput: @unchecked Sendable {}
|
||||
extension FfiProposedInput.OneOf_Value: @unchecked Sendable {}
|
||||
extension FfiTransactionBalance: @unchecked Sendable {}
|
||||
extension FfiChangeValue: @unchecked Sendable {}
|
||||
extension FfiMemoBytes: @unchecked Sendable {}
|
||||
|
@ -305,13 +451,9 @@ extension FfiProposal: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
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"),
|
||||
2: .same(proto: "feeRule"),
|
||||
3: .same(proto: "minTargetHeight"),
|
||||
4: .same(proto: "steps"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
@ -321,13 +463,63 @@ extension FfiProposal: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
// 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 2: try { try decoder.decodeSingularEnumField(value: &self.feeRule) }()
|
||||
case 3: try { try decoder.decodeSingularUInt32Field(value: &self.minTargetHeight) }()
|
||||
case 4: try { try decoder.decodeRepeatedMessageField(value: &self.steps) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if self.protoVersion != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.protoVersion, fieldNumber: 1)
|
||||
}
|
||||
if self.feeRule != .notSpecified {
|
||||
try visitor.visitSingularEnumField(value: self.feeRule, fieldNumber: 2)
|
||||
}
|
||||
if self.minTargetHeight != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.minTargetHeight, fieldNumber: 3)
|
||||
}
|
||||
if !self.steps.isEmpty {
|
||||
try visitor.visitRepeatedMessageField(value: self.steps, fieldNumber: 4)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: FfiProposal, rhs: FfiProposal) -> Bool {
|
||||
if lhs.protoVersion != rhs.protoVersion {return false}
|
||||
if lhs.feeRule != rhs.feeRule {return false}
|
||||
if lhs.minTargetHeight != rhs.minTargetHeight {return false}
|
||||
if lhs.steps != rhs.steps {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension FfiProposalStep: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".ProposalStep"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "transactionRequest"),
|
||||
2: .same(proto: "paymentOutputPools"),
|
||||
3: .same(proto: "anchorHeight"),
|
||||
4: .same(proto: "inputs"),
|
||||
5: .same(proto: "balance"),
|
||||
6: .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.decodeSingularStringField(value: &self.transactionRequest) }()
|
||||
case 2: try { try decoder.decodeRepeatedMessageField(value: &self.paymentOutputPools) }()
|
||||
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) }()
|
||||
case 6: try { try decoder.decodeSingularBoolField(value: &self.isShielding) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
@ -338,11 +530,11 @@ extension FfiProposal: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
// 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)
|
||||
try visitor.visitSingularStringField(value: self.transactionRequest, fieldNumber: 1)
|
||||
}
|
||||
if !self.paymentOutputPools.isEmpty {
|
||||
try visitor.visitRepeatedMessageField(value: self.paymentOutputPools, fieldNumber: 2)
|
||||
}
|
||||
if self.anchorHeight != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.anchorHeight, fieldNumber: 3)
|
||||
|
@ -353,34 +545,64 @@ extension FfiProposal: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
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 visitor.visitSingularBoolField(value: self.isShielding, fieldNumber: 6)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: FfiProposal, rhs: FfiProposal) -> Bool {
|
||||
if lhs.protoVersion != rhs.protoVersion {return false}
|
||||
static func ==(lhs: FfiProposalStep, rhs: FfiProposalStep) -> Bool {
|
||||
if lhs.transactionRequest != rhs.transactionRequest {return false}
|
||||
if lhs.paymentOutputPools != rhs.paymentOutputPools {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"
|
||||
extension FfiPaymentOutputPool: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".PaymentOutputPool"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "paymentIndex"),
|
||||
2: .same(proto: "valuePool"),
|
||||
]
|
||||
|
||||
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.paymentIndex) }()
|
||||
case 2: try { try decoder.decodeSingularEnumField(value: &self.valuePool) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if self.paymentIndex != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.paymentIndex, fieldNumber: 1)
|
||||
}
|
||||
if self.valuePool != .poolNotSpecified {
|
||||
try visitor.visitSingularEnumField(value: self.valuePool, fieldNumber: 2)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: FfiPaymentOutputPool, rhs: FfiPaymentOutputPool) -> Bool {
|
||||
if lhs.paymentIndex != rhs.paymentIndex {return false}
|
||||
if lhs.valuePool != rhs.valuePool {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension FfiReceivedOutput: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".ReceivedOutput"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "txid"),
|
||||
2: .same(proto: "valuePool"),
|
||||
|
@ -419,7 +641,7 @@ extension FfiProposedInput: SwiftProtobuf.Message, SwiftProtobuf._MessageImpleme
|
|||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: FfiProposedInput, rhs: FfiProposedInput) -> Bool {
|
||||
static func ==(lhs: FfiReceivedOutput, rhs: FfiReceivedOutput) -> Bool {
|
||||
if lhs.txid != rhs.txid {return false}
|
||||
if lhs.valuePool != rhs.valuePool {return false}
|
||||
if lhs.index != rhs.index {return false}
|
||||
|
@ -429,6 +651,170 @@ extension FfiProposedInput: SwiftProtobuf.Message, SwiftProtobuf._MessageImpleme
|
|||
}
|
||||
}
|
||||
|
||||
extension FfiPriorStepOutput: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".PriorStepOutput"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "stepIndex"),
|
||||
2: .same(proto: "paymentIndex"),
|
||||
]
|
||||
|
||||
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.stepIndex) }()
|
||||
case 2: try { try decoder.decodeSingularUInt32Field(value: &self.paymentIndex) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if self.stepIndex != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.stepIndex, fieldNumber: 1)
|
||||
}
|
||||
if self.paymentIndex != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.paymentIndex, fieldNumber: 2)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: FfiPriorStepOutput, rhs: FfiPriorStepOutput) -> Bool {
|
||||
if lhs.stepIndex != rhs.stepIndex {return false}
|
||||
if lhs.paymentIndex != rhs.paymentIndex {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension FfiPriorStepChange: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".PriorStepChange"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "stepIndex"),
|
||||
2: .same(proto: "changeIndex"),
|
||||
]
|
||||
|
||||
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.stepIndex) }()
|
||||
case 2: try { try decoder.decodeSingularUInt32Field(value: &self.changeIndex) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if self.stepIndex != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.stepIndex, fieldNumber: 1)
|
||||
}
|
||||
if self.changeIndex != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.changeIndex, fieldNumber: 2)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: FfiPriorStepChange, rhs: FfiPriorStepChange) -> Bool {
|
||||
if lhs.stepIndex != rhs.stepIndex {return false}
|
||||
if lhs.changeIndex != rhs.changeIndex {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: "receivedOutput"),
|
||||
2: .same(proto: "priorStepOutput"),
|
||||
3: .same(proto: "priorStepChange"),
|
||||
]
|
||||
|
||||
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 {
|
||||
var v: FfiReceivedOutput?
|
||||
var hadOneofValue = false
|
||||
if let current = self.value {
|
||||
hadOneofValue = true
|
||||
if case .receivedOutput(let m) = current {v = m}
|
||||
}
|
||||
try decoder.decodeSingularMessageField(value: &v)
|
||||
if let v = v {
|
||||
if hadOneofValue {try decoder.handleConflictingOneOf()}
|
||||
self.value = .receivedOutput(v)
|
||||
}
|
||||
}()
|
||||
case 2: try {
|
||||
var v: FfiPriorStepOutput?
|
||||
var hadOneofValue = false
|
||||
if let current = self.value {
|
||||
hadOneofValue = true
|
||||
if case .priorStepOutput(let m) = current {v = m}
|
||||
}
|
||||
try decoder.decodeSingularMessageField(value: &v)
|
||||
if let v = v {
|
||||
if hadOneofValue {try decoder.handleConflictingOneOf()}
|
||||
self.value = .priorStepOutput(v)
|
||||
}
|
||||
}()
|
||||
case 3: try {
|
||||
var v: FfiPriorStepChange?
|
||||
var hadOneofValue = false
|
||||
if let current = self.value {
|
||||
hadOneofValue = true
|
||||
if case .priorStepChange(let m) = current {v = m}
|
||||
}
|
||||
try decoder.decodeSingularMessageField(value: &v)
|
||||
if let v = v {
|
||||
if hadOneofValue {try decoder.handleConflictingOneOf()}
|
||||
self.value = .priorStepChange(v)
|
||||
}
|
||||
}()
|
||||
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
|
||||
switch self.value {
|
||||
case .receivedOutput?: try {
|
||||
guard case .receivedOutput(let v)? = self.value else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
|
||||
}()
|
||||
case .priorStepOutput?: try {
|
||||
guard case .priorStepOutput(let v)? = self.value else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
|
||||
}()
|
||||
case .priorStepChange?: try {
|
||||
guard case .priorStepChange(let v)? = self.value else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
|
||||
}()
|
||||
case nil: break
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: FfiProposedInput, rhs: FfiProposedInput) -> Bool {
|
||||
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 = [
|
||||
|
|
|
@ -6,12 +6,29 @@ syntax = "proto3";
|
|||
package cash.z.wallet.sdk.ffi;
|
||||
option swift_prefix = "Ffi";
|
||||
|
||||
// A data structure that describes a series of transactions to be created.
|
||||
message Proposal {
|
||||
// The version of this serialization format.
|
||||
uint32 protoVersion = 1;
|
||||
// The fee rule used in constructing this proposal
|
||||
FeeRule feeRule = 2;
|
||||
// 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 = 3;
|
||||
// The series of transactions to be created.
|
||||
repeated ProposalStep steps = 4;
|
||||
}
|
||||
|
||||
// A data structure that describes the inputs to be consumed and outputs to
|
||||
// be produced in a proposed transaction.
|
||||
message Proposal {
|
||||
uint32 protoVersion = 1;
|
||||
message ProposalStep {
|
||||
// ZIP 321 serialized transaction request
|
||||
string transactionRequest = 2;
|
||||
string transactionRequest = 1;
|
||||
// The vector of selected payment index / output pool mappings. Payment index
|
||||
// 0 corresponds to the payment with no explicit index.
|
||||
repeated PaymentOutputPool paymentOutputPools = 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.
|
||||
|
@ -21,16 +38,9 @@ message Proposal {
|
|||
// 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,
|
||||
// A flag indicating whether the step is for a shielding transaction,
|
||||
// used for determining which OVK to select for wallet-internal outputs.
|
||||
bool isShielding = 8;
|
||||
bool isShielding = 6;
|
||||
}
|
||||
|
||||
enum ValuePool {
|
||||
|
@ -47,14 +57,45 @@ enum ValuePool {
|
|||
Orchard = 3;
|
||||
}
|
||||
|
||||
// The unique identifier and value for each proposed input.
|
||||
message ProposedInput {
|
||||
// A mapping from ZIP 321 payment index to the output pool that has been chosen
|
||||
// for that payment, based upon the payment address and the selected inputs to
|
||||
// the transaction.
|
||||
message PaymentOutputPool {
|
||||
uint32 paymentIndex = 1;
|
||||
ValuePool valuePool = 2;
|
||||
}
|
||||
|
||||
// The unique identifier and value for each proposed input that does not
|
||||
// require a back-reference to a prior step of the proposal.
|
||||
message ReceivedOutput {
|
||||
bytes txid = 1;
|
||||
ValuePool valuePool = 2;
|
||||
uint32 index = 3;
|
||||
uint64 value = 4;
|
||||
}
|
||||
|
||||
// A reference a payment in a prior step of the proposal. This payment must
|
||||
// belong to the wallet.
|
||||
message PriorStepOutput {
|
||||
uint32 stepIndex = 1;
|
||||
uint32 paymentIndex = 2;
|
||||
}
|
||||
|
||||
// A reference a change output from a prior step of the proposal.
|
||||
message PriorStepChange {
|
||||
uint32 stepIndex = 1;
|
||||
uint32 changeIndex = 2;
|
||||
}
|
||||
|
||||
// The unique identifier and value for an input to be used in the transaction.
|
||||
message ProposedInput {
|
||||
oneof value {
|
||||
ReceivedOutput receivedOutput = 1;
|
||||
PriorStepOutput priorStepOutput = 2;
|
||||
PriorStepChange priorStepChange = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// The fee rule used in constructing a Proposal
|
||||
enum FeeRule {
|
||||
// Protobuf requires that enums have a zero discriminant as the default
|
||||
|
@ -72,15 +113,21 @@ enum FeeRule {
|
|||
|
||||
// The proposed change outputs and fee value.
|
||||
message TransactionBalance {
|
||||
// A list of change output values.
|
||||
repeated ChangeValue proposedChange = 1;
|
||||
// The fee to be paid by the proposed transaction, in zatoshis.
|
||||
uint64 feeRequired = 2;
|
||||
}
|
||||
|
||||
// A proposed change output. If the transparent value pool is selected,
|
||||
// the `memo` field must be null.
|
||||
message ChangeValue {
|
||||
// The value of a change output to be created, in zatoshis.
|
||||
uint64 value = 1;
|
||||
// The value pool in which the change output should be created.
|
||||
ValuePool valuePool = 2;
|
||||
// The optional memo that should be associated with the newly created change output.
|
||||
// Memos must not be present for transparent change outputs.
|
||||
MemoBytes memo = 3;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ let globalDBLock = NSLock()
|
|||
|
||||
actor ZcashRustBackend: ZcashRustBackendWelding {
|
||||
let minimumConfirmations: UInt32 = 10
|
||||
let useZIP317Fees = false
|
||||
let useZIP317Fees = true
|
||||
|
||||
let dbData: (String, UInt)
|
||||
let fsBlockDbRoot: (String, UInt)
|
||||
|
@ -619,8 +619,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
|
|||
func proposeShielding(
|
||||
account: Int32,
|
||||
memo: MemoBytes?,
|
||||
shieldingThreshold: Zatoshi
|
||||
) async throws -> FfiProposal {
|
||||
shieldingThreshold: Zatoshi,
|
||||
transparentReceiver: String?
|
||||
) async throws -> FfiProposal? {
|
||||
globalDBLock.lock()
|
||||
let proposal = zcashlc_propose_shielding(
|
||||
dbData.0,
|
||||
|
@ -628,6 +629,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
|
|||
account,
|
||||
memo?.bytes,
|
||||
UInt64(shieldingThreshold.amount),
|
||||
transparentReceiver.map { [CChar]($0.utf8CString) },
|
||||
networkType.networkId,
|
||||
minimumConfirmations,
|
||||
useZIP317Fees
|
||||
|
@ -646,44 +648,46 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
|
|||
))
|
||||
}
|
||||
|
||||
func createProposedTransaction(
|
||||
func createProposedTransactions(
|
||||
proposal: FfiProposal,
|
||||
usk: UnifiedSpendingKey
|
||||
) async throws -> Data {
|
||||
var contiguousTxIdBytes = ContiguousArray<UInt8>([UInt8](repeating: 0x0, count: 32))
|
||||
|
||||
) async throws -> [Data] {
|
||||
let proposalBytes = try proposal.serializedData(partial: false).bytes
|
||||
|
||||
globalDBLock.lock()
|
||||
let success = contiguousTxIdBytes.withUnsafeMutableBufferPointer { txIdBytePtr in
|
||||
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
|
||||
)
|
||||
}
|
||||
let txIdsPtr = proposalBytes.withUnsafeBufferPointer { proposalPtr in
|
||||
usk.bytes.withUnsafeBufferPointer { uskPtr in
|
||||
zcashlc_create_proposed_transactions(
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
globalDBLock.unlock()
|
||||
|
||||
guard success else {
|
||||
guard let txIdsPtr else {
|
||||
throw ZcashError.rustCreateToAddress(lastErrorMessage(fallback: "`createToAddress` failed with unknown error"))
|
||||
}
|
||||
|
||||
return contiguousTxIdBytes.withUnsafeBufferPointer { txIdBytePtr in
|
||||
Data(txIdBytePtr)
|
||||
defer { zcashlc_free_txids(txIdsPtr) }
|
||||
|
||||
var txIds: [Data] = []
|
||||
|
||||
for i in (0 ..< Int(txIdsPtr.pointee.len)) {
|
||||
let txId = FfiTxId(tuple: txIdsPtr.pointee.ptr.advanced(by: i).pointee)
|
||||
txIds.append(Data(txId.array))
|
||||
}
|
||||
|
||||
return txIds
|
||||
}
|
||||
|
||||
nonisolated func consensusBranchIdFor(height: Int32) throws -> Int32 {
|
||||
|
@ -823,3 +827,12 @@ extension FfiScanProgress {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct FfiTxId {
|
||||
var tuple: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)
|
||||
var array: [UInt8] {
|
||||
withUnsafeBytes(of: self.tuple) { buf in
|
||||
[UInt8](buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -212,23 +212,31 @@ protocol ZcashRustBackendWelding {
|
|||
/// that can then be authorized and made ready for submission to the network with
|
||||
/// `createProposedTransaction`.
|
||||
///
|
||||
/// Returns the proposal, or `nil` if the transparent balance that would be shielded
|
||||
/// is zero or below `shieldingThreshold`.
|
||||
///
|
||||
/// - parameter account: index of the given account
|
||||
/// - Parameter memo: the `Memo` for this transaction
|
||||
/// - Parameter transparentReceiver: a specific transparent receiver within the account
|
||||
/// that should be the source of transparent funds. Default is `nil` which
|
||||
/// will select whichever of the account's transparent receivers has funds
|
||||
/// to shield.
|
||||
/// - Throws: `rustShieldFunds` if rust layer returns error.
|
||||
func proposeShielding(
|
||||
account: Int32,
|
||||
memo: MemoBytes?,
|
||||
shieldingThreshold: Zatoshi
|
||||
) async throws -> FfiProposal
|
||||
shieldingThreshold: Zatoshi,
|
||||
transparentReceiver: String?
|
||||
) 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(
|
||||
func createProposedTransactions(
|
||||
proposal: FfiProposal,
|
||||
usk: UnifiedSpendingKey
|
||||
) async throws -> Data
|
||||
) async throws -> [Data]
|
||||
|
||||
/// Gets the consensus branch id for the given height
|
||||
/// - Parameter height: the height you what to know the branch id for
|
||||
|
|
|
@ -154,15 +154,70 @@ public protocol Synchronizer: AnyObject {
|
|||
/// - Parameter accountIndex: the optional accountId whose address is of interest. By default, the first account is used.
|
||||
/// - Returns the address or nil if account index is incorrect
|
||||
func getTransparentAddress(accountIndex: Int) async throws -> TransparentAddress
|
||||
|
||||
|
||||
/// Creates a proposal for transferring funds to the given recipient.
|
||||
///
|
||||
/// - Parameter accountIndex: the account from which to transfer funds.
|
||||
/// - Parameter recipient: the recipient's address.
|
||||
/// - Parameter amount: the amount to send in Zatoshi.
|
||||
/// - Parameter memo: an optional memo to include as part of the proposal's transactions. Use `nil` when sending to transparent receivers otherwise the function will throw an error.
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
func proposeTransfer(
|
||||
accountIndex: Int,
|
||||
recipient: Recipient,
|
||||
amount: Zatoshi,
|
||||
memo: Memo?
|
||||
) async throws -> Proposal
|
||||
|
||||
/// Creates a proposal for shielding any transparent funds received by the given account.
|
||||
///
|
||||
/// - Parameter accountIndex: the account for which to shield funds.
|
||||
/// - Parameter shieldingThreshold: the minimum transparent balance required before a proposal will be created.
|
||||
/// - Parameter memo: an optional memo to include as part of the proposal's transactions.
|
||||
/// - Parameter transparentReceiver: a specific transparent receiver within the account
|
||||
/// that should be the source of transparent funds. Default is `nil` which
|
||||
/// will select whichever of the account's transparent receivers has funds
|
||||
/// to shield.
|
||||
///
|
||||
/// Returns the proposal, or `nil` if the transparent balance that would be shielded
|
||||
/// is zero or below `shieldingThreshold`.
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
func proposeShielding(
|
||||
accountIndex: Int,
|
||||
shieldingThreshold: Zatoshi,
|
||||
memo: Memo,
|
||||
transparentReceiver: TransparentAddress?
|
||||
) async throws -> Proposal?
|
||||
|
||||
/// Creates the transactions in the given proposal.
|
||||
///
|
||||
/// - Parameter proposal: the proposal for which to create transactions.
|
||||
/// - Parameter spendingKey: the `UnifiedSpendingKey` associated with the account for which the proposal was created.
|
||||
///
|
||||
/// Returns a stream of objects for the transactions that were created as part of the
|
||||
/// proposal, indicating whether they were submitted to the network or if an error
|
||||
/// occurred.
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance
|
||||
/// or since the last wipe then this method throws `SynchronizerErrors.notPrepared`.
|
||||
func createProposedTransactions(
|
||||
proposal: Proposal,
|
||||
spendingKey: UnifiedSpendingKey
|
||||
) async throws -> AsyncThrowingStream<TransactionSubmitResult, Error>
|
||||
|
||||
/// Sends zatoshi.
|
||||
/// - Parameter spendingKey: the `UnifiedSpendingKey` that allows spends to occur.
|
||||
/// - Parameter zatoshi: the amount to send in Zatoshi.
|
||||
/// - Parameter toAddress: the recipient's address.
|
||||
/// - Parameter memo: an `Optional<Memo>`with the memo to include as part of the transaction. send `nil` when sending to transparent receivers otherwise the function will throw an error
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creating of synchronizer instance or since the last wipe then this method throws
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
@available(*, deprecated, message: "Upcoming SDK 2.1 will create multiple transactions at once for some recipients.")
|
||||
func sendToAddress(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
|
@ -173,9 +228,11 @@ public protocol Synchronizer: AnyObject {
|
|||
/// Shields transparent funds from the given private key into the best shielded pool of the account associated to the given `UnifiedSpendingKey`.
|
||||
/// - Parameter spendingKey: the `UnifiedSpendingKey` that allows to spend transparent funds
|
||||
/// - Parameter memo: the optional memo to include as part of the transaction.
|
||||
/// - Parameter shieldingThreshold: the minimum transparent balance required before a transaction will be created.
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creating of synchronizer instance or since the last wipe then this method throws
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
@available(*, deprecated, message: "Upcoming SDK 2.1 will create multiple transactions at once for some recipients.")
|
||||
func shieldFunds(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
memo: Memo,
|
||||
|
@ -227,7 +284,7 @@ public protocol Synchronizer: AnyObject {
|
|||
|
||||
/// Returns the latests UTXOs for the given address from the specified height on
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creating of synchronizer instance or since the last wipe then this method throws
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
func refreshUTXOs(address: TransparentAddress, from height: BlockHeight) async throws -> RefreshedUTXOs
|
||||
|
||||
|
@ -251,7 +308,7 @@ public protocol Synchronizer: AnyObject {
|
|||
/// `rewind(policy:)` itself doesn't start the sync process when it's done and it doesn't trigger notifications as regorg would. After it is done
|
||||
/// you have start the sync process by calling `start()`
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creating of synchronizer instance or since the last wipe then returned publisher emits
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance or since the last wipe then returned publisher emits
|
||||
/// `SynchronizerErrors.notPrepared` error.
|
||||
///
|
||||
/// - Parameter policy: the rewind policy
|
||||
|
@ -433,6 +490,19 @@ public enum RewindPolicy {
|
|||
case quick
|
||||
}
|
||||
|
||||
/// The result of submitting a transaction to the network.
|
||||
///
|
||||
/// - success: the transaction was successfully submitted to the mempool.
|
||||
/// - grpcFailure: the transaction failed to reach the lightwalletd server.
|
||||
/// - submitFailure: the transaction reached the lightwalletd server but failed to enter the mempool.
|
||||
/// - notAttempted: the transaction was created and is in the local wallet, but was not submitted to the network.
|
||||
public enum TransactionSubmitResult: Equatable {
|
||||
case success(txId: Data)
|
||||
case grpcFailure(txId: Data, error: LightWalletServiceError)
|
||||
case submitFailure(txId: Data, code: Int, description: String)
|
||||
case notAttempted(txId: Data)
|
||||
}
|
||||
|
||||
extension InternalSyncStatus {
|
||||
public static func == (lhs: InternalSyncStatus, rhs: InternalSyncStatus) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
|
|
|
@ -70,6 +70,46 @@ extension ClosureSDKSynchronizer: ClosureSynchronizer {
|
|||
}
|
||||
}
|
||||
|
||||
public func proposeTransfer(
|
||||
accountIndex: Int,
|
||||
recipient: Recipient,
|
||||
amount: Zatoshi,
|
||||
memo: Memo?,
|
||||
completion: @escaping (Result<Proposal, Error>) -> Void
|
||||
) {
|
||||
AsyncToClosureGateway.executeThrowingAction(completion) {
|
||||
try await self.synchronizer.proposeTransfer(accountIndex: accountIndex, recipient: recipient, amount: amount, memo: memo)
|
||||
}
|
||||
}
|
||||
|
||||
public func proposeShielding(
|
||||
accountIndex: Int,
|
||||
shieldingThreshold: Zatoshi,
|
||||
memo: Memo,
|
||||
transparentReceiver: TransparentAddress? = nil,
|
||||
completion: @escaping (Result<Proposal?, Error>) -> Void
|
||||
) {
|
||||
AsyncToClosureGateway.executeThrowingAction(completion) {
|
||||
try await self.synchronizer.proposeShielding(
|
||||
accountIndex: accountIndex,
|
||||
shieldingThreshold: shieldingThreshold,
|
||||
memo: memo,
|
||||
transparentReceiver: transparentReceiver
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public func createProposedTransactions(
|
||||
proposal: Proposal,
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
completion: @escaping (Result<AsyncThrowingStream<TransactionSubmitResult, Error>, Error>) -> Void
|
||||
) {
|
||||
AsyncToClosureGateway.executeThrowingAction(completion) {
|
||||
try await self.synchronizer.createProposedTransactions(proposal: proposal, spendingKey: spendingKey)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Upcoming SDK 2.1 will create multiple transactions at once for some recipients.")
|
||||
public func sendToAddress(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
|
@ -82,6 +122,7 @@ extension ClosureSDKSynchronizer: ClosureSynchronizer {
|
|||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Upcoming SDK 2.1 will create multiple transactions at once for some recipients.")
|
||||
public func shieldFunds(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
memo: Memo,
|
||||
|
|
|
@ -69,6 +69,43 @@ extension CombineSDKSynchronizer: CombineSynchronizer {
|
|||
}
|
||||
}
|
||||
|
||||
public func proposeTransfer(
|
||||
accountIndex: Int,
|
||||
recipient: Recipient,
|
||||
amount: Zatoshi,
|
||||
memo: Memo?
|
||||
) -> SinglePublisher<Proposal, Error> {
|
||||
AsyncToCombineGateway.executeThrowingAction() {
|
||||
try await self.synchronizer.proposeTransfer(accountIndex: accountIndex, recipient: recipient, amount: amount, memo: memo)
|
||||
}
|
||||
}
|
||||
|
||||
public func proposeShielding(
|
||||
accountIndex: Int,
|
||||
shieldingThreshold: Zatoshi,
|
||||
memo: Memo,
|
||||
transparentReceiver: TransparentAddress? = nil
|
||||
) -> SinglePublisher<Proposal?, Error> {
|
||||
AsyncToCombineGateway.executeThrowingAction() {
|
||||
try await self.synchronizer.proposeShielding(
|
||||
accountIndex: accountIndex,
|
||||
shieldingThreshold: shieldingThreshold,
|
||||
memo: memo,
|
||||
transparentReceiver: transparentReceiver
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public func createProposedTransactions(
|
||||
proposal: Proposal,
|
||||
spendingKey: UnifiedSpendingKey
|
||||
) -> SinglePublisher<AsyncThrowingStream<TransactionSubmitResult, Error>, Error> {
|
||||
AsyncToCombineGateway.executeThrowingAction() {
|
||||
try await self.synchronizer.createProposedTransactions(proposal: proposal, spendingKey: spendingKey)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Upcoming SDK 2.1 will create multiple transactions at once for some recipients.")
|
||||
public func sendToAddress(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
|
@ -80,6 +117,7 @@ extension CombineSDKSynchronizer: CombineSynchronizer {
|
|||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Upcoming SDK 2.1 will create multiple transactions at once for some recipients.")
|
||||
public func shieldFunds(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
memo: Memo,
|
||||
|
|
|
@ -265,6 +265,84 @@ public class SDKSynchronizer: Synchronizer {
|
|||
|
||||
// MARK: Synchronizer methods
|
||||
|
||||
public func proposeTransfer(accountIndex: Int, recipient: Recipient, amount: Zatoshi, memo: Memo?) async throws -> Proposal {
|
||||
try throwIfUnprepared()
|
||||
|
||||
if case Recipient.transparent = recipient, memo != nil {
|
||||
throw ZcashError.synchronizerSendMemoToTransparentAddress
|
||||
}
|
||||
|
||||
let proposal = try await transactionEncoder.proposeTransfer(
|
||||
accountIndex: accountIndex,
|
||||
recipient: recipient.stringEncoded,
|
||||
amount: amount,
|
||||
memoBytes: memo?.asMemoBytes()
|
||||
)
|
||||
|
||||
return proposal
|
||||
}
|
||||
|
||||
public func proposeShielding(
|
||||
accountIndex: Int,
|
||||
shieldingThreshold: Zatoshi,
|
||||
memo: Memo,
|
||||
transparentReceiver: TransparentAddress? = nil
|
||||
) async throws -> Proposal? {
|
||||
try throwIfUnprepared()
|
||||
|
||||
let proposal = try await transactionEncoder.proposeShielding(
|
||||
accountIndex: accountIndex,
|
||||
shieldingThreshold: shieldingThreshold,
|
||||
memoBytes: memo.asMemoBytes(),
|
||||
transparentReceiver: transparentReceiver?.stringEncoded
|
||||
)
|
||||
|
||||
return proposal
|
||||
}
|
||||
|
||||
public func createProposedTransactions(
|
||||
proposal: Proposal,
|
||||
spendingKey: UnifiedSpendingKey
|
||||
) async throws -> AsyncThrowingStream<TransactionSubmitResult, Error> {
|
||||
try throwIfUnprepared()
|
||||
|
||||
try await SaplingParameterDownloader.downloadParamsIfnotPresent(
|
||||
spendURL: initializer.spendParamsURL,
|
||||
spendSourceURL: initializer.saplingParamsSourceURL.spendParamFileURL,
|
||||
outputURL: initializer.outputParamsURL,
|
||||
outputSourceURL: initializer.saplingParamsSourceURL.outputParamFileURL,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let transactions = try await transactionEncoder.createProposedTransactions(
|
||||
proposal: proposal,
|
||||
spendingKey: spendingKey
|
||||
)
|
||||
var iterator = transactions.makeIterator()
|
||||
var submitFailed = false
|
||||
|
||||
return AsyncThrowingStream() {
|
||||
guard let transaction = iterator.next() else { return nil }
|
||||
|
||||
if submitFailed {
|
||||
return .notAttempted(txId: transaction.rawID)
|
||||
} else {
|
||||
let encodedTransaction = try transaction.encodedTransaction()
|
||||
|
||||
do {
|
||||
try await self.transactionEncoder.submit(transaction: encodedTransaction)
|
||||
return TransactionSubmitResult.success(txId: transaction.rawID)
|
||||
} catch ZcashError.serviceSubmitFailed(let error) {
|
||||
submitFailed = true
|
||||
return TransactionSubmitResult.grpcFailure(txId: transaction.rawID, error: error)
|
||||
} catch TransactionEncoderError.submitError(let code, let message) {
|
||||
submitFailed = true
|
||||
return TransactionSubmitResult.submitFailure(txId: transaction.rawID, code: code, description: message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func sendToAddress(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
|
@ -312,13 +390,21 @@ public class SDKSynchronizer: Synchronizer {
|
|||
throw ZcashError.synchronizerShieldFundsInsuficientTransparentFunds
|
||||
}
|
||||
|
||||
let transaction = try await transactionEncoder.createShieldingTransaction(
|
||||
spendingKey: spendingKey,
|
||||
guard let proposal = try await transactionEncoder.proposeShielding(
|
||||
accountIndex: Int(spendingKey.account),
|
||||
shieldingThreshold: shieldingThreshold,
|
||||
memoBytes: memo.asMemoBytes(),
|
||||
from: Int(spendingKey.account)
|
||||
transparentReceiver: nil
|
||||
) else { throw ZcashError.synchronizerShieldFundsInsuficientTransparentFunds }
|
||||
|
||||
let transactions = try await transactionEncoder.createProposedTransactions(
|
||||
proposal: proposal,
|
||||
spendingKey: spendingKey
|
||||
)
|
||||
|
||||
assert(transactions.count == 1, "Rust backend doesn't produce multiple transactions yet")
|
||||
let transaction = transactions[0]
|
||||
|
||||
let encodedTx = try transaction.encodedTransaction()
|
||||
|
||||
try await transactionEncoder.submit(transaction: encodedTx)
|
||||
|
@ -339,14 +425,21 @@ public class SDKSynchronizer: Synchronizer {
|
|||
throw ZcashError.synchronizerSendMemoToTransparentAddress
|
||||
}
|
||||
|
||||
let transaction = try await transactionEncoder.createTransaction(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: zatoshi,
|
||||
to: recipient.stringEncoded,
|
||||
memoBytes: memo?.asMemoBytes(),
|
||||
from: Int(spendingKey.account)
|
||||
let proposal = try await transactionEncoder.proposeTransfer(
|
||||
accountIndex: Int(spendingKey.account),
|
||||
recipient: recipient.stringEncoded,
|
||||
amount: zatoshi,
|
||||
memoBytes: memo?.asMemoBytes()
|
||||
)
|
||||
|
||||
let transactions = try await transactionEncoder.createProposedTransactions(
|
||||
proposal: proposal,
|
||||
spendingKey: spendingKey
|
||||
)
|
||||
|
||||
assert(transactions.count == 1, "Rust backend doesn't produce multiple transactions yet")
|
||||
let transaction = transactions[0]
|
||||
|
||||
let encodedTransaction = try transaction.encodedTransaction()
|
||||
|
||||
try await transactionEncoder.submit(transaction: encodedTransaction)
|
||||
|
|
|
@ -19,44 +19,58 @@ public enum TransactionEncoderError: Error {
|
|||
}
|
||||
|
||||
protocol TransactionEncoder {
|
||||
/// Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation
|
||||
/// doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using
|
||||
/// double-bangs for things).
|
||||
/// Creates a proposal for transferring funds to the given recipient.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - Parameter spendingKey: a `UnifiedSpendingKey` containing the spending key
|
||||
/// - Parameter zatoshi: the amount to send in `Zatoshi`
|
||||
/// - Parameter to: string containing the recipient address
|
||||
/// - Parameter MemoBytes: string containing the memo (optional)
|
||||
/// - Parameter accountIndex: index of the account that will be used to send the funds
|
||||
/// - Throws:
|
||||
/// - `walletTransEncoderCreateTransactionMissingSaplingParams` if the sapling parameters aren't downloaded.
|
||||
/// - Some `ZcashError.rust*` if the creation of transaction fails.
|
||||
func createTransaction(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to address: String,
|
||||
memoBytes: MemoBytes?,
|
||||
from accountIndex: Int
|
||||
) async throws -> ZcashTransaction.Overview
|
||||
|
||||
/// Creates a transaction that will attempt to shield transparent funds that are present on the blocks cache .throwing
|
||||
/// an exception whenever things are missing. When the provided wallet implementation doesn't throw an exception,
|
||||
/// we wrap the issue into a descriptive exception ourselves (rather than using double-bangs for things).
|
||||
/// - Parameter accountIndex: the account from which to transfer funds.
|
||||
/// - Parameter recipient: string containing the recipient's address.
|
||||
/// - Parameter amount: the amount to send in Zatoshi.
|
||||
/// - Parameter memoBytes: an optional memo to include as part of the proposal's transactions. Use `nil` when sending to transparent receivers otherwise the function will throw an error.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - Parameter spendingKey: `UnifiedSpendingKey` to spend the UTXOs
|
||||
/// - Parameter memoBytes: containing the memo (optional)
|
||||
/// - Parameter accountIndex: index of the account that will be used to send the funds
|
||||
/// - Throws:
|
||||
/// - `walletTransEncoderShieldFundsMissingSaplingParams` if the sapling parameters aren't downloaded.
|
||||
/// - Some `ZcashError.rust*` if the creation of transaction fails.
|
||||
func createShieldingTransaction(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
func proposeTransfer(
|
||||
accountIndex: Int,
|
||||
recipient: String,
|
||||
amount: Zatoshi,
|
||||
memoBytes: MemoBytes?
|
||||
) async throws -> Proposal
|
||||
|
||||
/// Creates a proposal for shielding any transparent funds received by the given account.
|
||||
///
|
||||
/// - Parameter accountIndex: the account for which to shield funds.
|
||||
/// - Parameter shieldingThreshold: the minimum transparent balance required before a proposal will be created.
|
||||
/// - Parameter memoBytes: an optional memo to include as part of the proposal's transactions.
|
||||
/// - Parameter transparentReceiver: a specific transparent receiver within the account
|
||||
/// that should be the source of transparent funds. Default is `nil` which
|
||||
/// will select whichever of the account's transparent receivers has funds
|
||||
/// to shield.
|
||||
///
|
||||
/// Returns the proposal, or `nil` if the transparent balance that would be shielded
|
||||
/// is zero or below `shieldingThreshold`.
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
func proposeShielding(
|
||||
accountIndex: Int,
|
||||
shieldingThreshold: Zatoshi,
|
||||
memoBytes: MemoBytes?,
|
||||
from accountIndex: Int
|
||||
) async throws -> ZcashTransaction.Overview
|
||||
transparentReceiver: String?
|
||||
) async throws -> Proposal?
|
||||
|
||||
/// Creates the transactions in the given proposal.
|
||||
///
|
||||
/// - Parameter proposal: the proposal for which to create transactions.
|
||||
/// - Parameter spendingKey: the `UnifiedSpendingKey` associated with the account for which the proposal was created.
|
||||
/// - Throws:
|
||||
/// - `walletTransEncoderCreateTransactionMissingSaplingParams` if the sapling parameters aren't downloaded.
|
||||
/// - Some `ZcashError.rust*` if the creation of transaction(s) fails.
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creation of the synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
func createProposedTransactions(
|
||||
proposal: Proposal,
|
||||
spendingKey: UnifiedSpendingKey
|
||||
) async throws -> [ZcashTransaction.Overview]
|
||||
|
||||
/// submits a transaction to the Zcash peer-to-peer network.
|
||||
/// - Parameter transaction: a transaction overview
|
||||
|
|
|
@ -54,93 +54,61 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
logger: initializer.logger
|
||||
)
|
||||
}
|
||||
|
||||
func createTransaction(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to address: String,
|
||||
memoBytes: MemoBytes?,
|
||||
from accountIndex: Int
|
||||
) async throws -> ZcashTransaction.Overview {
|
||||
let txId = try await createSpend(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: zatoshi,
|
||||
to: address,
|
||||
memoBytes: memoBytes,
|
||||
from: accountIndex
|
||||
|
||||
func proposeTransfer(
|
||||
accountIndex: Int,
|
||||
recipient: String,
|
||||
amount: Zatoshi,
|
||||
memoBytes: MemoBytes?
|
||||
) async throws -> Proposal {
|
||||
let proposal = try await rustBackend.proposeTransfer(
|
||||
account: Int32(accountIndex),
|
||||
to: recipient,
|
||||
value: amount.amount,
|
||||
memo: memoBytes
|
||||
)
|
||||
|
||||
logger.debug("transaction id: \(txId)")
|
||||
return try await repository.find(rawID: txId)
|
||||
return Proposal(inner: proposal)
|
||||
}
|
||||
|
||||
func createSpend(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to address: String,
|
||||
|
||||
func proposeShielding(
|
||||
accountIndex: Int,
|
||||
shieldingThreshold: Zatoshi,
|
||||
memoBytes: MemoBytes?,
|
||||
from accountIndex: Int
|
||||
) async throws -> Data {
|
||||
transparentReceiver: String? = nil
|
||||
) async throws -> Proposal? {
|
||||
guard let proposal = try await rustBackend.proposeShielding(
|
||||
account: Int32(accountIndex),
|
||||
memo: memoBytes,
|
||||
shieldingThreshold: shieldingThreshold,
|
||||
transparentReceiver: transparentReceiver
|
||||
) else { return nil }
|
||||
|
||||
return Proposal(inner: proposal)
|
||||
}
|
||||
|
||||
func createProposedTransactions(
|
||||
proposal: Proposal,
|
||||
spendingKey: UnifiedSpendingKey
|
||||
) async throws -> [ZcashTransaction.Overview] {
|
||||
guard ensureParams(spend: self.spendParamsURL, output: self.outputParamsURL) else {
|
||||
throw ZcashError.walletTransEncoderCreateTransactionMissingSaplingParams
|
||||
}
|
||||
|
||||
// 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,
|
||||
let txIds = try await rustBackend.createProposedTransactions(
|
||||
proposal: proposal.inner,
|
||||
usk: spendingKey
|
||||
)
|
||||
|
||||
return txId
|
||||
}
|
||||
|
||||
func createShieldingTransaction(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
shieldingThreshold: Zatoshi,
|
||||
memoBytes: MemoBytes?,
|
||||
from accountIndex: Int
|
||||
) async throws -> ZcashTransaction.Overview {
|
||||
let txId = try await createShieldingSpend(
|
||||
spendingKey: spendingKey,
|
||||
shieldingThreshold: shieldingThreshold,
|
||||
memo: memoBytes,
|
||||
accountIndex: accountIndex
|
||||
)
|
||||
|
||||
logger.debug("transaction id: \(txId)")
|
||||
return try await repository.find(rawID: txId)
|
||||
}
|
||||
logger.debug("transaction ids: \(txIds)")
|
||||
|
||||
func createShieldingSpend(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
shieldingThreshold: Zatoshi,
|
||||
memo: MemoBytes?,
|
||||
accountIndex: Int
|
||||
) async throws -> Data {
|
||||
guard ensureParams(spend: self.spendParamsURL, output: self.outputParamsURL) else {
|
||||
throw ZcashError.walletTransEncoderShieldFundsMissingSaplingParams
|
||||
var txs: [ZcashTransaction.Overview] = []
|
||||
|
||||
for txId in txIds {
|
||||
txs.append(try await repository.find(rawID: txId))
|
||||
}
|
||||
|
||||
// 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
|
||||
return txs
|
||||
}
|
||||
|
||||
func submit(
|
||||
|
|
|
@ -1397,6 +1397,78 @@ class SynchronizerMock: Synchronizer {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - proposeTransfer
|
||||
|
||||
var proposeTransferAccountIndexRecipientAmountMemoThrowableError: Error?
|
||||
var proposeTransferAccountIndexRecipientAmountMemoCallsCount = 0
|
||||
var proposeTransferAccountIndexRecipientAmountMemoCalled: Bool {
|
||||
return proposeTransferAccountIndexRecipientAmountMemoCallsCount > 0
|
||||
}
|
||||
var proposeTransferAccountIndexRecipientAmountMemoReceivedArguments: (accountIndex: Int, recipient: Recipient, amount: Zatoshi, memo: Memo?)?
|
||||
var proposeTransferAccountIndexRecipientAmountMemoReturnValue: Proposal!
|
||||
var proposeTransferAccountIndexRecipientAmountMemoClosure: ((Int, Recipient, Zatoshi, Memo?) async throws -> Proposal)?
|
||||
|
||||
func proposeTransfer(accountIndex: Int, recipient: Recipient, amount: Zatoshi, memo: Memo?) async throws -> Proposal {
|
||||
if let error = proposeTransferAccountIndexRecipientAmountMemoThrowableError {
|
||||
throw error
|
||||
}
|
||||
proposeTransferAccountIndexRecipientAmountMemoCallsCount += 1
|
||||
proposeTransferAccountIndexRecipientAmountMemoReceivedArguments = (accountIndex: accountIndex, recipient: recipient, amount: amount, memo: memo)
|
||||
if let closure = proposeTransferAccountIndexRecipientAmountMemoClosure {
|
||||
return try await closure(accountIndex, recipient, amount, memo)
|
||||
} else {
|
||||
return proposeTransferAccountIndexRecipientAmountMemoReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - proposeShielding
|
||||
|
||||
var proposeShieldingAccountIndexShieldingThresholdMemoTransparentReceiverThrowableError: Error?
|
||||
var proposeShieldingAccountIndexShieldingThresholdMemoTransparentReceiverCallsCount = 0
|
||||
var proposeShieldingAccountIndexShieldingThresholdMemoTransparentReceiverCalled: Bool {
|
||||
return proposeShieldingAccountIndexShieldingThresholdMemoTransparentReceiverCallsCount > 0
|
||||
}
|
||||
var proposeShieldingAccountIndexShieldingThresholdMemoTransparentReceiverReceivedArguments: (accountIndex: Int, shieldingThreshold: Zatoshi, memo: Memo, transparentReceiver: TransparentAddress?)?
|
||||
var proposeShieldingAccountIndexShieldingThresholdMemoTransparentReceiverReturnValue: Proposal?
|
||||
var proposeShieldingAccountIndexShieldingThresholdMemoTransparentReceiverClosure: ((Int, Zatoshi, Memo, TransparentAddress?) async throws -> Proposal?)?
|
||||
|
||||
func proposeShielding(accountIndex: Int, shieldingThreshold: Zatoshi, memo: Memo, transparentReceiver: TransparentAddress?) async throws -> Proposal? {
|
||||
if let error = proposeShieldingAccountIndexShieldingThresholdMemoTransparentReceiverThrowableError {
|
||||
throw error
|
||||
}
|
||||
proposeShieldingAccountIndexShieldingThresholdMemoTransparentReceiverCallsCount += 1
|
||||
proposeShieldingAccountIndexShieldingThresholdMemoTransparentReceiverReceivedArguments = (accountIndex: accountIndex, shieldingThreshold: shieldingThreshold, memo: memo, transparentReceiver: transparentReceiver)
|
||||
if let closure = proposeShieldingAccountIndexShieldingThresholdMemoTransparentReceiverClosure {
|
||||
return try await closure(accountIndex, shieldingThreshold, memo, transparentReceiver)
|
||||
} else {
|
||||
return proposeShieldingAccountIndexShieldingThresholdMemoTransparentReceiverReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - createProposedTransactions
|
||||
|
||||
var createProposedTransactionsProposalSpendingKeyThrowableError: Error?
|
||||
var createProposedTransactionsProposalSpendingKeyCallsCount = 0
|
||||
var createProposedTransactionsProposalSpendingKeyCalled: Bool {
|
||||
return createProposedTransactionsProposalSpendingKeyCallsCount > 0
|
||||
}
|
||||
var createProposedTransactionsProposalSpendingKeyReceivedArguments: (proposal: Proposal, spendingKey: UnifiedSpendingKey)?
|
||||
var createProposedTransactionsProposalSpendingKeyReturnValue: AsyncThrowingStream<TransactionSubmitResult, Error>!
|
||||
var createProposedTransactionsProposalSpendingKeyClosure: ((Proposal, UnifiedSpendingKey) async throws -> AsyncThrowingStream<TransactionSubmitResult, Error>)?
|
||||
|
||||
func createProposedTransactions(proposal: Proposal, spendingKey: UnifiedSpendingKey) async throws -> AsyncThrowingStream<TransactionSubmitResult, Error> {
|
||||
if let error = createProposedTransactionsProposalSpendingKeyThrowableError {
|
||||
throw error
|
||||
}
|
||||
createProposedTransactionsProposalSpendingKeyCallsCount += 1
|
||||
createProposedTransactionsProposalSpendingKeyReceivedArguments = (proposal: proposal, spendingKey: spendingKey)
|
||||
if let closure = createProposedTransactionsProposalSpendingKeyClosure {
|
||||
return try await closure(proposal, spendingKey)
|
||||
} else {
|
||||
return createProposedTransactionsProposalSpendingKeyReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - sendToAddress
|
||||
|
||||
var sendToAddressSpendingKeyZatoshiToAddressMemoThrowableError: Error?
|
||||
|
@ -2756,67 +2828,67 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
|
|||
|
||||
// MARK: - proposeShielding
|
||||
|
||||
var proposeShieldingAccountMemoShieldingThresholdThrowableError: Error?
|
||||
func setProposeShieldingAccountMemoShieldingThresholdThrowableError(_ param: Error?) async {
|
||||
proposeShieldingAccountMemoShieldingThresholdThrowableError = param
|
||||
var proposeShieldingAccountMemoShieldingThresholdTransparentReceiverThrowableError: Error?
|
||||
func setProposeShieldingAccountMemoShieldingThresholdTransparentReceiverThrowableError(_ param: Error?) async {
|
||||
proposeShieldingAccountMemoShieldingThresholdTransparentReceiverThrowableError = param
|
||||
}
|
||||
var proposeShieldingAccountMemoShieldingThresholdCallsCount = 0
|
||||
var proposeShieldingAccountMemoShieldingThresholdCalled: Bool {
|
||||
return proposeShieldingAccountMemoShieldingThresholdCallsCount > 0
|
||||
var proposeShieldingAccountMemoShieldingThresholdTransparentReceiverCallsCount = 0
|
||||
var proposeShieldingAccountMemoShieldingThresholdTransparentReceiverCalled: Bool {
|
||||
return proposeShieldingAccountMemoShieldingThresholdTransparentReceiverCallsCount > 0
|
||||
}
|
||||
var proposeShieldingAccountMemoShieldingThresholdReceivedArguments: (account: Int32, memo: MemoBytes?, shieldingThreshold: Zatoshi)?
|
||||
var proposeShieldingAccountMemoShieldingThresholdReturnValue: FfiProposal!
|
||||
func setProposeShieldingAccountMemoShieldingThresholdReturnValue(_ param: FfiProposal) async {
|
||||
proposeShieldingAccountMemoShieldingThresholdReturnValue = param
|
||||
var proposeShieldingAccountMemoShieldingThresholdTransparentReceiverReceivedArguments: (account: Int32, memo: MemoBytes?, shieldingThreshold: Zatoshi, transparentReceiver: String?)?
|
||||
var proposeShieldingAccountMemoShieldingThresholdTransparentReceiverReturnValue: FfiProposal?
|
||||
func setProposeShieldingAccountMemoShieldingThresholdTransparentReceiverReturnValue(_ param: FfiProposal?) async {
|
||||
proposeShieldingAccountMemoShieldingThresholdTransparentReceiverReturnValue = param
|
||||
}
|
||||
var proposeShieldingAccountMemoShieldingThresholdClosure: ((Int32, MemoBytes?, Zatoshi) async throws -> FfiProposal)?
|
||||
func setProposeShieldingAccountMemoShieldingThresholdClosure(_ param: ((Int32, MemoBytes?, Zatoshi) async throws -> FfiProposal)?) async {
|
||||
proposeShieldingAccountMemoShieldingThresholdClosure = param
|
||||
var proposeShieldingAccountMemoShieldingThresholdTransparentReceiverClosure: ((Int32, MemoBytes?, Zatoshi, String?) async throws -> FfiProposal?)?
|
||||
func setProposeShieldingAccountMemoShieldingThresholdTransparentReceiverClosure(_ param: ((Int32, MemoBytes?, Zatoshi, String?) async throws -> FfiProposal?)?) async {
|
||||
proposeShieldingAccountMemoShieldingThresholdTransparentReceiverClosure = param
|
||||
}
|
||||
|
||||
func proposeShielding(account: Int32, memo: MemoBytes?, shieldingThreshold: Zatoshi) async throws -> FfiProposal {
|
||||
if let error = proposeShieldingAccountMemoShieldingThresholdThrowableError {
|
||||
func proposeShielding(account: Int32, memo: MemoBytes?, shieldingThreshold: Zatoshi, transparentReceiver: String?) async throws -> FfiProposal? {
|
||||
if let error = proposeShieldingAccountMemoShieldingThresholdTransparentReceiverThrowableError {
|
||||
throw error
|
||||
}
|
||||
proposeShieldingAccountMemoShieldingThresholdCallsCount += 1
|
||||
proposeShieldingAccountMemoShieldingThresholdReceivedArguments = (account: account, memo: memo, shieldingThreshold: shieldingThreshold)
|
||||
if let closure = proposeShieldingAccountMemoShieldingThresholdClosure {
|
||||
return try await closure(account, memo, shieldingThreshold)
|
||||
proposeShieldingAccountMemoShieldingThresholdTransparentReceiverCallsCount += 1
|
||||
proposeShieldingAccountMemoShieldingThresholdTransparentReceiverReceivedArguments = (account: account, memo: memo, shieldingThreshold: shieldingThreshold, transparentReceiver: transparentReceiver)
|
||||
if let closure = proposeShieldingAccountMemoShieldingThresholdTransparentReceiverClosure {
|
||||
return try await closure(account, memo, shieldingThreshold, transparentReceiver)
|
||||
} else {
|
||||
return proposeShieldingAccountMemoShieldingThresholdReturnValue
|
||||
return proposeShieldingAccountMemoShieldingThresholdTransparentReceiverReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - createProposedTransaction
|
||||
// MARK: - createProposedTransactions
|
||||
|
||||
var createProposedTransactionProposalUskThrowableError: Error?
|
||||
func setCreateProposedTransactionProposalUskThrowableError(_ param: Error?) async {
|
||||
createProposedTransactionProposalUskThrowableError = param
|
||||
var createProposedTransactionsProposalUskThrowableError: Error?
|
||||
func setCreateProposedTransactionsProposalUskThrowableError(_ param: Error?) async {
|
||||
createProposedTransactionsProposalUskThrowableError = param
|
||||
}
|
||||
var createProposedTransactionProposalUskCallsCount = 0
|
||||
var createProposedTransactionProposalUskCalled: Bool {
|
||||
return createProposedTransactionProposalUskCallsCount > 0
|
||||
var createProposedTransactionsProposalUskCallsCount = 0
|
||||
var createProposedTransactionsProposalUskCalled: Bool {
|
||||
return createProposedTransactionsProposalUskCallsCount > 0
|
||||
}
|
||||
var createProposedTransactionProposalUskReceivedArguments: (proposal: FfiProposal, usk: UnifiedSpendingKey)?
|
||||
var createProposedTransactionProposalUskReturnValue: Data!
|
||||
func setCreateProposedTransactionProposalUskReturnValue(_ param: Data) async {
|
||||
createProposedTransactionProposalUskReturnValue = param
|
||||
var createProposedTransactionsProposalUskReceivedArguments: (proposal: FfiProposal, usk: UnifiedSpendingKey)?
|
||||
var createProposedTransactionsProposalUskReturnValue: [Data]!
|
||||
func setCreateProposedTransactionsProposalUskReturnValue(_ param: [Data]) async {
|
||||
createProposedTransactionsProposalUskReturnValue = param
|
||||
}
|
||||
var createProposedTransactionProposalUskClosure: ((FfiProposal, UnifiedSpendingKey) async throws -> Data)?
|
||||
func setCreateProposedTransactionProposalUskClosure(_ param: ((FfiProposal, UnifiedSpendingKey) async throws -> Data)?) async {
|
||||
createProposedTransactionProposalUskClosure = param
|
||||
var createProposedTransactionsProposalUskClosure: ((FfiProposal, UnifiedSpendingKey) async throws -> [Data])?
|
||||
func setCreateProposedTransactionsProposalUskClosure(_ param: ((FfiProposal, UnifiedSpendingKey) async throws -> [Data])?) async {
|
||||
createProposedTransactionsProposalUskClosure = param
|
||||
}
|
||||
|
||||
func createProposedTransaction(proposal: FfiProposal, usk: UnifiedSpendingKey) async throws -> Data {
|
||||
if let error = createProposedTransactionProposalUskThrowableError {
|
||||
func createProposedTransactions(proposal: FfiProposal, usk: UnifiedSpendingKey) async throws -> [Data] {
|
||||
if let error = createProposedTransactionsProposalUskThrowableError {
|
||||
throw error
|
||||
}
|
||||
createProposedTransactionProposalUskCallsCount += 1
|
||||
createProposedTransactionProposalUskReceivedArguments = (proposal: proposal, usk: usk)
|
||||
if let closure = createProposedTransactionProposalUskClosure {
|
||||
createProposedTransactionsProposalUskCallsCount += 1
|
||||
createProposedTransactionsProposalUskReceivedArguments = (proposal: proposal, usk: usk)
|
||||
if let closure = createProposedTransactionsProposalUskClosure {
|
||||
return try await closure(proposal, usk)
|
||||
} else {
|
||||
return createProposedTransactionProposalUskReturnValue
|
||||
return createProposedTransactionsProposalUskReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,8 +84,8 @@ class RustBackendMockHelper {
|
|||
await rustBackendMock.setGetNearestRewindHeightHeightReturnValue(-1)
|
||||
await rustBackendMock.setPutUnspentTransparentOutputTxidIndexScriptValueHeightClosure() { _, _, _, _, _ in }
|
||||
await rustBackendMock.setProposeTransferAccountToValueMemoThrowableError(ZcashError.rustCreateToAddress("mocked error"))
|
||||
await rustBackendMock.setProposeShieldingAccountMemoShieldingThresholdThrowableError(ZcashError.rustShieldFunds("mocked error"))
|
||||
await rustBackendMock.setCreateProposedTransactionProposalUskThrowableError(ZcashError.rustCreateToAddress("mocked error"))
|
||||
await rustBackendMock.setProposeShieldingAccountMemoShieldingThresholdTransparentReceiverThrowableError(ZcashError.rustShieldFunds("mocked error"))
|
||||
await rustBackendMock.setCreateProposedTransactionsProposalUskThrowableError(ZcashError.rustCreateToAddress("mocked error"))
|
||||
await rustBackendMock.setDecryptAndStoreTransactionTxBytesMinedHeightThrowableError(ZcashError.rustDecryptAndStoreTransaction("mock fail"))
|
||||
|
||||
await rustBackendMock.setInitDataDbSeedClosure() { seed in
|
||||
|
|
Loading…
Reference in New Issue