Compare commits
5 Commits
d8f389b8da
...
8fa86ff37f
Author | SHA1 | Date |
---|---|---|
Lukas Korba | 8fa86ff37f | |
Lukas Korba | f41ac27eb8 | |
Lukas Korba | 4a16957795 | |
Lukas Korba | bdfe0b6823 | |
Jack Grigg | 006861595d |
|
@ -6,6 +6,11 @@ and this library adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
# Unreleased
|
||||
|
||||
# 2.1.3 - 2024-03-28
|
||||
|
||||
## Fixed
|
||||
- Orchard subtree roots are now fetched alongside Sapling subtree roots.
|
||||
|
||||
# 2.1.2 - 2024-03-27
|
||||
|
||||
## Fixed
|
||||
|
|
|
@ -176,8 +176,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||
"state" : {
|
||||
"revision" : "8838b4f0ee4193349fed09f0248220d4ada271fc",
|
||||
"version" : "0.7.3"
|
||||
"revision" : "e2d8763f3a963fb0026b6160af2d211b527453cd",
|
||||
"version" : "0.7.4"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -122,8 +122,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||
"state" : {
|
||||
"revision" : "c7e5158edf5e62af15492d30237163b78af35ce9",
|
||||
"version" : "0.7.1"
|
||||
"revision" : "e2d8763f3a963fb0026b6160af2d211b527453cd",
|
||||
"version" : "0.7.4"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -16,7 +16,7 @@ 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.7.3")
|
||||
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", exact: "0.7.4")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
|
|
@ -31,19 +31,19 @@ extension UpdateSubtreeRootsAction: Action {
|
|||
logger.debug("Attempt to get subtree roots, this may fail because lightwalletd may not support Spend before Sync.")
|
||||
let stream = service.getSubtreeRoots(request)
|
||||
|
||||
var roots: [SubtreeRoot] = []
|
||||
var saplingRoots: [SubtreeRoot] = []
|
||||
|
||||
do {
|
||||
for try await subtreeRoot in stream {
|
||||
roots.append(subtreeRoot)
|
||||
saplingRoots.append(subtreeRoot)
|
||||
}
|
||||
} catch ZcashError.serviceSubtreeRootsStreamFailed(LightWalletServiceError.timeOut) {
|
||||
throw ZcashError.serviceSubtreeRootsStreamFailed(LightWalletServiceError.timeOut)
|
||||
}
|
||||
|
||||
logger.debug("Sapling tree has \(roots.count) subtrees")
|
||||
logger.debug("Sapling tree has \(saplingRoots.count) subtrees")
|
||||
do {
|
||||
try await rustBackend.putSaplingSubtreeRoots(startIndex: UInt64(request.startIndex), roots: roots)
|
||||
try await rustBackend.putSaplingSubtreeRoots(startIndex: UInt64(request.startIndex), roots: saplingRoots)
|
||||
|
||||
await context.update(state: .updateChainTip)
|
||||
} catch {
|
||||
|
@ -51,6 +51,35 @@ extension UpdateSubtreeRootsAction: Action {
|
|||
throw ZcashError.compactBlockProcessorPutSaplingSubtreeRoots(error)
|
||||
}
|
||||
|
||||
if !saplingRoots.isEmpty {
|
||||
logger.debug("Found Sapling subtree roots, SbS supported, fetching Orchard subtree roots")
|
||||
|
||||
var orchardRequest = GetSubtreeRootsArg()
|
||||
orchardRequest.shieldedProtocol = .orchard
|
||||
|
||||
let stream = service.getSubtreeRoots(orchardRequest)
|
||||
|
||||
var orchardRoots: [SubtreeRoot] = []
|
||||
|
||||
do {
|
||||
for try await subtreeRoot in stream {
|
||||
orchardRoots.append(subtreeRoot)
|
||||
}
|
||||
} catch ZcashError.serviceSubtreeRootsStreamFailed(LightWalletServiceError.timeOut) {
|
||||
throw ZcashError.serviceSubtreeRootsStreamFailed(LightWalletServiceError.timeOut)
|
||||
}
|
||||
|
||||
logger.debug("Orchard tree has \(orchardRoots.count) subtrees")
|
||||
do {
|
||||
try await rustBackend.putOrchardSubtreeRoots(startIndex: UInt64(orchardRequest.startIndex), roots: orchardRoots)
|
||||
|
||||
await context.update(state: .updateChainTip)
|
||||
} catch {
|
||||
logger.debug("putOrchardSubtreeRoots failed with error \(error.localizedDescription)")
|
||||
throw ZcashError.compactBlockProcessorPutOrchardSubtreeRoots(error)
|
||||
}
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
|
|
|
@ -333,6 +333,15 @@ public enum ZcashError: Equatable, Error {
|
|||
/// - `rustError` contains error generated by the rust layer.
|
||||
/// ZRUST0059
|
||||
case rustIsSeedRelevantToAnyDerivedAccount(_ rustError: String)
|
||||
/// Unable to allocate memory required to write blocks when calling ZcashRustBackend.putOrchardSubtreeRoots
|
||||
/// sourcery: code="ZRUST0060"
|
||||
/// ZRUST0060
|
||||
case rustPutOrchardSubtreeRootsAllocationProblem
|
||||
/// Error from rust layer when calling ZcashRustBackend.putOrchardSubtreeRoots
|
||||
/// - `rustError` contains error generated by the rust layer.
|
||||
/// sourcery: code="ZRUST0061"
|
||||
/// ZRUST0061
|
||||
case rustPutOrchardSubtreeRoots(_ rustError: String)
|
||||
/// SQLite query failed when fetching all accounts from the database.
|
||||
/// - `sqliteError` is error produced by SQLite library.
|
||||
/// ZADAO0001
|
||||
|
@ -578,6 +587,9 @@ public enum ZcashError: Equatable, Error {
|
|||
/// Getting the `supportedSyncAlgorithm` failed but it's supposed to always provide some value.
|
||||
/// ZCBPEO0021
|
||||
case compactBlockProcessorSupportedSyncAlgorithm
|
||||
/// Put Orchard subtree roots to the DB failed.
|
||||
/// ZCBPEO0022
|
||||
case compactBlockProcessorPutOrchardSubtreeRoots(_ error: Error)
|
||||
/// The synchronizer is unprepared.
|
||||
/// ZSYNCO0001
|
||||
case synchronizerNotPrepared
|
||||
|
@ -691,6 +703,8 @@ public enum ZcashError: Equatable, Error {
|
|||
case .rustProposeTransferFromURI: return "Error from rust layer when calling ZcashRustBackend."
|
||||
case .rustListAccounts: return "Error from rust layer when calling ZcashRustBackend."
|
||||
case .rustIsSeedRelevantToAnyDerivedAccount: return "Error from rust layer when calling ZcashRustBackend.rustIsSeedRelevantToAnyDerivedAccount"
|
||||
case .rustPutOrchardSubtreeRootsAllocationProblem: return "Unable to allocate memory required to write blocks when calling ZcashRustBackend.putOrchardSubtreeRoots"
|
||||
case .rustPutOrchardSubtreeRoots: return "Error from rust layer when calling ZcashRustBackend.putOrchardSubtreeRoots"
|
||||
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."
|
||||
|
@ -769,6 +783,7 @@ public enum ZcashError: Equatable, Error {
|
|||
case .compactBlockProcessorPutSaplingSubtreeRoots: return "Put sapling subtree roots to the DB failed."
|
||||
case .compactBlockProcessorLastScannedHeight: return "Getting the `lastScannedHeight` failed but it's supposed to always provide some value."
|
||||
case .compactBlockProcessorSupportedSyncAlgorithm: return "Getting the `supportedSyncAlgorithm` failed but it's supposed to always provide some value."
|
||||
case .compactBlockProcessorPutOrchardSubtreeRoots: return "Put Orchard subtree roots to the DB failed."
|
||||
case .synchronizerNotPrepared: return "The synchronizer is unprepared."
|
||||
case .synchronizerSendMemoToTransparentAddress: return "Memos can't be sent to transparent addresses."
|
||||
case .synchronizerShieldFundsInsuficientTransparentFunds: return "There is not enough transparent funds to cover fee for the shielding."
|
||||
|
@ -868,6 +883,8 @@ public enum ZcashError: Equatable, Error {
|
|||
case .rustProposeTransferFromURI: return .rustProposeTransferFromURI
|
||||
case .rustListAccounts: return .rustListAccounts
|
||||
case .rustIsSeedRelevantToAnyDerivedAccount: return .rustIsSeedRelevantToAnyDerivedAccount
|
||||
case .rustPutOrchardSubtreeRootsAllocationProblem: return .rustPutOrchardSubtreeRootsAllocationProblem
|
||||
case .rustPutOrchardSubtreeRoots: return .rustPutOrchardSubtreeRoots
|
||||
case .accountDAOGetAll: return .accountDAOGetAll
|
||||
case .accountDAOGetAllCantDecode: return .accountDAOGetAllCantDecode
|
||||
case .accountDAOFindBy: return .accountDAOFindBy
|
||||
|
@ -946,6 +963,7 @@ public enum ZcashError: Equatable, Error {
|
|||
case .compactBlockProcessorPutSaplingSubtreeRoots: return .compactBlockProcessorPutSaplingSubtreeRoots
|
||||
case .compactBlockProcessorLastScannedHeight: return .compactBlockProcessorLastScannedHeight
|
||||
case .compactBlockProcessorSupportedSyncAlgorithm: return .compactBlockProcessorSupportedSyncAlgorithm
|
||||
case .compactBlockProcessorPutOrchardSubtreeRoots: return .compactBlockProcessorPutOrchardSubtreeRoots
|
||||
case .synchronizerNotPrepared: return .synchronizerNotPrepared
|
||||
case .synchronizerSendMemoToTransparentAddress: return .synchronizerSendMemoToTransparentAddress
|
||||
case .synchronizerShieldFundsInsuficientTransparentFunds: return .synchronizerShieldFundsInsuficientTransparentFunds
|
||||
|
|
|
@ -181,6 +181,10 @@ public enum ZcashErrorCode: String {
|
|||
case rustListAccounts = "ZRUST0058"
|
||||
/// Error from rust layer when calling ZcashRustBackend.rustIsSeedRelevantToAnyDerivedAccount
|
||||
case rustIsSeedRelevantToAnyDerivedAccount = "ZRUST0059"
|
||||
/// Unable to allocate memory required to write blocks when calling ZcashRustBackend.putOrchardSubtreeRoots
|
||||
case rustPutOrchardSubtreeRootsAllocationProblem = "ZRUST0060"
|
||||
/// Error from rust layer when calling ZcashRustBackend.putOrchardSubtreeRoots
|
||||
case rustPutOrchardSubtreeRoots = "ZRUST0061"
|
||||
/// SQLite query failed when fetching all accounts from the database.
|
||||
case accountDAOGetAll = "ZADAO0001"
|
||||
/// Fetched accounts from SQLite but can't decode them.
|
||||
|
@ -337,6 +341,8 @@ public enum ZcashErrorCode: String {
|
|||
case compactBlockProcessorLastScannedHeight = "ZCBPEO0020"
|
||||
/// Getting the `supportedSyncAlgorithm` failed but it's supposed to always provide some value.
|
||||
case compactBlockProcessorSupportedSyncAlgorithm = "ZCBPEO0021"
|
||||
/// Put Orchard subtree roots to the DB failed.
|
||||
case compactBlockProcessorPutOrchardSubtreeRoots = "ZCBPEO0022"
|
||||
/// The synchronizer is unprepared.
|
||||
case synchronizerNotPrepared = "ZSYNCO0001"
|
||||
/// Memos can't be sent to transparent addresses.
|
||||
|
|
|
@ -360,6 +360,13 @@ enum ZcashErrorDefinition {
|
|||
/// - `rustError` contains error generated by the rust layer.
|
||||
// sourcery: code="ZRUST0059"
|
||||
case rustIsSeedRelevantToAnyDerivedAccount(_ rustError: String)
|
||||
/// Unable to allocate memory required to write blocks when calling ZcashRustBackend.putOrchardSubtreeRoots
|
||||
/// sourcery: code="ZRUST0060"
|
||||
case rustPutOrchardSubtreeRootsAllocationProblem
|
||||
/// Error from rust layer when calling ZcashRustBackend.putOrchardSubtreeRoots
|
||||
/// - `rustError` contains error generated by the rust layer.
|
||||
/// sourcery: code="ZRUST0061"
|
||||
case rustPutOrchardSubtreeRoots(_ rustError: String)
|
||||
|
||||
// MARK: - Account DAO
|
||||
|
||||
|
@ -654,6 +661,9 @@ enum ZcashErrorDefinition {
|
|||
/// Getting the `supportedSyncAlgorithm` failed but it's supposed to always provide some value.
|
||||
// sourcery: code="ZCBPEO0021"
|
||||
case compactBlockProcessorSupportedSyncAlgorithm
|
||||
/// Put Orchard subtree roots to the DB failed.
|
||||
// sourcery: code="ZCBPEO0022"
|
||||
case compactBlockProcessorPutOrchardSubtreeRoots(_ error: Error)
|
||||
|
||||
// MARK: - SDKSynchronizer
|
||||
|
||||
|
|
|
@ -571,6 +571,64 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
|
|||
}
|
||||
}
|
||||
|
||||
func putOrchardSubtreeRoots(startIndex: UInt64, roots: [SubtreeRoot]) async throws {
|
||||
var ffiSubtreeRootsVec: [FfiSubtreeRoot] = []
|
||||
|
||||
for root in roots {
|
||||
let hashPtr = UnsafeMutablePointer<UInt8>.allocate(capacity: root.rootHash.count)
|
||||
|
||||
let contiguousHashBytes = ContiguousArray(root.rootHash.bytes)
|
||||
|
||||
let result: Void? = contiguousHashBytes.withContiguousStorageIfAvailable { hashBytesPtr in
|
||||
// swiftlint:disable:next force_unwrapping
|
||||
hashPtr.initialize(from: hashBytesPtr.baseAddress!, count: hashBytesPtr.count)
|
||||
}
|
||||
|
||||
guard result != nil else {
|
||||
defer {
|
||||
hashPtr.deallocate()
|
||||
ffiSubtreeRootsVec.deallocateElements()
|
||||
}
|
||||
throw ZcashError.rustPutOrchardSubtreeRootsAllocationProblem
|
||||
}
|
||||
|
||||
ffiSubtreeRootsVec.append(
|
||||
FfiSubtreeRoot(
|
||||
root_hash_ptr: hashPtr,
|
||||
root_hash_ptr_len: UInt(contiguousHashBytes.count),
|
||||
completing_block_height: UInt32(root.completingBlockHeight)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
var contiguousFfiRoots = ContiguousArray(ffiSubtreeRootsVec)
|
||||
|
||||
let len = UInt(contiguousFfiRoots.count)
|
||||
|
||||
let rootsPtr = UnsafeMutablePointer<FfiSubtreeRoots>.allocate(capacity: 1)
|
||||
|
||||
defer {
|
||||
ffiSubtreeRootsVec.deallocateElements()
|
||||
rootsPtr.deallocate()
|
||||
}
|
||||
|
||||
try contiguousFfiRoots.withContiguousMutableStorageIfAvailable { ptr in
|
||||
var roots = FfiSubtreeRoots()
|
||||
roots.ptr = ptr.baseAddress
|
||||
roots.len = len
|
||||
|
||||
rootsPtr.initialize(to: roots)
|
||||
|
||||
globalDBLock.lock()
|
||||
let res = zcashlc_put_orchard_subtree_roots(dbData.0, dbData.1, startIndex, rootsPtr, networkType.networkId)
|
||||
globalDBLock.unlock()
|
||||
|
||||
guard res else {
|
||||
throw ZcashError.rustPutOrchardSubtreeRoots(lastErrorMessage(fallback: "`putOrchardSubtreeRoots` failed with unknown error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateChainTip(height: Int32) async throws {
|
||||
globalDBLock.lock()
|
||||
let result = zcashlc_update_chain_tip(dbData.0, dbData.1, height, networkType.networkId)
|
||||
|
|
|
@ -133,6 +133,8 @@ protocol ZcashRustBackendWelding {
|
|||
|
||||
func putSaplingSubtreeRoots(startIndex: UInt64, roots: [SubtreeRoot]) async throws
|
||||
|
||||
func putOrchardSubtreeRoots(startIndex: UInt64, roots: [SubtreeRoot]) async throws
|
||||
|
||||
/// Updates the wallet's view of the blockchain.
|
||||
///
|
||||
/// This method is used to provide the wallet with information about the state of the blockchain,
|
||||
|
|
|
@ -70,6 +70,7 @@ final class UpdateSubtreeRootsActionTests: ZcashTestCase {
|
|||
}
|
||||
}
|
||||
await tupple.rustBackendMock.setPutSaplingSubtreeRootsStartIndexRootsClosure({ _, _ in })
|
||||
await tupple.rustBackendMock.setPutOrchardSubtreeRootsStartIndexRootsClosure({ _, _ in })
|
||||
|
||||
do {
|
||||
let context = ActionContextMock.default()
|
||||
|
@ -77,13 +78,13 @@ final class UpdateSubtreeRootsActionTests: ZcashTestCase {
|
|||
let nextContext = try await updateSubtreeRootsActionAction.run(with: context) { _ in }
|
||||
|
||||
let acResult = nextContext.checkStateIs(.updateChainTip)
|
||||
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||
XCTAssertTrue(acResult == .called(2), "Check of state failed with '\(acResult)'")
|
||||
} catch {
|
||||
XCTFail("testUpdateSubtreeRootsAction_RootsAvailablePutRootsSuccess is not expected to fail. \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func testUpdateSubtreeRootsAction_RootsAvailablePutRootsFailure() async throws {
|
||||
func testUpdateSubtreeRootsAction_RootsAvailablePutSaplingRootsFailure() async throws {
|
||||
let loggerMock = LoggerMock()
|
||||
|
||||
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||
|
@ -98,6 +99,7 @@ final class UpdateSubtreeRootsActionTests: ZcashTestCase {
|
|||
}
|
||||
}
|
||||
await tupple.rustBackendMock.setPutSaplingSubtreeRootsStartIndexRootsThrowableError("putSaplingFailed")
|
||||
await tupple.rustBackendMock.setPutOrchardSubtreeRootsStartIndexRootsClosure({ _, _ in })
|
||||
|
||||
do {
|
||||
let context = ActionContextMock.default()
|
||||
|
@ -112,6 +114,36 @@ final class UpdateSubtreeRootsActionTests: ZcashTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
func testUpdateSubtreeRootsAction_RootsAvailablePutOrchardRootsFailure() async throws {
|
||||
let loggerMock = LoggerMock()
|
||||
|
||||
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
|
||||
let tupple = setupAction(loggerMock)
|
||||
let updateSubtreeRootsActionAction = tupple.action
|
||||
tupple.serviceMock.getSubtreeRootsClosure = { _ in
|
||||
AsyncThrowingStream { continuation in
|
||||
continuation.yield(SubtreeRoot())
|
||||
continuation.finish()
|
||||
}
|
||||
}
|
||||
await tupple.rustBackendMock.setPutSaplingSubtreeRootsStartIndexRootsClosure({ _, _ in })
|
||||
await tupple.rustBackendMock.setPutOrchardSubtreeRootsStartIndexRootsThrowableError("putOrchardFailed")
|
||||
|
||||
do {
|
||||
let context = ActionContextMock.default()
|
||||
|
||||
_ = try await updateSubtreeRootsActionAction.run(with: context) { _ in }
|
||||
|
||||
XCTFail("updateSubtreeRootsActionAction.run(with:) is excpected to fail but didn't.")
|
||||
} catch ZcashError.compactBlockProcessorPutOrchardSubtreeRoots {
|
||||
// this is expected result of this test
|
||||
} catch {
|
||||
XCTFail("testUpdateSubtreeRootsAction_RootsAvailablePutRootsFailure is not expected to fail. \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
// swiftlint:disable large_tuple
|
||||
private func setupAction(
|
||||
_ loggerMock: LoggerMock = LoggerMock()
|
||||
|
|
|
@ -2722,6 +2722,31 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
|
|||
try await putSaplingSubtreeRootsStartIndexRootsClosure!(startIndex, roots)
|
||||
}
|
||||
|
||||
// MARK: - putOrchardSubtreeRoots
|
||||
|
||||
var putOrchardSubtreeRootsStartIndexRootsThrowableError: Error?
|
||||
func setPutOrchardSubtreeRootsStartIndexRootsThrowableError(_ param: Error?) async {
|
||||
putOrchardSubtreeRootsStartIndexRootsThrowableError = param
|
||||
}
|
||||
var putOrchardSubtreeRootsStartIndexRootsCallsCount = 0
|
||||
var putOrchardSubtreeRootsStartIndexRootsCalled: Bool {
|
||||
return putOrchardSubtreeRootsStartIndexRootsCallsCount > 0
|
||||
}
|
||||
var putOrchardSubtreeRootsStartIndexRootsReceivedArguments: (startIndex: UInt64, roots: [SubtreeRoot])?
|
||||
var putOrchardSubtreeRootsStartIndexRootsClosure: ((UInt64, [SubtreeRoot]) async throws -> Void)?
|
||||
func setPutOrchardSubtreeRootsStartIndexRootsClosure(_ param: ((UInt64, [SubtreeRoot]) async throws -> Void)?) async {
|
||||
putOrchardSubtreeRootsStartIndexRootsClosure = param
|
||||
}
|
||||
|
||||
func putOrchardSubtreeRoots(startIndex: UInt64, roots: [SubtreeRoot]) async throws {
|
||||
if let error = putOrchardSubtreeRootsStartIndexRootsThrowableError {
|
||||
throw error
|
||||
}
|
||||
putOrchardSubtreeRootsStartIndexRootsCallsCount += 1
|
||||
putOrchardSubtreeRootsStartIndexRootsReceivedArguments = (startIndex: startIndex, roots: roots)
|
||||
try await putOrchardSubtreeRootsStartIndexRootsClosure!(startIndex, roots)
|
||||
}
|
||||
|
||||
// MARK: - updateChainTip
|
||||
|
||||
var updateChainTipHeightThrowableError: Error?
|
||||
|
|
Loading…
Reference in New Issue