Issue #166 Download UTXO set
This commit is contained in:
parent
7cb4750f29
commit
4abf914f9f
|
@ -824,13 +824,19 @@
|
|||
<rect key="frame" x="20" y="88" width="374" height="774"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="characterWrap" numberOfLines="0" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="X0E-Ba-xxX">
|
||||
<rect key="frame" x="166.5" y="0.0" width="41.5" height="387"/>
|
||||
<rect key="frame" x="166.5" y="0.0" width="41.5" height="258"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="characterWrap" numberOfLines="0" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ntO-Ig-rst">
|
||||
<rect key="frame" x="166.5" y="258" width="41.5" height="258"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="characterWrap" numberOfLines="0" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hl0-9u-TsZ" userLabel="extended key">
|
||||
<rect key="frame" x="0.0" y="387" width="374" height="387"/>
|
||||
<rect key="frame" x="0.0" y="516" width="374" height="258"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -853,8 +859,9 @@
|
|||
</view>
|
||||
<navigationItem key="navigationItem" title="Get Address" largeTitleDisplayMode="always" id="Uvy-EM-bSo"/>
|
||||
<connections>
|
||||
<outlet property="addressLabel" destination="X0E-Ba-xxX" id="9es-sw-gO5"/>
|
||||
<outlet property="spendingKeyLabel" destination="hl0-9u-TsZ" id="afF-Yq-bty"/>
|
||||
<outlet property="tAddressLabel" destination="ntO-Ig-rst" id="6ER-bQ-FVH"/>
|
||||
<outlet property="zAddressLabel" destination="X0E-Ba-xxX" id="9es-sw-gO5"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="kr1-B6-akA" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
|
@ -955,16 +962,16 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="246" verticalCompressionResistancePriority="250" alignment="center" spacing="13" translatesAutoresizingMaskIntoConstraints="NO" id="IIl-kO-2M8">
|
||||
<rect key="frame" x="0.0" y="152" width="398" height="112.5"/>
|
||||
<rect key="frame" x="0.0" y="152" width="398" height="173"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" text="Status:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3t3-Tr-UlI">
|
||||
<rect key="frame" x="0.0" y="37" width="95.5" height="38.5"/>
|
||||
<rect key="frame" x="0.0" y="67.5" width="95.5" height="38.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="32"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Jj9-7r-s2Y">
|
||||
<rect key="frame" x="108.5" y="42.5" width="289.5" height="27.5"/>
|
||||
<rect key="frame" x="108.5" y="73" width="289.5" height="27.5"/>
|
||||
<fontDescription key="fontDescription" type="italicSystem" pointSize="23"/>
|
||||
<color key="textColor" systemColor="scrollViewTexturedBackgroundColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -975,22 +982,22 @@
|
|||
</constraints>
|
||||
</stackView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Progress" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JPx-ol-2nc">
|
||||
<rect key="frame" x="0.0" y="288.5" width="390" height="43"/>
|
||||
<rect key="frame" x="0.0" y="349" width="390" height="43"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="36"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" progressViewStyle="bar" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="oKg-9s-8Ym">
|
||||
<rect key="frame" x="0.0" y="355.5" width="390" height="2.5"/>
|
||||
<rect key="frame" x="0.0" y="416" width="390" height="2.5"/>
|
||||
</progressView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="0%" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kXE-5z-HN2">
|
||||
<rect key="frame" x="0.0" y="381" width="398" height="39.5"/>
|
||||
<rect key="frame" x="0.0" y="441.5" width="398" height="39.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="33"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="G5M-gm-1ux">
|
||||
<rect key="frame" x="0.0" y="444.5" width="398" height="45"/>
|
||||
<rect key="frame" x="0.0" y="505" width="398" height="45"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="27"/>
|
||||
<state key="normal" title="Start"/>
|
||||
<connections>
|
||||
|
@ -998,7 +1005,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" verticalHuggingPriority="750" verticalCompressionResistancePriority="737" translatesAutoresizingMaskIntoConstraints="NO" id="fB9-xh-4fl" userLabel="Trailing View">
|
||||
<rect key="frame" x="0.0" y="513.5" width="398" height="260.5"/>
|
||||
<rect key="frame" x="0.0" y="574" width="398" height="200"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="750" constant="200" id="Bpt-XM-IZA"/>
|
||||
|
@ -1406,25 +1413,41 @@
|
|||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1h3-3y-iS9">
|
||||
<rect key="frame" x="131.5" y="225.5" width="151" height="50"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="50" id="v9k-94-gxX"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="29"/>
|
||||
<state key="normal" title="Get UTXOs!"/>
|
||||
<connections>
|
||||
<action selector="getButtonTapped:" destination="Ugl-B2-O3O" eventType="touchUpInside" id="Vlr-m4-asM"/>
|
||||
</connections>
|
||||
</button>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="ZXk-2m-Kua">
|
||||
<rect key="frame" x="65.5" y="225.5" width="283" height="100"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1h3-3y-iS9">
|
||||
<rect key="frame" x="0.0" y="0.0" width="283" height="50"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="50" id="v9k-94-gxX"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="29"/>
|
||||
<state key="normal" title="Get UTXOs!"/>
|
||||
<connections>
|
||||
<action selector="getButtonTapped:" destination="Ugl-B2-O3O" eventType="touchUpInside" id="Vlr-m4-asM"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tX1-H6-Zs8">
|
||||
<rect key="frame" x="0.0" y="50" width="283" height="50"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="50" id="JJc-9O-Kdl"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="29"/>
|
||||
<state key="normal" title="Get UTXO from cache!"/>
|
||||
<connections>
|
||||
<action selector="getFromCacheTapped:" destination="Ugl-B2-O3O" eventType="touchUpInside" id="3Pl-RP-4qg"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Mv9-ye-VIn">
|
||||
<rect key="frame" x="16" y="315.5" width="382" height="0.0"/>
|
||||
<rect key="frame" x="16" y="365.5" width="382" height="0.0"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="pcE-rS-DWU">
|
||||
<rect key="frame" x="0.0" y="355.5" width="414" height="418.5"/>
|
||||
<rect key="frame" x="0.0" y="405.5" width="414" height="368.5"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
</view>
|
||||
</subviews>
|
||||
|
@ -1452,6 +1475,7 @@
|
|||
<navigationItem key="navigationItem" id="VUG-Af-cu9"/>
|
||||
<connections>
|
||||
<outlet property="getButton" destination="1h3-3y-iS9" id="pdY-q5-oIL"/>
|
||||
<outlet property="getFromCache" destination="tX1-H6-Zs8" id="FQS-Hl-Hl9"/>
|
||||
<outlet property="messageLabel" destination="Mv9-ye-VIn" id="c6d-Fe-mrx"/>
|
||||
<outlet property="tAddressField" destination="9yg-Sp-H2E" id="oQV-JY-09h"/>
|
||||
<outlet property="validAddressLabel" destination="1NQ-co-Y8P" id="mHT-cp-vnn"/>
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
import UIKit
|
||||
import ZcashLightClientKit
|
||||
class GetAddressViewController: UIViewController {
|
||||
@IBOutlet weak var addressLabel: UILabel!
|
||||
@IBOutlet weak var zAddressLabel: UILabel!
|
||||
@IBOutlet weak var tAddressLabel: UILabel!
|
||||
@IBOutlet weak var spendingKeyLabel: UILabel! // THIS SHOULD BE SUPER SECRET!!!!!
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
@ -17,11 +18,14 @@ class GetAddressViewController: UIViewController {
|
|||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
addressLabel.text = legibleAddresses() ?? "No Addresses found"
|
||||
zAddressLabel.text = (try? DerivationTool.default.deriveShieldedAddress(seed: DemoAppConfig.seed, accountIndex: 0)) ?? "No Addresses found"
|
||||
tAddressLabel.text = (try? DerivationTool.default.deriveTransparentAddress(seed: DemoAppConfig.seed)) ?? "could not derive t-address"
|
||||
spendingKeyLabel.text = SampleStorage.shared.privateKey ?? "No Spending Key found"
|
||||
addressLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(addressTapped(_:))))
|
||||
addressLabel.isUserInteractionEnabled = true
|
||||
zAddressLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(addressTapped(_:))))
|
||||
zAddressLabel.isUserInteractionEnabled = true
|
||||
|
||||
tAddressLabel.isUserInteractionEnabled = true
|
||||
tAddressLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tAddressTapped(_:))))
|
||||
spendingKeyLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(spendingKeyTapped(_:))))
|
||||
spendingKeyLabel.isUserInteractionEnabled = true
|
||||
loggerProxy.info("Address: \(String(describing: Initializer.shared.getAddress()))")
|
||||
|
@ -40,10 +44,6 @@ class GetAddressViewController: UIViewController {
|
|||
}
|
||||
*/
|
||||
|
||||
func legibleAddresses() -> String? {
|
||||
Initializer.shared.getAddress()
|
||||
}
|
||||
|
||||
@IBAction func spendingKeyTapped(_ gesture: UIGestureRecognizer) {
|
||||
guard let key = SampleStorage.shared.privateKey else {
|
||||
loggerProxy.warn("nothing to copy")
|
||||
|
@ -60,7 +60,15 @@ class GetAddressViewController: UIViewController {
|
|||
|
||||
@IBAction func addressTapped(_ gesture: UIGestureRecognizer) {
|
||||
loggerProxy.event("copied to clipboard")
|
||||
UIPasteboard.general.string = legibleAddresses()
|
||||
UIPasteboard.general.string = try? DerivationTool.default.deriveShieldedAddress(seed: DemoAppConfig.seed, accountIndex: 0)
|
||||
let alert = UIAlertController(title: "", message: "Address Copied to clipboard", preferredStyle: UIAlertController.Style.alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@IBAction func tAddressTapped(_ gesture: UIGestureRecognizer) {
|
||||
loggerProxy.event("copied to clipboard")
|
||||
UIPasteboard.general.string = try? DerivationTool.default.deriveTransparentAddress(seed: DemoAppConfig.seed)
|
||||
let alert = UIAlertController(title: "", message: "Address Copied to clipboard", preferredStyle: UIAlertController.Style.alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
|
|
|
@ -12,10 +12,10 @@ import KRProgressHUD
|
|||
class GetUTXOsViewController: UIViewController {
|
||||
@IBOutlet weak var tAddressField: UITextField!
|
||||
@IBOutlet weak var getButton: UIButton!
|
||||
@IBOutlet weak var getFromCache: UIButton!
|
||||
@IBOutlet weak var validAddressLabel: UILabel!
|
||||
@IBOutlet weak var messageLabel: UILabel!
|
||||
|
||||
var service: LightWalletGRPCService = LightWalletGRPCService(endpoint: DemoAppConfig.endpoint)
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
@ -31,15 +31,17 @@ class GetUTXOsViewController: UIViewController {
|
|||
self.validAddressLabel.textColor = valid ? UIColor.systemGreen : UIColor.systemRed
|
||||
|
||||
self.getButton.isEnabled = valid
|
||||
self.getFromCache.isEnabled = valid
|
||||
}
|
||||
|
||||
@IBAction func getButtonTapped(_ sender: Any) {
|
||||
guard Initializer.shared.isValidTransparentAddress(tAddressField.text ?? ""),
|
||||
let tAddr = tAddressField.text else {
|
||||
self.messageLabel.text = "Invalid t-Address"
|
||||
return
|
||||
}
|
||||
KRProgressHUD.showMessage("fetching")
|
||||
service.fetchUTXOs(for: tAddr) { [weak self] (result) in
|
||||
AppDelegate.shared.sharedSynchronizer.latestUTXOs(address: tAddr) { (result) in
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
KRProgressHUD.dismiss()
|
||||
switch result {
|
||||
|
@ -53,6 +55,21 @@ class GetUTXOsViewController: UIViewController {
|
|||
}
|
||||
}
|
||||
|
||||
@IBAction func getFromCacheTapped(_ sender: Any) {
|
||||
guard Initializer.shared.isValidTransparentAddress(tAddressField.text ?? ""),
|
||||
let tAddr = tAddressField.text else {
|
||||
self.messageLabel.text = "Invalid t-Address"
|
||||
return
|
||||
}
|
||||
do {
|
||||
let utxos = try AppDelegate.shared.sharedSynchronizer.cachedUTXOs(address: tAddr)
|
||||
self.messageLabel.text = "found \(utxos.count) UTXOs for address \(tAddr) on cache"
|
||||
} catch {
|
||||
self.messageLabel.text = "Error \(error)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@IBAction func viewTapped(_ recognizer: UITapGestureRecognizer) {
|
||||
self.tAddressField.resignFirstResponder()
|
||||
}
|
||||
|
|
|
@ -15,4 +15,5 @@ enum StorageError: Error {
|
|||
case operationFailed
|
||||
case updateFailed
|
||||
case malformedEntity(fields: [String]?)
|
||||
case transactionFailed(underlyingError: Error)
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
//
|
||||
// UnspentTransactionOutputDAO.swift
|
||||
// ZcashLightClientKit
|
||||
//
|
||||
// Created by Francisco Gindre on 12/9/20.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct UTXO: UnspentTransactionOutputEntity {
|
||||
var address: String
|
||||
|
||||
var txid: Data
|
||||
|
||||
var index: Int32
|
||||
|
||||
var script: Data
|
||||
|
||||
var valueZat: Int64
|
||||
|
||||
var height: UInt64
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
//
|
||||
// UnspentTransactionOutputDAO.swift
|
||||
// ZcashLightClientKit
|
||||
//
|
||||
// Created by Francisco Gindre on 12/9/20.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct UTXO: UnspentTransactionOutputEntity, Decodable, Encodable {
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case address
|
||||
case txid
|
||||
case index
|
||||
case script
|
||||
case valueZat = "value_zat"
|
||||
case height
|
||||
}
|
||||
|
||||
var id: Int?
|
||||
|
||||
var address: String
|
||||
|
||||
var txid: Data
|
||||
|
||||
var index: Int
|
||||
|
||||
var script: Data
|
||||
|
||||
var valueZat: Int
|
||||
|
||||
var height: Int
|
||||
|
||||
}
|
||||
|
||||
import SQLite
|
||||
class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
|
||||
|
||||
func store(utxos: [UnspentTransactionOutputEntity]) throws {
|
||||
do {
|
||||
|
||||
let db = try dbProvider.connection()
|
||||
try dbProvider.connection().transaction {
|
||||
for utxo in utxos.map({ (u) -> UTXO in
|
||||
u as? UTXO ?? UTXO(id: nil,
|
||||
address: u.address,
|
||||
txid: u.txid,
|
||||
index: Int(u.index),
|
||||
script: u.script,
|
||||
valueZat: u.valueZat,
|
||||
height: u.height)
|
||||
}) {
|
||||
try db.run(table.insert(utxo))
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
throw StorageError.transactionFailed(underlyingError: error)
|
||||
}
|
||||
}
|
||||
|
||||
func clearAll(address: String?) throws {
|
||||
|
||||
if let tAddr = address {
|
||||
do {
|
||||
try dbProvider.connection().run(table.filter(TableColumns.address == tAddr).delete())
|
||||
} catch {
|
||||
throw StorageError.operationFailed
|
||||
}
|
||||
} else {
|
||||
do {
|
||||
try dbProvider.connection().run(table.delete())
|
||||
} catch {
|
||||
throw StorageError.operationFailed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let table = Table("utxos")
|
||||
|
||||
struct TableColumns {
|
||||
static var id = Expression<Int>("id")
|
||||
static var address = Expression<String>("address")
|
||||
static var txid = Expression<Blob>("txid")
|
||||
static var index = Expression<Int>("index")
|
||||
static var script = Expression<Blob>("script")
|
||||
static var valueZat = Expression<Int>("value_zat")
|
||||
static var height = Expression<Int>("height")
|
||||
}
|
||||
|
||||
var dbProvider: ConnectionProvider
|
||||
|
||||
init (dbProvider: ConnectionProvider) {
|
||||
self.dbProvider = dbProvider
|
||||
}
|
||||
|
||||
func createTableIfNeeded() throws {
|
||||
let statement = table.create(ifNotExists: true) { t in
|
||||
t.column(TableColumns.id, primaryKey: .autoincrement)
|
||||
t.column(TableColumns.address)
|
||||
t.column(TableColumns.txid)
|
||||
t.column(TableColumns.index)
|
||||
t.column(TableColumns.script)
|
||||
t.column(TableColumns.valueZat)
|
||||
t.column(TableColumns.height)
|
||||
}
|
||||
try dbProvider.connection().run(statement)
|
||||
}
|
||||
|
||||
func getAll(address: String?) throws -> [UnspentTransactionOutputEntity] {
|
||||
if let tAddress = address {
|
||||
let allTxs: [UTXO] = try dbProvider.connection().prepare(table.filter(TableColumns.address == tAddress)).map({ row in
|
||||
try row.decode()
|
||||
})
|
||||
return allTxs
|
||||
} else {
|
||||
let allTxs: [UTXO] = try dbProvider.connection().prepare(table).map({ row in
|
||||
try row.decode()
|
||||
})
|
||||
return allTxs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UTXORepositoryBuilder {
|
||||
static func build(initializer: Initializer) throws -> UnspentTransactionOutputRepository {
|
||||
let dao = UnspentTransactionOutputSQLDAO(dbProvider: SimpleConnectionProvider(path: initializer.pendingDbURL.path))
|
||||
try dao.createTableIfNeeded()
|
||||
return dao
|
||||
}
|
||||
}
|
|
@ -11,14 +11,14 @@ public protocol UnspentTransactionOutputEntity {
|
|||
|
||||
var address: String { get set }
|
||||
|
||||
var txid: Data {get set}
|
||||
var txid: Data { get set }
|
||||
|
||||
var index: Int32 {get set}
|
||||
var index: Int { get set }
|
||||
|
||||
var script: Data {get set}
|
||||
var script: Data { get set }
|
||||
|
||||
var valueZat: Int64 {get set}
|
||||
var valueZat: Int { get set }
|
||||
|
||||
var height: UInt64 {get set}
|
||||
var height: Int { get set }
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// UnspentTransactionOutputRepository.swift
|
||||
// ZcashLightClientKit
|
||||
//
|
||||
// Created by Francisco Gindre on 12/11/20.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol UnspentTransactionOutputRepository {
|
||||
|
||||
func getAll(address: String?) throws -> [UnspentTransactionOutputEntity]
|
||||
|
||||
func store(utxos: [UnspentTransactionOutputEntity]) throws
|
||||
|
||||
func clearAll(address: String?) throws
|
||||
}
|
|
@ -227,12 +227,13 @@ extension LightWalletGRPCService: LightWalletService {
|
|||
var utxos = [UnspentTransactionOutputEntity]()
|
||||
let response = self.compactTxStreamer.getAddressUtxosStream(arg) { (reply) in
|
||||
utxos.append(
|
||||
UTXO(address: tAddress,
|
||||
UTXO(id: nil,
|
||||
address: tAddress,
|
||||
txid: reply.txid,
|
||||
index: reply.index,
|
||||
index: Int(reply.index),
|
||||
script: reply.script,
|
||||
valueZat: reply.valueZat,
|
||||
height: UInt64(reply.valueZat)
|
||||
valueZat: Int(reply.valueZat),
|
||||
height: Int(reply.valueZat)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -169,6 +169,5 @@ public protocol LightWalletService {
|
|||
*/
|
||||
func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity,LightWalletServiceError>) -> Void)
|
||||
|
||||
|
||||
func fetchUTXOs(for tAddress: String, result: @escaping(Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void)
|
||||
}
|
||||
|
|
|
@ -130,6 +130,17 @@ public protocol Synchronizer {
|
|||
Blocking
|
||||
*/
|
||||
func latestHeight() throws -> BlockHeight
|
||||
|
||||
/**
|
||||
Gets the latest UTXOs for the given t-address and caches the result
|
||||
*/
|
||||
func latestUTXOs(address: String, result: @escaping (Result<[UnspentTransactionOutputEntity], Error>) -> Void)
|
||||
|
||||
/**
|
||||
gets the latest cached UTXOs for the given t-address for the given address
|
||||
*/
|
||||
func cachedUTXOs(address: String) throws -> [UnspentTransactionOutputEntity]
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -90,6 +90,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
|
||||
private var transactionManager: OutboundTransactionManager
|
||||
private var transactionRepository: TransactionRepository
|
||||
private var utxoRepository: UnspentTransactionOutputRepository
|
||||
|
||||
/**
|
||||
Creates an SDKSynchronizer instance
|
||||
|
@ -100,18 +101,21 @@ public class SDKSynchronizer: Synchronizer {
|
|||
self.init(status: .disconnected,
|
||||
initializer: initializer,
|
||||
transactionManager: try OutboundTransactionManagerBuilder.build(initializer: initializer),
|
||||
transactionRepository: initializer.transactionRepository)
|
||||
transactionRepository: initializer.transactionRepository,
|
||||
utxoRepository: try UTXORepositoryBuilder.build(initializer: initializer))
|
||||
|
||||
}
|
||||
|
||||
init(status: Status,
|
||||
initializer: Initializer,
|
||||
transactionManager: OutboundTransactionManager,
|
||||
transactionRepository: TransactionRepository) {
|
||||
transactionRepository: TransactionRepository,
|
||||
utxoRepository: UnspentTransactionOutputRepository) {
|
||||
self.status = status
|
||||
self.initializer = initializer
|
||||
self.transactionManager = transactionManager
|
||||
self.transactionRepository = transactionRepository
|
||||
self.utxoRepository = utxoRepository
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
@ -415,6 +419,33 @@ public class SDKSynchronizer: Synchronizer {
|
|||
try initializer.downloader.latestBlockHeight()
|
||||
}
|
||||
|
||||
public func latestUTXOs(address: String, result: @escaping (Result<[UnspentTransactionOutputEntity], Error>) -> Void) {
|
||||
guard initializer.isValidTransparentAddress(address) else {
|
||||
result(.failure(SynchronizerError.generalError(message: "invalid t-address")))
|
||||
return
|
||||
}
|
||||
|
||||
initializer.lightWalletService.fetchUTXOs(for: address, result: { [weak self] r in
|
||||
guard let self = self else { return }
|
||||
switch r {
|
||||
case .success(let utxos):
|
||||
do {
|
||||
try self.utxoRepository.clearAll(address: address)
|
||||
try self.utxoRepository.store(utxos: utxos)
|
||||
result(.success(utxos))
|
||||
} catch {
|
||||
result(.failure(SynchronizerError.generalError(message: "\(error)")))
|
||||
}
|
||||
case .failure(let error):
|
||||
result(.failure(SynchronizerError.connectionFailed(message: error)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func cachedUTXOs(address: String) throws -> [UnspentTransactionOutputEntity] {
|
||||
try utxoRepository.getAll(address: address)
|
||||
}
|
||||
|
||||
// MARK: notify state
|
||||
private func notify(progress: Float, height: BlockHeight) {
|
||||
NotificationCenter.default.post(name: Notification.Name.synchronizerProgressUpdated, object: self, userInfo: [
|
||||
|
|
Loading…
Reference in New Issue