Use zcash_client_sqlite to manage migrations for the wallet db.

This change removes responsibility for maintaining the state of
the wallet database from `ZcashLightClientKit` in favor of using
the migration system now provided by librustzcash. This will help
to ensure that the structure of the database is kept consistent with
the functions that query and update the database state.

Co-authored-by: Francisco Gindre <francisco.gindre@gmail.com>
This commit is contained in:
Kris Nuttycombe 2022-08-24 09:38:42 -06:00
parent 739c7a7237
commit 905ee401d1
25 changed files with 296 additions and 357 deletions

View File

@ -138,11 +138,11 @@
}, },
{ {
"package": "libzcashlc", "package": "libzcashlc",
"repositoryURL": "https://github.com/zcash-hackworks/zcash-light-client-ffi.git", "repositoryURL": "https://github.com/nuttycom/zcash-light-client-ffi.git",
"state": { "state": {
"branch": null, "branch": "bin/librustzcash_0_7-migrations",
"revision": "b7e8a2abab84c44046b4afe4ee4522a0fa2fcc7f", "revision": "dbf7ac51a7de0bbc2c1088973236d68ceb752361",
"version": "0.0.3" "version": null
} }
} }
] ]

View File

@ -49,7 +49,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
loggerProxy: loggerProxy loggerProxy: loggerProxy
) )
try! wallet.initialize() _ = try! wallet.initialize(with: DemoAppConfig.seed)
var storage = SampleStorage.shared var storage = SampleStorage.shared
storage!.seed = Data(DemoAppConfig.seed) storage!.seed = Data(DemoAppConfig.seed)
storage!.privateKey = try! DerivationTool(networkType: kZcashNetwork.networkType) storage!.privateKey = try! DerivationTool(networkType: kZcashNetwork.networkType)

View File

@ -109,18 +109,18 @@ class DerivationToolViewController: UIViewController {
throw DerivationErrors.couldNotDeriveSpendingKeys(underlyingError: DerivationErrors.unknown) throw DerivationErrors.couldNotDeriveSpendingKeys(underlyingError: DerivationErrors.unknown)
} }
guard let viewingKey = try derivationTool.deriveViewingKeys(seed: seedBytes, numberOfAccounts: 1).first else { guard let viewingKey = try derivationTool.deriveUnifiedFullViewingKeysFromSeed(seedBytes, numberOfAccounts: 1).first else {
throw DerivationErrors.couldNotDeriveViewingKeys(underlyingError: DerivationErrors.unknown) throw DerivationErrors.couldNotDeriveViewingKeys(underlyingError: DerivationErrors.unknown)
} }
let shieldedAddress = try derivationTool.deriveShieldedAddress(viewingKey: viewingKey) let unifiedAddress = try derivationTool.deriveUnifiedAddress(viewingKey: viewingKey.encoding)
let transparentAddress = try derivationTool.deriveTransparentAddress(seed: seedBytes) let transparentAddress = try derivationTool.deriveTransparentAddress(seed: seedBytes)
updateLabels( updateLabels(
spendingKey: spendingKey, spendingKey: spendingKey,
viewingKey: viewingKey, viewingKey: viewingKey.encoding,
shieldedAddress: shieldedAddress, shieldedAddress: unifiedAddress,
transaparentAddress: transparentAddress transaparentAddress: transparentAddress
) )
} }

View File

@ -9,7 +9,7 @@
import UIKit import UIKit
import ZcashLightClientKit import ZcashLightClientKit
class GetAddressViewController: UIViewController { class GetAddressViewController: UIViewController {
@IBOutlet weak var zAddressLabel: UILabel! @IBOutlet weak var unifiedAddressLabel: UILabel!
@IBOutlet weak var tAddressLabel: UILabel! @IBOutlet weak var tAddressLabel: UILabel!
@IBOutlet weak var spendingKeyLabel: UILabel! // THIS SHOULD BE SUPER SECRET!!!!! @IBOutlet weak var spendingKeyLabel: UILabel! // THIS SHOULD BE SUPER SECRET!!!!!
@ -18,16 +18,16 @@ class GetAddressViewController: UIViewController {
let derivationTool = DerivationTool(networkType: kZcashNetwork.networkType) let derivationTool = DerivationTool(networkType: kZcashNetwork.networkType)
// Do any additional setup after loading the view. // Do any additional setup after loading the view.
zAddressLabel.text = (try? derivationTool.deriveShieldedAddress(seed: DemoAppConfig.seed, accountIndex: 0)) ?? "No Addresses found" unifiedAddressLabel.text = (try? derivationTool.deriveUnifiedAddress(seed: DemoAppConfig.seed, accountIndex: 0)) ?? "No Addresses found"
tAddressLabel.text = (try? derivationTool.deriveTransparentAddress(seed: DemoAppConfig.seed)) ?? "could not derive t-address" tAddressLabel.text = (try? derivationTool.deriveTransparentAddress(seed: DemoAppConfig.seed)) ?? "could not derive t-address"
spendingKeyLabel.text = SampleStorage.shared.privateKey ?? "No Spending Key found" spendingKeyLabel.text = SampleStorage.shared.privateKey ?? "No Spending Key found"
zAddressLabel.addGestureRecognizer( unifiedAddressLabel.addGestureRecognizer(
UITapGestureRecognizer( UITapGestureRecognizer(
target: self, target: self,
action: #selector(addressTapped(_:)) action: #selector(addressTapped(_:))
) )
) )
zAddressLabel.isUserInteractionEnabled = true unifiedAddressLabel.isUserInteractionEnabled = true
tAddressLabel.isUserInteractionEnabled = true tAddressLabel.isUserInteractionEnabled = true
tAddressLabel.addGestureRecognizer( tAddressLabel.addGestureRecognizer(
@ -70,8 +70,7 @@ class GetAddressViewController: UIViewController {
@IBAction func addressTapped(_ gesture: UIGestureRecognizer) { @IBAction func addressTapped(_ gesture: UIGestureRecognizer) {
loggerProxy.event("copied to clipboard") loggerProxy.event("copied to clipboard")
UIPasteboard.general.string = try? DerivationTool(networkType: kZcashNetwork.networkType) UIPasteboard.general.string = unifiedAddressLabel.text
.deriveShieldedAddress(seed: DemoAppConfig.seed, accountIndex: 0)
let alert = UIAlertController( let alert = UIAlertController(
title: "", title: "",

View File

@ -42,13 +42,13 @@ class GetUTXOsViewController: UIViewController {
let derivationTool = DerivationTool(networkType: kZcashNetwork.networkType) let derivationTool = DerivationTool(networkType: kZcashNetwork.networkType)
// swiftlint:disable:next force_unwrapping // swiftlint:disable:next force_unwrapping
let spendingKey = try derivationTool.deriveSpendingKeys(seed: seed, numberOfAccounts: 1).first! let spendingKey = try derivationTool.deriveSpendingKeys(seed: seed, numberOfAccounts: 1).first!
let transparentSecretKey = try derivationTool.deriveTransparentPrivateKey(seed: seed) let transparentSecretKey = try derivationTool.deriveTransparentAccountPrivateKey(seed: seed, account: 0)
KRProgressHUD.showMessage("🛡 Shielding 🛡") KRProgressHUD.showMessage("🛡 Shielding 🛡")
AppDelegate.shared.sharedSynchronizer.shieldFunds( AppDelegate.shared.sharedSynchronizer.shieldFunds(
spendingKey: spendingKey, spendingKey: spendingKey,
transparentSecretKey: transparentSecretKey, transparentAccountPrivateKey: transparentSecretKey,
memo: "shielding is fun!", memo: "shielding is fun!",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in

View File

@ -33,7 +33,7 @@ class SendViewController: UIViewController {
super.viewDidLoad() super.viewDidLoad()
synchronizer = AppDelegate.shared.sharedSynchronizer synchronizer = AppDelegate.shared.sharedSynchronizer
// swiftlint:disable:next force_try // swiftlint:disable:next force_try
try! synchronizer.prepare() _ = try! synchronizer.prepare(with: DemoAppConfig.seed)
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(viewTapped(_:))) let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(viewTapped(_:)))
self.view.addGestureRecognizer(tapRecognizer) self.view.addGestureRecognizer(tapRecognizer)
setUp() setUp()

View File

@ -29,7 +29,7 @@ class SyncBlocksViewController: UIViewController {
let wallet = Initializer.shared let wallet = Initializer.shared
// swiftlint:disable:next force_try // swiftlint:disable:next force_try
try! wallet.initialize() _ = try! wallet.initialize(with: DemoAppConfig.seed)
processor = CompactBlockProcessor(initializer: wallet) processor = CompactBlockProcessor(initializer: wallet)
statusLabel.text = textFor(state: processor?.state ?? .stopped) statusLabel.text = textFor(state: processor?.state ?? .stopped)
progressBar.progress = 0 progressBar.progress = 0

View File

@ -86,8 +86,8 @@
"package": "libzcashlc", "package": "libzcashlc",
"repositoryURL": "https://github.com/zcash-hackworks/zcash-light-client-ffi.git", "repositoryURL": "https://github.com/zcash-hackworks/zcash-light-client-ffi.git",
"state": { "state": {
"branch": "librustzcash_0_7", "branch": "librustzcash_0_7_build_test",
"revision": "127a6b9d19841fea232bf0d0887ae5f2cad8df04", "revision": "5a48541c1da48eb082defaf0fa127657f4f99094",
"version": null "version": null
} }
} }

View File

@ -16,7 +16,7 @@ let package = Package(
dependencies: [ dependencies: [
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.8.0"), .package(url: "https://github.com/grpc/grpc-swift.git", from: "1.8.0"),
.package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.0"), .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.0"),
.package(name:"libzcashlc", url: "https://github.com/zcash-hackworks/zcash-light-client-ffi.git", branch: "librustzcash_0_7"), .package(name:"libzcashlc", url: "https://github.com/zcash-hackworks/zcash-light-client-ffi.git", branch: "librustzcash_0_7_build_test"),
], ],
targets: [ targets: [
.target( .target(

View File

@ -9,11 +9,6 @@ import Foundation
import SQLite import SQLite
class MigrationManager { class MigrationManager {
enum DataDbMigrations: Int32 {
case none = 0
case version1 = 1
}
enum CacheDbMigration: Int32 { enum CacheDbMigration: Int32 {
case none = 0 case none = 0
} }
@ -22,136 +17,27 @@ class MigrationManager {
case none = 0 case none = 0
} }
static let latestDataDbMigrationVersion: Int32 = DataDbMigrations.version1.rawValue
static let latestCacheDbMigrationVersion: Int32 = CacheDbMigration.none.rawValue static let latestCacheDbMigrationVersion: Int32 = CacheDbMigration.none.rawValue
static let latestPendingDbMigrationVersion: Int32 = PendingDbMigration.none.rawValue static let latestPendingDbMigrationVersion: Int32 = PendingDbMigration.none.rawValue
var cacheDb: ConnectionProvider var cacheDb: ConnectionProvider
var dataDb: ConnectionProvider
var pendingDb: ConnectionProvider var pendingDb: ConnectionProvider
var network: NetworkType var network: NetworkType
init( init(
cacheDbConnection: ConnectionProvider, cacheDbConnection: ConnectionProvider,
dataDbConnection: ConnectionProvider,
pendingDbConnection: ConnectionProvider, pendingDbConnection: ConnectionProvider,
networkType: NetworkType networkType: NetworkType
) { ) {
self.cacheDb = cacheDbConnection self.cacheDb = cacheDbConnection
self.dataDb = dataDbConnection
self.pendingDb = pendingDbConnection self.pendingDb = pendingDbConnection
self.network = networkType self.network = networkType
} }
func performMigration(ufvks: [UnifiedFullViewingKey]) throws { func performMigration(ufvks: [UnifiedFullViewingKey]) throws {
// TODO: DataDB migrations will be handled by rustBackend.initDataDb
// once https://github.com/zcash/librustzcash/pull/600 merges, and in
// the interim the old migrations here will fail if we try to run them
// due to changes to table column names in zcash/librustzcash.
//try migrateDataDb(ufvks: ufvks)
try migrateCacheDb() try migrateCacheDb()
try migratePendingDb() try migratePendingDb()
} }
func performVersion1Migration(viewingKeys: [UnifiedFullViewingKey]) throws {
LoggerProxy.debug("Starting migration version 1 from viewing Keys")
let db = try self.dataDb.connection()
let placeholder = "deriveMe"
let migrationStatement =
"""
BEGIN TRANSACTION;
PRAGMA foreign_keys = OFF;
DROP TABLE utxos;
CREATE TABLE IF NOT EXISTS utxos(
id_utxo INTEGER PRIMARY KEY,
address TEXT NOT NULL,
prevout_txid BLOB NOT NULL,
prevout_idx INTEGER NOT NULL,
script BLOB NOT NULL,
value_zat INTEGER NOT NULL,
height INTEGER NOT NULL,
spent_in_tx INTEGER,
FOREIGN KEY (spent_in_tx) REFERENCES transactions(id_tx),
CONSTRAINT tx_outpoint UNIQUE (prevout_txid, prevout_idx)
);
CREATE TABLE IF NOT EXISTS accounts_new (
account INTEGER PRIMARY KEY,
extfvk TEXT NOT NULL,
address TEXT NOT NULL,
transparent_address TEXT NOT NULL
);
INSERT INTO accounts_new SELECT account, extfvk, address, '\(placeholder)' FROM accounts;
DROP TABLE accounts;
ALTER TABLE accounts_new RENAME TO accounts;
PRAGMA user_version = 1;
PRAGMA foreign_keys = ON;
COMMIT TRANSACTION;
"""
LoggerProxy.debug("db.execute(\"\(migrationStatement)\")")
try db.execute(migrationStatement)
LoggerProxy.debug("db.run() succeeded")
// derive transparent (shielding) addresses
let accountsDao = AccountSQDAO(dbProvider: self.dataDb)
let accounts = try accountsDao.getAll()
guard !accounts.isEmpty else {
LoggerProxy.debug("no existing accounts found while performing this migration")
return
}
guard accounts.count == viewingKeys.count else {
let message = """
Number of accounts found and viewing keys provided don't match.
Found \(accounts.count) account(s) and there were \(viewingKeys.count) Viewing key(s) provided.
"""
LoggerProxy.debug(message)
throw StorageError.migrationFailedWithMessage(message: message)
}
let derivationTool = DerivationTool(networkType: self.network)
for tuple in zip(accounts, viewingKeys) {
// TODO: Should the v1 migration be changed to "migrate from pre-v1 database to v2"?
let tAddr = try derivationTool.deriveTransparentAddressFromPublicKey(tuple.1.encoding)
var account = tuple.0
account.transparentAddress = tAddr
try accountsDao.update(account)
}
// sanity check
guard try accountsDao.getAll().first(where: { $0.transparentAddress == placeholder }) == nil else {
LoggerProxy.error("Accounts Migration performed but the transparent addresses were not derived")
throw StorageError.migrationFailed(underlyingError: KeyDerivationErrors.unableToDerive)
}
}
func performVersion1Migration(_ seedBytes: [UInt8]) throws {
LoggerProxy.debug("Starting migration version 1")
// derive transparent (shielding) addresses
let accountsDao = AccountSQDAO(dbProvider: self.dataDb)
let accounts = try accountsDao.getAll()
guard !accounts.isEmpty else {
LoggerProxy.debug("no existing accounts found while performing this migration")
return
}
let derivationTool = DerivationTool(networkType: self.network)
let ufvks = try derivationTool.deriveUnifiedFullViewingKeysFromSeed(seedBytes, numberOfAccounts: accounts.count)
try performVersion1Migration(viewingKeys: ufvks)
}
} }
private extension MigrationManager { private extension MigrationManager {
@ -186,31 +72,6 @@ private extension MigrationManager {
LoggerProxy.debug("Cache Db - no migration needed") LoggerProxy.debug("Cache Db - no migration needed")
} }
} }
func migrateDataDb(ufvks: [UnifiedFullViewingKey]) throws {
let currentDataDbVersion = try dataDb.connection().getUserVersion()
LoggerProxy.debug(
"Attempting to perform migration for data Db - currentVersion: \(currentDataDbVersion)." +
"Latest version is: \(Self.latestDataDbMigrationVersion)"
)
if currentDataDbVersion < Self.latestDataDbMigrationVersion {
for dbVersion in (currentDataDbVersion + 1) ... Self.latestDataDbMigrationVersion {
guard let version = DataDbMigrations.init(rawValue: dbVersion) else {
LoggerProxy.error("failed to determine migration version")
throw StorageError.invalidMigrationVersion(version: dbVersion)
}
switch version {
case .version1:
try performVersion1Migration(viewingKeys: ufvks)
case .none:
break
}
}
} else {
LoggerProxy.debug("Data Db - no migration needed")
}
}
} }
extension Connection { extension Connection {

View File

@ -36,9 +36,8 @@ extension Account: UnifiedAddress {
get { get {
address address
} }
// swiftlint:disable unused_setter_value
set { set {
address = encoding address = newValue
} }
} }
} }

View File

@ -61,6 +61,12 @@ The [cash.z.wallet.sdk.block.CompactBlockProcessor] handles all the remaining Ru
functionality, related to processing blocks. functionality, related to processing blocks.
*/ */
public class Initializer { public class Initializer {
public enum InitializationResult {
case success
case seedRequired
}
private(set) var rustBackend: ZcashRustBackendWelding.Type private(set) var rustBackend: ZcashRustBackendWelding.Type
private(set) var alias: String private(set) var alias: String
private(set) var endpoint: LightWalletEndpoint private(set) var endpoint: LightWalletEndpoint
@ -188,7 +194,7 @@ public class Initializer {
- Parameters: - Parameters:
- viewingKeys: Extended Full Viewing Keys to initialize the DBs with - viewingKeys: Extended Full Viewing Keys to initialize the DBs with
*/ */
public func initialize() throws { public func initialize(with seed: [UInt8]?) throws -> InitializationResult {
do { do {
try storage.createTable() try storage.createTable()
} catch { } catch {
@ -196,9 +202,9 @@ public class Initializer {
} }
do { do {
try rustBackend.initDataDb(dbData: dataDbURL, networkType: network.networkType) if case .seedRequired = try rustBackend.initDataDb(dbData: dataDbURL, seed: seed, networkType: network.networkType) {
} catch RustWeldingError.dataDbNotEmpty { return .seedRequired
// this is fine }
} catch { } catch {
throw InitializerError.dataDbInitFailed throw InitializerError.dataDbInitFailed
} }
@ -242,12 +248,13 @@ public class Initializer {
let migrationManager = MigrationManager( let migrationManager = MigrationManager(
cacheDbConnection: SimpleConnectionProvider(path: cacheDbURL.path), cacheDbConnection: SimpleConnectionProvider(path: cacheDbURL.path),
dataDbConnection: SimpleConnectionProvider(path: dataDbURL.path),
pendingDbConnection: SimpleConnectionProvider(path: pendingDbURL.path), pendingDbConnection: SimpleConnectionProvider(path: pendingDbURL.path),
networkType: self.network.networkType networkType: self.network.networkType
) )
try migrationManager.performMigration(ufvks: viewingKeys) try migrationManager.performMigration(ufvks: viewingKeys)
return .success
} }
/** /**

View File

@ -39,13 +39,19 @@ class ZcashRustBackend: ZcashRustBackendWelding {
/** /**
* Sets up the internal structure of the data database. * Sets up the internal structure of the data database.
*/ */
static func initDataDb(dbData: URL, networkType: NetworkType) throws { static func initDataDb(dbData: URL, seed: [UInt8]?, networkType: NetworkType) throws -> DbInitResult {
let dbData = dbData.osStr() let dbData = dbData.osStr()
guard zcashlc_init_data_database(dbData.0, dbData.1, networkType.networkId) != 0 else { switch zcashlc_init_data_database(dbData.0, dbData.1, seed, UInt(seed?.count ?? 0), networkType.networkId) {
case 0: //ok
return DbInitResult.success
case 1:
return DbInitResult.seedRequired
default:
if let error = lastError() { if let error = lastError() {
throw throwDataDbError(error) throw throwDataDbError(error)
} else {
throw RustWeldingError.dataDbInitFailed(message: "Database initialization failed: unknown error")
} }
throw RustWeldingError.dataDbInitFailed(message: "unknown error")
} }
} }
@ -165,6 +171,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
return result return result
} }
// swiftlint:disable function_parameter_count // swiftlint:disable function_parameter_count
static func initBlocksTable( static func initBlocksTable(
dbData: URL, dbData: URL,

View File

@ -22,6 +22,15 @@ public enum ZcashRustBackendWeldingConstants {
static let validChain: Int32 = -1 static let validChain: Int32 = -1
} }
/**
Enumeration of potential return states for database initialization. If `seedRequired` is returned, the caller must
re-attempt initialization providing the seed
*/
public enum DbInitResult {
case success
case seedRequired
}
public protocol ZcashRustBackendWelding { public protocol ZcashRustBackendWelding {
/** /**
gets the latest error if available. Clear the existing error gets the latest error if available. Clear the existing error
@ -37,7 +46,7 @@ public protocol ZcashRustBackendWelding {
initializes the data db initializes the data db
- Parameter dbData: location of the data db sql file - Parameter dbData: location of the data db sql file
*/ */
static func initDataDb(dbData: URL, networkType: NetworkType) throws static func initDataDb(dbData: URL, seed: [UInt8]?, networkType: NetworkType) throws -> DbInitResult
/** /**
- Returns: true when the address is valid and shielded. Returns false in any other case - Returns: true when the address is valid and shielded. Returns false in any other case

View File

@ -80,7 +80,7 @@ public protocol Synchronizer {
/// prepares this initializer to operate. Initializes the internal state with the given /// prepares this initializer to operate. Initializes the internal state with the given
/// Extended Viewing Keys and a wallet birthday found in the initializer object /// Extended Viewing Keys and a wallet birthday found in the initializer object
func prepare() throws func prepare(with seed: [UInt8]?) throws -> Initializer.InitializationResult
///Starts this synchronizer within the given scope. ///Starts this synchronizer within the given scope.
/// ///

View File

@ -154,17 +154,18 @@ public class SDKSynchronizer: Synchronizer {
self.blockProcessor.stop() self.blockProcessor.stop()
} }
public func initialize() throws { public func prepare(with seed: [UInt8]?) throws -> Initializer.InitializationResult {
try self.initializer.initialize() if case .seedRequired = try self.initializer.initialize(with: seed) {
try self.blockProcessor.setStartHeight(initializer.walletBirthday) return .seedRequired
} }
public func prepare() throws {
try self.initializer.initialize()
try self.blockProcessor.setStartHeight(initializer.walletBirthday) try self.blockProcessor.setStartHeight(initializer.walletBirthday)
self.status = .disconnected self.status = .disconnected
return .success
} }
/// Starts the synchronizer /// Starts the synchronizer
/// - Throws: CompactBlockProcessorError when failures occur /// - Throws: CompactBlockProcessorError when failures occur
public func start(retry: Bool = false) throws { public func start(retry: Bool = false) throws {

View File

@ -64,7 +64,14 @@ class TransactionEnhancementTests: XCTestCase {
try? FileManager.default.removeItem(at: processorConfig.dataDb) try? FileManager.default.removeItem(at: processorConfig.dataDb)
_ = rustBackend.initAccountsTable(dbData: processorConfig.dataDb, seed: TestSeed().seed(), accounts: 1, networkType: network.networkType) _ = rustBackend.initAccountsTable(dbData: processorConfig.dataDb, seed: TestSeed().seed(), accounts: 1, networkType: network.networkType)
_ = try rustBackend.initDataDb(dbData: processorConfig.dataDb, networkType: network.networkType)
let dbInit = try rustBackend.initDataDb(dbData: processorConfig.dataDb, seed: nil, networkType: network.networkType)
guard case .success = dbInit else {
XCTFail("Failed to initDataDb. Expected `.success` got: \(String(describing: dbInit))")
return
}
_ = try rustBackend.initBlocksTable( _ = try rustBackend.initBlocksTable(
dbData: processorConfig.dataDb, dbData: processorConfig.dataDb,
height: Int32(birthday.height), height: Int32(birthday.height),

View File

@ -59,7 +59,13 @@ class BlockScanOperationTests: XCTestCase {
func testSingleDownloadAndScanOperation() { func testSingleDownloadAndScanOperation() {
logger = SampleLogger(logLevel: .debug) logger = SampleLogger(logLevel: .debug)
XCTAssertNoThrow(try rustWelding.initDataDb(dbData: dataDbURL, networkType: network.networkType)) var dbInit: DbInitResult!
XCTAssertNoThrow(try { dbInit = try ZcashRustBackend.initDataDb(dbData: self.dataDbURL, seed: nil, networkType: .testnet) }())
guard case .success = dbInit else {
XCTFail("Failed to initDataDb. Expected `.success` got: \(String(describing: dbInit))")
return
}
let downloadStartedExpect = XCTestExpectation(description: "\(self.description) download started") let downloadStartedExpect = XCTestExpectation(description: "\(self.description) download started")
let downloadExpect = XCTestExpectation(description: "\(self.description) download") let downloadExpect = XCTestExpectation(description: "\(self.description) download")
@ -157,8 +163,12 @@ class BlockScanOperationTests: XCTestCase {
name: SDKMetrics.notificationName, name: SDKMetrics.notificationName,
object: nil object: nil
) )
try self.rustWelding.initDataDb(dbData: dataDbURL, networkType: network.networkType) let dbInit = try self.rustWelding.initDataDb(dbData: dataDbURL, seed: nil, networkType: network.networkType)
guard case .success = dbInit else {
XCTFail("Failed to initDataDb. Expected `.success` got: \(dbInit)")
return
}
guard try self.rustWelding.initAccountsTable(dbData: self.dataDbURL, ufvks: [ufvk], networkType: network.networkType) else { guard try self.rustWelding.initAccountsTable(dbData: self.dataDbURL, ufvks: [ufvk], networkType: network.networkType) else {
XCTFail("failed to init account table") XCTFail("failed to init account table")

View File

@ -55,7 +55,13 @@ class CompactBlockProcessorTests: XCTestCase {
backend: ZcashRustBackend.self, backend: ZcashRustBackend.self,
config: processorConfig config: processorConfig
) )
try ZcashRustBackend.initDataDb(dbData: processorConfig.dataDb, networkType: .testnet) let dbInit = try ZcashRustBackend.initDataDb(dbData: processorConfig.dataDb, seed: nil, networkType: .testnet)
guard case .success = dbInit else {
XCTFail("Failed to initDataDb. Expected `.success` got: \(dbInit)")
return
}
downloadStartedExpect = XCTestExpectation(description: "\(self.description) downloadStartedExpect") downloadStartedExpect = XCTestExpectation(description: "\(self.description) downloadStartedExpect")
stopNotificationExpectation = XCTestExpectation(description: "\(self.description) stopNotificationExpectation") stopNotificationExpectation = XCTestExpectation(description: "\(self.description) stopNotificationExpectation")
updatedNotificationExpectation = XCTestExpectation(description: "\(self.description) updatedNotificationExpectation") updatedNotificationExpectation = XCTestExpectation(description: "\(self.description) updatedNotificationExpectation")

View File

@ -46,9 +46,14 @@ class CompactBlockReorgTests: XCTestCase {
info.estimatedHeight = UInt64(mockLatestHeight) info.estimatedHeight = UInt64(mockLatestHeight)
info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
} }
try ZcashRustBackend.initDataDb(dbData: processorConfig.dataDb, networkType: .testnet) let dbInit = try ZcashRustBackend.initDataDb(dbData: processorConfig.dataDb, seed: nil, networkType: .testnet)
guard case .success = dbInit else {
XCTFail("Failed to initDataDb. Expected `.success` got: \(dbInit)")
return
}
let storage = CompactBlockStorage.init(connectionProvider: SimpleConnectionProvider(path: processorConfig.cacheDb.absoluteString)) let storage = CompactBlockStorage.init(connectionProvider: SimpleConnectionProvider(path: processorConfig.cacheDb.absoluteString))
try! storage.createTable() try! storage.createTable()

View File

@ -37,9 +37,15 @@ class ZcashRustBackendTests: XCTestCase {
func testInitWithShortSeedAndFail() { func testInitWithShortSeedAndFail() {
let seed = "testreferencealice" let seed = "testreferencealice"
XCTAssertNoThrow(try ZcashRustBackend.initDataDb(dbData: dbData!, networkType: networkType)) var dbInit: DbInitResult!
XCTAssertNoThrow(try { dbInit = try ZcashRustBackend.initDataDb(dbData: self.dbData!, seed: nil, networkType: self.networkType) }())
guard case .success = dbInit else {
XCTFail("Failed to initDataDb. Expected `.success` got: \(String(describing: dbInit))")
return
}
_ = ZcashRustBackend.initAccountsTable(dbData: dbData!, seed: Array(seed.utf8), accounts: 1, networkType: networkType) _ = ZcashRustBackend.initAccountsTable(dbData: dbData!, seed: Array(seed.utf8), accounts: 1, networkType: networkType)
XCTAssertNotNil(ZcashRustBackend.getLastError()) XCTAssertNotNil(ZcashRustBackend.getLastError())
} }
@ -99,7 +105,15 @@ class ZcashRustBackendTests: XCTestCase {
return return
} }
let seed = "testreferencealicetestreferencealice" let seed = "testreferencealicetestreferencealice"
XCTAssertNoThrow(try ZcashRustBackend.initDataDb(dbData: dbData!, networkType: networkType))
var dbInit: DbInitResult!
XCTAssertNoThrow(try { dbInit = try ZcashRustBackend.initDataDb(dbData: self.dbData!, seed: nil, networkType: self.networkType) }())
guard case .success = dbInit else {
XCTFail("Failed to initDataDb. Expected `.success` got: \(String(describing: dbInit))")
return
}
XCTAssertEqual(ZcashRustBackend.getLastError(), nil) XCTAssertEqual(ZcashRustBackend.getLastError(), nil)
XCTAssertNotNil(ZcashRustBackend.initAccountsTable(dbData: dbData!, seed: Array(seed.utf8), accounts: 1, networkType: networkType)) XCTAssertNotNil(ZcashRustBackend.initAccountsTable(dbData: dbData!, seed: Array(seed.utf8), accounts: 1, networkType: networkType))

View File

@ -49,7 +49,15 @@ class WalletTests: XCTestCase {
) )
let synchronizer = try SDKSynchronizer(initializer: wallet) let synchronizer = try SDKSynchronizer(initializer: wallet)
XCTAssertNoThrow(try synchronizer.prepare())
var dbInit: Initializer.InitializationResult!
XCTAssertNoThrow(try { dbInit = try synchronizer.prepare(with: nil) }())
guard case .success = dbInit else {
XCTFail("Failed to initDataDb. Expected `.success` got: \(String(describing: dbInit))")
return
}
// fileExists actually sucks, so attempting to delete the file and checking what happens is far better :) // fileExists actually sucks, so attempting to delete the file and checking what happens is far better :)
XCTAssertNoThrow( try FileManager.default.removeItem(at: dbData!) ) XCTAssertNoThrow( try FileManager.default.removeItem(at: dbData!) )

View File

@ -77,6 +77,10 @@ extension LightWalletServiceMockResponse {
} }
class MockRustBackend: ZcashRustBackendWelding { class MockRustBackend: ZcashRustBackendWelding {
static func initDataDb(dbData: URL, seed: [UInt8]?, networkType: ZcashLightClientKit.NetworkType) throws -> ZcashLightClientKit.DbInitResult {
.seedRequired
}
static func clearUtxos(dbData: URL, address: String, sinceHeight: BlockHeight, networkType: NetworkType) throws -> Int32 { static func clearUtxos(dbData: URL, address: String, sinceHeight: BlockHeight, networkType: NetworkType) throws -> Int32 {
-1 -1
} }
@ -246,7 +250,7 @@ class MockRustBackend: ZcashRustBackendWelding {
static func initDataDb(dbData: URL, networkType: NetworkType) throws { static func initDataDb(dbData: URL, networkType: NetworkType) throws {
if !mockDataDb { if !mockDataDb {
try rustBackend.initDataDb(dbData: dbData, networkType: networkType) _ = try rustBackend.initDataDb(dbData: dbData, seed: nil, networkType: networkType)
} }
} }

View File

@ -21,6 +21,7 @@ class TestCoordinator {
case notificationFromUnknownSynchronizer case notificationFromUnknownSynchronizer
case notMockLightWalletService case notMockLightWalletService
case builderError case builderError
case seedRequiredForMigration
} }
enum SyncThreshold { enum SyncThreshold {
@ -291,6 +292,7 @@ enum TestSynchronizerBuilder {
unifiedFullViewingKey: UnifiedFullViewingKey, unifiedFullViewingKey: UnifiedFullViewingKey,
walletBirthday: BlockHeight, walletBirthday: BlockHeight,
network: ZcashNetwork, network: ZcashNetwork,
seed: [UInt8]? = nil,
loggerProxy: Logger? = nil loggerProxy: Logger? = nil
) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) { ) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) {
let initializer = Initializer( let initializer = Initializer(
@ -308,7 +310,9 @@ enum TestSynchronizerBuilder {
) )
let synchronizer = try SDKSynchronizer(initializer: initializer) let synchronizer = try SDKSynchronizer(initializer: initializer)
try synchronizer.prepare() if case .seedRequired = try synchronizer.prepare(with: seed) {
throw TestCoordinator.CoordinatorError.seedRequiredForMigration
}
return ([spendingKey], synchronizer) return ([spendingKey], synchronizer)
} }