Skip to content

Commit

Permalink
Migrate to latest in-progress revision of Rust crates
Browse files Browse the repository at this point in the history
- New backend method `ZcashRustBackend.isSeedRelevantToWallet`
- `ZcashRustBackend.scanBlocks` now takes a `fromState` argument.

Co-authored-by: Lukas Korba <[email protected]>
  • Loading branch information
str4d and LukasKorba committed Mar 15, 2024
1 parent 6c9b7a9 commit c7a3e6f
Show file tree
Hide file tree
Showing 16 changed files with 157 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,7 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : {
"revision" : "7c801be1f445402a433b32835a50d832e8a50437",
"version" : "0.6.0"
"revision" : "e4ee7f62741fa04008c8c9b7769bd58764b1a5ce"
}
}
],
Expand Down
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 `533a7d4def586d0eda2993ad3ac4396452727158`.
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", revision: "e4ee7f62741fa04008c8c9b7769bd58764b1a5ce")
],
targets: [
.target(
Expand Down
5 changes: 4 additions & 1 deletion Sources/ZcashLightClientKit/Block/Scan/BlockScanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions Sources/ZcashLightClientKit/Entity/TransactionEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
6 changes: 6 additions & 0 deletions Sources/ZcashLightClientKit/Error/ZcashError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,10 @@ public enum ZcashError: Equatable, Error {
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0056
case rustGetWalletSummary(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.isSeedRelevantToWallet
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0057
case rustIsSeedRelevantToWallet(_ rustError: String)
/// SQLite query failed when fetching all accounts from the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZADAO0001
Expand Down Expand Up @@ -676,6 +680,7 @@ public enum ZcashError: Equatable, Error {
case .rustLatestCachedBlockHeight: return "Error from rust layer when calling ZcashRustBackend.latestCachedBlockHeight"
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 .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."
Expand Down Expand Up @@ -850,6 +855,7 @@ public enum ZcashError: Equatable, Error {
case .rustLatestCachedBlockHeight: return .rustLatestCachedBlockHeight
case .rustScanProgressOutOfRange: return .rustScanProgressOutOfRange
case .rustGetWalletSummary: return .rustGetWalletSummary
case .rustIsSeedRelevantToWallet: return .rustIsSeedRelevantToWallet
case .accountDAOGetAll: return .accountDAOGetAll
case .accountDAOGetAllCantDecode: return .accountDAOGetAllCantDecode
case .accountDAOFindBy: return .accountDAOFindBy
Expand Down
2 changes: 2 additions & 0 deletions Sources/ZcashLightClientKit/Error/ZcashErrorCode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ public enum ZcashErrorCode: String {
case rustScanProgressOutOfRange = "ZRUST0055"
/// Error from rust layer when calling ZcashRustBackend.getWalletSummary
case rustGetWalletSummary = "ZRUST0056"
/// Error from rust layer when calling ZcashRustBackend.isSeedRelevantToWallet
case rustIsSeedRelevantToWallet = "ZRUST0057"
/// SQLite query failed when fetching all accounts from the database.
case accountDAOGetAll = "ZADAO0001"
/// Fetched accounts from SQLite but can't decode them.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ enum ZcashErrorDefinition {
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0056"
case rustGetWalletSummary(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.isSeedRelevantToWallet
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0057"
case rustIsSeedRelevantToWallet(_ rustError: String)

// MARK: - Account DAO

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,10 @@ extension LightWalletGRPCService: LightWalletService {
}
}

func getTreeState(_ id: BlockID) async throws -> TreeState {
try await compactTxStreamer.getTreeState(id)
}

func closeConnection() {
_ = channel.close()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
36 changes: 34 additions & 2 deletions Sources/ZcashLightClientKit/Rust/ZcashRustBackend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,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,
Expand Down Expand Up @@ -595,9 +615,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 {
Expand Down Expand Up @@ -828,6 +859,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] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,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.
Expand Down Expand Up @@ -173,9 +178,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
Expand Down
2 changes: 2 additions & 0 deletions Sources/ZcashLightClientKit/Synchronizer/Dependencies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,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)
Expand All @@ -133,6 +134,7 @@ enum Dependencies {
return BlockScannerImpl(
config: blockScannerConfig,
rustBackend: rustBackend,
service: service,
transactionRepository: transactionRepository,
metrics: metrics,
logger: logger
Expand Down
4 changes: 4 additions & 0 deletions Tests/TestUtils/DarkSideWalletService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions Tests/TestUtils/FakeService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -2222,6 +2246,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?
Expand Down Expand Up @@ -2737,34 +2794,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
}
}

Expand Down
4 changes: 2 additions & 2 deletions Tests/TestUtils/Stubs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down

0 comments on commit c7a3e6f

Please sign in to comment.