Issue #166 Download UTXO set

This commit is contained in:
Francisco Gindre 2020-12-11 17:15:29 -03:00
parent 7cb4750f29
commit 4abf914f9f
12 changed files with 288 additions and 69 deletions

View File

@ -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"/>

View File

@ -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)

View File

@ -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()
}

View File

@ -15,4 +15,5 @@ enum StorageError: Error {
case operationFailed
case updateFailed
case malformedEntity(fields: [String]?)
case transactionFailed(underlyingError: Error)
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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 }
}

View File

@ -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
}

View File

@ -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)
)
)
}

View File

@ -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)
}

View File

@ -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]
}
/**

View File

@ -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: [