WIP - shield funds
This commit is contained in:
parent
6ec126d471
commit
45fa30838f
|
@ -121,6 +121,18 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
|
||||||
return allTxs
|
return allTxs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func balance(address: String) throws -> Int {
|
||||||
|
|
||||||
|
guard let sum = try dbProvider.connection().scalar(
|
||||||
|
table.select(TableColumns.valueZat.sum)
|
||||||
|
.filter(TableColumns.address == address)
|
||||||
|
) else {
|
||||||
|
throw StorageError.operationFailed
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UTXORepositoryBuilder {
|
class UTXORepositoryBuilder {
|
||||||
|
|
|
@ -11,6 +11,8 @@ protocol UnspentTransactionOutputRepository {
|
||||||
|
|
||||||
func getAll(address: String?) throws -> [UnspentTransactionOutputEntity]
|
func getAll(address: String?) throws -> [UnspentTransactionOutputEntity]
|
||||||
|
|
||||||
|
func balance(address: String) throws -> Int
|
||||||
|
|
||||||
func store(utxos: [UnspentTransactionOutputEntity]) throws
|
func store(utxos: [UnspentTransactionOutputEntity]) throws
|
||||||
|
|
||||||
func clearAll(address: String?) throws
|
func clearAll(address: String?) throws
|
||||||
|
|
|
@ -225,7 +225,6 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
||||||
UInt(outputParamsPath.lengthOfBytes(using: .utf8)))
|
UInt(outputParamsPath.lengthOfBytes(using: .utf8)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static func shieldFunds(dbCache: URL, dbData: URL, account: Int32, tsk: String, extsk: String, memo: String?, spendParamsPath: String, outputParamsPath: String) -> Int64 {
|
static func shieldFunds(dbCache: URL, dbData: URL, account: Int32, tsk: String, extsk: String, memo: String?, spendParamsPath: String, outputParamsPath: String) -> Int64 {
|
||||||
let dbData = dbData.osStr()
|
let dbData = dbData.osStr()
|
||||||
let dbCache = dbCache.osStr()
|
let dbCache = dbCache.osStr()
|
||||||
|
@ -356,6 +355,22 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
||||||
return sk
|
return sk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func deriveTransparentAddressFromSecretKey(_ tsk: String) throws -> String? {
|
||||||
|
|
||||||
|
guard !tsk.containsCStringNullBytesBeforeStringEnding() else {
|
||||||
|
throw RustWeldingError.malformedStringInput
|
||||||
|
}
|
||||||
|
guard let tAddrCStr = zcashlc_derive_transparent_address_from_secret_key([CChar](tsk.utf8CString)) else {
|
||||||
|
if let error = lastError() {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let tAddr = String(validatingUTF8: tAddrCStr)
|
||||||
|
|
||||||
|
return tAddr
|
||||||
|
}
|
||||||
|
|
||||||
static func consensusBranchIdFor(height: Int32) throws -> Int32 {
|
static func consensusBranchIdFor(height: Int32) throws -> Int32 {
|
||||||
let branchId = zcashlc_branch_id_for_height(height)
|
let branchId = zcashlc_branch_id_for_height(height)
|
||||||
|
|
||||||
|
@ -365,6 +380,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
||||||
|
|
||||||
return branchId
|
return branchId
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension ZcashRustBackend {
|
private extension ZcashRustBackend {
|
||||||
|
|
|
@ -255,9 +255,21 @@ public protocol ZcashRustBackendWelding {
|
||||||
*/
|
*/
|
||||||
static func deriveTransparentAddressFromSeed(seed: [UInt8]) throws -> String?
|
static func deriveTransparentAddressFromSeed(seed: [UInt8]) throws -> String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
Derives a transparent secret key from Seed
|
||||||
|
- Parameter seed: an array of bytes containing the seed
|
||||||
|
- Returns: an optional String containing the transparent secret (private) key
|
||||||
|
*/
|
||||||
static func deriveTransparentPrivateKeyFromSeed(seed: [UInt8]) throws -> String?
|
static func deriveTransparentPrivateKeyFromSeed(seed: [UInt8]) throws -> String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
Derives a transparent address from a secret key
|
||||||
|
- Parameter tsk: a hex string containing the Secret Key
|
||||||
|
- Returns: an optional String containing the transparent address.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static func deriveTransparentAddressFromSecretKey(_ tsk: String) throws -> String?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Gets the consensus branch id for the given height
|
Gets the consensus branch id for the given height
|
||||||
- Parameter height: the height you what to know the branch id for
|
- Parameter height: the height you what to know the branch id for
|
||||||
|
|
|
@ -24,6 +24,12 @@ public enum SynchronizerError: Error {
|
||||||
case parameterMissing(underlyingError: Error)
|
case parameterMissing(underlyingError: Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ShieldFundsError: Error {
|
||||||
|
case noUTXOFound
|
||||||
|
case insuficientTransparentFunds
|
||||||
|
case shieldingFailed(underlyingError: Error)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Primary interface for interacting with the SDK. Defines the contract that specific
|
Primary interface for interacting with the SDK. Defines the contract that specific
|
||||||
implementations like SdkSynchronizer fulfill.
|
implementations like SdkSynchronizer fulfill.
|
||||||
|
@ -73,6 +79,16 @@ public protocol Synchronizer {
|
||||||
*/
|
*/
|
||||||
func sendToAddress(spendingKey: String, zatoshi: Int64, toAddress: String, memo: String?, from accountIndex: Int, resultBlock: @escaping (_ result: Result<PendingTransactionEntity, Error>) -> Void)
|
func sendToAddress(spendingKey: String, zatoshi: Int64, toAddress: String, memo: String?, from accountIndex: Int, resultBlock: @escaping (_ result: Result<PendingTransactionEntity, Error>) -> Void)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sends zatoshi.
|
||||||
|
- Parameter spendingKey: the key that allows spends to occur.
|
||||||
|
- Parameter transparentSecretKey: the key that allows to spend transaprent funds
|
||||||
|
- Parameter zatoshi: the amount of zatoshi to send.
|
||||||
|
- Parameter memo: the optional memo to include as part of the transaction.
|
||||||
|
- Parameter accountIndex: the optional account id to use. By default, the first account is used.
|
||||||
|
*/
|
||||||
|
func shieldFunds(spendingKey: String, transparentSecretKey: String, memo: String?, from accountIndex: Int, resultBlock: @escaping (_ result: Result<PendingTransactionEntity, Error>) -> Void)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Attempts to cancel a transaction that is about to be sent. Typically, cancellation is only
|
Attempts to cancel a transaction that is about to be sent. Typically, cancellation is only
|
||||||
an option if the transaction has not yet been submitted to the server.
|
an option if the transaction has not yet been submitted to the server.
|
||||||
|
|
|
@ -68,26 +68,27 @@ public protocol KeyDeriving {
|
||||||
*/
|
*/
|
||||||
func deriveShieldedAddress(viewingKey: String) throws -> String
|
func deriveShieldedAddress(viewingKey: String) throws -> String
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Validates the given viewing key
|
Validates the given viewing key
|
||||||
- Throws DerivationError when it's invalid
|
- Throws DerivationError when it's invalid
|
||||||
*/
|
*/
|
||||||
func validateViewingKey(viewingKey: String) throws
|
func validateViewingKey(viewingKey: String) throws
|
||||||
|
|
||||||
|
|
||||||
// WIP probably shouldn't be used just yet. Why?
|
// WIP probably shouldn't be used just yet. Why?
|
||||||
// - because we need the private key associated with this seed and this function doesn't return it.
|
// - because we need the private key associated with this seed and this function doesn't return it.
|
||||||
// - the underlying implementation needs to be split out into a few lower-level calls
|
// - the underlying implementation needs to be split out into a few lower-level calls
|
||||||
func deriveTransparentAddress(seed: [UInt8]) throws -> String
|
func deriveTransparentAddress(seed: [UInt8]) throws -> String
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Derives a SecretKey to spend transparent funds from the given seed
|
Derives a SecretKey to spend transparent funds from the given seed
|
||||||
*/
|
*/
|
||||||
func deriveTransparentPrivateKey(seed: [UInt8]) throws -> String
|
func deriveTransparentPrivateKey(seed: [UInt8]) throws -> String
|
||||||
|
|
||||||
|
/**
|
||||||
|
Derives a transparent address from the given transparent Secret Key
|
||||||
|
*/
|
||||||
|
func deriveTransparentAddressFromPrivateKey(_ tsk: String) throws -> String
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum KeyDerivationErrors: Error {
|
public enum KeyDerivationErrors: Error {
|
||||||
|
@ -227,7 +228,7 @@ public class DerivationTool: KeyDeriving {
|
||||||
|
|
||||||
public func validateViewingKey(viewingKey: String) throws {
|
public func validateViewingKey(viewingKey: String) throws {
|
||||||
// TODO
|
// TODO
|
||||||
throw KeyDerivationErrors.unableToDerive
|
// throw KeyDerivationErrors.unableToDerive
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -275,4 +276,14 @@ extension DerivationTool: KeyValidation {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public func deriveTransparentAddressFromPrivateKey(_ tsk: String) throws -> String {
|
||||||
|
do {
|
||||||
|
guard let tAddr = try rustwelding.deriveTransparentAddressFromSecretKey(tsk) else {
|
||||||
|
throw KeyDerivationErrors.unableToDerive
|
||||||
|
}
|
||||||
|
return tAddr
|
||||||
|
} catch {
|
||||||
|
throw KeyDerivationErrors.derivationError(underlyingError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ enum TransactionManagerError: Error {
|
||||||
case cancelled(tx: PendingTransactionEntity)
|
case cancelled(tx: PendingTransactionEntity)
|
||||||
case internalInconsistency(tx: PendingTransactionEntity)
|
case internalInconsistency(tx: PendingTransactionEntity)
|
||||||
case submitFailed(tx: PendingTransactionEntity, errorCode: Int)
|
case submitFailed(tx: PendingTransactionEntity, errorCode: Int)
|
||||||
|
case shieldingEncodingFailed(tx: PendingTransactionEntity, reason: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
class PersistentTransactionManager: OutboundTransactionManager {
|
class PersistentTransactionManager: OutboundTransactionManager {
|
||||||
|
@ -39,6 +40,45 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
||||||
return insertedTx
|
return insertedTx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encodeShieldingTransaction(spendingKey: String, tsk: String, pendingTransaction: PendingTransactionEntity, result: @escaping (Result<PendingTransactionEntity, Error>) -> Void) {
|
||||||
|
queue.async { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
let derivationTool = DerivationTool()
|
||||||
|
guard let vk = try? derivationTool.deriveViewingKey(spendingKey: spendingKey),
|
||||||
|
let zAddr = try? derivationTool.deriveShieldedAddress(viewingKey: vk) else {
|
||||||
|
result(.failure(TransactionManagerError.shieldingEncodingFailed(tx: pendingTransaction, reason: "There was an error Deriving your keys")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard pendingTransaction.toAddress == zAddr else {
|
||||||
|
result(.failure(TransactionManagerError.shieldingEncodingFailed(tx: pendingTransaction, reason: "the recipient address does not match your derived shielded address. Shielding transactions addresses must match the ones derived from your keys. This is a serious error. We are not letting you encode this shielding transaction because it can lead to loss of funds")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
let encodedTransaction = try self.encoder.createShieldingTransaction(spendingKey: spendingKey, tSecretKey: tsk, memo: pendingTransaction.memo?.asZcashTransactionMemo(), from: pendingTransaction.accountIndex)
|
||||||
|
let transaction = try self.encoder.expandEncodedTransaction(encodedTransaction)
|
||||||
|
|
||||||
|
var pending = pendingTransaction
|
||||||
|
pending.encodeAttempts = pending.encodeAttempts + 1
|
||||||
|
pending.raw = encodedTransaction.raw
|
||||||
|
pending.rawTransactionId = encodedTransaction.transactionId
|
||||||
|
pending.expiryHeight = transaction.expiryHeight ?? BlockHeight.empty()
|
||||||
|
pending.minedHeight = transaction.minedHeight ?? BlockHeight.empty()
|
||||||
|
try self.repository.update(pending)
|
||||||
|
result(.success(pending))
|
||||||
|
} catch StorageError.updateFailed {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
result(.failure(TransactionManagerError.updateFailed(tx: pendingTransaction)))
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
result(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func encode(spendingKey: String, pendingTransaction: PendingTransactionEntity, result: @escaping (Result<PendingTransactionEntity, Error>) -> Void) {
|
func encode(spendingKey: String, pendingTransaction: PendingTransactionEntity, result: @escaping (Result<PendingTransactionEntity, Error>) -> Void) {
|
||||||
|
|
||||||
queue.async { [weak self] in
|
queue.async { [weak self] in
|
||||||
|
@ -60,6 +100,13 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
||||||
result(.failure(TransactionManagerError.updateFailed(tx: pendingTransaction)))
|
result(.failure(TransactionManagerError.updateFailed(tx: pendingTransaction)))
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
do {
|
||||||
|
try self.updateOnFailure(tx: pendingTransaction, error: error)
|
||||||
|
} catch {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
result(.failure(TransactionManagerError.updateFailed(tx: pendingTransaction)))
|
||||||
|
}
|
||||||
|
}
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
result(.failure(error))
|
result(.failure(error))
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,39 @@ protocol TransactionEncoder {
|
||||||
*/
|
*/
|
||||||
func createTransaction(spendingKey: String, zatoshi: Int, to: String, memo: String?, from accountIndex: Int, result: @escaping TransactionEncoderResultBlock)
|
func createTransaction(spendingKey: String, zatoshi: Int, to: String, memo: String?, from accountIndex: Int, result: @escaping TransactionEncoderResultBlock)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a transaction that will attempt to shield transparent funds that are present on the cacheDB .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).
|
||||||
|
Blocking
|
||||||
|
|
||||||
|
- Parameters:
|
||||||
|
- Parameter spendingKey: a string containing the spending key
|
||||||
|
- Parameter tSecretKey: transparent secret key to spend the UTXOs
|
||||||
|
- Parameter memo: string containing the memo (optional)
|
||||||
|
- Parameter accountIndex: index of the account that will be used to send the funds
|
||||||
|
|
||||||
|
- Throws: a TransactionEncoderError
|
||||||
|
*/
|
||||||
|
func createShieldingTransaction(spendingKey: String, tSecretKey: String, memo: String?, from accountIndex: Int) throws -> EncodedTransaction
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a transaction that will attempt to shield transparent funds that are present on the cacheDB .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).
|
||||||
|
Non-Blocking
|
||||||
|
|
||||||
|
- Parameters:
|
||||||
|
- Parameter spendingKey: a string containing the spending key
|
||||||
|
- Parameter tSecretKey: transparent secret key to spend the UTXOs
|
||||||
|
- Parameter memo: string containing the memo (optional)
|
||||||
|
- Parameter accountIndex: index of the account that will be used to send the funds
|
||||||
|
|
||||||
|
- Returns: a TransactionEncoderResultBlock
|
||||||
|
*/
|
||||||
|
|
||||||
|
func createShieldingTransaction(spendingKey: String, tSecretKey: String, memo: String?, from accountIndex: Int, result: @escaping TransactionEncoderResultBlock)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Fetch the Transaction Entity from the encoded representation
|
Fetch the Transaction Entity from the encoded representation
|
||||||
- Parameter encodedTransaction: The encoded transaction to expand
|
- Parameter encodedTransaction: The encoded transaction to expand
|
||||||
|
|
|
@ -16,6 +16,8 @@ import Foundation
|
||||||
protocol OutboundTransactionManager {
|
protocol OutboundTransactionManager {
|
||||||
func initSpend(zatoshi: Int, toAddress: String, memo: String?, from accountIndex: Int) throws -> PendingTransactionEntity
|
func initSpend(zatoshi: Int, toAddress: String, memo: String?, from accountIndex: Int) throws -> PendingTransactionEntity
|
||||||
|
|
||||||
|
func encodeShieldingTransaction(spendingKey: String, tsk: String, pendingTransaction: PendingTransactionEntity, result: @escaping (Result<PendingTransactionEntity, Error>) -> Void)
|
||||||
|
|
||||||
func encode(spendingKey: String, pendingTransaction: PendingTransactionEntity, result: @escaping (Result<PendingTransactionEntity, Error>) -> Void)
|
func encode(spendingKey: String, pendingTransaction: PendingTransactionEntity, result: @escaping (Result<PendingTransactionEntity, Error>) -> Void)
|
||||||
|
|
||||||
func submit(pendingTransaction: PendingTransactionEntity, result: @escaping (Result<PendingTransactionEntity, Error>) -> Void)
|
func submit(pendingTransaction: PendingTransactionEntity, result: @escaping (Result<PendingTransactionEntity, Error>) -> Void)
|
||||||
|
|
|
@ -15,15 +15,18 @@ class WalletTransactionEncoder: TransactionEncoder {
|
||||||
private var outputParamsURL: URL
|
private var outputParamsURL: URL
|
||||||
private var spendParamsURL: URL
|
private var spendParamsURL: URL
|
||||||
private var dataDbURL: URL
|
private var dataDbURL: URL
|
||||||
|
private var cacheDbURL: URL
|
||||||
|
|
||||||
init(rust: ZcashRustBackendWelding.Type,
|
init(rust: ZcashRustBackendWelding.Type,
|
||||||
dataDb: URL,
|
dataDb: URL,
|
||||||
|
cacheDb: URL,
|
||||||
repository: TransactionRepository,
|
repository: TransactionRepository,
|
||||||
outputParams: URL,
|
outputParams: URL,
|
||||||
spendParams: URL) {
|
spendParams: URL) {
|
||||||
|
|
||||||
self.rustBackend = rust
|
self.rustBackend = rust
|
||||||
self.dataDbURL = dataDb
|
self.dataDbURL = dataDb
|
||||||
|
self.cacheDbURL = cacheDb
|
||||||
self.repository = repository
|
self.repository = repository
|
||||||
self.outputParamsURL = outputParams
|
self.outputParamsURL = outputParams
|
||||||
self.spendParamsURL = spendParams
|
self.spendParamsURL = spendParams
|
||||||
|
@ -34,6 +37,7 @@ class WalletTransactionEncoder: TransactionEncoder {
|
||||||
convenience init(initializer: Initializer) {
|
convenience init(initializer: Initializer) {
|
||||||
self.init(rust: initializer.rustBackend,
|
self.init(rust: initializer.rustBackend,
|
||||||
dataDb: initializer.dataDbURL,
|
dataDb: initializer.dataDbURL,
|
||||||
|
cacheDb: initializer.cacheDbURL,
|
||||||
repository: initializer.transactionRepository,
|
repository: initializer.transactionRepository,
|
||||||
outputParams: initializer.outputParamsURL,
|
outputParams: initializer.outputParamsURL,
|
||||||
spendParams: initializer.spendParamsURL)
|
spendParams: initializer.spendParamsURL)
|
||||||
|
@ -99,6 +103,51 @@ class WalletTransactionEncoder: TransactionEncoder {
|
||||||
return Int(txId)
|
return Int(txId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createShieldingTransaction(spendingKey: String, tSecretKey: String, memo: String?, from accountIndex: Int) throws -> EncodedTransaction {
|
||||||
|
let txId = try createShieldingSpend(spendingKey: spendingKey, tsk: tSecretKey, memo: memo, accountIndex: accountIndex)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let transaction = try repository.findBy(id: txId)
|
||||||
|
|
||||||
|
guard let tx = transaction else {
|
||||||
|
throw TransactionEncoderError.notFound(transactionId: txId)
|
||||||
|
}
|
||||||
|
|
||||||
|
LoggerProxy.debug("sentTransaction id: \(txId)")
|
||||||
|
return EncodedTransaction(transactionId: tx.transactionId , raw: tx.raw)
|
||||||
|
} catch {
|
||||||
|
throw TransactionEncoderError.notFound(transactionId: txId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createShieldingTransaction(spendingKey: String, tSecretKey: String, memo: String?, from accountIndex: Int, result: @escaping TransactionEncoderResultBlock) {
|
||||||
|
queue.async {
|
||||||
|
result(.failure(RustWeldingError.genericError(message: "not implemented")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createShieldingSpend(spendingKey: String, tsk: String, memo: String?, accountIndex: Int) throws -> Int {
|
||||||
|
guard ensureParams(spend: self.spendParamsURL, output: self.spendParamsURL) else {
|
||||||
|
throw TransactionEncoderError.missingParams
|
||||||
|
}
|
||||||
|
|
||||||
|
let txId = rustBackend.shieldFunds(dbCache: self.cacheDbURL,
|
||||||
|
dbData: self.dataDbURL,
|
||||||
|
account: Int32(accountIndex),
|
||||||
|
tsk: tsk,
|
||||||
|
extsk: spendingKey,
|
||||||
|
memo: memo,
|
||||||
|
spendParamsPath: self.spendParamsURL.path,
|
||||||
|
outputParamsPath: self.outputParamsURL.path)
|
||||||
|
|
||||||
|
guard txId > 0 else {
|
||||||
|
throw rustBackend.lastError() ?? RustWeldingError.genericError(message: "create spend failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return Int(txId)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func ensureParams(spend: URL, output: URL) -> Bool {
|
func ensureParams(spend: URL, output: URL) -> Bool {
|
||||||
|
|
||||||
let readableSpend = FileManager.default.isReadableFile(atPath: spend.path)
|
let readableSpend = FileManager.default.isReadableFile(atPath: spend.path)
|
||||||
|
|
|
@ -79,6 +79,8 @@ public class SDKSynchronizer: Synchronizer {
|
||||||
public static let error = "SDKSynchronizer.error"
|
public static let error = "SDKSynchronizer.error"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static let shieldingThreshold: Int = 10000
|
||||||
|
|
||||||
public private(set) var status: Status {
|
public private(set) var status: Status {
|
||||||
didSet {
|
didSet {
|
||||||
notify(status: status)
|
notify(status: status)
|
||||||
|
@ -346,6 +348,52 @@ public class SDKSynchronizer: Synchronizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func shieldFunds(spendingKey: String, transparentSecretKey: String, memo: String?, from accountIndex: Int, resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void) {
|
||||||
|
|
||||||
|
// let's see if there are funds to shield
|
||||||
|
let derivationTool = DerivationTool.default
|
||||||
|
|
||||||
|
do {
|
||||||
|
let tAddr = try derivationTool.deriveTransparentAddressFromPrivateKey(transparentSecretKey)
|
||||||
|
let tBalance = try utxoRepository.balance(address: tAddr)
|
||||||
|
|
||||||
|
guard tBalance > Self.shieldingThreshold else {
|
||||||
|
resultBlock(.failure(ShieldFundsError.insuficientTransparentFunds))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let vk = try derivationTool.deriveViewingKey(spendingKey: spendingKey)
|
||||||
|
let zAddr = try derivationTool.deriveShieldedAddress(viewingKey: vk)
|
||||||
|
|
||||||
|
let shieldingSpend = try transactionManager.initSpend(zatoshi: tBalance, toAddress: zAddr, memo: memo, from: 0)
|
||||||
|
|
||||||
|
transactionManager.encodeShieldingTransaction(spendingKey: spendingKey, tsk: transparentSecretKey, pendingTransaction: shieldingSpend) {[weak self] (result) in
|
||||||
|
guard let self = self else { return }
|
||||||
|
switch result {
|
||||||
|
|
||||||
|
case .success(let tx):
|
||||||
|
self.transactionManager.submit(pendingTransaction: tx) { (submitResult) in
|
||||||
|
switch submitResult {
|
||||||
|
case .success(let submittedTx):
|
||||||
|
resultBlock(.success(submittedTx))
|
||||||
|
case .failure(let submissionError):
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
resultBlock(.failure(submissionError))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
resultBlock(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
resultBlock(.failure(error))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func createToAddress(spendingKey: String, zatoshi: Int64, toAddress: String, memo: String?, from accountIndex: Int, resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void) {
|
func createToAddress(spendingKey: String, zatoshi: Int64, toAddress: String, memo: String?, from accountIndex: Int, resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void) {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
Loading…
Reference in New Issue