Remove `AccountRepository`
This removes the last direct access to the `accounts` table; all access now goes through the Rust FFI.
This commit is contained in:
parent
dd9942b6ab
commit
86defc8b4a
|
@ -176,8 +176,7 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||
"state" : {
|
||||
"revision" : "7c801be1f445402a433b32835a50d832e8a50437",
|
||||
"version" : "0.6.0"
|
||||
"revision" : "4eccfb5ea825f5f0ebb3cab964d08ef21103feb4"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -122,8 +122,7 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||
"state" : {
|
||||
"revision" : "7c801be1f445402a433b32835a50d832e8a50437",
|
||||
"version" : "0.6.0"
|
||||
"revision" : "4eccfb5ea825f5f0ebb3cab964d08ef21103feb4"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -16,7 +16,8 @@ let package = Package(
|
|||
dependencies: [
|
||||
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.19.1"),
|
||||
.package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1"),
|
||||
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", exact: "0.6.0")
|
||||
// Compiled from revision `c2ac47300d062b76134c515589301b202277e3fa`.
|
||||
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", revision: "4eccfb5ea825f5f0ebb3cab964d08ef21103feb4")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
|
|
@ -28,7 +28,6 @@ actor CompactBlockProcessor {
|
|||
private let configProvider: ConfigProvider
|
||||
private var afterSyncHooksManager = AfterSyncHooksManager()
|
||||
|
||||
private let accountRepository: AccountRepository
|
||||
var blockDownloaderService: BlockDownloaderService
|
||||
private var latestBlocksDataProvider: LatestBlocksDataProvider
|
||||
private let logger: Logger
|
||||
|
@ -142,20 +141,6 @@ actor CompactBlockProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
/// Initializes a CompactBlockProcessor instance
|
||||
/// - Parameters:
|
||||
/// - service: concrete implementation of `LightWalletService` protocol
|
||||
/// - storage: concrete implementation of `CompactBlockRepository` protocol
|
||||
/// - backend: a class that complies to `ZcashRustBackendWelding`
|
||||
/// - config: `Configuration` struct for this processor
|
||||
init(container: DIContainer, config: Configuration) {
|
||||
self.init(
|
||||
container: container,
|
||||
config: config,
|
||||
accountRepository: AccountRepositoryBuilder.build(dataDbURL: config.dataDb, readOnly: true, logger: container.resolve(Logger.self))
|
||||
)
|
||||
}
|
||||
|
||||
/// Initializes a CompactBlockProcessor instance from an Initialized object
|
||||
/// - Parameters:
|
||||
/// - initializer: an instance that complies to CompactBlockDownloading protocol
|
||||
|
@ -171,20 +156,23 @@ actor CompactBlockProcessor {
|
|||
saplingParamsSourceURL: initializer.saplingParamsSourceURL,
|
||||
walletBirthdayProvider: walletBirthdayProvider,
|
||||
network: initializer.network
|
||||
),
|
||||
accountRepository: initializer.accountRepository
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// Initializes a CompactBlockProcessor instance
|
||||
/// - Parameters:
|
||||
/// - service: concrete implementation of `LightWalletService` protocol
|
||||
/// - storage: concrete implementation of `CompactBlockRepository` protocol
|
||||
/// - backend: a class that complies to `ZcashRustBackendWelding`
|
||||
/// - config: `Configuration` struct for this processor
|
||||
init(
|
||||
container: DIContainer,
|
||||
config: Configuration,
|
||||
accountRepository: AccountRepository
|
||||
config: Configuration
|
||||
) {
|
||||
Dependencies.setupCompactBlockProcessor(
|
||||
in: container,
|
||||
config: config,
|
||||
accountRepository: accountRepository
|
||||
config: config
|
||||
)
|
||||
|
||||
let configProvider = ConfigProvider(config: config)
|
||||
|
@ -200,7 +188,6 @@ actor CompactBlockProcessor {
|
|||
self.storage = container.resolve(CompactBlockRepository.self)
|
||||
self.config = config
|
||||
self.transactionRepository = container.resolve(TransactionRepository.self)
|
||||
self.accountRepository = accountRepository
|
||||
self.fileManager = container.resolve(ZcashFileManager.self)
|
||||
self.configProvider = configProvider
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ protocol UTXOFetcher {
|
|||
}
|
||||
|
||||
struct UTXOFetcherImpl {
|
||||
let accountRepository: AccountRepository
|
||||
let blockDownloaderService: BlockDownloaderService
|
||||
let config: UTXOFetcherConfig
|
||||
let rustBackend: ZcashRustBackendWelding
|
||||
|
@ -37,8 +36,7 @@ extension UTXOFetcherImpl: UTXOFetcher {
|
|||
) async throws -> (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity]) {
|
||||
try Task.checkCancellation()
|
||||
|
||||
let accounts = try accountRepository.getAll()
|
||||
.map { $0.account }
|
||||
let accounts = try await rustBackend.listAccounts()
|
||||
|
||||
var tAddresses: [TransparentAddress] = []
|
||||
for account in accounts {
|
||||
|
|
|
@ -14,11 +14,6 @@ protocol AccountEntity {
|
|||
}
|
||||
|
||||
struct DbAccount: AccountEntity, Encodable, Decodable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case account
|
||||
case ufvk
|
||||
}
|
||||
|
||||
let account: Int
|
||||
let ufvk: String
|
||||
}
|
||||
|
@ -38,171 +33,3 @@ extension DbAccount: Hashable {
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
protocol AccountRepository {
|
||||
func getAll() throws -> [AccountEntity]
|
||||
func findBy(account: Int) throws -> AccountEntity?
|
||||
func update(_ account: AccountEntity) throws
|
||||
}
|
||||
|
||||
class AccountSQDAO: AccountRepository {
|
||||
enum TableColums {
|
||||
static let account = Expression<Int>("account")
|
||||
static let extfvk = Expression<String>("ufvk")
|
||||
}
|
||||
|
||||
let table = Table("accounts")
|
||||
|
||||
let dbProvider: ConnectionProvider
|
||||
let logger: Logger
|
||||
|
||||
init(dbProvider: ConnectionProvider, logger: Logger) {
|
||||
self.dbProvider = dbProvider
|
||||
self.logger = logger
|
||||
}
|
||||
|
||||
/// - Throws:
|
||||
/// - `accountDAOGetAllCantDecode` if account data fetched from the db can't be decoded to the `Account` object.
|
||||
/// - `accountDAOGetAll` if sqlite query fetching account data failed.
|
||||
func getAll() throws -> [AccountEntity] {
|
||||
do {
|
||||
globalDBLock.lock()
|
||||
defer { globalDBLock.unlock() }
|
||||
|
||||
return try dbProvider.connection()
|
||||
.prepare(table)
|
||||
.map { row -> DbAccount in
|
||||
do {
|
||||
return try row.decode()
|
||||
} catch {
|
||||
throw ZcashError.accountDAOGetAllCantDecode(error)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
if let error = error as? ZcashError {
|
||||
throw error
|
||||
} else {
|
||||
throw ZcashError.accountDAOGetAll(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// - Throws:
|
||||
/// - `accountDAOFindByCantDecode` if account data fetched from the db can't be decoded to the `Account` object.
|
||||
/// - `accountDAOFindBy` if sqlite query fetching account data failed.
|
||||
func findBy(account: Int) throws -> AccountEntity? {
|
||||
let query = table.filter(TableColums.account == account).limit(1)
|
||||
do {
|
||||
globalDBLock.lock()
|
||||
defer { globalDBLock.unlock() }
|
||||
|
||||
return try dbProvider.connection()
|
||||
.prepare(query)
|
||||
.map {
|
||||
do {
|
||||
return try $0.decode() as DbAccount
|
||||
} catch {
|
||||
throw ZcashError.accountDAOFindByCantDecode(error)
|
||||
}
|
||||
}
|
||||
.first
|
||||
} catch {
|
||||
if let error = error as? ZcashError {
|
||||
throw error
|
||||
} else {
|
||||
throw ZcashError.accountDAOFindBy(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// - Throws:
|
||||
/// - `accountDAOUpdate` if sqlite query updating account failed.
|
||||
/// - `accountDAOUpdatedZeroRows` if sqlite query updating account pass but it affects 0 rows.
|
||||
func update(_ account: AccountEntity) throws {
|
||||
guard let acc = account as? DbAccount else {
|
||||
throw ZcashError.accountDAOUpdateInvalidAccount
|
||||
}
|
||||
|
||||
let updatedRows: Int
|
||||
do {
|
||||
globalDBLock.lock()
|
||||
defer { globalDBLock.unlock() }
|
||||
|
||||
updatedRows = try dbProvider.connection().run(table.filter(TableColums.account == acc.account).update(acc))
|
||||
} catch {
|
||||
throw ZcashError.accountDAOUpdate(error)
|
||||
}
|
||||
|
||||
if updatedRows == 0 {
|
||||
logger.error("attempted to update pending transactions but no rows were updated")
|
||||
throw ZcashError.accountDAOUpdatedZeroRows
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CachingAccountDao: AccountRepository {
|
||||
let dao: AccountRepository
|
||||
lazy var cache: [Int: AccountEntity] = {
|
||||
var accountCache: [Int: AccountEntity] = [:]
|
||||
guard let all = try? dao.getAll() else {
|
||||
return accountCache
|
||||
}
|
||||
|
||||
for acc in all {
|
||||
accountCache[acc.account] = acc
|
||||
}
|
||||
|
||||
return accountCache
|
||||
}()
|
||||
|
||||
init(dao: AccountRepository) {
|
||||
self.dao = dao
|
||||
}
|
||||
|
||||
func getAll() throws -> [AccountEntity] {
|
||||
guard cache.isEmpty else {
|
||||
return cache.values.sorted(by: { $0.account < $1.account })
|
||||
}
|
||||
|
||||
let all = try dao.getAll()
|
||||
|
||||
for acc in all {
|
||||
cache[acc.account] = acc
|
||||
}
|
||||
|
||||
return all
|
||||
}
|
||||
|
||||
func findBy(account: Int) throws -> AccountEntity? {
|
||||
if let acc = cache[account] {
|
||||
return acc
|
||||
}
|
||||
|
||||
let acc = try dao.findBy(account: account)
|
||||
cache[account] = acc
|
||||
|
||||
return acc
|
||||
}
|
||||
|
||||
func update(_ account: AccountEntity) throws {
|
||||
try dao.update(account)
|
||||
}
|
||||
}
|
||||
|
||||
enum AccountRepositoryBuilder {
|
||||
static func build(dataDbURL: URL, readOnly: Bool = false, caching: Bool = false, logger: Logger) -> AccountRepository {
|
||||
if caching {
|
||||
return CachingAccountDao(
|
||||
dao: AccountSQDAO(
|
||||
dbProvider: SimpleConnectionProvider(path: dataDbURL.path, readonly: readOnly),
|
||||
logger: logger
|
||||
)
|
||||
)
|
||||
} else {
|
||||
return AccountSQDAO(
|
||||
dbProvider: SimpleConnectionProvider(path: dataDbURL.path, readonly: readOnly),
|
||||
logger: logger
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -325,6 +325,10 @@ public enum ZcashError: Equatable, Error {
|
|||
/// - `rustError` contains error generated by the rust layer.
|
||||
/// ZRUST0057
|
||||
case rustProposeTransferFromURI(_ rustError: String)
|
||||
/// Error from rust layer when calling ZcashRustBackend.
|
||||
/// - `rustError` contains error generated by the rust layer.
|
||||
/// ZRUST0058
|
||||
case rustListAccounts(_ rustError: String)
|
||||
/// SQLite query failed when fetching all accounts from the database.
|
||||
/// - `sqliteError` is error produced by SQLite library.
|
||||
/// ZADAO0001
|
||||
|
@ -681,6 +685,7 @@ public enum ZcashError: Equatable, Error {
|
|||
case .rustScanProgressOutOfRange: return "Rust layer's call ZcashRustBackend.getScanProgress returned values that after computation are outside of allowed range 0-100%."
|
||||
case .rustGetWalletSummary: return "Error from rust layer when calling ZcashRustBackend.getWalletSummary"
|
||||
case .rustProposeTransferFromURI: return "Error from rust layer when calling ZcashRustBackend."
|
||||
case .rustListAccounts: return "Error from rust layer when calling ZcashRustBackend."
|
||||
case .accountDAOGetAll: return "SQLite query failed when fetching all accounts from the database."
|
||||
case .accountDAOGetAllCantDecode: return "Fetched accounts from SQLite but can't decode them."
|
||||
case .accountDAOFindBy: return "SQLite query failed when seaching for accounts in the database."
|
||||
|
@ -856,6 +861,7 @@ public enum ZcashError: Equatable, Error {
|
|||
case .rustScanProgressOutOfRange: return .rustScanProgressOutOfRange
|
||||
case .rustGetWalletSummary: return .rustGetWalletSummary
|
||||
case .rustProposeTransferFromURI: return .rustProposeTransferFromURI
|
||||
case .rustListAccounts: return .rustListAccounts
|
||||
case .accountDAOGetAll: return .accountDAOGetAll
|
||||
case .accountDAOGetAllCantDecode: return .accountDAOGetAllCantDecode
|
||||
case .accountDAOFindBy: return .accountDAOFindBy
|
||||
|
|
|
@ -177,6 +177,8 @@ public enum ZcashErrorCode: String {
|
|||
case rustGetWalletSummary = "ZRUST0056"
|
||||
/// Error from rust layer when calling ZcashRustBackend.
|
||||
case rustProposeTransferFromURI = "ZRUST0057"
|
||||
/// Error from rust layer when calling ZcashRustBackend.
|
||||
case rustListAccounts = "ZRUST0058"
|
||||
/// SQLite query failed when fetching all accounts from the database.
|
||||
case accountDAOGetAll = "ZADAO0001"
|
||||
/// Fetched accounts from SQLite but can't decode them.
|
||||
|
|
|
@ -352,6 +352,10 @@ enum ZcashErrorDefinition {
|
|||
/// - `rustError` contains error generated by the rust layer.
|
||||
// sourcery: code="ZRUST0057"
|
||||
case rustProposeTransferFromURI(_ rustError: String)
|
||||
/// Error from rust layer when calling ZcashRustBackend.
|
||||
/// - `rustError` contains error generated by the rust layer.
|
||||
// sourcery: code="ZRUST0058"
|
||||
case rustListAccounts(_ rustError: String)
|
||||
|
||||
// MARK: - Account DAO
|
||||
|
||||
|
|
|
@ -120,7 +120,6 @@ public class Initializer {
|
|||
let saplingParamsSourceURL: SaplingParamsSourceURL
|
||||
var lightWalletService: LightWalletService
|
||||
let transactionRepository: TransactionRepository
|
||||
let accountRepository: AccountRepository
|
||||
let storage: CompactBlockRepository
|
||||
var blockDownloaderService: BlockDownloaderService
|
||||
let network: ZcashNetwork
|
||||
|
@ -272,12 +271,6 @@ public class Initializer {
|
|||
self.alias = alias
|
||||
self.lightWalletService = container.resolve(LightWalletService.self)
|
||||
self.transactionRepository = container.resolve(TransactionRepository.self)
|
||||
self.accountRepository = AccountRepositoryBuilder.build(
|
||||
dataDbURL: urls.dataDbURL,
|
||||
readOnly: true,
|
||||
caching: true,
|
||||
logger: container.resolve(Logger.self)
|
||||
)
|
||||
self.storage = container.resolve(CompactBlockRepository.self)
|
||||
self.blockDownloaderService = container.resolve(BlockDownloaderService.self)
|
||||
self.network = network
|
||||
|
@ -419,7 +412,7 @@ public class Initializer {
|
|||
self.walletBirthday = checkpoint.height
|
||||
|
||||
// If there are no accounts it must be created, the default amount of accounts is 1
|
||||
if let seed, try accountRepository.getAll().isEmpty {
|
||||
if let seed, try await rustBackend.listAccounts().isEmpty {
|
||||
var chainTip: UInt32?
|
||||
|
||||
if walletMode == .restoreWallet {
|
||||
|
|
|
@ -49,6 +49,31 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
|
|||
}
|
||||
}
|
||||
|
||||
func listAccounts() async throws -> [Int32] {
|
||||
globalDBLock.lock()
|
||||
let accountsPtr = zcashlc_list_accounts(
|
||||
dbData.0,
|
||||
dbData.1,
|
||||
networkType.networkId
|
||||
)
|
||||
globalDBLock.unlock()
|
||||
|
||||
guard let accountsPtr else {
|
||||
throw ZcashError.rustListAccounts(lastErrorMessage(fallback: "`listAccounts` failed with unknown error"))
|
||||
}
|
||||
|
||||
defer { zcashlc_free_accounts(accountsPtr) }
|
||||
|
||||
var accounts: [Int32] = []
|
||||
|
||||
for i in (0 ..< Int(accountsPtr.pointee.len)) {
|
||||
let account = accountsPtr.pointee.ptr.advanced(by: i).pointee
|
||||
accounts.append(Int32(account.account_index))
|
||||
}
|
||||
|
||||
return accounts
|
||||
}
|
||||
|
||||
func createAccount(seed: [UInt8], treeState: TreeState, recoverUntil: UInt32?) async throws -> UnifiedSpendingKey {
|
||||
var rUntil: Int64 = -1
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@ public enum DbInitResult {
|
|||
|
||||
// sourcery: mockActor
|
||||
protocol ZcashRustBackendWelding {
|
||||
/// Returns a list of the accounts in the wallet.
|
||||
func listAccounts() async throws -> [Int32]
|
||||
|
||||
/// Adds the next available account-level spend authority, given the current set of [ZIP 316]
|
||||
/// account identifiers known, to the wallet database.
|
||||
///
|
||||
|
|
|
@ -100,8 +100,7 @@ enum Dependencies {
|
|||
|
||||
static func setupCompactBlockProcessor(
|
||||
in container: DIContainer,
|
||||
config: CompactBlockProcessor.Configuration,
|
||||
accountRepository: AccountRepository
|
||||
config: CompactBlockProcessor.Configuration
|
||||
) {
|
||||
container.register(type: BlockDownloader.self, isSingleton: true) { di in
|
||||
let service = di.resolve(LightWalletService.self)
|
||||
|
@ -163,7 +162,6 @@ enum Dependencies {
|
|||
let logger = di.resolve(Logger.self)
|
||||
|
||||
return UTXOFetcherImpl(
|
||||
accountRepository: accountRepository,
|
||||
blockDownloaderService: blockDownloaderService,
|
||||
config: utxoFetcherConfig,
|
||||
rustBackend: rustBackend,
|
||||
|
|
|
@ -667,8 +667,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
// CompactBlockProcessor dependency update
|
||||
Dependencies.setupCompactBlockProcessor(
|
||||
in: initializer.container,
|
||||
config: await blockProcessor.config,
|
||||
accountRepository: initializer.accountRepository
|
||||
config: await blockProcessor.config
|
||||
)
|
||||
|
||||
// INITIALIZER
|
||||
|
|
|
@ -2213,6 +2213,37 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
|
|||
self.consensusBranchIdForHeightClosure = consensusBranchIdForHeightClosure
|
||||
}
|
||||
|
||||
// MARK: - listAccounts
|
||||
|
||||
var listAccountsThrowableError: Error?
|
||||
func setListAccountsThrowableError(_ param: Error?) async {
|
||||
listAccountsThrowableError = param
|
||||
}
|
||||
var listAccountsCallsCount = 0
|
||||
var listAccountsCalled: Bool {
|
||||
return listAccountsCallsCount > 0
|
||||
}
|
||||
var listAccountsReturnValue: [Int32]!
|
||||
func setListAccountsReturnValue(_ param: [Int32]) async {
|
||||
listAccountsReturnValue = param
|
||||
}
|
||||
var listAccountsClosure: (() async throws -> [Int32])?
|
||||
func setListAccountsClosure(_ param: (() async throws -> [Int32])?) async {
|
||||
listAccountsClosure = param
|
||||
}
|
||||
|
||||
func listAccounts() async throws -> [Int32] {
|
||||
if let error = listAccountsThrowableError {
|
||||
throw error
|
||||
}
|
||||
listAccountsCallsCount += 1
|
||||
if let closure = listAccountsClosure {
|
||||
return try await closure()
|
||||
} else {
|
||||
return listAccountsReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - createAccount
|
||||
|
||||
var createAccountSeedTreeStateRecoverUntilThrowableError: Error?
|
||||
|
|
Loading…
Reference in New Issue