create + prepare API

This commit is contained in:
Francisco Gindre 2021-05-05 16:08:57 -03:00
parent 5aa8afd2aa
commit 39837009a4
3 changed files with 49 additions and 89 deletions

View File

@ -66,6 +66,7 @@ public class Initializer {
private(set) var transactionRepository: TransactionRepository private(set) var transactionRepository: TransactionRepository
private(set) var accountRepository: AccountRepository private(set) var accountRepository: AccountRepository
private(set) var downloader: CompactBlockDownloader private(set) var downloader: CompactBlockDownloader
private(set) public var viewingKeys: [UnifiedViewingKey]
private(set) public var walletBirthday: WalletBirthday private(set) public var walletBirthday: WalletBirthday
/** /**
Constructs the Initializer Constructs the Initializer
@ -83,6 +84,7 @@ public class Initializer {
endpoint: LightWalletEndpoint, endpoint: LightWalletEndpoint,
spendParamsURL: URL, spendParamsURL: URL,
outputParamsURL: URL, outputParamsURL: URL,
viewingKeys: [UnifiedViewingKey],
walletBirthday: BlockHeight = ZcashSDK.SAPLING_ACTIVATION_HEIGHT, walletBirthday: BlockHeight = ZcashSDK.SAPLING_ACTIVATION_HEIGHT,
alias: String = "", alias: String = "",
loggerProxy: Logger? = nil) { loggerProxy: Logger? = nil) {
@ -104,6 +106,7 @@ public class Initializer {
downloader: CompactBlockDownloader(service: lwdService, storage: storage), downloader: CompactBlockDownloader(service: lwdService, storage: storage),
spendParamsURL: spendParamsURL, spendParamsURL: spendParamsURL,
outputParamsURL: outputParamsURL, outputParamsURL: outputParamsURL,
viewingKeys: viewingKeys,
walletBirthday: walletBirthday, walletBirthday: walletBirthday,
alias: alias, alias: alias,
loggerProxy: loggerProxy loggerProxy: loggerProxy
@ -125,6 +128,7 @@ public class Initializer {
downloader: CompactBlockDownloader, downloader: CompactBlockDownloader,
spendParamsURL: URL, spendParamsURL: URL,
outputParamsURL: URL, outputParamsURL: URL,
viewingKeys: [UnifiedViewingKey],
walletBirthday: BlockHeight, walletBirthday: BlockHeight,
alias: String = "", alias: String = "",
loggerProxy: Logger? = nil loggerProxy: Logger? = nil
@ -144,69 +148,13 @@ public class Initializer {
self.transactionRepository = repository self.transactionRepository = repository
self.accountRepository = accountRepository self.accountRepository = accountRepository
self.downloader = downloader self.downloader = downloader
self.viewingKeys = viewingKeys
self.walletBirthday = WalletBirthday.birthday(with: walletBirthday) self.walletBirthday = WalletBirthday.birthday(with: walletBirthday)
} }
/**
Initialize the wallet with the given seed and return the related private keys for each
account specified or null if the wallet was previously initialized and block data exists on
disk. When this method returns null, that signals that the wallet will need to retrieve the
private keys from its own secure storage. In other words, the private keys are only given out
once for each set of database files. Subsequent calls to [initialize] will only load the Rust
library and return null.
'compactBlockCache.db' and 'transactionData.db' files are created by this function (if they
do not already exist). These files can be given a prefix for scenarios where multiple wallets
operate in one app--for instance, when sweeping funds from another wallet seed.
- Parameters:
- seedProvider: the seed to use for initializing this wallet.
- walletBirthdayHeight: the height corresponding to when the wallet seed was created. If null, this signals that the wallet is being born.
- numberOfAccounts: the number of accounts to create from this seed.
*/
public func initialize(seedBytes: [UInt8], numberOfAccounts: Int = 1) throws -> [String]? {
do {
try rustBackend.initDataDb(dbData: dataDbURL)
} catch RustWeldingError.dataDbNotEmpty {
// this is fine
} catch {
throw InitializerError.dataDbInitFailed
}
do {
try rustBackend.initBlocksTable(dbData: dataDbURL, height: Int32(walletBirthday.height), hash: walletBirthday.hash, time: walletBirthday.time, saplingTree: walletBirthday.tree)
} catch RustWeldingError.dataDbNotEmpty {
// this is fine
} catch {
throw InitializerError.dataDbInitFailed
}
let lastDownloaded = (try? downloader.storage.latestHeight()) ?? self.walletBirthday.height
// resume from last downloaded block
lowerBoundHeight = max(walletBirthday.height, lastDownloaded)
var accounts: [String]? = nil
do {
guard let a = rustBackend.initAccountsTable(dbData: dataDbURL, seed: seedBytes, accounts: Int32(numberOfAccounts)) else {
throw rustBackend.lastError() ?? InitializerError.accountInitFailed
}
accounts = a
} catch RustWeldingError.dataDbNotEmpty {
// this is fine
} catch {
throw InitializerError.dataDbInitFailed
}
let migrationManager = MigrationManager(cacheDbConnection: SimpleConnectionProvider(path: cacheDbURL.path),
dataDbConnection: SimpleConnectionProvider(path: dataDbURL.path), pendingDbConnection: SimpleConnectionProvider(path: pendingDbURL.path))
try migrationManager.performMigration(seedBytes: seedBytes)
return accounts
}
/** /**
__TEMPORARILY UNAVAILABLE__
Initialize the wallet with the given seed and return the related private keys for each Initialize the wallet with the given seed and return the related private keys for each
account specified or null if the wallet was previously initialized and block data exists on account specified or null if the wallet was previously initialized and block data exists on
disk. When this method returns null, that signals that the wallet will need to retrieve the disk. When this method returns null, that signals that the wallet will need to retrieve the
@ -221,7 +169,7 @@ public class Initializer {
- viewingKeys: Extended Full Viewing Keys to initialize the DBs with - viewingKeys: Extended Full Viewing Keys to initialize the DBs with
*/ */
public func initialize(unifiedViewingKeys: [UnifiedViewingKey], walletBirthday: BlockHeight) throws { public func initialize() throws {
do { do {
try rustBackend.initDataDb(dbData: dataDbURL) try rustBackend.initDataDb(dbData: dataDbURL)
@ -230,23 +178,21 @@ public class Initializer {
} catch { } catch {
throw InitializerError.dataDbInitFailed throw InitializerError.dataDbInitFailed
} }
let birthday = WalletBirthday.birthday(with: walletBirthday)
self.walletBirthday = birthday
do { do {
try rustBackend.initBlocksTable(dbData: dataDbURL, height: Int32(birthday.height), hash: birthday.hash, time: birthday.time, saplingTree: birthday.tree) try rustBackend.initBlocksTable(dbData: dataDbURL, height: Int32(walletBirthday.height), hash: walletBirthday.hash, time: walletBirthday.time, saplingTree: walletBirthday.tree)
} catch RustWeldingError.dataDbNotEmpty { } catch RustWeldingError.dataDbNotEmpty {
// this is fine // this is fine
} catch { } catch {
throw InitializerError.dataDbInitFailed throw InitializerError.dataDbInitFailed
} }
let lastDownloaded = (try? downloader.storage.latestHeight()) ?? birthday.height let lastDownloaded = (try? downloader.storage.latestHeight()) ?? walletBirthday.height
// resume from last downloaded block // resume from last downloaded block
lowerBoundHeight = max(birthday.height, lastDownloaded) lowerBoundHeight = max(walletBirthday.height, lastDownloaded)
do { do {
guard try rustBackend.initAccountsTable(dbData: dataDbURL, uvks: unifiedViewingKeys) else { guard try rustBackend.initAccountsTable(dbData: dataDbURL, uvks: viewingKeys) else {
throw rustBackend.lastError() ?? InitializerError.accountInitFailed throw rustBackend.lastError() ?? InitializerError.accountInitFailed
} }
} catch RustWeldingError.dataDbNotEmpty { } catch RustWeldingError.dataDbNotEmpty {
@ -258,7 +204,7 @@ public class Initializer {
let migrationManager = MigrationManager(cacheDbConnection: SimpleConnectionProvider(path: cacheDbURL.path), let migrationManager = MigrationManager(cacheDbConnection: SimpleConnectionProvider(path: cacheDbURL.path),
dataDbConnection: SimpleConnectionProvider(path: dataDbURL.path), pendingDbConnection: SimpleConnectionProvider(path: pendingDbURL.path)) dataDbConnection: SimpleConnectionProvider(path: dataDbURL.path), pendingDbConnection: SimpleConnectionProvider(path: pendingDbURL.path))
try migrationManager.performMigration(uvks: unifiedViewingKeys) try migrationManager.performMigration(uvks: viewingKeys)
} }
/** /**

View File

@ -13,6 +13,7 @@ Represents errors thrown by a Synchronizer
*/ */
public enum SynchronizerError: Error { public enum SynchronizerError: Error {
case initFailed(message: String) case initFailed(message: String)
case notPrepared
case syncFailed case syncFailed
case connectionFailed(message: Error) case connectionFailed(message: Error)
case generalError(message: String) case generalError(message: String)
@ -68,14 +69,9 @@ public protocol Synchronizer {
var progress: Float { get } var progress: Float { get }
/** /**
Initialize the internal state with the given Extended Viewing Keys and a wallet birthday prepares this initializer to operate. Initializes the internal state with the given Extended Viewing Keys and a wallet birthday found in the initializer object
- Parameter viewingKeys: an array containing the viewing keys to initialize the internal state
- Parameter walletBirthday: the eldest when the different keys differ in birthday
- Throws: Initializer Errors and RustWeldingError if fails
- Note: The subsequent initializations don't have any effect or failure
*/ */
func initialize(unifiedViewingKeys: [UnifiedViewingKey], walletBirthday: BlockHeight) throws func prepare() throws
/** /**
Starts this synchronizer within the given scope. Starts this synchronizer within the given scope.
@ -222,6 +218,10 @@ public protocol Synchronizer {
The Status of the synchronizer The Status of the synchronizer
*/ */
public enum Status { public enum Status {
/**
This synchronizer is not ready to start
*/
case unprepared
/** /**
Indicates that [stop] has been called on this Synchronizer and it will no longer be used. Indicates that [stop] has been called on this Synchronizer and it will no longer be used.
*/ */

View File

@ -98,7 +98,7 @@ public class SDKSynchronizer: Synchronizer {
*/ */
public convenience init(initializer: Initializer) throws { public convenience init(initializer: Initializer) throws {
try self.init(status: .disconnected, try self.init(status: .unprepared,
initializer: initializer, initializer: initializer,
transactionManager: try OutboundTransactionManagerBuilder.build(initializer: initializer), transactionManager: try OutboundTransactionManagerBuilder.build(initializer: initializer),
transactionRepository: initializer.transactionRepository, transactionRepository: initializer.transactionRepository,
@ -128,26 +128,36 @@ public class SDKSynchronizer: Synchronizer {
self.blockProcessor.stop() self.blockProcessor.stop()
} }
public func initialize(unifiedViewingKeys: [UnifiedViewingKey], walletBirthday: BlockHeight) throws { public func initialize() throws {
try self.initializer.initialize(unifiedViewingKeys: unifiedViewingKeys, walletBirthday: walletBirthday) try self.initializer.initialize()
try self.blockProcessor.setStartHeight(WalletBirthday.birthday(with: walletBirthday).height) try self.blockProcessor.setStartHeight(initializer.walletBirthday.height)
} }
public func prepare() throws {
try self.initializer.initialize()
try self.blockProcessor.setStartHeight(initializer.walletBirthday.height)
self.status = .disconnected
}
/** /**
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 {
guard status == .stopped || status == .disconnected || status == .synced else {
assert(true,"warning: synchronizer started when already started") // TODO: remove this assertion sometime in the near future
return
}
do { switch status {
try blockProcessor.start(retry: retry) case .unprepared:
} catch { throw SynchronizerError.notPrepared
throw mapError(error) case .syncing:
assert(true,"warning: synchronizer started when already started") // TODO: remove this assertion sometime in the near future
LoggerProxy.debug("warning: synchronizer started when already started")
return
default:
do {
try blockProcessor.start(retry: retry)
} catch {
throw mapError(error)
}
} }
} }
@ -156,7 +166,10 @@ public class SDKSynchronizer: Synchronizer {
*/ */
public func stop() { public func stop() {
guard status != .stopped, status != .disconnected else { return } guard status != .stopped, status != .disconnected else {
LoggerProxy.info("attempted to stop when status was: \(status)")
return
}
blockProcessor.stop(cancelTasks: true) blockProcessor.stop(cancelTasks: true)
} }
@ -562,7 +575,8 @@ public class SDKSynchronizer: Synchronizer {
NotificationCenter.default.post(name: Notification.Name.synchronizerSynced, object: self) NotificationCenter.default.post(name: Notification.Name.synchronizerSynced, object: self)
case .syncing: case .syncing:
NotificationCenter.default.post(name: Notification.Name.synchronizerSyncing, object: self) NotificationCenter.default.post(name: Notification.Name.synchronizerSyncing, object: self)
case .unprepared:
break
} }
} }
// MARK: book keeping // MARK: book keeping