Migrate to latest in-progress revision of Rust crates

- New backend method `ZcashRustBackend.isSeedRelevantToWallet`
- `ZcashRustBackend.scanBlocks` now takes a `fromState` argument.

Co-authored-by: Lukas Korba <lukas.korba@seznam.cz>
This commit is contained in:
Jack Grigg 2024-03-15 16:01:39 +00:00
parent 86defc8b4a
commit a5a0ef0ac1
17 changed files with 158 additions and 32 deletions

View File

@ -176,7 +176,7 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : {
"revision" : "4eccfb5ea825f5f0ebb3cab964d08ef21103feb4"
"revision" : "d1d038d653806f23fc291931823d95f2dc411486"
}
}
],

View File

@ -122,7 +122,7 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : {
"revision" : "4eccfb5ea825f5f0ebb3cab964d08ef21103feb4"
"revision" : "d1d038d653806f23fc291931823d95f2dc411486"
}
}
],

View File

@ -16,8 +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"),
// Compiled from revision `c2ac47300d062b76134c515589301b202277e3fa`.
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", revision: "4eccfb5ea825f5f0ebb3cab964d08ef21103feb4")
// Compiled from revision `70cce1272c26ed52fbe7bfa334be781373b64bfd`.
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", revision: "d1d038d653806f23fc291931823d95f2dc411486")
],
targets: [
.target(

View File

@ -23,6 +23,7 @@ protocol BlockScanner {
struct BlockScannerImpl {
let config: BlockScannerConfig
let rustBackend: ZcashRustBackendWelding
let service: LightWalletService
let transactionRepository: TransactionRepository
let metrics: SDKMetrics
let logger: Logger
@ -56,7 +57,9 @@ extension BlockScannerImpl: BlockScanner {
let scanSummary: ScanSummary
let scanStartTime = Date()
do {
scanSummary = try await self.rustBackend.scanBlocks(fromHeight: Int32(startHeight), limit: batchSize)
let fromState = try await service.getTreeState(BlockID(height: startHeight - 1))
scanSummary = try await self.rustBackend.scanBlocks(fromHeight: Int32(startHeight), fromState: fromState, limit: batchSize)
} catch {
logger.debug("block scanning failed with error: \(String(describing: error))")
throw error

View File

@ -101,8 +101,8 @@ extension ZcashTransaction.Output {
static let rawID = Expression<Blob>("txid")
static let pool = Expression<Int>("output_pool")
static let index = Expression<Int>("output_index")
static let toAccount = Expression<Int?>("to_account")
static let fromAccount = Expression<Int?>("from_account")
static let toAccount = Expression<Int?>("to_account_id")
static let fromAccount = Expression<Int?>("from_account_id")
static let toAddress = Expression<String?>("to_address")
static let value = Expression<Int64>("value")
static let isChange = Expression<Bool>("is_change")

View File

@ -329,6 +329,10 @@ public enum ZcashError: Equatable, Error {
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0058
case rustListAccounts(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.isSeedRelevantToWallet
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0059
case rustIsSeedRelevantToWallet(_ rustError: String)
/// SQLite query failed when fetching all accounts from the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZADAO0001
@ -686,6 +690,7 @@ public enum ZcashError: Equatable, Error {
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 .rustIsSeedRelevantToWallet: return "Error from rust layer when calling ZcashRustBackend.isSeedRelevantToWallet"
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."
@ -862,6 +867,7 @@ public enum ZcashError: Equatable, Error {
case .rustGetWalletSummary: return .rustGetWalletSummary
case .rustProposeTransferFromURI: return .rustProposeTransferFromURI
case .rustListAccounts: return .rustListAccounts
case .rustIsSeedRelevantToWallet: return .rustIsSeedRelevantToWallet
case .accountDAOGetAll: return .accountDAOGetAll
case .accountDAOGetAllCantDecode: return .accountDAOGetAllCantDecode
case .accountDAOFindBy: return .accountDAOFindBy

View File

@ -179,6 +179,8 @@ public enum ZcashErrorCode: String {
case rustProposeTransferFromURI = "ZRUST0057"
/// Error from rust layer when calling ZcashRustBackend.
case rustListAccounts = "ZRUST0058"
/// Error from rust layer when calling ZcashRustBackend.isSeedRelevantToWallet
case rustIsSeedRelevantToWallet = "ZRUST0059"
/// SQLite query failed when fetching all accounts from the database.
case accountDAOGetAll = "ZADAO0001"
/// Fetched accounts from SQLite but can't decode them.

View File

@ -356,6 +356,10 @@ enum ZcashErrorDefinition {
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0058"
case rustListAccounts(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.isSeedRelevantToWallet
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0059"
case rustIsSeedRelevantToWallet(_ rustError: String)
// MARK: - Account DAO

View File

@ -276,6 +276,10 @@ extension LightWalletGRPCService: LightWalletService {
}
}
func getTreeState(_ id: BlockID) async throws -> TreeState {
try await compactTxStreamer.getTreeState(id)
}
func closeConnection() {
_ = channel.close()
}

View File

@ -196,4 +196,6 @@ protocol LightWalletService: AnyObject {
/// - Parameters:
/// - request: Request to send to GetSubtreeRoots.
func getSubtreeRoots(_ request: GetSubtreeRootsArg) -> AsyncThrowingStream<SubtreeRoot, Error>
func getTreeState(_ id: BlockID) async throws -> TreeState
}

View File

@ -105,6 +105,26 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
return ffiBinaryKeyPtr.pointee.unsafeToUnifiedSpendingKey(network: networkType)
}
func isSeedRelevantToWallet(seed: [UInt8]) async throws -> Bool {
globalDBLock.lock()
let result = zcashlc_is_seed_relevant_to_wallet(
dbData.0,
dbData.1,
seed,
UInt(seed.count),
networkType.networkId
)
globalDBLock.unlock()
// -1 is the error sentinel.
guard result >= 0 else {
throw ZcashError.rustIsSeedRelevantToWallet(lastErrorMessage(fallback: "`isSeedRelevantToWallet` failed with unknown error"))
}
// 0 is false, 1 is true.
return result != 0
}
func proposeTransfer(
account: Int32,
to address: String,
@ -648,9 +668,20 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
return scanRanges
}
func scanBlocks(fromHeight: Int32, limit: UInt32 = 0) async throws -> ScanSummary {
func scanBlocks(fromHeight: Int32, fromState: TreeState, limit: UInt32 = 0) async throws -> ScanSummary {
let fromStateBytes = try fromState.serializedData(partial: false).bytes
globalDBLock.lock()
let summaryPtr = zcashlc_scan_blocks(fsBlockDbRoot.0, fsBlockDbRoot.1, dbData.0, dbData.1, fromHeight, limit, networkType.networkId)
let summaryPtr = zcashlc_scan_blocks(
fsBlockDbRoot.0,
fsBlockDbRoot.1,
dbData.0,
dbData.1,
fromHeight,
fromStateBytes,
UInt(fromStateBytes.count),
limit,
networkType.networkId)
globalDBLock.unlock()
guard let summaryPtr else {
@ -881,6 +912,7 @@ extension FfiScanProgress {
}
}
// swiftlint:disable large_tuple line_length
struct FfiTxId {
var tuple: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)
var array: [UInt8] {

View File

@ -44,6 +44,11 @@ protocol ZcashRustBackendWelding {
/// - Throws: `rustCreateAccount`.
func createAccount(seed: [UInt8], treeState: TreeState, recoverUntil: UInt32?) async throws -> UnifiedSpendingKey
/// Checks whether the given seed is relevant to any of the accounts in the wallet.
///
/// - parameter seed: byte array of the seed
func isSeedRelevantToWallet(seed: [UInt8]) async throws -> Bool
/// Scans a transaction for any information that can be decrypted by the accounts in the wallet, and saves it to the wallet.
/// - parameter tx: the transaction to decrypt
/// - parameter minedHeight: height on which this transaction was mined. this is used to fetch the consensus branch ID.
@ -176,9 +181,10 @@ protocol ZcashRustBackendWelding {
/// cache, an error will be signalled.
///
/// - parameter fromHeight: scan starting from the given height.
/// - parameter fromState: The TreeState Protobuf object for the height prior to `fromHeight`
/// - parameter limit: scan up to limit blocks.
/// - Throws: `rustScanBlocks` if rust layer returns error.
func scanBlocks(fromHeight: Int32, limit: UInt32) async throws -> ScanSummary
func scanBlocks(fromHeight: Int32, fromState: TreeState, limit: UInt32) async throws -> ScanSummary
/// Upserts a UTXO into the data db database
/// - parameter txid: the txid bytes for the UTXO

View File

@ -119,6 +119,7 @@ enum Dependencies {
}
container.register(type: BlockScanner.self, isSingleton: true) { di in
let service = di.resolve(LightWalletService.self)
let rustBackend = di.resolve(ZcashRustBackendWelding.self)
let transactionRepository = di.resolve(TransactionRepository.self)
let metrics = di.resolve(SDKMetrics.self)
@ -132,6 +133,7 @@ enum Dependencies {
return BlockScannerImpl(
config: blockScannerConfig,
rustBackend: rustBackend,
service: service,
transactionRepository: transactionRepository,
metrics: metrics,
logger: logger

View File

@ -190,6 +190,10 @@ class DarksideWalletService: LightWalletService {
func getSubtreeRoots(_ request: ZcashLightClientKit.GetSubtreeRootsArg) -> AsyncThrowingStream<ZcashLightClientKit.SubtreeRoot, Error> {
service.getSubtreeRoots(request)
}
func getTreeState(_ id: BlockID) async throws -> TreeState {
try await service.getTreeState(id)
}
}
enum DarksideWalletDConstants: NetworkConstants {

View File

@ -82,4 +82,8 @@ class MockLightWalletService: LightWalletService {
func getSubtreeRoots(_ request: ZcashLightClientKit.GetSubtreeRootsArg) -> AsyncThrowingStream<ZcashLightClientKit.SubtreeRoot, Error> {
service.getSubtreeRoots(request)
}
func getTreeState(_ id: BlockID) async throws -> TreeState {
try await service.getTreeState(id)
}
}

View File

@ -963,6 +963,30 @@ class LightWalletServiceMock: LightWalletService {
}
}
// MARK: - getTreeState
var getTreeStateThrowableError: Error?
var getTreeStateCallsCount = 0
var getTreeStateCalled: Bool {
return getTreeStateCallsCount > 0
}
var getTreeStateReceivedId: BlockID?
var getTreeStateReturnValue: TreeState!
var getTreeStateClosure: ((BlockID) async throws -> TreeState)?
func getTreeState(_ id: BlockID) async throws -> TreeState {
if let error = getTreeStateThrowableError {
throw error
}
getTreeStateCallsCount += 1
getTreeStateReceivedId = id
if let closure = getTreeStateClosure {
return try await closure(id)
} else {
return getTreeStateReturnValue
}
}
}
class LightWalletdInfoMock: LightWalletdInfo {
@ -2277,6 +2301,39 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
}
}
// MARK: - isSeedRelevantToWallet
var isSeedRelevantToWalletSeedThrowableError: Error?
func setIsSeedRelevantToWalletSeedThrowableError(_ param: Error?) async {
isSeedRelevantToWalletSeedThrowableError = param
}
var isSeedRelevantToWalletSeedCallsCount = 0
var isSeedRelevantToWalletSeedCalled: Bool {
return isSeedRelevantToWalletSeedCallsCount > 0
}
var isSeedRelevantToWalletSeedReceivedSeed: [UInt8]?
var isSeedRelevantToWalletSeedReturnValue: Bool!
func setIsSeedRelevantToWalletSeedReturnValue(_ param: Bool) async {
isSeedRelevantToWalletSeedReturnValue = param
}
var isSeedRelevantToWalletSeedClosure: (([UInt8]) async throws -> Bool)?
func setIsSeedRelevantToWalletSeedClosure(_ param: (([UInt8]) async throws -> Bool)?) async {
isSeedRelevantToWalletSeedClosure = param
}
func isSeedRelevantToWallet(seed: [UInt8]) async throws -> Bool {
if let error = isSeedRelevantToWalletSeedThrowableError {
throw error
}
isSeedRelevantToWalletSeedCallsCount += 1
isSeedRelevantToWalletSeedReceivedSeed = seed
if let closure = isSeedRelevantToWalletSeedClosure {
return try await closure(seed)
} else {
return isSeedRelevantToWalletSeedReturnValue
}
}
// MARK: - decryptAndStoreTransaction
var decryptAndStoreTransactionTxBytesMinedHeightThrowableError: Error?
@ -2792,34 +2849,34 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
// MARK: - scanBlocks
var scanBlocksFromHeightLimitThrowableError: Error?
func setScanBlocksFromHeightLimitThrowableError(_ param: Error?) async {
scanBlocksFromHeightLimitThrowableError = param
var scanBlocksFromHeightFromStateLimitThrowableError: Error?
func setScanBlocksFromHeightFromStateLimitThrowableError(_ param: Error?) async {
scanBlocksFromHeightFromStateLimitThrowableError = param
}
var scanBlocksFromHeightLimitCallsCount = 0
var scanBlocksFromHeightLimitCalled: Bool {
return scanBlocksFromHeightLimitCallsCount > 0
var scanBlocksFromHeightFromStateLimitCallsCount = 0
var scanBlocksFromHeightFromStateLimitCalled: Bool {
return scanBlocksFromHeightFromStateLimitCallsCount > 0
}
var scanBlocksFromHeightLimitReceivedArguments: (fromHeight: Int32, limit: UInt32)?
var scanBlocksFromHeightLimitReturnValue: ScanSummary!
func setScanBlocksFromHeightLimitReturnValue(_ param: ScanSummary) async {
scanBlocksFromHeightLimitReturnValue = param
var scanBlocksFromHeightFromStateLimitReceivedArguments: (fromHeight: Int32, fromState: TreeState, limit: UInt32)?
var scanBlocksFromHeightFromStateLimitReturnValue: ScanSummary!
func setScanBlocksFromHeightFromStateLimitReturnValue(_ param: ScanSummary) async {
scanBlocksFromHeightFromStateLimitReturnValue = param
}
var scanBlocksFromHeightLimitClosure: ((Int32, UInt32) async throws -> ScanSummary)?
func setScanBlocksFromHeightLimitClosure(_ param: ((Int32, UInt32) async throws -> ScanSummary)?) async {
scanBlocksFromHeightLimitClosure = param
var scanBlocksFromHeightFromStateLimitClosure: ((Int32, TreeState, UInt32) async throws -> ScanSummary)?
func setScanBlocksFromHeightFromStateLimitClosure(_ param: ((Int32, TreeState, UInt32) async throws -> ScanSummary)?) async {
scanBlocksFromHeightFromStateLimitClosure = param
}
func scanBlocks(fromHeight: Int32, limit: UInt32) async throws -> ScanSummary {
if let error = scanBlocksFromHeightLimitThrowableError {
func scanBlocks(fromHeight: Int32, fromState: TreeState, limit: UInt32) async throws -> ScanSummary {
if let error = scanBlocksFromHeightFromStateLimitThrowableError {
throw error
}
scanBlocksFromHeightLimitCallsCount += 1
scanBlocksFromHeightLimitReceivedArguments = (fromHeight: fromHeight, limit: limit)
if let closure = scanBlocksFromHeightLimitClosure {
return try await closure(fromHeight, limit)
scanBlocksFromHeightFromStateLimitCallsCount += 1
scanBlocksFromHeightFromStateLimitReceivedArguments = (fromHeight: fromHeight, fromState: fromState, limit: limit)
if let closure = scanBlocksFromHeightFromStateLimitClosure {
return try await closure(fromHeight, fromState, limit)
} else {
return scanBlocksFromHeightLimitReturnValue
return scanBlocksFromHeightFromStateLimitReturnValue
}
}

View File

@ -102,8 +102,8 @@ class RustBackendMockHelper {
try await rustBackend.suggestScanRanges()
}
await rustBackendMock.setScanBlocksFromHeightLimitClosure() { fromHeight, limit in
try await rustBackend.scanBlocks(fromHeight: fromHeight, limit: limit)
await rustBackendMock.setScanBlocksFromHeightFromStateLimitClosure { fromHeight, fromState, limit in
try await rustBackend.scanBlocks(fromHeight: fromHeight, fromState: fromState, limit: limit)
}
}