Transaction data access improvements + Create Spend + Get Balance Screen (#33)
* Transaction encoder implementation tests WIP * Create Spend and Create Transaction + Test [WIP] * New! Get Balance Screen * Send to address
This commit is contained in:
parent
908d8ac823
commit
c772934d3d
|
@ -1,4 +1,4 @@
|
|||
platform :ios, '11.0'
|
||||
platform :ios, '12.0'
|
||||
|
||||
target 'ZcashLightClientSample' do
|
||||
# Comment the next line if you don't want to use dynamic frameworks
|
||||
|
|
|
@ -56,8 +56,8 @@ SPEC CHECKSUMS:
|
|||
SQLite.swift: d2b4642190917051ce6bd1d49aab565fe794eea3
|
||||
SwiftGRPC: f8fcfecb547c96cc6913de619f95fa3cd09838ee
|
||||
SwiftProtobuf: 4fd9645e69b72cbae6ec8da5be0cdd20ca6565dd
|
||||
ZcashLightClientKit: 5c5b46a9c1f5b293d493241d542215155e9bbfd8
|
||||
ZcashLightClientKit: 3f257088ec27eae318ad2448775593f93334e43c
|
||||
|
||||
PODFILE CHECKSUM: bc6f10f9eb1279ca097cfa020229bb9cfb768223
|
||||
PODFILE CHECKSUM: 1e92cf7111b50439bcbf88bd8118908750cc6e7c
|
||||
|
||||
COCOAPODS: 1.8.4
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0D2343EE238C91B900606F71 /* sapling-output.params in Resources */ = {isa = PBXBuildFile; fileRef = 0D2343EC238C91B900606F71 /* sapling-output.params */; };
|
||||
0D2343EF238C91B900606F71 /* sapling-spend.params in Resources */ = {isa = PBXBuildFile; fileRef = 0D2343ED238C91B900606F71 /* sapling-spend.params */; };
|
||||
0D756A94236C761E009B041B /* GetAddressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D756A93236C761E009B041B /* GetAddressViewController.swift */; };
|
||||
0D7A4A83236CCD88001F4DD8 /* SyncBlocksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7A4A82236CCD88001F4DD8 /* SyncBlocksViewController.swift */; };
|
||||
0D907F162322CC5900D641FE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D907F152322CC5900D641FE /* AppDelegate.swift */; };
|
||||
|
@ -16,6 +18,7 @@
|
|||
0D907F202322CC5B00D641FE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0D907F1E2322CC5B00D641FE /* LaunchScreen.storyboard */; };
|
||||
0D907F2B2322CC5B00D641FE /* ZcashLightClientSampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D907F2A2322CC5B00D641FE /* ZcashLightClientSampleTests.swift */; };
|
||||
0D907F362322CC5B00D641FE /* ZcashLightClientSampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D907F352322CC5B00D641FE /* ZcashLightClientSampleUITests.swift */; };
|
||||
0DCD3DC7238D88B100DD3EC4 /* GetBalanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD3DC6238D88B100DD3EC4 /* GetBalanceViewController.swift */; };
|
||||
0DDFB33C236B743000AED892 /* LatestHeightViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DDFB33B236B743000AED892 /* LatestHeightViewController.swift */; };
|
||||
0DDFB33E236B844900AED892 /* DemoAppConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DDFB33D236B844900AED892 /* DemoAppConfig.swift */; };
|
||||
30AB89006A2D6891F32BFBF0 /* Pods_ZcashLightClientSample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17A69D88CA6DB45FA9D21E75 /* Pods_ZcashLightClientSample.framework */; };
|
||||
|
@ -41,6 +44,8 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0D2343EC238C91B900606F71 /* sapling-output.params */ = {isa = PBXFileReference; lastKnownFileType = file; name = "sapling-output.params"; path = "../../../ZcashLightClientKitTests/sapling-output.params"; sourceTree = "<group>"; };
|
||||
0D2343ED238C91B900606F71 /* sapling-spend.params */ = {isa = PBXFileReference; lastKnownFileType = file; name = "sapling-spend.params"; path = "../../../ZcashLightClientKitTests/sapling-spend.params"; sourceTree = "<group>"; };
|
||||
0D756A93236C761E009B041B /* GetAddressViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetAddressViewController.swift; sourceTree = "<group>"; };
|
||||
0D7A4A82236CCD88001F4DD8 /* SyncBlocksViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncBlocksViewController.swift; sourceTree = "<group>"; };
|
||||
0D907F122322CC5900D641FE /* ZcashLightClientSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ZcashLightClientSample.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -56,6 +61,7 @@
|
|||
0D907F312322CC5B00D641FE /* ZcashLightClientSampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ZcashLightClientSampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0D907F352322CC5B00D641FE /* ZcashLightClientSampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZcashLightClientSampleUITests.swift; sourceTree = "<group>"; };
|
||||
0D907F372322CC5B00D641FE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0DCD3DC6238D88B100DD3EC4 /* GetBalanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = GetBalanceViewController.swift; path = "Get Balance/GetBalanceViewController.swift"; sourceTree = "<group>"; };
|
||||
0DDFB33B236B743000AED892 /* LatestHeightViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatestHeightViewController.swift; sourceTree = "<group>"; };
|
||||
0DDFB33D236B844900AED892 /* DemoAppConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoAppConfig.swift; sourceTree = "<group>"; };
|
||||
17A69D88CA6DB45FA9D21E75 /* Pods_ZcashLightClientSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ZcashLightClientSample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -138,6 +144,10 @@
|
|||
0D907F142322CC5900D641FE /* ZcashLightClientSample */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0DCD3DC5238D888B00DD3EC4 /* Get Balance */,
|
||||
0DCD3DC4238D886400DD3EC4 /* Send Funds */,
|
||||
0D2343EC238C91B900606F71 /* sapling-output.params */,
|
||||
0D2343ED238C91B900606F71 /* sapling-spend.params */,
|
||||
0D7A4A81236CCCDB001F4DD8 /* Sync Blocks */,
|
||||
0D756A92236C75FE009B041B /* Get Address */,
|
||||
0DDFB33A236B733700AED892 /* Latest Block Height */,
|
||||
|
@ -148,6 +158,7 @@
|
|||
0D907F1E2322CC5B00D641FE /* LaunchScreen.storyboard */,
|
||||
0D907F212322CC5B00D641FE /* Info.plist */,
|
||||
0DDFB33D236B844900AED892 /* DemoAppConfig.swift */,
|
||||
0DCD3DC6238D88B100DD3EC4 /* GetBalanceViewController.swift */,
|
||||
);
|
||||
path = ZcashLightClientSample;
|
||||
sourceTree = "<group>";
|
||||
|
@ -170,6 +181,20 @@
|
|||
path = ZcashLightClientSampleUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0DCD3DC4238D886400DD3EC4 /* Send Funds */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = "Send Funds";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0DCD3DC5238D888B00DD3EC4 /* Get Balance */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = "Get Balance";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0DDFB33A236B733700AED892 /* Latest Block Height */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -313,6 +338,8 @@
|
|||
0D907F202322CC5B00D641FE /* LaunchScreen.storyboard in Resources */,
|
||||
0D907F1D2322CC5B00D641FE /* Assets.xcassets in Resources */,
|
||||
0D907F1B2322CC5900D641FE /* Main.storyboard in Resources */,
|
||||
0D2343EE238C91B900606F71 /* sapling-output.params in Resources */,
|
||||
0D2343EF238C91B900606F71 /* sapling-spend.params in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -440,6 +467,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0DCD3DC7238D88B100DD3EC4 /* GetBalanceViewController.swift in Sources */,
|
||||
0D756A94236C761E009B041B /* GetAddressViewController.swift in Sources */,
|
||||
0D907F182322CC5900D641FE /* ViewController.swift in Sources */,
|
||||
0DDFB33C236B743000AED892 /* LatestHeightViewController.swift in Sources */,
|
||||
|
|
|
@ -13,12 +13,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
|
||||
var window: UIWindow?
|
||||
private var wallet: Initializer?
|
||||
@UserDefaults var addresses: [String]?
|
||||
var addresses: [String]?
|
||||
var sharedWallet: Initializer {
|
||||
if let wallet = wallet {
|
||||
return wallet
|
||||
} else {
|
||||
let wallet = Initializer(cacheDbURL:try! __cacheDbURL() , dataDbURL: try! __dataDbURL(), endpoint: DemoAppConfig.endpoint)
|
||||
let wallet = Initializer(cacheDbURL:try! __cacheDbURL() , dataDbURL: try! __dataDbURL(), endpoint: DemoAppConfig.endpoint, spendParamsURL: try! __spendParamsURL(), outputParamsURL: try! __outputParamsURL())
|
||||
self.addresses = try! wallet.initialize(seedProvider: DemoAppConfig(), walletBirthdayHeight: BlockHeight(DemoAppConfig.birthdayHeight)) // Init or DIE
|
||||
self.wallet = wallet
|
||||
return wallet
|
||||
|
@ -94,3 +94,13 @@ func __cacheDbURL() throws -> URL {
|
|||
func __dataDbURL() throws -> URL {
|
||||
try __documentsDirectory().appendingPathComponent("data.db", isDirectory: false)
|
||||
}
|
||||
|
||||
|
||||
func __spendParamsURL() throws -> URL {
|
||||
try __documentsDirectory().appendingPathComponent("sapling-spend.params", isDirectory: false)
|
||||
|
||||
}
|
||||
|
||||
func __outputParamsURL() throws -> URL {
|
||||
try __documentsDirectory().appendingPathComponent("sapling-output.params", isDirectory: false)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15400" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Ewq-Xy-xHb">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Ewq-Xy-xHb">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
|
@ -79,6 +79,26 @@
|
|||
<segue destination="eja-yc-RHW" kind="show" id="fZ3-Vb-Oxe"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="v8L-AQ-cAg" style="IBUITableViewCellStyleDefault" id="RKO-CX-5oF">
|
||||
<rect key="frame" x="0.0" y="158.5" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="RKO-CX-5oF" id="WdD-zf-ng7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Get Balance" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="v8L-AQ-cAg">
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="r9r-pi-Z4o" kind="show" id="dvg-Kp-7LV"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
|
@ -291,5 +311,38 @@
|
|||
</objects>
|
||||
<point key="canvasLocation" x="-92" y="40"/>
|
||||
</scene>
|
||||
<!--Get Balance View Controller-->
|
||||
<scene sceneID="jf5-6v-Lfw">
|
||||
<objects>
|
||||
<viewController id="r9r-pi-Z4o" customClass="GetBalanceViewController" customModule="ZcashLightClientSample" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="n2N-jn-4DV">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GbA-U2-oep">
|
||||
<rect key="frame" x="0.0" y="88" width="414" height="774"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="32"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="GbA-U2-oep" firstAttribute="leading" secondItem="5sH-IW-gs5" secondAttribute="leading" id="FaP-La-2qn"/>
|
||||
<constraint firstItem="5sH-IW-gs5" firstAttribute="trailing" secondItem="GbA-U2-oep" secondAttribute="trailing" id="FaU-Yc-Sfo"/>
|
||||
<constraint firstItem="5sH-IW-gs5" firstAttribute="bottom" secondItem="GbA-U2-oep" secondAttribute="bottom" id="XAB-SE-nvz"/>
|
||||
<constraint firstItem="GbA-U2-oep" firstAttribute="top" secondItem="5sH-IW-gs5" secondAttribute="top" id="icf-gK-HIL"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="5sH-IW-gs5"/>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" id="HmT-B1-R92"/>
|
||||
<connections>
|
||||
<outlet property="balance" destination="GbA-U2-oep" id="Y61-Hu-0LP"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="R73-h3-yvk" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1830" y="1623"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// GetBalanceViewController.swift
|
||||
// ZcashLightClientSample
|
||||
//
|
||||
// Created by Francisco Gindre on 11/26/19.
|
||||
// Copyright © 2019 Electric Coin Company. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import ZcashLightClientKit
|
||||
class GetBalanceViewController: UIViewController {
|
||||
|
||||
@IBOutlet weak var balance: UILabel!
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
self.title = "Account 0 Balance"
|
||||
self.balance.text = "\(Initializer.shared.getBalance().asHumanReadableZecBalance()) ZEC"
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// MARK: - Navigation
|
||||
|
||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
// Get the new view controller using segue.destination.
|
||||
// Pass the selected object to the new view controller.
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
extension Int64 {
|
||||
func asHumanReadableZecBalance() -> Double {
|
||||
Double(self) / Double(ZATOSHI_PER_ZEC)
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ Pod::Spec.new do |s|
|
|||
s.public_header_files = 'ZcashLightClientKit/**/*.h'
|
||||
s.source_files = 'ZcashLightClientKit/**/*.{swift,h,a}'
|
||||
s.module_map = 'ZcashLightClientKit.modulemap'
|
||||
s.swift_version = '5.0'
|
||||
s.swift_version = '5.1'
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.dependency 'SwiftGRPC'
|
||||
s.dependency 'SQLite.swift'
|
||||
|
@ -34,8 +34,8 @@ Pod::Spec.new do |s|
|
|||
CMD
|
||||
|
||||
s.test_spec 'Tests' do | test_spec |
|
||||
test_spec.source_files = 'ZcashLightClientKitTests/**/*.{swift,db}'
|
||||
test_spec.ios.resources = 'ZcashLightClientKitTests/**/*.{db}'
|
||||
test_spec.source_files = 'ZcashLightClientKitTests/**/*.{swift}'
|
||||
test_spec.ios.resources = 'ZcashLightClientKitTests/**/*.{db,params}'
|
||||
test_spec.dependency 'SwiftGRPC'
|
||||
test_spec.dependency 'SQLite.swift'
|
||||
end
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// PendingTransactionDao.swift
|
||||
// ZcashLightClientKit
|
||||
//
|
||||
// Created by Francisco Gindre on 11/19/19.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class PendingTransactionSQLDAO: PendingTransactionRepository {
|
||||
|
||||
var dbProvider: ConnectionProvider
|
||||
|
||||
init(dbProvider: ConnectionProvider) {
|
||||
self.dbProvider = dbProvider
|
||||
}
|
||||
|
||||
func create(_ transaction: PendingTransactionEntity) throws -> Int64 {
|
||||
-1
|
||||
}
|
||||
|
||||
func update(_ transaction: PendingTransactionEntity) throws {
|
||||
|
||||
}
|
||||
|
||||
func delete(_ transaction: PendingTransactionEntity) throws {
|
||||
|
||||
}
|
||||
|
||||
func cancel(_ transaction: PendingTransactionEntity) throws {
|
||||
|
||||
}
|
||||
|
||||
func find(by id: Int64) throws -> PendingTransactionEntity? {
|
||||
nil
|
||||
}
|
||||
|
||||
func getAll() throws -> [PendingTransactionEntity] {
|
||||
[]
|
||||
}
|
||||
|
||||
}
|
|
@ -45,7 +45,7 @@ struct TransactionBuilder {
|
|||
// Optional values
|
||||
|
||||
var toAddress: String?
|
||||
if let to = bindings[ConfirmedColumns.toAddress.rawValue] as? String {
|
||||
if let to = bindings[ConfirmedColumns.toAddress.rawValue] as? String {
|
||||
toAddress = to
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ struct TransactionBuilder {
|
|||
}
|
||||
|
||||
var memo: Data?
|
||||
if let memoBlob = bindings[ConfirmedColumns.memo.rawValue] as? Blob {
|
||||
if let memoBlob = bindings[ConfirmedColumns.memo.rawValue] as? Blob {
|
||||
memo = Data(blob: memoBlob)
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ struct TransactionBuilder {
|
|||
// Optional values
|
||||
|
||||
var memo: Data?
|
||||
if let memoBlob = bindings[ReceivedColumns.memo.rawValue] as? Blob {
|
||||
if let memoBlob = bindings[ReceivedColumns.memo.rawValue] as? Blob {
|
||||
memo = Data(blob: memoBlob)
|
||||
}
|
||||
|
||||
|
|
|
@ -42,8 +42,6 @@ struct ConfirmedTransaction: ConfirmedTransactionEntity {
|
|||
var rawTransactionId: Data?
|
||||
}
|
||||
|
||||
|
||||
|
||||
class TransactionSQLDAO: TransactionRepository {
|
||||
|
||||
struct TableStructure {
|
||||
|
@ -72,9 +70,10 @@ class TransactionSQLDAO: TransactionRepository {
|
|||
try dbProvider.connection().scalar(transactions.filter(TableStructure.minedHeight == nil).count)
|
||||
}
|
||||
|
||||
func findBy(id: Int) throws -> TransactionEntity? {
|
||||
func findBy(id: Int64) throws -> TransactionEntity? {
|
||||
let query = transactions.filter(TableStructure.id == Int64(id)).limit(1)
|
||||
let entity: Transaction? = try dbProvider.connection().prepare(query).map({ try $0.decode() }).first
|
||||
let sequence = try dbProvider.connection().prepare(query)
|
||||
let entity: Transaction? = try sequence.map({ try $0.decode() }).first
|
||||
return entity
|
||||
}
|
||||
|
||||
|
@ -198,7 +197,7 @@ extension Data {
|
|||
}
|
||||
|
||||
extension Array where Element == UInt8 {
|
||||
var data : Data{
|
||||
var data: Data {
|
||||
return Data(self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import Foundation
|
||||
|
||||
public protocol AccountEntity: Hashable {
|
||||
var account:Int { get set }
|
||||
var account: Int { get set }
|
||||
var extfvk: String { get set }
|
||||
var address: String { get set }
|
||||
}
|
||||
|
@ -28,5 +28,3 @@ public extension AccountEntity {
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// EncodedTransactionEntity.swift
|
||||
// ZcashLightClientKit
|
||||
//
|
||||
// Created by Francisco Gindre on 11/19/19.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct EncodedTransaction: SignedTransactionEntity {
|
||||
var transactionId: Data
|
||||
var raw: Data?
|
||||
}
|
||||
|
||||
extension EncodedTransaction: Hashable {
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(transactionId)
|
||||
hasher.combine(raw)
|
||||
}
|
||||
|
||||
static func == (lhs: Self, rhs: Self) -> Bool {
|
||||
guard lhs.transactionId == rhs.transactionId else { return false }
|
||||
guard lhs.raw == rhs.raw else { return false }
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
//
|
||||
// PendingTransactionEntity.swift
|
||||
// ZcashLightClientKit
|
||||
//
|
||||
// Created by Francisco Gindre on 11/19/19.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol PendingTransactionEntity: SignedTransactionEntity, AbstractTransaction, RawIdentifiable {
|
||||
var toAddress: String { get set }
|
||||
var accountIndex: Int { get set }
|
||||
var minedHeight: BlockHeight { get set }
|
||||
var expiryHeight: BlockHeight { get set }
|
||||
var cancelled: Int { get set }
|
||||
var encodeAttempts: Int { get set }
|
||||
var submitAttempts: Int { get set }
|
||||
var errorMesssage: String? { get set }
|
||||
var errorCode: Int? { get set }
|
||||
var createTime: TimeInterval { get set }
|
||||
|
||||
func isSameTransactionId<T: RawIdentifiable> (other: T) -> Bool
|
||||
func isPending(currentHeight: Int) -> Bool
|
||||
|
||||
var isCreating: Bool { get }
|
||||
var isFailedEncoding: Bool { get }
|
||||
var isFailedSubmit: Bool { get }
|
||||
var isFailure: Bool { get }
|
||||
var isCancelled: Bool { get }
|
||||
var isMined: Bool { get }
|
||||
var isSubmitted: Bool { get }
|
||||
var isSubmitSuccess: Bool { get }
|
||||
}
|
||||
|
||||
public extension PendingTransactionEntity {
|
||||
func isSameTransaction<T: RawIdentifiable>(other: T) -> Bool {
|
||||
guard let selfId = self.rawTransactionId, let otherId = other.rawTransactionId else { return false }
|
||||
return selfId == otherId
|
||||
}
|
||||
|
||||
var isCreating: Bool {
|
||||
(raw?.isEmpty ?? true) != false && submitAttempts <= 0 && !isFailedSubmit && !isFailedEncoding
|
||||
}
|
||||
|
||||
var isFailedEncoding: Bool {
|
||||
(raw?.isEmpty ?? true) != false && encodeAttempts > 0
|
||||
}
|
||||
|
||||
var isFailedSubmit: Bool {
|
||||
errorMesssage != nil || (errorCode != nil && (errorCode ?? 0) < 0)
|
||||
}
|
||||
|
||||
var isFailure: Bool {
|
||||
isFailedEncoding || isFailedSubmit
|
||||
}
|
||||
|
||||
var isCancelled: Bool {
|
||||
cancelled > 0
|
||||
}
|
||||
|
||||
var isMined: Bool {
|
||||
minedHeight > 0
|
||||
}
|
||||
|
||||
var isSubmitted: Bool {
|
||||
submitAttempts > 0
|
||||
}
|
||||
|
||||
func isPending(currentHeight: Int = -1) -> Bool {
|
||||
// not mined and not expired and successfully created
|
||||
!isSubmitSuccess && minedHeight == -1 && (expiryHeight == -1 || expiryHeight > currentHeight) && raw != nil
|
||||
}
|
||||
|
||||
var isSubmitSuccess: Bool {
|
||||
submitAttempts > 0 && (errorCode != nil && (errorCode ?? -1) >= 0) && errorMesssage == nil
|
||||
}
|
||||
}
|
|
@ -66,73 +66,3 @@ public protocol ConfirmedTransactionEntity: MinedTransactionEntity, SignedTransa
|
|||
var toAddress: String? { get set }
|
||||
var expiryHeight: BlockHeight? { get set }
|
||||
}
|
||||
|
||||
public protocol PendingTransactionEntity: SignedTransactionEntity, AbstractTransaction, RawIdentifiable {
|
||||
var toAddress: String { get set }
|
||||
var accountIndex: Int { get set }
|
||||
var minedHeight: BlockHeight { get set }
|
||||
var expiryHeight: BlockHeight { get set }
|
||||
var cancelled: Int { get set }
|
||||
var encodeAttempts: Int { get set }
|
||||
var submitAttempts: Int { get set }
|
||||
var errorMesssage: String? { get set }
|
||||
var errorCode: Int? { get set }
|
||||
var createTime: TimeInterval { get set }
|
||||
|
||||
func isSameTransactionId<T: RawIdentifiable> (other: T) -> Bool
|
||||
func isPending(currentHeight: Int) -> Bool
|
||||
|
||||
var isCreating: Bool { get }
|
||||
var isFailedEncoding: Bool { get }
|
||||
var isFailedSubmit: Bool { get }
|
||||
var isFailure: Bool { get }
|
||||
var isCancelled: Bool { get }
|
||||
var isMined: Bool { get }
|
||||
var isSubmitted: Bool { get }
|
||||
var isSubmitSuccess: Bool { get }
|
||||
}
|
||||
|
||||
public extension PendingTransactionEntity {
|
||||
func isSameTransaction<T: RawIdentifiable>(other: T) -> Bool {
|
||||
guard let selfId = self.rawTransactionId, let otherId = other.rawTransactionId else { return false }
|
||||
return selfId == otherId
|
||||
}
|
||||
|
||||
var isCreating: Bool {
|
||||
(raw?.isEmpty ?? true) != false && submitAttempts <= 0 && !isFailedSubmit && !isFailedEncoding
|
||||
}
|
||||
|
||||
var isFailedEncoding: Bool {
|
||||
(raw?.isEmpty ?? true) != false && encodeAttempts > 0
|
||||
}
|
||||
|
||||
var isFailedSubmit: Bool {
|
||||
errorMesssage != nil || (errorCode != nil && (errorCode ?? 0) < 0)
|
||||
}
|
||||
|
||||
var isFailure: Bool {
|
||||
isFailedEncoding || isFailedSubmit
|
||||
}
|
||||
|
||||
var isCancelled: Bool {
|
||||
cancelled > 0
|
||||
}
|
||||
|
||||
var isMined: Bool {
|
||||
minedHeight > 0
|
||||
}
|
||||
|
||||
var isSubmitted: Bool {
|
||||
submitAttempts > 0
|
||||
}
|
||||
|
||||
func isPending(currentHeight: Int = -1) -> Bool {
|
||||
// not mined and not expired and successfully created
|
||||
!isSubmitSuccess && minedHeight == -1 && (expiryHeight == -1 || expiryHeight > currentHeight) && raw != nil
|
||||
}
|
||||
|
||||
var isSubmitSuccess: Bool {
|
||||
submitAttempts > 0 && (errorCode != nil && (errorCode ?? -1) >= 0) && errorMesssage == nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,17 +45,20 @@ public class Initializer {
|
|||
|
||||
private var rustBackend: ZcashRustBackendWelding.Type = ZcashRustBackend.self
|
||||
private var lowerBoundHeight: BlockHeight = SAPLING_ACTIVATION_HEIGHT
|
||||
private var cacheDbURL: URL
|
||||
private var dataDbURL: URL
|
||||
|
||||
private(set) var cacheDbURL: URL
|
||||
private(set) var dataDbURL: URL
|
||||
private(set) var spendParamsURL: URL
|
||||
private(set) var outputParamsURL: URL
|
||||
private var walletBirthday: WalletBirthday?
|
||||
|
||||
public private(set) var endpoint: LightWalletEndpoint
|
||||
|
||||
public init (cacheDbURL: URL, dataDbURL: URL, endpoint: LightWalletEndpoint) {
|
||||
public init (cacheDbURL: URL, dataDbURL: URL, endpoint: LightWalletEndpoint, spendParamsURL: URL, outputParamsURL: URL) {
|
||||
self.cacheDbURL = cacheDbURL
|
||||
self.dataDbURL = dataDbURL
|
||||
self.endpoint = endpoint
|
||||
self.spendParamsURL = spendParamsURL
|
||||
self.outputParamsURL = outputParamsURL
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -113,7 +116,11 @@ public class Initializer {
|
|||
}
|
||||
|
||||
public func getAddress(index account: Int = 0) -> String? {
|
||||
return rustBackend.getAddress(dbData: dataDbURL, account: Int32(account))
|
||||
rustBackend.getAddress(dbData: dataDbURL, account: Int32(account))
|
||||
}
|
||||
|
||||
public func getBalance(account index: Int = 0) -> Int64 {
|
||||
rustBackend.getBalance(dbData: dataDbURL, account: Int32(index))
|
||||
}
|
||||
|
||||
// TODO: make internal
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// PendingTransactionRepository.swift
|
||||
// ZcashLightClientKit
|
||||
//
|
||||
// Created by Francisco Gindre on 11/19/19.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol PendingTransactionRepository {
|
||||
func create(_ transaction: PendingTransactionEntity) throws -> Int64
|
||||
func update(_ transaction: PendingTransactionEntity) throws
|
||||
func delete(_ transaction: PendingTransactionEntity) throws
|
||||
func cancel(_ transaction: PendingTransactionEntity) throws
|
||||
func find(by id: Int64) throws -> PendingTransactionEntity?
|
||||
func getAll() throws -> [PendingTransactionEntity]
|
||||
}
|
|
@ -12,9 +12,9 @@ enum TransactionRepositoryError: Error {
|
|||
}
|
||||
|
||||
protocol TransactionRepository {
|
||||
func countAll() throws -> Int
|
||||
func countAll() throws -> Int
|
||||
func countUnmined() throws -> Int
|
||||
func findBy(id: Int) throws -> TransactionEntity?
|
||||
func findBy(id: Int64) throws -> TransactionEntity?
|
||||
func findBy(rawId: Data) throws -> TransactionEntity?
|
||||
func findAllSentTransactions(limit: Int) throws -> [ConfirmedTransactionEntity]?
|
||||
func findAllReceivedTransactions(limit: Int) throws -> [ConfirmedTransactionEntity]?
|
||||
|
|
|
@ -7,3 +7,15 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Data {
|
||||
func asZcashTransactionMemo() -> String? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func encodeAsZcashTransactionMemo() -> Data? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,10 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
|
||||
static func lastError() -> RustWeldingError? {
|
||||
guard let message = getLastError() else { return nil }
|
||||
|
||||
if message.contains("couldn't load Sapling spend parameters") {
|
||||
return RustWeldingError.saplingSpendParametersNotFound
|
||||
}
|
||||
return RustWeldingError.genericError(message: message)
|
||||
}
|
||||
|
||||
|
@ -126,7 +130,9 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
let dbData = dbData.osStr()
|
||||
let spendParams = spendParams.osStr()
|
||||
let outputParams = outputParams.osStr()
|
||||
return zcashlc_send_to_address(dbData.0, dbData.1, account, extsk, to, value, memo, spendParams.0, spendParams.1, outputParams.0, outputParams.1)
|
||||
let memoBytes = memo ?? ""
|
||||
|
||||
return zcashlc_send_to_address(dbData.0, dbData.1, account, extsk, to, value, memoBytes, spendParams.0, spendParams.1, outputParams.0, outputParams.1)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ public enum RustWeldingError: Error {
|
|||
case genericError(message: String)
|
||||
case dataDbInitFailed(message: String)
|
||||
case dataDbNotEmpty
|
||||
case saplingSpendParametersNotFound
|
||||
}
|
||||
|
||||
public struct ZcashRustBackendWeldingConstants {
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// TransactionEncoder.swift
|
||||
// ZcashLightClientKit
|
||||
//
|
||||
// Created by Francisco Gindre on 11/20/19.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
typealias TransactionEncoderResultBlock = (_ result: Result<EncodedTransaction,Error>) -> Void
|
||||
|
||||
public enum TransactionEncoderError: Error {
|
||||
case notFound(transactionId: Int64)
|
||||
case NotEncoded(transactionId: Int64)
|
||||
case missingParams
|
||||
case spendingKeyWrongNetwork
|
||||
}
|
||||
|
||||
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).
|
||||
Blocking
|
||||
*/
|
||||
func createTransaction(spendingKey: String, zatoshi: Int64, to: String, memo: String?, from accountIndex: Int) throws -> EncodedTransaction
|
||||
|
||||
/**
|
||||
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).
|
||||
Non-blocking
|
||||
*/
|
||||
func createTransaction(spendingKey: String, zatoshi: Int64, to: String, memo: String?, from accountIndex: Int, result: @escaping TransactionEncoderResultBlock)
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// WalletTransactionEncoder.swift
|
||||
// ZcashLightClientKit
|
||||
//
|
||||
// Created by Francisco Gindre on 11/20/19.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class WalletTransactionEncoder: TransactionEncoder {
|
||||
var rustBackend: ZcashRustBackend.Type
|
||||
var repository: TransactionRepository
|
||||
var initializer: Initializer
|
||||
init(rust: ZcashRustBackend.Type, repository: TransactionRepository, initializer: Initializer) {
|
||||
self.rustBackend = rust
|
||||
self.repository = repository
|
||||
self.initializer = initializer
|
||||
}
|
||||
|
||||
func createTransaction(spendingKey: String, zatoshi: Int64, to: String, memo: String?, from accountIndex: Int) throws -> EncodedTransaction {
|
||||
|
||||
let txId = try createSpend(spendingKey: spendingKey, zatoshi: zatoshi, to: to, memo: memo, from: accountIndex)
|
||||
|
||||
do {
|
||||
let transaction = try repository.findBy(id: txId)
|
||||
|
||||
guard let tx = transaction else {
|
||||
throw TransactionEncoderError.notFound(transactionId: txId)
|
||||
}
|
||||
|
||||
print("sentTransaction id: \(txId)")
|
||||
return EncodedTransaction(transactionId: tx.transactionId , raw: tx.raw)
|
||||
} catch {
|
||||
throw TransactionEncoderError.notFound(transactionId: txId)
|
||||
}
|
||||
}
|
||||
|
||||
func createTransaction(spendingKey: String, zatoshi: Int64, to: String, memo: String?, from accountIndex: Int, result: @escaping TransactionEncoderResultBlock) {
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
do {
|
||||
result(.success(try self.createTransaction(spendingKey: spendingKey, zatoshi: zatoshi, to: to, memo: memo, from: accountIndex)))
|
||||
} catch {
|
||||
result(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createSpend(spendingKey: String, zatoshi: Int64, to address: String, memo: String?, from accountIndex: Int) throws -> Int64 {
|
||||
guard ensureParams(spend: initializer.spendParamsURL, output: initializer.spendParamsURL),
|
||||
let spend = URL(string: initializer.spendParamsURL.path), let output = URL(string: initializer.outputParamsURL.path) else {
|
||||
throw TransactionEncoderError.missingParams
|
||||
}
|
||||
|
||||
|
||||
let txId = rustBackend.sendToAddress(dbData: initializer.dataDbURL, account: Int32(accountIndex), extsk: spendingKey, to: address, value: Int64(zatoshi), memo: memo, spendParams: spend, outputParams: output)
|
||||
|
||||
guard txId > 0 else {
|
||||
throw rustBackend.lastError() ?? RustWeldingError.genericError(message: "create spend failed")
|
||||
}
|
||||
|
||||
return txId
|
||||
}
|
||||
|
||||
func ensureParams(spend: URL, output: URL) -> Bool {
|
||||
|
||||
let readableSpend = FileManager.default.isReadableFile(atPath: spend.path)
|
||||
let readableOutput = FileManager.default.isReadableFile(atPath: output.path)
|
||||
|
||||
return readableSpend && readableOutput // Todo: change this to something that makes sense
|
||||
}
|
||||
}
|
|
@ -31,7 +31,7 @@ class WalletTests: XCTestCase {
|
|||
|
||||
func testWalletInitialization() {
|
||||
|
||||
let wallet = Initializer(cacheDbURL: cacheData, dataDbURL: dbData, endpoint: LightWalletEndpoint(address: "localhost", port: "9067", secure: false))
|
||||
let wallet = Initializer(cacheDbURL: cacheData, dataDbURL: dbData, endpoint: LightWalletEndpointBuilder.default, spendParamsURL: try! __spendParamsURL(), outputParamsURL: try! __outputParamsURL())
|
||||
|
||||
XCTAssertNoThrow(try wallet.initialize(seedProvider: SampleSeedProvider(), walletBirthdayHeight: SAPLING_ACTIVATION_HEIGHT))
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
//
|
||||
// WalletTransactionEncoderTests.swift
|
||||
// ZcashLightClientKit-Unit-Tests
|
||||
//
|
||||
// Created by Francisco Gindre on 11/20/19.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import ZcashLightClientKit
|
||||
class WalletTransactionEncoderTests: XCTestCase {
|
||||
|
||||
var repository: TransactionRepository!
|
||||
var rustBackend = ZcashRustBackend.self
|
||||
var transactionEncoder: WalletTransactionEncoder!
|
||||
var dataDbHandle = TestDbHandle(originalDb: TestDbBuilder.prePopulatedDataDbURL()!)
|
||||
var cacheDbHandle = TestDbHandle(originalDb: TestDbBuilder.prePopulatedCacheDbURL()!)
|
||||
var initializer: Initializer!
|
||||
let spendingKey = "secret-extended-key-test1qvpevftsqqqqpqy52ut2vv24a2qh7nsukew7qg9pq6djfwyc3xt5vaxuenshp2hhspp9qmqvdh0gs2ljpwxders5jkwgyhgln0drjqaguaenfhehz4esdl4kwlm5t9q0l6wmzcrvcf5ed6dqzvct3e2ge7f6qdvzhp02m7sp5a0qjssrwpdh7u6tq89hl3wchuq8ljq8r8rwd6xdwh3nry9at80z7amnj3s6ah4jevnvfr08gxpws523z95g6dmn4wm6l3658kd4xcq9rc0qn"
|
||||
let recipientAddress = "ztestsapling1ctuamfer5xjnnrdr3xdazenljx0mu0gutcf9u9e74tr2d3jwjnt0qllzxaplu54hgc2tyjdc2p6"
|
||||
let zpend: Int64 = 500_000
|
||||
|
||||
override func setUp() {
|
||||
try! dataDbHandle.setUp()
|
||||
try! cacheDbHandle.setUp()
|
||||
|
||||
initializer = Initializer(cacheDbURL: cacheDbHandle.readWriteDb, dataDbURL: dataDbHandle.readWriteDb, endpoint: LightWalletEndpointBuilder.default, spendParamsURL: try! __spendParamsURL(), outputParamsURL: try! __outputParamsURL())
|
||||
|
||||
repository = TransactionSQLDAO(dbProvider: dataDbHandle.connectionProvider(readwrite: false))
|
||||
transactionEncoder = WalletTransactionEncoder(rust: rustBackend.self, repository: repository, initializer: initializer)
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
repository = nil
|
||||
dataDbHandle.dispose()
|
||||
cacheDbHandle.dispose()
|
||||
|
||||
}
|
||||
|
||||
func testCreateTransaction() {
|
||||
var transaction: EncodedTransaction?
|
||||
XCTAssertNoThrow(try { transaction = try transactionEncoder.createTransaction(spendingKey: spendingKey, zatoshi: zpend, to: recipientAddress, memo: nil, from: 0)}())
|
||||
guard let tx = transaction else {
|
||||
XCTFail("transaction is nil. error: \(String(describing: rustBackend.getLastError()))")
|
||||
return
|
||||
}
|
||||
|
||||
var retrievedTx: TransactionEntity?
|
||||
XCTAssertNoThrow(try { retrievedTx = try repository.findBy(rawId: tx.transactionId) }())
|
||||
|
||||
XCTAssertNotNil(retrievedTx, "transaction not found")
|
||||
}
|
||||
|
||||
func testCreateSpend() {
|
||||
|
||||
XCTAssert(initializer.getBalance() >= zpend)
|
||||
|
||||
var spendId: Int64?
|
||||
|
||||
XCTAssertNoThrow(try { spendId = try transactionEncoder.createSpend(spendingKey: self.spendingKey, zatoshi: self.zpend, to: self.recipientAddress, memo: nil, from: 0) }())
|
||||
|
||||
guard let id = spendId else {
|
||||
XCTFail("failed to create spend. error: \(String(describing: rustBackend.getLastError()))")
|
||||
return
|
||||
}
|
||||
|
||||
var tx: TransactionEntity?
|
||||
XCTAssertNoThrow(try { tx = try repository.findBy(id: id)}())
|
||||
XCTAssertNotNil(tx, "Transaction Id: \(id), not found. rust error: \(String(describing: rustBackend.getLastError()))")
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
_ = try! transactionEncoder.createSpend(spendingKey: self.spendingKey, zatoshi: self.zpend, to: self.recipientAddress, memo: nil, from: 0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
|
@ -10,6 +10,28 @@ import Foundation
|
|||
import SQLite
|
||||
@testable import ZcashLightClientKit
|
||||
|
||||
struct TestDbHandle {
|
||||
var originalDb: URL
|
||||
var readWriteDb: URL
|
||||
|
||||
init(originalDb: URL) {
|
||||
self.originalDb = originalDb
|
||||
self.readWriteDb = FileManager.default.temporaryDirectory.appendingPathComponent(self.originalDb.lastPathComponent.appending("_\(Date().timeIntervalSince1970)")) // avoid files clashing because crashing tests failed to remove previous ones by incrementally changing the filename
|
||||
}
|
||||
|
||||
func setUp() throws {
|
||||
try FileManager.default.copyItem(at: originalDb, to: readWriteDb)
|
||||
}
|
||||
|
||||
func dispose() {
|
||||
try? FileManager.default.removeItem(at: readWriteDb)
|
||||
}
|
||||
|
||||
func connectionProvider(readwrite: Bool = true) -> ConnectionProvider {
|
||||
SimpleConnectionProvider(path: self.readWriteDb.absoluteString, readonly: !readwrite)
|
||||
}
|
||||
}
|
||||
|
||||
class TestDbBuilder {
|
||||
|
||||
enum TestBuilderError: Error {
|
||||
|
@ -28,6 +50,14 @@ class TestDbBuilder {
|
|||
return compactBlockDao
|
||||
}
|
||||
|
||||
static func prePopulatedCacheDbURL() -> URL? {
|
||||
Bundle(for: TestDbBuilder.self).url(forResource: "cache", withExtension: "db")
|
||||
}
|
||||
|
||||
static func prePopulatedDataDbURL() -> URL? {
|
||||
Bundle(for: TestDbBuilder.self).url(forResource: "test_data", withExtension: "db")
|
||||
}
|
||||
|
||||
static func prepopulatedDataDbProvider() -> ConnectionProvider? {
|
||||
let bundle = Bundle(for: TestDbBuilder.self)
|
||||
guard let url = bundle.url(forResource: "ZcashSdk_Data", withExtension: "db") else { return nil }
|
||||
|
|
|
@ -8,8 +8,15 @@
|
|||
|
||||
import Foundation
|
||||
import SwiftGRPC
|
||||
|
||||
import ZcashLightClientKit
|
||||
import XCTest
|
||||
|
||||
class LightWalletEndpointBuilder {
|
||||
static var `default`: LightWalletEndpoint {
|
||||
LightWalletEndpoint(address: "localhost", port: "9067", secure: false)
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelProvider {
|
||||
func channel() -> SwiftGRPC.Channel {
|
||||
Channel(address: Constants.address, secure: false)
|
||||
|
@ -55,13 +62,47 @@ func __dataDbURL() throws -> URL {
|
|||
try __documentsDirectory().appendingPathComponent("data.db", isDirectory: false)
|
||||
}
|
||||
|
||||
func __spendParamsURL() throws -> URL {
|
||||
Bundle.testBundle.url(forResource: "sapling-spend", withExtension: "params")!
|
||||
}
|
||||
|
||||
func __outputParamsURL() throws -> URL {
|
||||
Bundle.testBundle.url(forResource: "sapling-output", withExtension: "params")!
|
||||
}
|
||||
|
||||
func copyParametersToDocuments() throws -> (spend: URL, output: URL) {
|
||||
|
||||
let spendURL = try __documentsDirectory().appendingPathComponent("sapling-spend.params", isDirectory: false)
|
||||
let outputURL = try __documentsDirectory().appendingPathComponent("sapling-output.params", isDirectory: false)
|
||||
try FileManager.default.copyItem(at: try __spendParamsURL(), to: spendURL)
|
||||
try FileManager.default.copyItem(at: try __outputParamsURL(), to: outputURL)
|
||||
|
||||
return (spendURL, outputURL)
|
||||
}
|
||||
|
||||
func deleteParametersFromDocuments() throws {
|
||||
let documents = try __documentsDirectory()
|
||||
deleteParamsFrom(spend: documents.appendingPathComponent("sapling-spend.params"), output: documents.appendingPathComponent("sapling-output.params"))
|
||||
}
|
||||
func deleteParamsFrom(spend: URL, output: URL) {
|
||||
try? FileManager.default.removeItem(at: spend)
|
||||
try? FileManager.default.removeItem(at: output)
|
||||
}
|
||||
|
||||
func parametersReady() -> Bool {
|
||||
|
||||
guard let output = try? __documentsDirectory().appendingPathComponent("sapling-output.params", isDirectory: false),
|
||||
let spend = try? __documentsDirectory().appendingPathComponent("sapling-spend.params", isDirectory: false),
|
||||
guard let output = try? __outputParamsURL(),
|
||||
let spend = try? __spendParamsURL(),
|
||||
FileManager.default.isReadableFile(atPath: output.absoluteString),
|
||||
FileManager.default.isReadableFile(atPath: spend.absoluteString) else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
class StubTest: XCTestCase {}
|
||||
extension Bundle {
|
||||
static var testBundle: Bundle {
|
||||
Bundle(for: StubTest.self)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue