[#209] Add support for multiple instances of the SDKSynchronizer
Closes #209. [#845] Introduce ZcashSynchronizerAlias enum Closes #845. [#852] SDKSynchronizer using queues label based on the alias Closes #852. [#847] Remove posibility to use DatabaseStorageManager as singleton Closes #847. [#850] Remove synchronizerConnectionStateChanged notification Closes #850. [#855] Add check if the Alias is already used Closes #855 - Added `UsedAliasesChecker` utility which is used to register aliases that are in use. - `prepare()` and `wipe()` methods now check if the current alias can't be used and if not then `InitializerError.aliasAlreadyInUse` is thrown/emitted. - Some public methods that could cause harm if used with the Alias that is already in use now throw `SynchronizerError.notPrepared`. Thanks to this the client app is forced to call `prepare()` first. And `prepare()` does check for the Alias. - Added tests for new conditions. [#849] Make InternalSyncProgress aware of the Alias Closes #849. [#853] Only instance with default Alias migrates legacy cache DB Closes #853. [#851] Apply the Alias to the URLs Closes #851. - `Initializer` now updates paths according to alias before paths are used anywhere in the SDK. - Paths update can fail. It would be incovenient for the client apps to handle errors thrown from `Initiliazer` constructor. So the error is then handled in `SDKSynchronizer.prepare()` or `SDKSynchronizer.wipe()`. [#846] Stop using SDKMetrics as singleton (#862) - metrics are not longer a singleton - tests fixed - metrics outside init of the synchronizer [#848] Make logger aware of the alias - logger is now an instance passed throughout the sdk instead of a static proxy [#848] Make logger aware of the alias (#868) - comments addressed [#848] Make logger aware of the alias (#868) - returning protocol back Fix typos [#856] Add possibility to test multiple synchronizers in the sample app Closes #856. - Added `alias` property to `Synchronizer`. - Added `SyncBlocksListViewController` which provides UI to use multiple synchronizers at once. [#209] Add changelog - Add changelog for #209. - Overall improve readability of the rendered changelog. Tickets references are now prefixed with `###` instead of `- `. Fix compilation
This commit is contained in:
parent
2ff6b81b4b
commit
5c979f42e6
273
CHANGELOG.md
273
CHANGELOG.md
|
@ -1,7 +1,30 @@
|
|||
# unreleased
|
||||
- [#831] Add support for alternative APIs
|
||||
### [#209] Support Initializer Aliases
|
||||
|
||||
There are two new protocols (`ClosureSynchronizer` and `CombineSynchronizer`). And there are two new
|
||||
Added `ZcashSynchronizerAlias` enum which is used to identify an instance of the `SDKSynchronizer`. All the paths
|
||||
to all resources (databases, filesystem block storage...) are updated automatically inside the SDK according to the
|
||||
alias. So it's safe to create multiple instances of the `SDKSynchronizer`. Each instance must have unique Alias. If
|
||||
the `default` alias is used then the SDK works the same as before this change was introduced.
|
||||
|
||||
The SDK now also checks which aliases are used and it prevents situations when two instances of the `SDKSynchronizer`
|
||||
has the same alias. Methods `prepare()` and `wipe()` do checks for used alias. And those methods fail
|
||||
with `InitializerError.aliasAlreadyInUse` if the alias is already used.
|
||||
|
||||
If the alias check fails in the `prepare()` method then the status of the `SDKSynchronizer` isn't switched from `unprepared`.
|
||||
These methods newly throw `SynchronizerError.notPrepared` error when the status is `unprepared`:
|
||||
- `sendToAddress(spendingKey:zatoshi:toAddress:memo:) async throws -> PendingTransactionEntity`
|
||||
- `shieldFundsspendingKey:memo:shieldingThreshold:) async throws -> PendingTransactionEntity`
|
||||
- `latestUTXOs(address:) async throws -> [UnspentTransactionOutputEntity]`
|
||||
- `refreshUTXOs(address:from:) async throws -> RefreshedUTXOs`
|
||||
- `rewind(policy:) -> AnyPublisher<Void, Error>`
|
||||
|
||||
Provided file URLs to resources (databases, filesystem block storage...) are now parsed inside the SDK and updated
|
||||
according to the alias. If some error during this happens then `SDKSynchronzer.prepare()` method throws
|
||||
`InitializerError.cantUpdateURLWithAlias` error.
|
||||
|
||||
### [#831] Add support for alternative APIs
|
||||
|
||||
There are two new protocols (`ClosureSynchronizer` and `CombineSynchronizer`). And there are two new
|
||||
objects which conform to respective protocols (`ClosureSDKSynchronizer` and `CombineSDKSynchronizer`). These
|
||||
new objects offer alternative API for the `SDKSynchronizer`. Now the client app can choose which technology
|
||||
it wants to use to communicate with Zcash SDK and it isn't forced to use async.
|
||||
|
@ -13,25 +36,24 @@ These methods in the `SDKSynchronizer` are now async:
|
|||
|
||||
Non async `SDKsynchronizer.latestHeight(result:)` were moved to `ClosureSDKSynchronizer`.
|
||||
|
||||
- [#724] Switch from event based notifications to state based notifications
|
||||
### [#724] Switch from event based notifications to state based notifications
|
||||
|
||||
The `SDKSynchronizer` no longer uses `NotificationCenter` to send notifications.
|
||||
The `SDKSynchronizer` no longer uses `NotificationCenter` to send notifications.
|
||||
Notifications are replaced with `Combine` publishers. Check the migrating document and
|
||||
documentation in the code to get more information.
|
||||
|
||||
- [#826] Change how the SDK is initialized
|
||||
### [#826] Change how the SDK is initialized
|
||||
|
||||
- `viewingKeys` and `walletBirthday` are removed from `Initializer` constuctor. These parameters
|
||||
are moved to `SDKSynchronizer.prepare` function.
|
||||
- Constructor of the `SDKSynchronizer` no longer throws exception.
|
||||
- Any value emitted from `lastState` stream before `SDKSynchronizer.prepare` is called has
|
||||
`latestScannedHeight` set to 0.
|
||||
- `Initializer.initialize` function isn't public anymore. To initialize SDK call `SDKSynchronizer.prepare`
|
||||
- `viewingKeys` and `walletBirthday` are removed from `Initializer` constuctor. These parameters moved to
|
||||
`SDKSynchronizer.prepare` function.
|
||||
- Constructor of the `SDKSynchronizer` no longer throws exception.
|
||||
- Any value emitted from `lastState` stream before `SDKSynchronizer.prepare` is called has `latestScannedHeight` set to 0.
|
||||
- `Initializer.initialize` function isn't public anymore. To initialize SDK call `SDKSynchronizer.prepare`
|
||||
instead.
|
||||
|
||||
|
||||
# 0.19.1-beta
|
||||
## Checkpoints added
|
||||
### Checkpoints added
|
||||
Mainnet
|
||||
````
|
||||
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2002500.json
|
||||
|
@ -45,7 +67,7 @@ Testnet
|
|||
Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2250000.json
|
||||
````
|
||||
## Fixed
|
||||
- [#821] `failedToWriteMetadata` at sync startup
|
||||
### [#821] `failedToWriteMetadata` at sync startup
|
||||
contains no public API changes.
|
||||
Adds `func shouldClearBlockCacheAndUpdateInternalState() -> BlockHeight?` to `SyncRanges`
|
||||
so that the compact block processor can advert internal states that are not consistent and
|
||||
|
@ -59,51 +81,50 @@ For concrete examples check out the tests in:
|
|||
Removed linter binary from repository
|
||||
|
||||
# 0.19.0-beta
|
||||
- [#816] Improve how rewind call can be used
|
||||
### [#816] Improve how rewind call can be used
|
||||
|
||||
`SDKSynchronizer.rewind(policy:)` function can be now called anytime. It returns `AnyPublisher` which
|
||||
`SDKSynchronizer.rewind(policy:)` function can be now called anytime. It returns `AnyPublisher` which
|
||||
completes or fails when the rewind is done. For more details read the documentation for this method
|
||||
in the code.
|
||||
|
||||
- [#801] Improve how wipe call can be used
|
||||
### [#801] Improve how wipe call can be used
|
||||
|
||||
`SDKSynchronizer.wipe()` function can be now called anytime. It returns `AnyPublisher` which
|
||||
`SDKSynchronizer.wipe()` function can be now called anytime. It returns `AnyPublisher` which
|
||||
completes or fails when the wipe is done. For more details read the documentation for this method
|
||||
in the code.
|
||||
|
||||
- [#793] Send synchronizerStopped notification only when sync process stops
|
||||
### [#793] Send synchronizerStopped notification only when sync process stops
|
||||
|
||||
`synchronizerStopped` notification is now sent after the sync process stops. It's
|
||||
`synchronizerStopped` notification is now sent after the sync process stops. It's
|
||||
not sent right when `stop()` method is called.
|
||||
|
||||
- [#795] Include sapling-spend file into bundle for tests
|
||||
### [#795] Include sapling-spend file into bundle for tests
|
||||
|
||||
This is only an internal change and doesn't change the behavior of the SDK. `Initializer`'s
|
||||
This is only an internal change and doesn't change the behavior of the SDK. `Initializer`'s
|
||||
constructor has a new parameter `saplingParamsSourceURL`. Use `SaplingParamsSourceURL.default`
|
||||
value for this parameter.
|
||||
|
||||
- [#764] Refactor communication between components inside th SDK
|
||||
### [#764] Refactor communication between components inside th SDK
|
||||
|
||||
This is mostly an internal change. A consequence of this change is that all the notifications
|
||||
This is mostly an internal change. A consequence of this change is that all the notifications
|
||||
delivered via `NotificationCenter` with the prefix `blockProcessor` are now gone. If affected
|
||||
notifications were used in your code use notifications with the prefix `synchronizer` now.
|
||||
These notifications are defined in `SDKSynchronizer.swift`.
|
||||
|
||||
- [#759] Remove Jazz-generated HTML docs
|
||||
### [#759] Remove Jazz-generated HTML docs
|
||||
|
||||
We remove these documents since they are outdated and we rely on the docs in the
|
||||
code itself.
|
||||
We remove these documents since they are outdated and we rely on the docs in the code itself.
|
||||
|
||||
- [#726] Modularize GRPC layer
|
||||
### [#726] Modularize GRPC layer
|
||||
|
||||
This is mostly internal change. `LightWalletService` is no longer public. If it
|
||||
This is mostly internal change. `LightWalletService` is no longer public. If it
|
||||
is used in your code replace it by using `SDKSynchronizer` API.
|
||||
|
||||
- [#770] Update GRPC swift library
|
||||
This updates to GRPC-Swift 1.14.0.
|
||||
### [#770] Update GRPC swift library
|
||||
This updates to GRPC-Swift 1.14.0.
|
||||
|
||||
|
||||
Checkpoints added:
|
||||
### Checkpoints added:
|
||||
|
||||
Mainnet:
|
||||
````
|
||||
|
@ -175,32 +196,32 @@ to move away from cacheDb.
|
|||
|
||||
## Other Issues Fixed by this PR:
|
||||
|
||||
- [#587] ShieldFundsTests:
|
||||
### [#587] ShieldFundsTests:
|
||||
- https://github.com/zcash/ZcashLightClientKit/issues/720
|
||||
- https://github.com/zcash/ZcashLightClientKit/issues/587
|
||||
- https://github.com/zcash/ZcashLightClientKit/issues/667
|
||||
|
||||
- [#443] Delete blocks from cache after processing them
|
||||
### [#443] Delete blocks from cache after processing them
|
||||
Closes https://github.com/zcash/ZcashLightClientKit/issues/443
|
||||
- [#754] adopt name change in libzashlc package that fixes a deprecation in SPM
|
||||
### [#754] adopt name change in libzashlc package that fixes a deprecation in SPM
|
||||
Closes https://github.com/zcash/ZcashLightClientKit/issues/754
|
||||
|
||||
# 0.18.1-beta
|
||||
- [#767] implement getRecipients() for Synchronizer.
|
||||
### [#767] implement getRecipients() for Synchronizer.
|
||||
|
||||
This implements `getRecipients()` function which retrieves the possible
|
||||
recipients from a sent transaction. These can either be addresses or
|
||||
internal accounts depending on the transaction being a shielding tx
|
||||
or a regular outgoing transaction.
|
||||
|
||||
Other changes:
|
||||
- Fix version of zcash-light-client-ffi to 0.1.1
|
||||
|
||||
- Enhance error reporting on a test make Mock comply with protocol
|
||||
Other changes:
|
||||
- Fix version of zcash-light-client-ffi to 0.1.1
|
||||
- Enhance error reporting on a test make Mock comply with protocol
|
||||
|
||||
# 0.18.0-beta
|
||||
|
||||
## Farewell Cocoapods.
|
||||
- [#612] Remove Support for Cocoapods (#706)
|
||||
### [#612] Remove Support for Cocoapods (#706)
|
||||
|
||||
It wouldn't have been possible to release an SDK without you, pal.
|
||||
|
||||
|
@ -211,7 +232,7 @@ We've been communicating this for a long time. Although, if you really need Coco
|
|||
please let us know by opening an issue in our repo and we'll talk about it.
|
||||
|
||||
|
||||
## New Checkpoints
|
||||
### New Checkpoints
|
||||
Checkpoints
|
||||
|
||||
Mainnet
|
||||
|
@ -238,67 +259,67 @@ Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2200000.json
|
|||
|
||||
## Bugfixes
|
||||
|
||||
- [#645] Default rewind after ReOrg is 20 blocks when it should be 10
|
||||
This fixes an issue where the default reorg was 20 blocks rewind instead of 10. The
|
||||
### [#645] Default rewind after ReOrg is 20 blocks when it should be 10
|
||||
This fixes an issue where the default reorg was 20 blocks rewind instead of 10. The
|
||||
reorg count was incremented before calling the rewind height computing function.
|
||||
|
||||
## Use Librustzcash database views to query and represent transactions
|
||||
|
||||
- [#556] Change data structures which represent transactions.
|
||||
### [#556] Change data structures which represent transactions.
|
||||
|
||||
These data types are gone: `Transaction`, `TransactionEntity`, `ConfirmedTransaction`,
|
||||
These data types are gone: `Transaction`, `TransactionEntity`, `ConfirmedTransaction`,
|
||||
`ConfirmedTransactionEntity`. And these data types were added: `ZcashTransaction.Overview`,
|
||||
`ZcashTransaction.Received`, `ZcashTransaction.Sent`.
|
||||
|
||||
New data structures are very similar to the old ones. Although there many breaking changes.
|
||||
New data structures are very similar to the old ones. Although there many breaking changes.
|
||||
The APIs of the `SDKSynchronizer` remain unchanged in their behavior. They return different
|
||||
data types. **When adopting this change, you should check which data types are used by methods
|
||||
of the `SDKSynchronizer` in your code and change them accordingly.**
|
||||
|
||||
New transaction structures no longer have a `memo` property. This responds to the fact that
|
||||
New transaction structures no longer have a `memo` property. This responds to the fact that
|
||||
Zcash transactions can have either none or multiple memos. To get memos for the transaction
|
||||
the `SDKSynchronizer` has now new methods to fetch those:
|
||||
- `func getMemos(for transaction: ZcashTransaction.Overview) throws -> [Memo]`,
|
||||
- `func getMemos(for receivedTransaction: ZcashTransaction.Received) throws -> [Memo]`
|
||||
- `func getMemos(for sentTransaction: ZcashTransaction.Sent) throws -> [Memo]`
|
||||
- `func getMemos(for transaction: ZcashTransaction.Overview) throws -> [Memo]`,
|
||||
- `func getMemos(for receivedTransaction: ZcashTransaction.Received) throws -> [Memo]`
|
||||
- `func getMemos(for sentTransaction: ZcashTransaction.Sent) throws -> [Memo]`
|
||||
|
||||
## CompactBlockProcessor is now internal
|
||||
- [#671] Make CompactBlockProcessor Internal.
|
||||
### [#671] Make CompactBlockProcessor Internal.
|
||||
|
||||
The CompactBlockProcessor is no longer a public class/API. Any direct access will
|
||||
The CompactBlockProcessor is no longer a public class/API. Any direct access will
|
||||
end up as a compiler error. Recommended way how to handle things is via `SDKSynchronizer`
|
||||
from now on. The Demo app has been updated accordingly as well.
|
||||
|
||||
## We've changed how we download and scan blocks. Status reporting has changed.
|
||||
|
||||
- [#657] Change how blocks are downloaded and scanned.
|
||||
### [#657] Change how blocks are downloaded and scanned.
|
||||
|
||||
In previous versions, the SDK first downloaded all the blocks and then it
|
||||
In previous versions, the SDK first downloaded all the blocks and then it
|
||||
scanned all the blocks. This approach requires a lot of disk space. The SDK now
|
||||
behaves differently. It downloads a batch of blocks (100 by default), scans those, and
|
||||
removes those blocks from the disk. And repeats this until all the blocks are processed.
|
||||
|
||||
`SyncStatus` was changed. `.downloading`, `.validating`, and `.scanning` symbols
|
||||
`SyncStatus` was changed. `.downloading`, `.validating`, and `.scanning` symbols
|
||||
were removed. And the `.scanning` symbol was added. The removed phases of the sync
|
||||
process are now reported as one phase.
|
||||
|
||||
Notifications were also changed similarly. These notifications were
|
||||
Notifications were also changed similarly. These notifications were
|
||||
removed: `SDKSynchronizerDownloading`, `SDKSyncronizerValidating`, and `SDKSyncronizerScanning`.
|
||||
And the `SDKSynchronizerSyncing` notification was added. The added notification replaces
|
||||
the removed notifications.
|
||||
|
||||
## New Wipe Method to delete wallet information. Use with care.
|
||||
- [#677] Add support for wallet wipe into SDK. Add new method `Synchronizer.wipe()`.
|
||||
### [#677] Add support for wallet wipe into SDK. Add new method `Synchronizer.wipe()`.
|
||||
|
||||
## Benchmarking APIs: A primer
|
||||
- [#663] Foundations for the benchmarking/performance testing in the SDK.
|
||||
### [#663] Foundations for the benchmarking/performance testing in the SDK.
|
||||
|
||||
This change presents 2 building blocks for the future automated tests, consisting
|
||||
This change presents 2 building blocks for the future automated tests, consisting
|
||||
of a new SDKMetrics interface to control flow of the data in the SDK and
|
||||
new performance (unit) test measuring synchronization of 100 mainnet blocks.
|
||||
|
||||
# 0.17.6-beta
|
||||
- [#756] 0.17.5-beta updates to libzcashlc 0.2.0 when it shouldn't
|
||||
### [#756] 0.17.5-beta updates to libzcashlc 0.2.0 when it shouldn't
|
||||
|
||||
Updated checkpoints to the ones present in 0.18.0-beta
|
||||
# 0.17.5-beta
|
||||
|
@ -328,11 +349,12 @@ Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2170000.json
|
|||
````
|
||||
|
||||
# 0.17.4-beta
|
||||
- [#665] Fix testShieldFunds() `get_transparent_balance` error
|
||||
### [#665] Fix testShieldFunds() `get_transparent_balance` error
|
||||
updates `libzcashlc` to `0.1.1` to fix an error where getting a
|
||||
transparent balance on an empty database would fail.
|
||||
|
||||
# 0.17.3-beta
|
||||
- [#646] SDK sync process resumes to previously saved block height
|
||||
### [#646] SDK sync process resumes to previously saved block height
|
||||
This change adds an internal storage test on UserDefaults that tells the
|
||||
SDK where sync was left off when cancelled whatever the reason for it
|
||||
to restart on a later attempt. This fixes some issues around syncing
|
||||
|
@ -367,23 +389,23 @@ Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1631000.json
|
|||
```
|
||||
|
||||
# 0.17.2-beta
|
||||
- [#660] Fix the situation when any rewind causes full rescan
|
||||
### [#660] Fix the situation when any rewind causes full rescan
|
||||
|
||||
# 0.17.1-beta
|
||||
- [#651] Change the rewind behavior. Now if the rewind is used while the sync process is in progress then an exception is thrown.
|
||||
- [#616] Download Stream generates too many updates on the main thread
|
||||
### [#651] Change the rewind behavior. Now if the rewind is used while the sync process is in progress then an exception is thrown.
|
||||
### [#616] Download Stream generates too many updates on the main thread
|
||||
> **WARNING**: Notifications from SDK are no longer delivered on main thread.
|
||||
|
||||
- [#585] Fix RewindRescanTests (#656)
|
||||
- Cleanup warnings (#655)
|
||||
- [#637] Make sapling parameter download part of processing blocks (#650)
|
||||
- [#631] Verify SHA1 correctness of Sapling files after downloading (#643)
|
||||
- Add benchmarking info to SyncBlocksViewController (#649)
|
||||
- [#639] Provide an API to estimate TextMemo length limit correctly (#640)
|
||||
- [#597] Bump up SQLite Swift to 0.14.1 (#638)
|
||||
- [#488] Delete cache db when sync ends
|
||||
### [#585] Fix RewindRescanTests (#656)
|
||||
### Cleanup warnings (#655)
|
||||
### [#637] Make sapling parameter download part of processing blocks (#650)
|
||||
### [#631] Verify SHA1 correctness of Sapling files after downloading (#643)
|
||||
### Add benchmarking info to SyncBlocksViewController (#649)
|
||||
### [#639] Provide an API to estimate TextMemo length limit correctly (#640)
|
||||
### [#597] Bump up SQLite Swift to 0.14.1 (#638)
|
||||
### [#488] Delete cache db when sync ends
|
||||
|
||||
- Added Checkpoints
|
||||
### Added Checkpoints
|
||||
|
||||
Mainnet
|
||||
````
|
||||
|
@ -403,45 +425,44 @@ Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2130000.json
|
|||
|
||||
# Summary of 0.17.0-beta
|
||||
|
||||
- [#321] Validate UA
|
||||
- [#384] Adopt Type Safe Memos in the FFI and SDK
|
||||
- [#355] Update lib.rs to lastest librustzcash master
|
||||
- [#373] Demo App shows ZEC balances in scientific notation
|
||||
- [#380] One of the initAccountsTable() is dead code (except for tests)
|
||||
- [#374] XCTest don't load Resources from the module's bundle
|
||||
- [#375] User can't go twice in a row to SendFundsViewController
|
||||
- [#490] Rebase long dated PRs on top of the feature branches
|
||||
- [#510] Change references of Shielded address to Sapling Address
|
||||
- [#511] Derivation functions should only return a single resul
|
||||
- [#512] Remove derivation of t-address from pubkey
|
||||
- [#520] Use UA Test Vector for Recipient Test
|
||||
- [#544] Change Demo App to use USK and new rolling addresses
|
||||
- [#602] Improve error logging for InitializerError and RustWeldingError
|
||||
- [#579] Fix database lock
|
||||
- [#595] Update Travis to use Xcode 14
|
||||
- [#592] Fix various tests and deleted some that are not useful anymore
|
||||
- [#523] Make a CompactBlockProcessor an Actor
|
||||
- [#593] Fix testSmallDownloadAsync test
|
||||
- [#577] Fix: reduce batch size when reaching increased load part of the chain
|
||||
- [#575] make Memo and MemoBytes parameters nullable so they can be omitted
|
||||
when sending to transparent receivers.
|
||||
- commit `1979e41` Fix pre populated Db to have transactions from darksidewalletd seed
|
||||
- commit `a483537` Ensure that the persisted test database has had migrations applied.
|
||||
- commit `1273d30` Clarify & make regular how migrations are applied.
|
||||
- commit `78856c6` Fix: successive launches of the application fail because the closed range of the migrations to apply would be invalid (lower range > that upper range)
|
||||
- commit `7847a71` Fix incorrect encoding of optional strings in PendingTransaction.
|
||||
- commit `789cf01` Add Fee field to Transaction, ConfirmedTransaction, ReceivedTransactions and Pen dingTransactions. Update Notes DAOs with new fields
|
||||
- commit `849083f` Fix UInt32 conversions to SQL in PendingTransactionDao
|
||||
- commit `fae15ce` Fix sent_notes.to_address column reference.
|
||||
- commit `23f1f5d` Merge pull request #562 from zcash/fix_UnifiedTypecodesTests
|
||||
- commit `30a9c06` Replace `db.run` with `db.execute` to fix migration issues
|
||||
- commit `0fbf90d` Add migration to re-create pending_transactions table with nullable columns.
|
||||
- commit `36932a2` Use PendingTransactionEntity.internalAccount for shielding.
|
||||
- commit `f5d7aa0` Modify PendingTransactionEntity to be able to represent internal shielding tx.
|
||||
- [#561] Fix unified typecodes tests
|
||||
- [#530] Implement ability to extract available typecodes from UA
|
||||
### [#321] Validate UA
|
||||
### [#384] Adopt Type Safe Memos in the FFI and SDK
|
||||
### [#355] Update lib.rs to lastest librustzcash master
|
||||
### [#373] Demo App shows ZEC balances in scientific notation
|
||||
### [#380] One of the initAccountsTable() is dead code (except for tests)
|
||||
### [#374] XCTest don't load Resources from the module's bundle
|
||||
### [#375] User can't go twice in a row to SendFundsViewController
|
||||
### [#490] Rebase long dated PRs on top of the feature branches
|
||||
### [#510] Change references of Shielded address to Sapling Address
|
||||
### [#511] Derivation functions should only return a single resul
|
||||
### [#512] Remove derivation of t-address from pubkey
|
||||
### [#520] Use UA Test Vector for Recipient Test
|
||||
### [#544] Change Demo App to use USK and new rolling addresses
|
||||
### [#602] Improve error logging for InitializerError and RustWeldingError
|
||||
### [#579] Fix database lock
|
||||
### [#595] Update Travis to use Xcode 14
|
||||
### [#592] Fix various tests and deleted some that are not useful anymore
|
||||
### [#523] Make a CompactBlockProcessor an Actor
|
||||
### [#593] Fix testSmallDownloadAsync test
|
||||
### [#577] Fix: reduce batch size when reaching increased load part of the chain
|
||||
### [#575] make Memo and MemoBytes parameters nullable so they can be omitted when sending to transparent receivers.
|
||||
### commit `1979e41` Fix pre populated Db to have transactions from darksidewalletd seed
|
||||
### commit `a483537` Ensure that the persisted test database has had migrations applied.
|
||||
### commit `1273d30` Clarify & make regular how migrations are applied.
|
||||
### commit `78856c6` Fix: successive launches of the application fail because the closed range of the migrations to apply would be invalid (lower range > that upper range)
|
||||
### commit `7847a71` Fix incorrect encoding of optional strings in PendingTransaction.
|
||||
### commit `789cf01` Add Fee field to Transaction, ConfirmedTransaction, ReceivedTransactions and Pen dingTransactions. Update Notes DAOs with new fields
|
||||
### commit `849083f` Fix UInt32 conversions to SQL in PendingTransactionDao
|
||||
### commit `fae15ce` Fix sent_notes.to_address column reference.
|
||||
### commit `23f1f5d` Merge pull request #562 from zcash/fix_UnifiedTypecodesTests
|
||||
### commit `30a9c06` Replace `db.run` with `db.execute` to fix migration issues
|
||||
### commit `0fbf90d` Add migration to re-create pending_transactions table with nullable columns.
|
||||
### commit `36932a2` Use PendingTransactionEntity.internalAccount for shielding.
|
||||
### commit `f5d7aa0` Modify PendingTransactionEntity to be able to represent internal shielding tx.
|
||||
### [#561] Fix unified typecodes tests
|
||||
### [#530] Implement ability to extract available typecodes from UA
|
||||
|
||||
- Added Checkpoints
|
||||
### Added Checkpoints
|
||||
|
||||
Mainnet
|
||||
````
|
||||
|
@ -457,7 +478,7 @@ Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2110000.json
|
|||
````
|
||||
|
||||
# 0.17.0-beta.rc1
|
||||
- Added Checkpoints
|
||||
### Added Checkpoints
|
||||
|
||||
Mainnet
|
||||
````
|
||||
|
@ -484,25 +505,25 @@ Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2100000.json
|
|||
````
|
||||
|
||||
# 0.17.0-alpha.5
|
||||
- point to libzcashlc 0.1.0-beta.3. This fixes an issue spending change notes
|
||||
### point to libzcashlc 0.1.0-beta.3. This fixes an issue spending change notes
|
||||
# 0.17.0-alpha.4
|
||||
- point to libzcashlc 0.1.0-beta.2
|
||||
### point to libzcashlc 0.1.0-beta.2
|
||||
|
||||
# 0.17.0-alpha.3
|
||||
- [#602] Improve error logging for InitializerError and RustWeldingError
|
||||
### [#602] Improve error logging for InitializerError and RustWeldingError
|
||||
|
||||
# 0.17.0-alpha.2
|
||||
- [#579] Fix database lock
|
||||
- [#592] Fix various tests and deleted some that are not useful anymore
|
||||
- [#581] getTransparentBalanceForAccount error not handled
|
||||
### [#579] Fix database lock
|
||||
### [#592] Fix various tests and deleted some that are not useful anymore
|
||||
### [#581] getTransparentBalanceForAccount error not handled
|
||||
|
||||
# 0.17.0-alpha.1
|
||||
See MIGRATING.md
|
||||
|
||||
# 0.16-13-beta
|
||||
- [#597] SDK does not build with SQLite 0.14
|
||||
### [#597] SDK does not build with SQLite 0.14
|
||||
# 0.16.12-beta
|
||||
Checkpoints added:
|
||||
### Checkpoints added:
|
||||
Mainnet
|
||||
````
|
||||
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1832500.json
|
||||
|
@ -515,7 +536,7 @@ Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1847500.json
|
|||
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1850000.json
|
||||
````
|
||||
# 0.16.11-beta
|
||||
Checkpoints added:
|
||||
### Checkpoints added:
|
||||
Mainnet
|
||||
````
|
||||
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1812500.json
|
||||
|
@ -528,7 +549,7 @@ Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1827500.json
|
|||
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1830000.json
|
||||
````
|
||||
# 0.16.10-beta
|
||||
- [#532] [0.16.x-beta] Download does not stop correctly
|
||||
### [#532] [0.16.x-beta] Download does not stop correctly
|
||||
|
||||
Issue Reported:
|
||||
|
||||
|
@ -551,7 +572,7 @@ Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1810000.json
|
|||
````
|
||||
|
||||
# 0.16.9-beta
|
||||
Checkpoints added:
|
||||
### Checkpoints added:
|
||||
Mainnet
|
||||
````
|
||||
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1787500.json
|
||||
|
@ -564,7 +585,7 @@ Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1802500.json
|
|||
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1805000.json
|
||||
````
|
||||
# 0.16.8-beta
|
||||
Checkpoints added:
|
||||
### Checkpoints added:
|
||||
Mainnet
|
||||
````
|
||||
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1775000.json
|
||||
|
@ -581,7 +602,7 @@ Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2010000.json
|
|||
````
|
||||
|
||||
# 0.16.7-beta
|
||||
- [#455] revert queue priority downgrade changes from [#435] (#456)
|
||||
### [#455] revert queue priority downgrade changes from [#435] (#456)
|
||||
|
||||
This reverts queue priority changes from commit `a5d0e447748257d2af5c9101391dd05a5ce929a2` since we detected it might prevent downloads to be scheduled in a timely fashion
|
||||
|
||||
|
|
|
@ -49,6 +49,10 @@
|
|||
0DDFB33C236B743000AED892 /* LatestHeightViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DDFB33B236B743000AED892 /* LatestHeightViewController.swift */; };
|
||||
0DDFB33E236B844900AED892 /* DemoAppConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DDFB33D236B844900AED892 /* DemoAppConfig.swift */; };
|
||||
0DF53E6723A438F100D7249C /* PaginatedTransactionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF53E6623A438F100D7249C /* PaginatedTransactionsViewController.swift */; };
|
||||
343CB69729CDC05D0063BF41 /* SyncBlocksListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 343CB69629CDC05D0063BF41 /* SyncBlocksListViewController.swift */; };
|
||||
343CB69829CDC05D0063BF41 /* SyncBlocksListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 343CB69629CDC05D0063BF41 /* SyncBlocksListViewController.swift */; };
|
||||
34CC8FD029CDC212001E06E9 /* SynchronizerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34CC8FCF29CDC212001E06E9 /* SynchronizerCell.swift */; };
|
||||
34CC8FD129CDC212001E06E9 /* SynchronizerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34CC8FCF29CDC212001E06E9 /* SynchronizerCell.swift */; };
|
||||
F94912632790D7C4004BB3DE /* ZcashLightClientSample-Mainnet-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = F94912622790D7C4004BB3DE /* ZcashLightClientSample-Mainnet-Info.plist */; };
|
||||
F9D63D1F27CD114A00F4DC5F /* ZcashLightClientKit in Frameworks */ = {isa = PBXBuildFile; productRef = F9D63D1E27CD114A00F4DC5F /* ZcashLightClientKit */; };
|
||||
F9D63D2227CD125300F4DC5F /* KRProgressHUD in Frameworks */ = {isa = PBXBuildFile; productRef = F9D63D2127CD125300F4DC5F /* KRProgressHUD */; };
|
||||
|
@ -95,6 +99,8 @@
|
|||
0DDFB33D236B844900AED892 /* DemoAppConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoAppConfig.swift; sourceTree = "<group>"; };
|
||||
0DF53E6623A438F100D7249C /* PaginatedTransactionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginatedTransactionsViewController.swift; sourceTree = "<group>"; };
|
||||
22AE1E6F756FD3EA41A397FC /* Pods_ZcashLightClientSampleTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ZcashLightClientSampleTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
343CB69629CDC05D0063BF41 /* SyncBlocksListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncBlocksListViewController.swift; sourceTree = "<group>"; };
|
||||
34CC8FCF29CDC212001E06E9 /* SynchronizerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchronizerCell.swift; sourceTree = "<group>"; };
|
||||
372CC57DB80CC242BA556A30 /* Pods_ZcashLightClientSampleUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ZcashLightClientSampleUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
541D36743362A797BF085D14 /* Pods_ZcashLightClientSample_Mainnet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ZcashLightClientSample_Mainnet.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EBA186B246C4205F9BF71EED /* Pods_ZcashLightClientSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ZcashLightClientSample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -182,6 +188,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
0D7A4A82236CCD88001F4DD8 /* SyncBlocksViewController.swift */,
|
||||
343CB69629CDC05D0063BF41 /* SyncBlocksListViewController.swift */,
|
||||
34CC8FCF29CDC212001E06E9 /* SynchronizerCell.swift */,
|
||||
);
|
||||
path = "Sync Blocks";
|
||||
sourceTree = "<group>";
|
||||
|
@ -518,6 +526,8 @@
|
|||
0D1BE47F2581937100F78BE3 /* GetUTXOsViewController.swift in Sources */,
|
||||
0DA58B962397F2CB004596EA /* TransactionsDataSource.swift in Sources */,
|
||||
0DF53E6723A438F100D7249C /* PaginatedTransactionsViewController.swift in Sources */,
|
||||
34CC8FD029CDC212001E06E9 /* SynchronizerCell.swift in Sources */,
|
||||
343CB69729CDC05D0063BF41 /* SyncBlocksListViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -548,6 +558,8 @@
|
|||
0DA1C4C827D11D9500E5006E /* GetUTXOsViewController.swift in Sources */,
|
||||
0DA1C4CC27D11D9500E5006E /* TransactionsDataSource.swift in Sources */,
|
||||
0DA1C4CD27D11D9500E5006E /* PaginatedTransactionsViewController.swift in Sources */,
|
||||
34CC8FD129CDC212001E06E9 /* SynchronizerCell.swift in Sources */,
|
||||
343CB69829CDC05D0063BF41 /* SyncBlocksListViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -710,7 +722,7 @@
|
|||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "$(inherited)";
|
||||
INFOPLIST_FILE = ZcashLightClientSample/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -733,7 +745,7 @@
|
|||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "$(inherited)";
|
||||
INFOPLIST_FILE = ZcashLightClientSample/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -796,7 +808,7 @@
|
|||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "$(inherited)";
|
||||
INFOPLIST_FILE = "ZcashLightClientSample-Mainnet-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -819,7 +831,7 @@
|
|||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "$(inherited)";
|
||||
INFOPLIST_FILE = "ZcashLightClientSample-Mainnet-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
|
|
@ -33,7 +33,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
|
||||
var sharedViewingKey: UnifiedFullViewingKey {
|
||||
return try! DerivationTool(networkType: kZcashNetwork.networkType)
|
||||
.deriveUnifiedSpendingKey(seed: DemoAppConfig.seed, accountIndex: 0)
|
||||
.deriveUnifiedSpendingKey(seed: DemoAppConfig.defaultSeed, accountIndex: 0)
|
||||
.deriveFullViewingKey()
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
return wallet
|
||||
} else {
|
||||
let wallet = Initializer(
|
||||
cacheDbURL: nil,
|
||||
fsBlockDbRoot: try! fsBlockDbRootURLHelper(),
|
||||
dataDbURL: try! dataDbURLHelper(),
|
||||
pendingDbURL: try! pendingDbURLHelper(),
|
||||
|
@ -49,8 +50,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
network: kZcashNetwork,
|
||||
spendParamsURL: try! spendParamsURLHelper(),
|
||||
outputParamsURL: try! outputParamsURLHelper(),
|
||||
saplingParamsSourceURL: SaplingParamsSourceURL.default,
|
||||
loggerProxy: loggerProxy
|
||||
saplingParamsSourceURL: SaplingParamsSourceURL.default
|
||||
)
|
||||
|
||||
self.wallet = wallet
|
||||
|
|
|
@ -77,11 +77,31 @@
|
|||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="eja-yc-RHW" kind="show" id="fZ3-Vb-Oxe"/>
|
||||
<segue destination="eja-yc-RHW" kind="show" id="Lk2-gP-A8l"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="aXz-Nu-7Iz" style="IBUITableViewCellStyleDefault" id="UML-hy-jTe">
|
||||
<rect key="frame" x="0.0" y="181.00000381469727" width="390" height="43.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="UML-hy-jTe" id="6nt-IH-7Kp">
|
||||
<rect key="frame" x="0.0" y="0.0" width="390" height="43.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Sync multiple wallets" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="aXz-Nu-7Iz">
|
||||
<rect key="frame" x="20" y="0.0" width="350" height="43.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="TrV-Ny-JYh" kind="show" id="ItX-87-rau"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="v8L-AQ-cAg" style="IBUITableViewCellStyleDefault" id="RKO-CX-5oF">
|
||||
<rect key="frame" x="0.0" y="181.00000381469727" width="390" height="43.666667938232422"/>
|
||||
<rect key="frame" x="0.0" y="224.66667175292969" width="390" height="43.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="RKO-CX-5oF" id="WdD-zf-ng7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="390" height="43.666667938232422"/>
|
||||
|
@ -101,7 +121,7 @@
|
|||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="CS3-Hm-JPK" style="IBUITableViewCellStyleDefault" id="lX6-BP-STv">
|
||||
<rect key="frame" x="0.0" y="224.66667175292969" width="390" height="43.666667938232422"/>
|
||||
<rect key="frame" x="0.0" y="268.33333969116211" width="390" height="43.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="lX6-BP-STv" id="uYl-mR-smC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="390" height="43.666667938232422"/>
|
||||
|
@ -121,7 +141,7 @@
|
|||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="hfc-dh-b2p" style="IBUITableViewCellStyleDefault" id="U6y-0k-TWn">
|
||||
<rect key="frame" x="0.0" y="268.33333969116211" width="390" height="43.666667938232422"/>
|
||||
<rect key="frame" x="0.0" y="312.00000762939453" width="390" height="43.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="U6y-0k-TWn" id="7aj-Lt-9o9">
|
||||
<rect key="frame" x="0.0" y="0.0" width="390" height="43.666667938232422"/>
|
||||
|
@ -141,7 +161,7 @@
|
|||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="oRc-aQ-3vN" style="IBUITableViewCellStyleDefault" id="rBT-Qj-4GX">
|
||||
<rect key="frame" x="0.0" y="312.00000762939453" width="390" height="43.666667938232422"/>
|
||||
<rect key="frame" x="0.0" y="355.66667556762695" width="390" height="43.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="rBT-Qj-4GX" id="7t0-PZ-KW4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="390" height="43.666667938232422"/>
|
||||
|
@ -161,7 +181,7 @@
|
|||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="mo0-CA-kMx" style="IBUITableViewCellStyleDefault" id="cYW-no-fyA">
|
||||
<rect key="frame" x="0.0" y="355.66667556762695" width="390" height="43.666667938232422"/>
|
||||
<rect key="frame" x="0.0" y="399.33334350585938" width="390" height="43.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="cYW-no-fyA" id="uu3-KF-LDB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="390" height="43.666667938232422"/>
|
||||
|
@ -181,7 +201,7 @@
|
|||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="3ht-A5-ABT" style="IBUITableViewCellStyleDefault" id="Bvc-KS-SGJ">
|
||||
<rect key="frame" x="0.0" y="399.33334350585938" width="390" height="43.666667938232422"/>
|
||||
<rect key="frame" x="0.0" y="443.0000114440918" width="390" height="43.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Bvc-KS-SGJ" id="Hsp-0f-x3X">
|
||||
<rect key="frame" x="0.0" y="0.0" width="390" height="43.666667938232422"/>
|
||||
|
@ -201,7 +221,7 @@
|
|||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="Hl7-xV-yrM" style="IBUITableViewCellStyleDefault" id="6sF-sD-nYj">
|
||||
<rect key="frame" x="0.0" y="443.0000114440918" width="390" height="43.666667938232422"/>
|
||||
<rect key="frame" x="0.0" y="486.66667938232422" width="390" height="43.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="6sF-sD-nYj" id="Uwh-62-xaj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="390" height="43.666667938232422"/>
|
||||
|
@ -221,7 +241,7 @@
|
|||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="HyY-vt-5nR" style="IBUITableViewCellStyleDefault" id="KkJ-uD-G5q">
|
||||
<rect key="frame" x="0.0" y="486.66667938232422" width="390" height="43.666667938232422"/>
|
||||
<rect key="frame" x="0.0" y="530.33334732055664" width="390" height="43.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="KkJ-uD-G5q" id="VL6-QM-S0g">
|
||||
<rect key="frame" x="0.0" y="0.0" width="390" height="43.666667938232422"/>
|
||||
|
@ -241,7 +261,7 @@
|
|||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="XCC-pZ-eel" style="IBUITableViewCellStyleDefault" id="dgB-c9-cv9">
|
||||
<rect key="frame" x="0.0" y="530.33334732055664" width="390" height="43.666667938232422"/>
|
||||
<rect key="frame" x="0.0" y="574.00001525878906" width="390" height="43.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="dgB-c9-cv9" id="C46-42-3yU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="390" height="43.666667938232422"/>
|
||||
|
@ -261,7 +281,7 @@
|
|||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="B3i-Nh-NA5" style="IBUITableViewCellStyleDefault" id="VPb-7U-IKD">
|
||||
<rect key="frame" x="0.0" y="574.00001525878906" width="390" height="43.666667938232422"/>
|
||||
<rect key="frame" x="0.0" y="617.66668319702148" width="390" height="43.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VPb-7U-IKD" id="IWX-T9-THE">
|
||||
<rect key="frame" x="0.0" y="0.0" width="390" height="43.666667938232422"/>
|
||||
|
@ -278,7 +298,7 @@
|
|||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="qHq-xq-jFS" style="IBUITableViewCellStyleDefault" id="XHY-aU-r1N">
|
||||
<rect key="frame" x="0.0" y="617.66668319702148" width="390" height="43.666667938232422"/>
|
||||
<rect key="frame" x="0.0" y="661.33335113525391" width="390" height="43.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="XHY-aU-r1N" id="fbk-CU-wgr">
|
||||
<rect key="frame" x="0.0" y="0.0" width="390" height="43.666667938232422"/>
|
||||
|
@ -938,7 +958,7 @@
|
|||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="vQP-aT-BeF" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="889" y="983"/>
|
||||
<point key="canvasLocation" x="1165" y="1125"/>
|
||||
</scene>
|
||||
<!--Sync Blocks-->
|
||||
<scene sceneID="mqi-cb-0xH">
|
||||
|
@ -959,16 +979,16 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="246" verticalCompressionResistancePriority="250" ambiguous="YES" alignment="center" spacing="13" translatesAutoresizingMaskIntoConstraints="NO" id="IIl-kO-2M8">
|
||||
<rect key="frame" x="0.0" y="79" width="374" height="114"/>
|
||||
<rect key="frame" x="0.0" y="79.000000000000014" width="374" height="201.33333333333337"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" text="Status:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3t3-Tr-UlI">
|
||||
<rect key="frame" x="0.0" y="38" width="95.666666666666671" height="38.333333333333343"/>
|
||||
<rect key="frame" x="0.0" y="81.666666666666657" width="95.666666666666671" height="38.333333333333343"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="32"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Jj9-7r-s2Y">
|
||||
<rect key="frame" x="108.66666666666666" y="43.333333333333343" width="265.33333333333337" height="27.666666666666671"/>
|
||||
<rect key="frame" x="108.66666666666666" y="87" width="265.33333333333337" height="27.666666666666671"/>
|
||||
<fontDescription key="fontDescription" type="italicSystem" pointSize="23"/>
|
||||
<color key="textColor" systemColor="scrollViewTexturedBackgroundColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -979,22 +999,22 @@
|
|||
</constraints>
|
||||
</stackView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Progress" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JPx-ol-2nc">
|
||||
<rect key="frame" x="0.0" y="217" width="366" height="43"/>
|
||||
<rect key="frame" x="0.0" y="304.33333333333331" width="366" height="43"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="36"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" ambiguous="YES" progressViewStyle="bar" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="oKg-9s-8Ym">
|
||||
<rect key="frame" x="0.0" y="284" width="366" height="2.6666666666666856"/>
|
||||
<rect key="frame" x="0.0" y="371.33333333333331" width="366" height="2.6666666666666856"/>
|
||||
</progressView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="0%" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kXE-5z-HN2">
|
||||
<rect key="frame" x="0.0" y="309.66666666666669" width="374" height="39.666666666666686"/>
|
||||
<rect key="frame" x="0.0" y="397" width="374" height="39.666666666666686"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="33"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="G5M-gm-1ux">
|
||||
<rect key="frame" x="0.0" y="373.33333333333331" width="374" height="45"/>
|
||||
<rect key="frame" x="0.0" y="460.66666666666663" width="374" height="45"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="27"/>
|
||||
<state key="normal" title="Start"/>
|
||||
<connections>
|
||||
|
@ -1002,19 +1022,19 @@
|
|||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XK2-GA-Ufv">
|
||||
<rect key="frame" x="0.0" y="442.33333333333337" width="374" height="14.333333333333314"/>
|
||||
<rect key="frame" x="0.0" y="529.66666666666663" width="374" height="14.333333333333371"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ROk-70-c41">
|
||||
<rect key="frame" x="0.0" y="480.66666666666663" width="374" height="14.333333333333314"/>
|
||||
<rect key="frame" x="0.0" y="568" width="374" height="14.333333333333371"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" verticalHuggingPriority="750" verticalCompressionResistancePriority="737" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fB9-xh-4fl" userLabel="Trailing View">
|
||||
<rect key="frame" x="0.0" y="519" width="374" height="200"/>
|
||||
<rect key="frame" x="0.0" y="606.33333333333337" width="374" height="112.66666666666663"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="750" constant="20" id="Bpt-XM-IZA"/>
|
||||
|
@ -1059,7 +1079,7 @@
|
|||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="K0g-OR-DhR" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1800.0000000000002" y="948.88392857142856"/>
|
||||
<point key="canvasLocation" x="-911" y="1125"/>
|
||||
</scene>
|
||||
<!--Latest Height View Controller-->
|
||||
<scene sceneID="4pw-mR-qER">
|
||||
|
@ -1120,6 +1140,104 @@
|
|||
</objects>
|
||||
<point key="canvasLocation" x="-92" y="40"/>
|
||||
</scene>
|
||||
<!--Sync Blocks List View Controller-->
|
||||
<scene sceneID="n6m-C7-arI">
|
||||
<objects>
|
||||
<viewController id="TrV-Ny-JYh" customClass="SyncBlocksListViewController" customModule="ZcashLightClientSample" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Mvd-Eh-7re">
|
||||
<rect key="frame" x="0.0" y="0.0" width="390" height="844"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Building synchronizers..." textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="L9B-eh-47V">
|
||||
<rect key="frame" x="102.33333333333333" y="411.66666666666669" width="185.33333333333337" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="medium" translatesAutoresizingMaskIntoConstraints="NO" id="8fp-u6-7zg">
|
||||
<rect key="frame" x="185" y="442.66666666666669" width="20" height="20"/>
|
||||
</activityIndicatorView>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="-1" estimatedSectionHeaderHeight="-1" sectionFooterHeight="-1" estimatedSectionFooterHeight="-1" translatesAutoresizingMaskIntoConstraints="NO" id="yV0-C4-Vmo">
|
||||
<rect key="frame" x="0.0" y="91" width="390" height="719"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="SynchronizerCell" id="Fmb-3y-8Me" customClass="SynchronizerCell" customModule="ZcashLightClientSample" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="50" width="390" height="56.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Fmb-3y-8Me" id="8Cl-PW-Fub">
|
||||
<rect key="frame" x="0.0" y="0.0" width="390" height="56.666667938232422"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wz9-uv-BrL">
|
||||
<rect key="frame" x="20" y="5" width="365" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="249" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="35e-K9-Fhw">
|
||||
<rect key="frame" x="20" y="31" width="300.33333333333331" height="20.666666666666671"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="22h-cY-kOF">
|
||||
<rect key="frame" x="330.33333333333331" y="11.333333333333332" width="49.666666666666686" height="34.333333333333343"/>
|
||||
<state key="normal" title="Button"/>
|
||||
<buttonConfiguration key="configuration" style="gray" image="play.circle" catalog="system"/>
|
||||
<connections>
|
||||
<action selector="buttonTap" destination="Fmb-3y-8Me" eventType="touchUpInside" id="oOi-6s-EPR"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="35e-K9-Fhw" secondAttribute="bottom" constant="5" id="6QT-Qu-mm2"/>
|
||||
<constraint firstItem="35e-K9-Fhw" firstAttribute="leading" secondItem="8Cl-PW-Fub" secondAttribute="leadingMargin" id="Id9-Nz-Z5B"/>
|
||||
<constraint firstAttribute="trailing" secondItem="22h-cY-kOF" secondAttribute="trailing" constant="10" id="Lum-bA-kNs"/>
|
||||
<constraint firstItem="35e-K9-Fhw" firstAttribute="top" secondItem="wz9-uv-BrL" secondAttribute="bottom" constant="5" id="Zcv-wy-n4H"/>
|
||||
<constraint firstItem="wz9-uv-BrL" firstAttribute="leading" secondItem="8Cl-PW-Fub" secondAttribute="leadingMargin" id="Zfy-iO-eYD"/>
|
||||
<constraint firstItem="22h-cY-kOF" firstAttribute="centerY" secondItem="8Cl-PW-Fub" secondAttribute="centerY" id="hhI-ZR-t3b"/>
|
||||
<constraint firstItem="wz9-uv-BrL" firstAttribute="top" secondItem="8Cl-PW-Fub" secondAttribute="top" constant="5" id="w9f-8m-CAD"/>
|
||||
<constraint firstItem="22h-cY-kOF" firstAttribute="leading" secondItem="35e-K9-Fhw" secondAttribute="trailing" constant="10" id="yDL-hS-Qfd"/>
|
||||
<constraint firstAttribute="trailing" secondItem="wz9-uv-BrL" secondAttribute="trailing" constant="5" id="yGT-mO-DCE"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="alias" destination="wz9-uv-BrL" id="oIj-s1-ybD"/>
|
||||
<outlet property="button" destination="22h-cY-kOF" id="fpV-mN-dOf"/>
|
||||
<outlet property="status" destination="35e-K9-Fhw" id="qJO-c2-ToS"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="TrV-Ny-JYh" id="vKf-b7-Da8"/>
|
||||
<outlet property="delegate" destination="TrV-Ny-JYh" id="Mvs-lm-Gda"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="JgX-JR-Ucx"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="8fp-u6-7zg" firstAttribute="centerX" secondItem="Mvd-Eh-7re" secondAttribute="centerX" id="Je0-TJ-wAB"/>
|
||||
<constraint firstItem="yV0-C4-Vmo" firstAttribute="leading" secondItem="JgX-JR-Ucx" secondAttribute="leading" id="QVk-ab-40B"/>
|
||||
<constraint firstItem="L9B-eh-47V" firstAttribute="centerX" secondItem="Mvd-Eh-7re" secondAttribute="centerX" id="SOe-AL-Kyw"/>
|
||||
<constraint firstItem="yV0-C4-Vmo" firstAttribute="top" secondItem="JgX-JR-Ucx" secondAttribute="top" id="d0x-3f-cNh"/>
|
||||
<constraint firstItem="L9B-eh-47V" firstAttribute="centerY" secondItem="Mvd-Eh-7re" secondAttribute="centerY" id="hjx-M6-GUF"/>
|
||||
<constraint firstItem="JgX-JR-Ucx" firstAttribute="bottom" secondItem="yV0-C4-Vmo" secondAttribute="bottom" id="o8t-uP-lYc"/>
|
||||
<constraint firstItem="JgX-JR-Ucx" firstAttribute="trailing" secondItem="yV0-C4-Vmo" secondAttribute="trailing" id="rLz-fK-IRg"/>
|
||||
<constraint firstItem="8fp-u6-7zg" firstAttribute="top" secondItem="L9B-eh-47V" secondAttribute="bottom" constant="10" id="yCs-fV-jVm"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" id="YVw-fR-qFq"/>
|
||||
<connections>
|
||||
<outlet property="loadingIndicator" destination="8fp-u6-7zg" id="Clc-vR-dAx"/>
|
||||
<outlet property="loadingLabel" destination="L9B-eh-47V" id="JjL-VT-mgg"/>
|
||||
<outlet property="table" destination="yV0-C4-Vmo" id="nNW-NA-pUM"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="mjJ-pc-PRf" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-240" y="1125"/>
|
||||
</scene>
|
||||
<!--Get Balance View Controller-->
|
||||
<scene sceneID="jf5-6v-Lfw">
|
||||
<objects>
|
||||
|
@ -1185,7 +1303,7 @@
|
|||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="R73-h3-yvk" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1830" y="1623"/>
|
||||
<point key="canvasLocation" x="440" y="1125"/>
|
||||
</scene>
|
||||
<!--Get UTXOs for tAddr-->
|
||||
<scene sceneID="lqr-tV-bOy">
|
||||
|
@ -1284,7 +1402,7 @@
|
|||
</connections>
|
||||
</tapGestureRecognizer>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-370" y="1329"/>
|
||||
<point key="canvasLocation" x="1880" y="1126"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<inferredMetricsTieBreakers>
|
||||
|
@ -1292,6 +1410,7 @@
|
|||
<segue reference="snP-Bc-obL"/>
|
||||
</inferredMetricsTieBreakers>
|
||||
<resources>
|
||||
<image name="play.circle" catalog="system" width="128" height="123"/>
|
||||
<systemColor name="labelColor">
|
||||
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
|
|
|
@ -12,14 +12,39 @@ import MnemonicSwift
|
|||
|
||||
// swiftlint:disable force_try
|
||||
enum DemoAppConfig {
|
||||
struct SynchronizerInitData {
|
||||
let alias: ZcashSynchronizerAlias
|
||||
let birthday: BlockHeight
|
||||
let seed: [UInt8]
|
||||
}
|
||||
|
||||
static var host = ZcashSDK.isMainnet ? "lightwalletd.electriccoin.co" : "lightwalletd.testnet.electriccoin.co"
|
||||
static var port: Int = 9067
|
||||
static var birthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 1386000
|
||||
|
||||
static var seed = try! Mnemonic.deterministicSeedBytes(from: """
|
||||
static var defaultBirthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 1386000
|
||||
static var defaultSeed = try! Mnemonic.deterministicSeedBytes(from: """
|
||||
live combine flight accident slow soda mind bright absent bid hen shy decade biology amazing mix enlist ensure biology rhythm snap duty soap armor
|
||||
""")
|
||||
|
||||
static let otherSynchronizers: [SynchronizerInitData] = [
|
||||
SynchronizerInitData(
|
||||
alias: .custom("alt-sync-1"),
|
||||
birthday: 2270000,
|
||||
seed: try! Mnemonic.deterministicSeedBytes(from: """
|
||||
celery very reopen verify cook page cricket shield guilt picnic survey doctor include choice they stairs breeze sort route mask carpet \
|
||||
coral clinic glass
|
||||
""")
|
||||
),
|
||||
SynchronizerInitData(
|
||||
alias: .custom("alt-sync-2"),
|
||||
birthday: 2270000,
|
||||
seed: try! Mnemonic.deterministicSeedBytes(from: """
|
||||
horse museum parrot simple scissors head baby december tool donor impose job draw outer photo much minimum door gun vessel matrix vacant \
|
||||
magnet lumber
|
||||
""")
|
||||
)
|
||||
]
|
||||
|
||||
static var address: String {
|
||||
"\(host):\(port)"
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class GetUTXOsViewController: UIViewController {
|
|||
do {
|
||||
let derivationTool = DerivationTool(networkType: kZcashNetwork.networkType)
|
||||
|
||||
let usk = try derivationTool.deriveUnifiedSpendingKey(seed: DemoAppConfig.seed, accountIndex: 0)
|
||||
let usk = try derivationTool.deriveUnifiedSpendingKey(seed: DemoAppConfig.defaultSeed, accountIndex: 0)
|
||||
|
||||
KRProgressHUD.showMessage("🛡 Shielding 🛡")
|
||||
|
||||
|
|
|
@ -86,7 +86,8 @@ class SaplingParametersViewController: UIViewController {
|
|||
spendURL: spendParameter,
|
||||
spendSourceURL: SaplingParamsSourceURL.default.spendParamFileURL,
|
||||
outputURL: outputParameter,
|
||||
outputSourceURL: SaplingParamsSourceURL.default.outputParamFileURL
|
||||
outputSourceURL: SaplingParamsSourceURL.default.outputParamFileURL,
|
||||
logger: loggerProxy
|
||||
)
|
||||
spendPath.text = urls.spend.path
|
||||
outputPath.text = urls.output.path
|
||||
|
|
|
@ -44,9 +44,9 @@ class SendViewController: UIViewController {
|
|||
setUp()
|
||||
|
||||
closureSynchronizer.prepare(
|
||||
with: DemoAppConfig.seed,
|
||||
with: DemoAppConfig.defaultSeed,
|
||||
viewingKeys: [AppDelegate.shared.sharedViewingKey],
|
||||
walletBirthday: DemoAppConfig.birthdayHeight
|
||||
walletBirthday: DemoAppConfig.defaultBirthdayHeight
|
||||
) { result in
|
||||
loggerProxy.debug("Prepare result: \(result)")
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ class SendViewController: UIViewController {
|
|||
networkType: kZcashNetwork.networkType
|
||||
)
|
||||
.deriveUnifiedSpendingKey(
|
||||
seed: DemoAppConfig.seed,
|
||||
seed: DemoAppConfig.defaultSeed,
|
||||
accountIndex: 0
|
||||
)
|
||||
else {
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
//
|
||||
// SyncBlocksListViewController.swift
|
||||
// ZcashLightClientSample
|
||||
//
|
||||
// Created by Michal Fousek on 24.03.2023.
|
||||
// Copyright © 2023 Electric Coin Company. All rights reserved.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import UIKit
|
||||
import ZcashLightClientKit
|
||||
|
||||
// swiftlint:disable force_try force_cast
|
||||
|
||||
class SyncBlocksListViewController: UIViewController {
|
||||
@IBOutlet var table: UITableView!
|
||||
@IBOutlet var loadingLabel: UILabel!
|
||||
@IBOutlet var loadingIndicator: UIActivityIndicatorView!
|
||||
|
||||
var synchronizers: [Synchronizer] = []
|
||||
var synchronizerData: [DemoAppConfig.SynchronizerInitData] = []
|
||||
var cancellables: [AnyCancellable] = []
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
table.isHidden = true
|
||||
loadingLabel.isHidden = false
|
||||
loadingIndicator.startAnimating()
|
||||
|
||||
navigationItem.title = "List of synchronizers"
|
||||
|
||||
synchronizerData = [
|
||||
DemoAppConfig.SynchronizerInitData(alias: .default, birthday: DemoAppConfig.defaultBirthdayHeight, seed: DemoAppConfig.defaultSeed)
|
||||
] + DemoAppConfig.otherSynchronizers
|
||||
|
||||
makeSynchronizers() { [weak self] synchronizers in
|
||||
self?.synchronizers = synchronizers
|
||||
self?.table.reloadData()
|
||||
|
||||
self?.loadingLabel.isHidden = true
|
||||
self?.loadingIndicator.stopAnimating()
|
||||
self?.table.isHidden = false
|
||||
|
||||
self?.subscribeToSynchronizers()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
cancellables = []
|
||||
Task(priority: .userInitiated) {
|
||||
for synchronizer in synchronizers {
|
||||
await synchronizer.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func didTapOnButton(index: Int) async {
|
||||
let synchronizerData = synchronizerData[index]
|
||||
let synchronizer = synchronizers[index]
|
||||
let syncStatus = synchronizer.latestState.syncStatus
|
||||
|
||||
loggerProxy.debug("Processing synchronizer with alias \(synchronizer.alias.description) \(index)")
|
||||
|
||||
switch syncStatus {
|
||||
case .stopped, .unprepared, .synced, .disconnected, .error:
|
||||
do {
|
||||
if syncStatus == .unprepared {
|
||||
let viewingKey = try! DerivationTool(networkType: kZcashNetwork.networkType)
|
||||
.deriveUnifiedSpendingKey(seed: synchronizerData.seed, accountIndex: 0)
|
||||
.deriveFullViewingKey()
|
||||
|
||||
_ = try! await synchronizer.prepare(
|
||||
with: synchronizerData.seed,
|
||||
viewingKeys: [viewingKey],
|
||||
walletBirthday: synchronizerData.birthday
|
||||
)
|
||||
}
|
||||
|
||||
try await synchronizer.start(retry: false)
|
||||
} catch {
|
||||
loggerProxy.error("Can't start synchronizer: \(error)")
|
||||
}
|
||||
case .syncing, .enhancing, .fetching:
|
||||
await synchronizer.stop()
|
||||
}
|
||||
}
|
||||
|
||||
private func makeSynchronizers(completion: @escaping ([Synchronizer]) -> Void) {
|
||||
DispatchQueue.global().async { [weak self] in
|
||||
guard let self else { return completion([]) }
|
||||
let otherSynchronizers = DemoAppConfig.otherSynchronizers.map { self.makeSynchronizer(from: $0) }
|
||||
let synchronizers = [AppDelegate.shared.sharedSynchronizer] + otherSynchronizers
|
||||
|
||||
DispatchQueue.main.async {
|
||||
completion(synchronizers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func makeSynchronizer(from data: DemoAppConfig.SynchronizerInitData) -> Synchronizer {
|
||||
let initializer = Initializer(
|
||||
cacheDbURL: nil,
|
||||
fsBlockDbRoot: try! fsBlockDbRootURLHelper(),
|
||||
dataDbURL: try! dataDbURLHelper(),
|
||||
pendingDbURL: try! pendingDbURLHelper(),
|
||||
endpoint: DemoAppConfig.endpoint,
|
||||
network: kZcashNetwork,
|
||||
spendParamsURL: try! spendParamsURLHelper(),
|
||||
outputParamsURL: try! outputParamsURLHelper(),
|
||||
saplingParamsSourceURL: SaplingParamsSourceURL.default,
|
||||
alias: data.alias,
|
||||
logLevel: .debug
|
||||
)
|
||||
|
||||
return SDKSynchronizer(initializer: initializer)
|
||||
}
|
||||
|
||||
private func subscribeToSynchronizers() {
|
||||
for (index, synchronizer) in synchronizers.enumerated() {
|
||||
synchronizer.stateStream
|
||||
.throttle(for: .seconds(0.3), scheduler: DispatchQueue.main, latest: true)
|
||||
.sink(
|
||||
receiveValue: { [weak self] _ in
|
||||
self?.table.reloadRows(at: [IndexPath(item: index, section: 0)], with: .none)
|
||||
}
|
||||
)
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SyncBlocksListViewController: UITableViewDataSource {
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return synchronizers.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = table.dequeueReusableCell(withIdentifier: "SynchronizerCell", for: indexPath) as! SynchronizerCell
|
||||
let synchronizer = synchronizers[indexPath.row]
|
||||
|
||||
let synchronizerStatus = synchronizer.latestState.syncStatus
|
||||
|
||||
cell.alias.text = synchronizer.alias.description
|
||||
cell.status.text = synchronizerStatus.text
|
||||
|
||||
let image: UIImage?
|
||||
switch synchronizerStatus {
|
||||
case .unprepared, .synced, .stopped, .disconnected, .error:
|
||||
image = UIImage(systemName: "play.circle")
|
||||
case .syncing, .enhancing, .fetching:
|
||||
image = UIImage(systemName: "stop.circle")
|
||||
}
|
||||
|
||||
cell.button.setTitle("", for: .normal)
|
||||
cell.button.setTitle("", for: .highlighted)
|
||||
cell.button.setImage(image, for: .normal)
|
||||
cell.button.setImage(image, for: .highlighted)
|
||||
|
||||
cell.indexPath = indexPath
|
||||
cell.didTapOnButton = { [weak self] indexPath in
|
||||
Task {
|
||||
await self?.didTapOnButton(index: indexPath.row)
|
||||
}
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
extension SyncStatus {
|
||||
var text: String {
|
||||
switch self {
|
||||
case let .syncing(progress):
|
||||
return "Syncing 🤖 \(floor(progress.progress * 1000) / 10)%"
|
||||
case let .error(error):
|
||||
return "error 💔 \(error.localizedDescription)"
|
||||
case .stopped:
|
||||
return "Stopped 🚫"
|
||||
case .synced:
|
||||
return "Synced 😎"
|
||||
case let .enhancing(progress):
|
||||
return "Enhancing 🤖 \(floor(progress.progress * 1000) / 10)%"
|
||||
case .fetching:
|
||||
return "Fetching UTXOs"
|
||||
case .unprepared:
|
||||
return "Unprepared"
|
||||
case .disconnected:
|
||||
return "Disconnected"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -82,7 +82,7 @@ class SyncBlocksViewController: UIViewController {
|
|||
progressLabel.text = "\(floor(progress.progress * 1000) / 10)%"
|
||||
|
||||
if let currentMetric {
|
||||
let report = SDKMetrics.shared.popBlock(operation: currentMetric)?.last
|
||||
let report = synchronizer.metrics.popBlock(operation: currentMetric)?.last
|
||||
metricLabel.text = currentMetricName + report.debugDescription
|
||||
}
|
||||
|
||||
|
@ -114,7 +114,7 @@ class SyncBlocksViewController: UIViewController {
|
|||
|
||||
func accumulateMetrics() {
|
||||
guard let currentMetric else { return }
|
||||
if let reports = SDKMetrics.shared.popBlock(operation: currentMetric) {
|
||||
if let reports = synchronizer.metrics.popBlock(operation: currentMetric) {
|
||||
for report in reports {
|
||||
accumulatedMetrics = .accumulate(accumulatedMetrics, current: report)
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ class SyncBlocksViewController: UIViewController {
|
|||
}
|
||||
|
||||
func overallSummary() {
|
||||
let cumulativeSummary = SDKMetrics.shared.cumulativeSummary()
|
||||
let cumulativeSummary = synchronizer.metrics.cumulativeSummary()
|
||||
|
||||
let downloadedBlocksReport = cumulativeSummary.downloadedBlocksReport ?? SDKMetrics.ReportSummary.zero
|
||||
let validatedBlocksReport = cumulativeSummary.validatedBlocksReport ?? SDKMetrics.ReportSummary.zero
|
||||
|
@ -157,13 +157,13 @@ class SyncBlocksViewController: UIViewController {
|
|||
if syncStatus == .unprepared {
|
||||
// swiftlint:disable:next force_try
|
||||
_ = try! await synchronizer.prepare(
|
||||
with: DemoAppConfig.seed,
|
||||
with: DemoAppConfig.defaultSeed,
|
||||
viewingKeys: [AppDelegate.shared.sharedViewingKey],
|
||||
walletBirthday: DemoAppConfig.birthdayHeight
|
||||
walletBirthday: DemoAppConfig.defaultBirthdayHeight
|
||||
)
|
||||
}
|
||||
|
||||
SDKMetrics.shared.enableMetrics()
|
||||
synchronizer.metrics.enableMetrics()
|
||||
try await synchronizer.start()
|
||||
updateUI()
|
||||
} catch {
|
||||
|
@ -172,7 +172,7 @@ class SyncBlocksViewController: UIViewController {
|
|||
}
|
||||
default:
|
||||
await synchronizer.stop()
|
||||
SDKMetrics.shared.disableMetrics()
|
||||
synchronizer.metrics.disableMetrics()
|
||||
updateUI()
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// SynchronizerCell.swift
|
||||
// ZcashLightClientSample
|
||||
//
|
||||
// Created by Michal Fousek on 24.03.2023.
|
||||
// Copyright © 2023 Electric Coin Company. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class SynchronizerCell: UITableViewCell {
|
||||
@IBOutlet var alias: UILabel!
|
||||
@IBOutlet var status: UILabel!
|
||||
@IBOutlet var button: UIButton!
|
||||
|
||||
var indexPath: IndexPath?
|
||||
var didTapOnButton: ((IndexPath) -> Void)?
|
||||
|
||||
@IBAction func buttonTap() {
|
||||
guard let indexPath else { return }
|
||||
didTapOnButton?(indexPath)
|
||||
}
|
||||
}
|
|
@ -154,6 +154,7 @@ actor CompactBlockProcessor {
|
|||
/// - parameter spendParamsURL: absolute file path of the sapling-spend.params file
|
||||
/// - parameter outputParamsURL: absolute file path of the sapling-output.params file
|
||||
struct Configuration {
|
||||
let alias: ZcashSynchronizerAlias
|
||||
let saplingParamsSourceURL: SaplingParamsSourceURL
|
||||
public var fsBlockCacheRoot: URL
|
||||
public var dataDb: URL
|
||||
|
@ -176,6 +177,7 @@ actor CompactBlockProcessor {
|
|||
}
|
||||
|
||||
init (
|
||||
alias: ZcashSynchronizerAlias,
|
||||
cacheDbURL: URL? = nil,
|
||||
fsBlockCacheRoot: URL,
|
||||
dataDb: URL,
|
||||
|
@ -190,6 +192,7 @@ actor CompactBlockProcessor {
|
|||
saplingActivation: BlockHeight,
|
||||
network: ZcashNetwork
|
||||
) {
|
||||
self.alias = alias
|
||||
self.fsBlockCacheRoot = fsBlockCacheRoot
|
||||
self.dataDb = dataDb
|
||||
self.spendParamsURL = spendParamsURL
|
||||
|
@ -207,6 +210,7 @@ actor CompactBlockProcessor {
|
|||
}
|
||||
|
||||
init(
|
||||
alias: ZcashSynchronizerAlias,
|
||||
fsBlockCacheRoot: URL,
|
||||
dataDb: URL,
|
||||
spendParamsURL: URL,
|
||||
|
@ -215,6 +219,7 @@ actor CompactBlockProcessor {
|
|||
walletBirthdayProvider: @escaping () -> BlockHeight,
|
||||
network: ZcashNetwork
|
||||
) {
|
||||
self.alias = alias
|
||||
self.fsBlockCacheRoot = fsBlockCacheRoot
|
||||
self.dataDb = dataDb
|
||||
self.spendParamsURL = spendParamsURL
|
||||
|
@ -269,6 +274,9 @@ actor CompactBlockProcessor {
|
|||
|
||||
private var afterSyncHooksManager = AfterSyncHooksManager()
|
||||
|
||||
let metrics: SDKMetrics
|
||||
let logger: Logger
|
||||
|
||||
/// Don't update this variable directly. Use `updateState()` method.
|
||||
var state: State = .stopped
|
||||
|
||||
|
@ -325,7 +333,7 @@ actor CompactBlockProcessor {
|
|||
|
||||
private var cancelableTask: Task<Void, Error>?
|
||||
|
||||
let internalSyncProgress = InternalSyncProgress(storage: UserDefaults.standard)
|
||||
private let internalSyncProgress: InternalSyncProgress
|
||||
|
||||
/// Initializes a CompactBlockProcessor instance
|
||||
/// - Parameters:
|
||||
|
@ -337,7 +345,9 @@ actor CompactBlockProcessor {
|
|||
service: LightWalletService,
|
||||
storage: CompactBlockRepository,
|
||||
backend: ZcashRustBackendWelding.Type,
|
||||
config: Configuration
|
||||
config: Configuration,
|
||||
metrics: SDKMetrics,
|
||||
logger: Logger
|
||||
) {
|
||||
self.init(
|
||||
service: service,
|
||||
|
@ -347,19 +357,27 @@ actor CompactBlockProcessor {
|
|||
repository: TransactionRepositoryBuilder.build(
|
||||
dataDbURL: config.dataDb
|
||||
),
|
||||
accountRepository: AccountRepositoryBuilder.build(dataDbURL: config.dataDb, readOnly: true)
|
||||
accountRepository: AccountRepositoryBuilder.build(dataDbURL: config.dataDb, readOnly: true, logger: logger),
|
||||
metrics: metrics,
|
||||
logger: logger
|
||||
)
|
||||
}
|
||||
|
||||
/// Initializes a CompactBlockProcessor instance from an Initialized object
|
||||
/// - Parameters:
|
||||
/// - initializer: an instance that complies to CompactBlockDownloading protocol
|
||||
init(initializer: Initializer, walletBirthdayProvider: @escaping () -> BlockHeight) {
|
||||
init(
|
||||
initializer: Initializer,
|
||||
metrics: SDKMetrics,
|
||||
logger: Logger,
|
||||
walletBirthdayProvider: @escaping () -> BlockHeight
|
||||
) {
|
||||
self.init(
|
||||
service: initializer.lightWalletService,
|
||||
storage: initializer.storage,
|
||||
backend: initializer.rustBackend,
|
||||
config: Configuration(
|
||||
alias: initializer.alias,
|
||||
fsBlockCacheRoot: initializer.fsBlockDbRoot,
|
||||
dataDb: initializer.dataDbURL,
|
||||
spendParamsURL: initializer.spendParamsURL,
|
||||
|
@ -369,7 +387,9 @@ actor CompactBlockProcessor {
|
|||
network: initializer.network
|
||||
),
|
||||
repository: initializer.transactionRepository,
|
||||
accountRepository: initializer.accountRepository
|
||||
accountRepository: initializer.accountRepository,
|
||||
metrics: metrics,
|
||||
logger: logger
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -379,14 +399,22 @@ actor CompactBlockProcessor {
|
|||
backend: ZcashRustBackendWelding.Type,
|
||||
config: Configuration,
|
||||
repository: TransactionRepository,
|
||||
accountRepository: AccountRepository
|
||||
accountRepository: AccountRepository,
|
||||
metrics: SDKMetrics,
|
||||
logger: Logger
|
||||
) {
|
||||
self.metrics = metrics
|
||||
self.logger = logger
|
||||
let internalSyncProgress = InternalSyncProgress(alias: config.alias, storage: UserDefaults.standard, logger: logger)
|
||||
self.internalSyncProgress = internalSyncProgress
|
||||
let blockDownloaderService = BlockDownloaderServiceImpl(service: service, storage: storage)
|
||||
let blockDownloader = BlockDownloaderImpl(
|
||||
service: service,
|
||||
downloaderService: blockDownloaderService,
|
||||
storage: storage,
|
||||
internalSyncProgress: internalSyncProgress
|
||||
internalSyncProgress: internalSyncProgress,
|
||||
metrics: metrics,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
self.blockDownloaderService = blockDownloaderService
|
||||
|
@ -397,7 +425,12 @@ actor CompactBlockProcessor {
|
|||
dataDB: config.dataDb,
|
||||
networkType: config.network.networkType
|
||||
)
|
||||
self.blockValidator = BlockValidatorImpl(config: blockValidatorConfig, rustBackend: backend)
|
||||
self.blockValidator = BlockValidatorImpl(
|
||||
config: blockValidatorConfig,
|
||||
rustBackend: backend,
|
||||
metrics: metrics,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let blockScannerConfig = BlockScannerConfig(
|
||||
fsBlockCacheRoot: config.fsBlockCacheRoot,
|
||||
|
@ -405,7 +438,13 @@ actor CompactBlockProcessor {
|
|||
networkType: config.network.networkType,
|
||||
scanningBatchSize: config.scanningBatchSize
|
||||
)
|
||||
self.blockScanner = BlockScannerImpl(config: blockScannerConfig, rustBackend: backend, transactionRepository: repository)
|
||||
self.blockScanner = BlockScannerImpl(
|
||||
config: blockScannerConfig,
|
||||
rustBackend: backend,
|
||||
transactionRepository: repository,
|
||||
metrics: metrics,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let blockEnhancerConfig = BlockEnhancerConfig(dataDb: config.dataDb, networkType: config.network.networkType)
|
||||
self.blockEnhancer = BlockEnhancerImpl(
|
||||
|
@ -413,7 +452,9 @@ actor CompactBlockProcessor {
|
|||
config: blockEnhancerConfig,
|
||||
internalSyncProgress: internalSyncProgress,
|
||||
rustBackend: backend,
|
||||
transactionRepository: repository
|
||||
transactionRepository: repository,
|
||||
metrics: metrics,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let utxoFetcherConfig = UTXOFetcherConfig(
|
||||
|
@ -426,7 +467,9 @@ actor CompactBlockProcessor {
|
|||
blockDownloaderService: blockDownloaderService,
|
||||
config: utxoFetcherConfig,
|
||||
internalSyncProgress: internalSyncProgress,
|
||||
rustBackend: backend
|
||||
rustBackend: backend,
|
||||
metrics: metrics,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let saplingParametersHandlerConfig = SaplingParametersHandlerConfig(
|
||||
|
@ -436,7 +479,11 @@ actor CompactBlockProcessor {
|
|||
spendParamsURL: config.spendParamsURL,
|
||||
saplingParamsSourceURL: config.saplingParamsSourceURL
|
||||
)
|
||||
self.saplingParametersHandler = SaplingParametersHandlerImpl(config: saplingParametersHandlerConfig, rustBackend: backend)
|
||||
self.saplingParametersHandler = SaplingParametersHandlerImpl(
|
||||
config: saplingParametersHandlerConfig,
|
||||
rustBackend: backend,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
self.service = service
|
||||
self.rustBackend = backend
|
||||
|
@ -517,19 +564,19 @@ actor CompactBlockProcessor {
|
|||
switch self.state {
|
||||
case .error(let error):
|
||||
// max attempts have been reached
|
||||
LoggerProxy.info("max retry attempts reached with error: \(error)")
|
||||
logger.info("max retry attempts reached with error: \(error)")
|
||||
await notifyError(CompactBlockProcessorError.maxAttemptsReached(attempts: self.maxAttempts))
|
||||
await updateState(.stopped)
|
||||
case .stopped:
|
||||
// max attempts have been reached
|
||||
LoggerProxy.info("max retry attempts reached")
|
||||
logger.info("max retry attempts reached")
|
||||
await notifyError(CompactBlockProcessorError.maxAttemptsReached(attempts: self.maxAttempts))
|
||||
case .synced:
|
||||
// max attempts have been reached
|
||||
LoggerProxy.warn("max retry attempts reached on synced state, this indicates malfunction")
|
||||
logger.warn("max retry attempts reached on synced state, this indicates malfunction")
|
||||
await notifyError(CompactBlockProcessorError.maxAttemptsReached(attempts: self.maxAttempts))
|
||||
case .syncing, .enhancing, .fetching, .handlingSaplingFiles:
|
||||
LoggerProxy.debug("Warning: compact block processor was started while busy!!!!")
|
||||
logger.debug("Warning: compact block processor was started while busy!!!!")
|
||||
afterSyncHooksManager.insert(hook: .anotherSync)
|
||||
}
|
||||
return
|
||||
|
@ -567,21 +614,21 @@ actor CompactBlockProcessor {
|
|||
///
|
||||
/// - Note: If this is called while sync is in progress then the sync process is stopped first and then rewind is executed.
|
||||
func rewind(context: AfterSyncHooksManager.RewindContext) async {
|
||||
LoggerProxy.debug("Starting rewid")
|
||||
logger.debug("Starting rewind")
|
||||
switch self.state {
|
||||
case .syncing, .enhancing, .fetching, .handlingSaplingFiles:
|
||||
LoggerProxy.debug("Stopping sync because of rewind")
|
||||
logger.debug("Stopping sync because of rewind")
|
||||
afterSyncHooksManager.insert(hook: .rewind(context))
|
||||
stop()
|
||||
|
||||
case .stopped, .error, .synced:
|
||||
LoggerProxy.debug("Sync doesn't run. Executing rewind.")
|
||||
logger.debug("Sync doesn't run. Executing rewind.")
|
||||
await doRewind(context: context)
|
||||
}
|
||||
}
|
||||
|
||||
private func doRewind(context: AfterSyncHooksManager.RewindContext) async {
|
||||
LoggerProxy.debug("Executing rewind.")
|
||||
logger.debug("Executing rewind.")
|
||||
let lastDownloaded = await internalSyncProgress.latestDownloadedBlockHeight
|
||||
let height = Int32(context.height ?? lastDownloaded)
|
||||
let nearestHeight = rustBackend.getNearestRewindHeight(
|
||||
|
@ -623,21 +670,21 @@ actor CompactBlockProcessor {
|
|||
// MARK: Wipe
|
||||
|
||||
func wipe(context: AfterSyncHooksManager.WipeContext) async {
|
||||
LoggerProxy.debug("Starting wipe")
|
||||
logger.debug("Starting wipe")
|
||||
switch self.state {
|
||||
case .syncing, .enhancing, .fetching, .handlingSaplingFiles:
|
||||
LoggerProxy.debug("Stopping sync because of wipe")
|
||||
logger.debug("Stopping sync because of wipe")
|
||||
afterSyncHooksManager.insert(hook: .wipe(context))
|
||||
stop()
|
||||
|
||||
case .stopped, .error, .synced:
|
||||
LoggerProxy.debug("Sync doesn't run. Executing wipe.")
|
||||
logger.debug("Sync doesn't run. Executing wipe.")
|
||||
await doWipe(context: context)
|
||||
}
|
||||
}
|
||||
|
||||
private func doWipe(context: AfterSyncHooksManager.WipeContext) async {
|
||||
LoggerProxy.debug("Executing wipe.")
|
||||
logger.debug("Executing wipe.")
|
||||
context.prewipe()
|
||||
|
||||
await updateState(.stopped)
|
||||
|
@ -691,7 +738,7 @@ actor CompactBlockProcessor {
|
|||
do {
|
||||
let totalProgressRange = computeTotalProgressRange(from: ranges)
|
||||
|
||||
LoggerProxy.debug("""
|
||||
logger.debug("""
|
||||
Syncing with ranges:
|
||||
downloaded but not scanned: \
|
||||
\(ranges.downloadedButUnscannedRange?.lowerBound ?? -1)...\(ranges.downloadedButUnscannedRange?.upperBound ?? -1)
|
||||
|
@ -715,7 +762,7 @@ actor CompactBlockProcessor {
|
|||
}
|
||||
|
||||
if let range = ranges.downloadedButUnscannedRange {
|
||||
LoggerProxy.debug("Starting scan with downloaded but not scanned blocks with range: \(range.lowerBound)...\(range.upperBound)")
|
||||
logger.debug("Starting scan with downloaded but not scanned blocks with range: \(range.lowerBound)...\(range.upperBound)")
|
||||
try await blockScanner.scanBlocks(at: range, totalProgressRange: totalProgressRange) { [weak self] lastScannedHeight in
|
||||
let progress = BlockProgress(
|
||||
startHeight: totalProgressRange.lowerBound,
|
||||
|
@ -727,13 +774,13 @@ actor CompactBlockProcessor {
|
|||
}
|
||||
|
||||
if let range = ranges.downloadAndScanRange {
|
||||
LoggerProxy.debug("Starting sync with range: \(range.lowerBound)...\(range.upperBound)")
|
||||
logger.debug("Starting sync with range: \(range.lowerBound)...\(range.upperBound)")
|
||||
try await downloadAndScanBlocks(at: range, totalProgressRange: totalProgressRange)
|
||||
}
|
||||
|
||||
if let range = ranges.enhanceRange {
|
||||
anyActionExecuted = true
|
||||
LoggerProxy.debug("Enhancing with range: \(range.lowerBound)...\(range.upperBound)")
|
||||
logger.debug("Enhancing with range: \(range.lowerBound)...\(range.upperBound)")
|
||||
await updateState(.enhancing)
|
||||
let transactions = try await blockEnhancer.enhance(at: range) { [weak self] progress in
|
||||
await self?.notifyProgress(.enhance(progress))
|
||||
|
@ -743,34 +790,34 @@ actor CompactBlockProcessor {
|
|||
|
||||
if let range = ranges.fetchUTXORange {
|
||||
anyActionExecuted = true
|
||||
LoggerProxy.debug("Fetching UTXO with range: \(range.lowerBound)...\(range.upperBound)")
|
||||
logger.debug("Fetching UTXO with range: \(range.lowerBound)...\(range.upperBound)")
|
||||
await updateState(.fetching)
|
||||
let result = try await utxoFetcher.fetch(at: range)
|
||||
await send(event: .storedUTXOs(result))
|
||||
}
|
||||
|
||||
LoggerProxy.debug("Fetching sapling parameters")
|
||||
logger.debug("Fetching sapling parameters")
|
||||
await updateState(.handlingSaplingFiles)
|
||||
try await saplingParametersHandler.handleIfNeeded()
|
||||
|
||||
LoggerProxy.debug("Clearing cache")
|
||||
logger.debug("Clearing cache")
|
||||
try await clearCompactBlockCache()
|
||||
|
||||
if !Task.isCancelled {
|
||||
await processBatchFinished(height: anyActionExecuted ? ranges.latestBlockHeight : nil)
|
||||
}
|
||||
} catch {
|
||||
LoggerProxy.error("Sync failed with error: \(error)")
|
||||
logger.error("Sync failed with error: \(error)")
|
||||
|
||||
if Task.isCancelled {
|
||||
LoggerProxy.info("Processing cancelled.")
|
||||
logger.info("Processing cancelled.")
|
||||
await updateState(.stopped)
|
||||
await handleAfterSyncHooks()
|
||||
} else {
|
||||
if case BlockValidatorError.validationFailed(let height) = error {
|
||||
await validationFailed(at: height)
|
||||
} else {
|
||||
LoggerProxy.error("processing failed with error: \(error)")
|
||||
logger.error("processing failed with error: \(error)")
|
||||
await fail(error)
|
||||
}
|
||||
}
|
||||
|
@ -787,7 +834,7 @@ actor CompactBlockProcessor {
|
|||
} else if let rewindContext = afterSyncHooksManager.shouldExecuteRewindHook() {
|
||||
await doRewind(context: rewindContext)
|
||||
} else if afterSyncHooksManager.shouldExecuteAnotherSyncHook() {
|
||||
LoggerProxy.debug("Starting new sync.")
|
||||
logger.debug("Starting new sync.")
|
||||
await nextBatch()
|
||||
}
|
||||
}
|
||||
|
@ -809,7 +856,7 @@ actor CompactBlockProcessor {
|
|||
for i in 0..<loopsCount {
|
||||
let processingRange = computeSingleLoopDownloadRange(fullRange: range, loopCounter: i, batchSize: batchSize)
|
||||
|
||||
LoggerProxy.debug("Sync loop #\(i + 1) range: \(processingRange.lowerBound)...\(processingRange.upperBound)")
|
||||
logger.debug("Sync loop #\(i + 1) range: \(processingRange.lowerBound)...\(processingRange.upperBound)")
|
||||
|
||||
try await blockDownloader.downloadAndStoreBlocks(
|
||||
using: downloadStream,
|
||||
|
@ -822,7 +869,7 @@ actor CompactBlockProcessor {
|
|||
try await blockValidator.validate()
|
||||
} catch {
|
||||
guard let validationError = error as? BlockValidatorError else {
|
||||
LoggerProxy.error("Block validation failed with generic error: \(error)")
|
||||
logger.error("Block validation failed with generic error: \(error)")
|
||||
throw error
|
||||
}
|
||||
|
||||
|
@ -834,7 +881,7 @@ actor CompactBlockProcessor {
|
|||
throw genericError
|
||||
|
||||
case .failedWithUnknownError:
|
||||
LoggerProxy.error("validation failed without a specific error")
|
||||
logger.error("validation failed without a specific error")
|
||||
throw CompactBlockProcessorError.generalError(message: "validation failed without a specific error")
|
||||
}
|
||||
}
|
||||
|
@ -849,7 +896,7 @@ actor CompactBlockProcessor {
|
|||
await self?.notifyProgress(.syncing(progress))
|
||||
}
|
||||
} catch {
|
||||
LoggerProxy.error("Scanning failed with error: \(error)")
|
||||
logger.error("Scanning failed with error: \(error)")
|
||||
throw error
|
||||
}
|
||||
|
||||
|
@ -898,7 +945,7 @@ actor CompactBlockProcessor {
|
|||
}
|
||||
|
||||
func notifyProgress(_ progress: CompactBlockProgress) async {
|
||||
LoggerProxy.debug("progress: \(progress)")
|
||||
logger.debug("progress: \(progress)")
|
||||
await send(event: .progressUpdated(progress))
|
||||
}
|
||||
|
||||
|
@ -917,7 +964,7 @@ actor CompactBlockProcessor {
|
|||
|
||||
func severeFailure(_ error: Error) async {
|
||||
cancelableTask?.cancel()
|
||||
LoggerProxy.error("show stopper failure: \(error)")
|
||||
logger.error("show stopper failure: \(error)")
|
||||
self.backoffTimer?.invalidate()
|
||||
self.retryAttempts = config.retries
|
||||
self.processingError = error
|
||||
|
@ -927,7 +974,7 @@ actor CompactBlockProcessor {
|
|||
|
||||
func fail(_ error: Error) async {
|
||||
// TODO: [#713] specify: failure. https://github.com/zcash/ZcashLightClientKit/issues/713
|
||||
LoggerProxy.error("\(error)")
|
||||
logger.error("\(error)")
|
||||
cancelableTask?.cancel()
|
||||
self.retryAttempts += 1
|
||||
self.processingError = error
|
||||
|
@ -981,7 +1028,7 @@ actor CompactBlockProcessor {
|
|||
await self.processNewBlocks(ranges: ranges)
|
||||
case let .wait(latestHeight, latestDownloadHeight):
|
||||
// Lightwalletd might be syncing
|
||||
LoggerProxy.info(
|
||||
logger.info(
|
||||
"Lightwalletd might be syncing: latest downloaded block height is: \(latestDownloadHeight) " +
|
||||
"while latest blockheight is reported at: \(latestHeight)"
|
||||
)
|
||||
|
@ -1045,7 +1092,7 @@ actor CompactBlockProcessor {
|
|||
|
||||
private func clearCompactBlockCache() async throws {
|
||||
try await storage.clear()
|
||||
LoggerProxy.info("Cache removed")
|
||||
logger.info("Cache removed")
|
||||
}
|
||||
|
||||
private func setTimer() async {
|
||||
|
@ -1058,7 +1105,7 @@ actor CompactBlockProcessor {
|
|||
Task { [self] in
|
||||
guard let self else { return }
|
||||
if await self.shouldStart {
|
||||
LoggerProxy.debug(
|
||||
self.logger.debug(
|
||||
"""
|
||||
Timer triggered: Starting compact Block processor!.
|
||||
Processor State: \(await self.state)
|
||||
|
@ -1230,7 +1277,7 @@ extension CompactBlockProcessor {
|
|||
skipped.append(utxo)
|
||||
}
|
||||
} catch {
|
||||
LoggerProxy.info("failed to put utxo - error: \(error)")
|
||||
logger.info("failed to put utxo - error: \(error)")
|
||||
skipped.append(utxo)
|
||||
}
|
||||
}
|
||||
|
@ -1394,6 +1441,13 @@ extension CompactBlockProcessor {
|
|||
throw CacheDbMigrationError.fsCacheMigrationFailedSameURL
|
||||
}
|
||||
|
||||
// Instance with alias `default` is same as instance before the Alias was introduced. So it makes sense that only this instance handles
|
||||
// legacy cache DB. Any instance with different than `default` alias was created after the Alias was introduced and at this point legacy
|
||||
// cache DB is't anymore. So there is nothing to migrate for instances with not default Alias.
|
||||
guard config.alias == .default else {
|
||||
return
|
||||
}
|
||||
|
||||
// if the URL provided is not readable, it means that the client has a reference
|
||||
// to the cacheDb file but it has been deleted in a prior sync cycle. there's
|
||||
// nothing to do here.
|
||||
|
|
|
@ -20,13 +20,16 @@ class MigrationManager {
|
|||
|
||||
var pendingDb: ConnectionProvider
|
||||
var network: NetworkType
|
||||
let logger: Logger
|
||||
|
||||
init(
|
||||
pendingDbConnection: ConnectionProvider,
|
||||
networkType: NetworkType
|
||||
networkType: NetworkType,
|
||||
logger: Logger
|
||||
) {
|
||||
self.pendingDb = pendingDbConnection
|
||||
self.network = networkType
|
||||
self.logger = logger
|
||||
}
|
||||
|
||||
func performMigration() throws {
|
||||
|
@ -39,7 +42,7 @@ private extension MigrationManager {
|
|||
// getUserVersion returns a default value of zero for an unmigrated database.
|
||||
let currentPendingDbVersion = try pendingDb.connection().getUserVersion()
|
||||
|
||||
LoggerProxy.debug(
|
||||
logger.debug(
|
||||
"Attempting to perform migration for pending Db - currentVersion: \(currentPendingDbVersion)." +
|
||||
"Latest version is: \(Self.nextPendingDbMigration.rawValue - 1)"
|
||||
)
|
||||
|
|
|
@ -10,8 +10,6 @@ import Foundation
|
|||
import SQLite
|
||||
|
||||
class DatabaseStorageManager {
|
||||
static var shared = DatabaseStorageManager()
|
||||
|
||||
private var readOnly: [URL: Connection] = [:]
|
||||
private var readWrite: [URL: Connection] = [:]
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@ struct BlockDownloaderImpl {
|
|||
let downloaderService: BlockDownloaderService
|
||||
let storage: CompactBlockRepository
|
||||
let internalSyncProgress: InternalSyncProgress
|
||||
let metrics: SDKMetrics
|
||||
let logger: Logger
|
||||
}
|
||||
|
||||
extension BlockDownloaderImpl: BlockDownloader {
|
||||
|
@ -62,14 +64,14 @@ extension BlockDownloaderImpl: BlockDownloader {
|
|||
totalProgressRange: CompactBlockRange
|
||||
) async throws {
|
||||
var buffer: [ZcashCompactBlock] = []
|
||||
LoggerProxy.debug("Downloading blocks in range: \(range.lowerBound)...\(range.upperBound)")
|
||||
logger.debug("Downloading blocks in range: \(range.lowerBound)...\(range.upperBound)")
|
||||
|
||||
var startTime = Date()
|
||||
var counter = 0
|
||||
var lastDownloadedBlockHeight = -1
|
||||
|
||||
let pushMetrics: (BlockHeight, Date, Date) -> Void = { lastDownloadedBlockHeight, startTime, finishTime in
|
||||
SDKMetrics.shared.pushProgressReport(
|
||||
metrics.pushProgressReport(
|
||||
progress: BlockProgress(
|
||||
startHeight: totalProgressRange.lowerBound,
|
||||
targetHeight: totalProgressRange.upperBound,
|
||||
|
|
|
@ -29,15 +29,17 @@ struct BlockEnhancerImpl {
|
|||
let internalSyncProgress: InternalSyncProgress
|
||||
let rustBackend: ZcashRustBackendWelding.Type
|
||||
let transactionRepository: TransactionRepository
|
||||
let metrics: SDKMetrics
|
||||
let logger: Logger
|
||||
|
||||
private func enhance(transaction: ZcashTransaction.Overview) async throws -> ZcashTransaction.Overview {
|
||||
LoggerProxy.debug("Zoom.... Enhance... Tx: \(transaction.rawID.toHexStringTxId())")
|
||||
logger.debug("Zoom.... Enhance... Tx: \(transaction.rawID.toHexStringTxId())")
|
||||
|
||||
let fetchedTransaction = try await blockDownloaderService.fetchTransaction(txId: transaction.rawID)
|
||||
|
||||
let transactionID = fetchedTransaction.rawID.toHexStringTxId()
|
||||
let block = String(describing: transaction.minedHeight)
|
||||
LoggerProxy.debug("Decrypting and storing transaction id: \(transactionID) block: \(block)")
|
||||
logger.debug("Decrypting and storing transaction id: \(transactionID) block: \(block)")
|
||||
|
||||
let decryptionResult = rustBackend.decryptAndStoreTransaction(
|
||||
dbData: config.dataDb,
|
||||
|
@ -78,7 +80,7 @@ extension BlockEnhancerImpl: BlockEnhancer {
|
|||
func enhance(at range: CompactBlockRange, didEnhance: (EnhancementProgress) async -> Void) async throws -> [ZcashTransaction.Overview] {
|
||||
try Task.checkCancellation()
|
||||
|
||||
LoggerProxy.debug("Started Enhancing range: \(range)")
|
||||
logger.debug("Started Enhancing range: \(range)")
|
||||
|
||||
var retries = 0
|
||||
let maxRetries = 5
|
||||
|
@ -90,7 +92,7 @@ extension BlockEnhancerImpl: BlockEnhancer {
|
|||
|
||||
guard !transactions.isEmpty else {
|
||||
await internalSyncProgress.set(range.upperBound, .latestEnhancedHeight)
|
||||
LoggerProxy.debug("no transactions detected on range: \(range.lowerBound)...\(range.upperBound)")
|
||||
logger.debug("no transactions detected on range: \(range.lowerBound)...\(range.upperBound)")
|
||||
return []
|
||||
}
|
||||
|
||||
|
@ -116,7 +118,7 @@ extension BlockEnhancerImpl: BlockEnhancer {
|
|||
}
|
||||
} catch {
|
||||
retries += 1
|
||||
LoggerProxy.error("could not enhance txId \(transaction.rawID.toHexStringTxId()) - Error: \(error)")
|
||||
logger.error("could not enhance txId \(transaction.rawID.toHexStringTxId()) - Error: \(error)")
|
||||
if retries > maxRetries {
|
||||
throw error
|
||||
}
|
||||
|
@ -124,7 +126,7 @@ extension BlockEnhancerImpl: BlockEnhancer {
|
|||
}
|
||||
}
|
||||
|
||||
SDKMetrics.shared.pushProgressReport(
|
||||
metrics.pushProgressReport(
|
||||
progress: BlockProgress(
|
||||
startHeight: range.lowerBound,
|
||||
targetHeight: range.upperBound,
|
||||
|
@ -136,14 +138,14 @@ extension BlockEnhancerImpl: BlockEnhancer {
|
|||
operation: .enhancement
|
||||
)
|
||||
} catch {
|
||||
LoggerProxy.error("error enhancing transactions! \(error)")
|
||||
logger.error("error enhancing transactions! \(error)")
|
||||
throw error
|
||||
}
|
||||
|
||||
await internalSyncProgress.set(range.upperBound, .latestEnhancedHeight)
|
||||
|
||||
if Task.isCancelled {
|
||||
LoggerProxy.debug("Warning: compactBlockEnhancement on range \(range) cancelled")
|
||||
logger.debug("Warning: compactBlockEnhancement on range \(range) cancelled")
|
||||
}
|
||||
|
||||
return (try? transactionRepository.find(in: range, limit: Int.max, kind: .all)) ?? []
|
||||
|
|
|
@ -28,6 +28,8 @@ struct UTXOFetcherImpl {
|
|||
let config: UTXOFetcherConfig
|
||||
let internalSyncProgress: InternalSyncProgress
|
||||
let rustBackend: ZcashRustBackendWelding.Type
|
||||
let metrics: SDKMetrics
|
||||
let logger: Logger
|
||||
}
|
||||
|
||||
extension UTXOFetcherImpl: UTXOFetcher {
|
||||
|
@ -77,12 +79,12 @@ extension UTXOFetcherImpl: UTXOFetcher {
|
|||
|
||||
await internalSyncProgress.set(utxo.height, .latestUTXOFetchedHeight)
|
||||
} catch {
|
||||
LoggerProxy.error("failed to put utxo - error: \(error)")
|
||||
logger.error("failed to put utxo - error: \(error)")
|
||||
skipped.append(utxo)
|
||||
}
|
||||
}
|
||||
|
||||
SDKMetrics.shared.pushProgressReport(
|
||||
metrics.pushProgressReport(
|
||||
progress: BlockProgress(
|
||||
startHeight: range.lowerBound,
|
||||
targetHeight: range.upperBound,
|
||||
|
@ -99,7 +101,7 @@ extension UTXOFetcherImpl: UTXOFetcher {
|
|||
await internalSyncProgress.set(range.upperBound, .latestUTXOFetchedHeight)
|
||||
|
||||
if Task.isCancelled {
|
||||
LoggerProxy.debug("Warning: fetchUnspentTxOutputs on range \(range) cancelled")
|
||||
logger.debug("Warning: fetchUnspentTxOutputs on range \(range) cancelled")
|
||||
}
|
||||
|
||||
return result
|
||||
|
|
|
@ -13,6 +13,7 @@ class FSCompactBlockRepository {
|
|||
let contentProvider: SortedDirectoryListing
|
||||
let fileWriter: FSBlockFileWriter
|
||||
let metadataStore: FSMetadataStore
|
||||
let logger: Logger
|
||||
|
||||
private let fileManager = FileManager()
|
||||
private let storageBatchSize = 10
|
||||
|
@ -33,13 +34,15 @@ class FSCompactBlockRepository {
|
|||
metadataStore: FSMetadataStore,
|
||||
blockDescriptor: ZcashCompactBlockDescriptor,
|
||||
contentProvider: SortedDirectoryListing,
|
||||
fileWriter: FSBlockFileWriter = .atomic
|
||||
fileWriter: FSBlockFileWriter = .atomic,
|
||||
logger: Logger
|
||||
) {
|
||||
self.fsBlockDbRoot = fsBlockDbRoot
|
||||
self.metadataStore = metadataStore
|
||||
self.blockDescriptor = blockDescriptor
|
||||
self.contentProvider = contentProvider
|
||||
self.fileWriter = fileWriter
|
||||
self.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,7 +78,7 @@ extension FSCompactBlockRepository: CompactBlockRepository {
|
|||
do {
|
||||
try self.fileWriter.writeToURL(block.data, blockURL)
|
||||
} catch {
|
||||
LoggerProxy.error("Failed to write block: \(block.height) to path: \(blockURL.path) with error: \(error)")
|
||||
logger.error("Failed to write block: \(block.height) to path: \(blockURL.path) with error: \(error)")
|
||||
throw CompactBlockRepositoryError.failedToWriteBlock(block)
|
||||
}
|
||||
|
||||
|
@ -90,7 +93,7 @@ extension FSCompactBlockRepository: CompactBlockRepository {
|
|||
// if there are any remaining blocks on the cache store them
|
||||
try await self.metadataStore.saveBlocksMeta(savedBlocks)
|
||||
} catch {
|
||||
LoggerProxy.error("failed to Block save to cache error: \(error.localizedDescription)")
|
||||
logger.error("failed to Block save to cache error: \(error.localizedDescription)")
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +118,9 @@ extension FSCompactBlockRepository: CompactBlockRepository {
|
|||
}
|
||||
|
||||
func clear() async throws {
|
||||
try self.fileManager.removeItem(at: self.fsBlockDbRoot)
|
||||
if self.fileManager.fileExists(atPath: self.fsBlockDbRoot.path) {
|
||||
try self.fileManager.removeItem(at: self.fsBlockDbRoot)
|
||||
}
|
||||
try create()
|
||||
}
|
||||
}
|
||||
|
@ -210,12 +215,13 @@ struct FSMetadataStore {
|
|||
}
|
||||
|
||||
extension FSMetadataStore {
|
||||
static func live(fsBlockDbRoot: URL, rustBackend: ZcashRustBackendWelding.Type) -> FSMetadataStore {
|
||||
static func live(fsBlockDbRoot: URL, rustBackend: ZcashRustBackendWelding.Type, logger: Logger) -> FSMetadataStore {
|
||||
FSMetadataStore { blocks in
|
||||
try await FSMetadataStore.saveBlocksMeta(
|
||||
blocks,
|
||||
fsBlockDbRoot: fsBlockDbRoot,
|
||||
rustBackend: rustBackend
|
||||
rustBackend: rustBackend,
|
||||
logger: logger
|
||||
)
|
||||
} rewindToHeight: { height in
|
||||
guard rustBackend.rewindCacheToHeight(fsBlockDbRoot: fsBlockDbRoot, height: Int32(height)) else {
|
||||
|
@ -235,7 +241,12 @@ extension FSMetadataStore {
|
|||
/// - Throws `CompactBlockRepositoryError.failedToWriteMetadata` if the
|
||||
/// operation fails. the underlying error is logged through `LoggerProxy`
|
||||
/// - Note: This shouldn't be called in parallel by many threads or workers. Won't do anything if `blocks` is empty
|
||||
static func saveBlocksMeta(_ blocks: [ZcashCompactBlock], fsBlockDbRoot: URL, rustBackend: ZcashRustBackendWelding.Type) async throws {
|
||||
static func saveBlocksMeta(
|
||||
_ blocks: [ZcashCompactBlock],
|
||||
fsBlockDbRoot: URL,
|
||||
rustBackend: ZcashRustBackendWelding.Type,
|
||||
logger: Logger
|
||||
) async throws {
|
||||
guard !blocks.isEmpty else { return }
|
||||
|
||||
do {
|
||||
|
@ -243,7 +254,7 @@ extension FSMetadataStore {
|
|||
throw CompactBlockRepositoryError.failedToWriteMetadata
|
||||
}
|
||||
} catch {
|
||||
LoggerProxy.error("Failed to write metadata with error: \(error)")
|
||||
logger.error("Failed to write metadata with error: \(error)")
|
||||
throw CompactBlockRepositoryError.failedToWriteMetadata
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ protocol SaplingParametersHandler {
|
|||
struct SaplingParametersHandlerImpl {
|
||||
let config: SaplingParametersHandlerConfig
|
||||
let rustBackend: ZcashRustBackendWelding.Type
|
||||
let logger: Logger
|
||||
}
|
||||
|
||||
extension SaplingParametersHandlerImpl: SaplingParametersHandler {
|
||||
|
@ -46,7 +47,7 @@ extension SaplingParametersHandlerImpl: SaplingParametersHandler {
|
|||
// if sapling balance can't be detected of we fail to obtain the balance
|
||||
// for some reason we shall not proceed to download the parameters and
|
||||
// retry in the following attempt to sync.
|
||||
LoggerProxy.error("Couldn't Fetch shielded balance. Won't attempt to download sapling parameters")
|
||||
logger.error("Couldn't Fetch shielded balance. Won't attempt to download sapling parameters")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -54,7 +55,8 @@ extension SaplingParametersHandlerImpl: SaplingParametersHandler {
|
|||
spendURL: config.spendParamsURL,
|
||||
spendSourceURL: config.saplingParamsSourceURL.spendParamFileURL,
|
||||
outputURL: config.outputParamsURL,
|
||||
outputSourceURL: config.saplingParamsSourceURL.outputParamFileURL
|
||||
outputSourceURL: config.saplingParamsSourceURL.outputParamFileURL,
|
||||
logger: logger
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ struct BlockScannerImpl {
|
|||
let config: BlockScannerConfig
|
||||
let rustBackend: ZcashRustBackendWelding.Type
|
||||
let transactionRepository: TransactionRepository
|
||||
let metrics: SDKMetrics
|
||||
let logger: Logger
|
||||
}
|
||||
|
||||
extension BlockScannerImpl: BlockScanner {
|
||||
|
@ -50,7 +52,7 @@ extension BlockScannerImpl: BlockScanner {
|
|||
networkType: config.networkType
|
||||
) else {
|
||||
let error: Error = rustBackend.lastError() ?? CompactBlockProcessorError.unknown
|
||||
LoggerProxy.debug("block scanning failed with error: \(String(describing: error))")
|
||||
logger.debug("block scanning failed with error: \(String(describing: error))")
|
||||
throw error
|
||||
}
|
||||
let scanFinishTime = Date()
|
||||
|
@ -67,7 +69,7 @@ extension BlockScannerImpl: BlockScanner {
|
|||
progressHeight: lastScannedHeight
|
||||
)
|
||||
|
||||
SDKMetrics.shared.pushProgressReport(
|
||||
metrics.pushProgressReport(
|
||||
progress: progress,
|
||||
start: scanStartTime,
|
||||
end: scanFinishTime,
|
||||
|
@ -77,7 +79,7 @@ extension BlockScannerImpl: BlockScanner {
|
|||
|
||||
let heightCount = lastScannedHeight - previousScannedHeight
|
||||
let seconds = scanFinishTime.timeIntervalSinceReferenceDate - scanStartTime.timeIntervalSinceReferenceDate
|
||||
LoggerProxy.debug("Scanned \(heightCount) blocks in \(seconds) seconds")
|
||||
logger.debug("Scanned \(heightCount) blocks in \(seconds) seconds")
|
||||
}
|
||||
|
||||
await Task.yield()
|
||||
|
|
|
@ -41,24 +41,37 @@ actor InternalSyncProgress {
|
|||
case latestDownloadedBlockHeight
|
||||
case latestEnhancedHeight
|
||||
case latestUTXOFetchedHeight
|
||||
|
||||
func with(_ alias: ZcashSynchronizerAlias) -> String {
|
||||
switch alias {
|
||||
case .`default`:
|
||||
return self.rawValue
|
||||
case let .custom(rawAlias):
|
||||
return "\(self.rawValue)_\(rawAlias)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let alias: ZcashSynchronizerAlias
|
||||
private let storage: InternalSyncProgressStorage
|
||||
let logger: Logger
|
||||
|
||||
var latestDownloadedBlockHeight: BlockHeight { load(.latestDownloadedBlockHeight) }
|
||||
var latestEnhancedHeight: BlockHeight { load(.latestEnhancedHeight) }
|
||||
var latestUTXOFetchedHeight: BlockHeight { load(.latestUTXOFetchedHeight) }
|
||||
|
||||
init(storage: InternalSyncProgressStorage) {
|
||||
init(alias: ZcashSynchronizerAlias, storage: InternalSyncProgressStorage, logger: Logger) {
|
||||
self.alias = alias
|
||||
self.storage = storage
|
||||
self.logger = logger
|
||||
}
|
||||
|
||||
func load(_ key: Key) -> BlockHeight {
|
||||
storage.integer(forKey: key.rawValue)
|
||||
storage.integer(forKey: key.with(alias))
|
||||
}
|
||||
|
||||
func set(_ value: BlockHeight, _ key: Key) {
|
||||
storage.set(value, forKey: key.rawValue)
|
||||
storage.set(value, forKey: key.with(alias))
|
||||
storage.synchronize()
|
||||
}
|
||||
|
||||
|
@ -103,7 +116,7 @@ actor InternalSyncProgress {
|
|||
latestScannedHeight: BlockHeight,
|
||||
walletBirthday: BlockHeight
|
||||
) throws -> CompactBlockProcessor.NextState {
|
||||
LoggerProxy.debug("""
|
||||
logger.debug("""
|
||||
Init numbers:
|
||||
latestBlockHeight: \(latestBlockHeight)
|
||||
latestDownloadedHeight: \(latestDownloadedBlockHeight)
|
||||
|
@ -147,7 +160,7 @@ actor InternalSyncProgress {
|
|||
}
|
||||
|
||||
if latestScannedHeight > latestDownloadedBlockHeight {
|
||||
LoggerProxy.warn("""
|
||||
logger.warn("""
|
||||
InternalSyncProgress found inconsistent state.
|
||||
latestBlockHeight: \(latestBlockHeight)
|
||||
--> latestDownloadedHeight: \(latestDownloadedBlockHeight)
|
||||
|
|
|
@ -28,6 +28,8 @@ protocol BlockValidator {
|
|||
struct BlockValidatorImpl {
|
||||
let config: BlockValidatorConfig
|
||||
let rustBackend: ZcashRustBackendWelding.Type
|
||||
let metrics: SDKMetrics
|
||||
let logger: Logger
|
||||
}
|
||||
|
||||
extension BlockValidatorImpl: BlockValidator {
|
||||
|
@ -43,7 +45,7 @@ extension BlockValidatorImpl: BlockValidator {
|
|||
)
|
||||
let finishTime = Date()
|
||||
|
||||
SDKMetrics.shared.pushProgressReport(
|
||||
metrics.pushProgressReport(
|
||||
progress: BlockProgress(startHeight: 0, targetHeight: 0, progressHeight: 0),
|
||||
start: startTime,
|
||||
end: finishTime,
|
||||
|
@ -54,7 +56,7 @@ extension BlockValidatorImpl: BlockValidator {
|
|||
switch result {
|
||||
case 0:
|
||||
let rustError = rustBackend.lastError()
|
||||
LoggerProxy.debug("Block validation failed with error: \(String(describing: rustError))")
|
||||
logger.debug("Block validation failed with error: \(String(describing: rustError))")
|
||||
if let rustError {
|
||||
throw BlockValidatorError.failedWithError(rustError)
|
||||
} else {
|
||||
|
@ -62,11 +64,11 @@ extension BlockValidatorImpl: BlockValidator {
|
|||
}
|
||||
|
||||
case ZcashRustBackendWeldingConstants.validChain:
|
||||
LoggerProxy.debug("validateChainFinished")
|
||||
logger.debug("validateChainFinished")
|
||||
return
|
||||
|
||||
default:
|
||||
LoggerProxy.debug("Block validation failed at height: \(result)")
|
||||
logger.debug("Block validation failed at height: \(result)")
|
||||
throw BlockValidatorError.validationFailed(height: BlockHeight(result))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ import Foundation
|
|||
///
|
||||
/// If you are looking for documentation for a specific method or property look for it in the `Synchronizer` protocol.
|
||||
public protocol ClosureSynchronizer {
|
||||
var alias: ZcashSynchronizerAlias { get }
|
||||
|
||||
var latestState: SynchronizerState { get }
|
||||
var connectionState: ConnectionState { get }
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ public typealias Single = AnyPublisher
|
|||
///
|
||||
/// If you are looking for documentation for a specific method or property look for it in the `Synchronizer` protocol.
|
||||
public protocol CombineSynchronizer {
|
||||
var alias: ZcashSynchronizerAlias { get }
|
||||
|
||||
var latestState: SynchronizerState { get }
|
||||
var connectionState: ConnectionState { get }
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import Foundation
|
||||
import SQLite
|
||||
|
||||
struct PendingTransaction: PendingTransactionEntity, Decodable, Encodable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case toAddress = "to_address"
|
||||
|
@ -230,9 +231,11 @@ class PendingTransactionSQLDAO: PendingTransactionRepository {
|
|||
static let table = Table("pending_transactions")
|
||||
|
||||
var dbProvider: ConnectionProvider
|
||||
let logger: Logger
|
||||
|
||||
init(dbProvider: ConnectionProvider) {
|
||||
init(dbProvider: ConnectionProvider, logger: Logger) {
|
||||
self.dbProvider = dbProvider
|
||||
self.logger = logger
|
||||
}
|
||||
|
||||
func closeDBConnection() {
|
||||
|
@ -253,7 +256,7 @@ class PendingTransactionSQLDAO: PendingTransactionRepository {
|
|||
|
||||
let updatedRows = try dbProvider.connection().run(Self.table.filter(TableColumns.id == id).update(pendingTx))
|
||||
if updatedRows == 0 {
|
||||
LoggerProxy.error("attempted to update pending transactions but no rows were updated")
|
||||
logger.error("attempted to update pending transactions but no rows were updated")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -308,7 +311,7 @@ class PendingTransactionSQLDAO: PendingTransactionRepository {
|
|||
.run(transaction.update([TableColumns.minedHeight <- height]))
|
||||
|
||||
if updatedRows == 0 {
|
||||
LoggerProxy.error("attempted to update a row but none was updated")
|
||||
logger.error("attempted to update a row but none was updated")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,9 +55,11 @@ class AccountSQDAO: AccountRepository {
|
|||
let table = Table("accounts")
|
||||
|
||||
var dbProvider: ConnectionProvider
|
||||
let logger: Logger
|
||||
|
||||
init (dbProvider: ConnectionProvider) {
|
||||
init (dbProvider: ConnectionProvider, logger: Logger) {
|
||||
self.dbProvider = dbProvider
|
||||
self.logger = logger
|
||||
}
|
||||
|
||||
func getAll() throws -> [AccountEntity] {
|
||||
|
@ -79,7 +81,7 @@ class AccountSQDAO: AccountRepository {
|
|||
}
|
||||
let updatedRows = try dbProvider.connection().run(table.filter(TableColums.account == acc.account).update(acc))
|
||||
if updatedRows == 0 {
|
||||
LoggerProxy.error("attempted to update pending transactions but no rows were updated")
|
||||
logger.error("attempted to update pending transactions but no rows were updated")
|
||||
throw DatabaseStorageError.updateFailed
|
||||
}
|
||||
}
|
||||
|
@ -135,11 +137,19 @@ class CachingAccountDao: AccountRepository {
|
|||
}
|
||||
|
||||
enum AccountRepositoryBuilder {
|
||||
static func build(dataDbURL: URL, readOnly: Bool = false, caching: Bool = false) -> AccountRepository {
|
||||
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)))
|
||||
return CachingAccountDao(
|
||||
dao: AccountSQDAO(
|
||||
dbProvider: SimpleConnectionProvider(path: dataDbURL.path, readonly: readOnly),
|
||||
logger: logger
|
||||
)
|
||||
)
|
||||
} else {
|
||||
return AccountSQDAO(dbProvider: SimpleConnectionProvider(path: dataDbURL.path, readonly: readOnly))
|
||||
return AccountSQDAO(
|
||||
dbProvider: SimpleConnectionProvider(path: dataDbURL.path, readonly: readOnly),
|
||||
logger: logger
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,8 +40,6 @@ extension SynchronizerError: LocalizedError {
|
|||
return "We couldn't find this account number."
|
||||
case .lightwalletdValidationFailed(underlyingError: let underlyingError):
|
||||
return "We connected to the network but we couldn't verify the server. `lightwalletdValidationFailed` error: \(underlyingError)."
|
||||
case .wipeAttemptWhileProcessing:
|
||||
return "Can't execute wipe while sync process is in progress."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
// Created by Michal Fousek on 23.03.2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension URL {
|
||||
/// Try to update URLs with `alias`.
|
||||
///
|
||||
/// If the `default` alias is used then the URL isn't changed at all.
|
||||
/// If the `custom("anotherInstance")` is used then last path component or the URL is updated like this:
|
||||
/// - /some/path/to.file -> /some/path/c_anotherInstance_to.file
|
||||
/// - /some/path/to/directory -> /some/path/to/c_anotherInstance_directory
|
||||
///
|
||||
/// If the URLs can't be parsed then `nil` is returned.
|
||||
func updateLastPathComponent(with alias: ZcashSynchronizerAlias) -> URL? {
|
||||
let lastPathComponent = self.lastPathComponent
|
||||
guard !lastPathComponent.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch alias {
|
||||
case .`default`:
|
||||
// When using default alias everything should work as before aliases to be backwards compatible.
|
||||
return self
|
||||
|
||||
case .custom:
|
||||
let newLastPathComponent = "\(alias.description)_\(lastPathComponent)"
|
||||
return self.deletingLastPathComponent()
|
||||
.appendingPathComponent(newLastPathComponent)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,6 +17,8 @@ public enum InitializerError: Error {
|
|||
case dataDbInitFailed(Error)
|
||||
case accountInitFailed(Error)
|
||||
case invalidViewingKey(key: String)
|
||||
case aliasAlreadyInUse(ZcashSynchronizerAlias)
|
||||
case cantUpdateURLWithAlias(URL)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,20 +70,58 @@ public struct SaplingParamsSourceURL {
|
|||
}
|
||||
}
|
||||
|
||||
/// This identifies different instances of the synchronizer. It is usefull when the client app wants to support multiple wallets (with different
|
||||
/// seeds) in one app. If the client app support only one wallet then it doesn't have to care about alias atall.
|
||||
///
|
||||
/// When custom alias is used to create instance of the synchronizer then paths to all resources (databases, storages...) are updated accordingly to
|
||||
/// be sure that each instance is using unique paths to resources.
|
||||
///
|
||||
/// Custom alias identifiers shouldn't contain any confidential information because it may be logged. It also should have a reasonable length and
|
||||
/// form. It will be part of the paths to the files (databases, storage...)
|
||||
///
|
||||
/// IMPORTANT: Always use `default` alias for one of the instances of the synchronizer.
|
||||
public enum ZcashSynchronizerAlias: Hashable {
|
||||
case `default`
|
||||
case custom(String)
|
||||
}
|
||||
|
||||
extension ZcashSynchronizerAlias: CustomStringConvertible {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .`default`:
|
||||
return "default"
|
||||
case let .custom(alias):
|
||||
return "c_\(alias)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Wrapper for all the Rust backend functionality that does not involve processing blocks. This
|
||||
class initializes the Rust backend and the supporting data required to exercise those abilities.
|
||||
The [cash.z.wallet.sdk.block.CompactBlockProcessor] handles all the remaining Rust backend
|
||||
functionality, related to processing blocks.
|
||||
*/
|
||||
// swiftlint:disable type_body_length
|
||||
public class Initializer {
|
||||
struct URLs {
|
||||
let fsBlockDbRoot: URL
|
||||
let dataDbURL: URL
|
||||
let pendingDbURL: URL
|
||||
let spendParamsURL: URL
|
||||
let outputParamsURL: URL
|
||||
}
|
||||
|
||||
public enum InitializationResult {
|
||||
case success
|
||||
case seedRequired
|
||||
}
|
||||
|
||||
// This is used to uniquely identify instance of the SDKSynchronizer. It's used when checking if the Alias is already used or not.
|
||||
let id = UUID()
|
||||
|
||||
let rustBackend: ZcashRustBackendWelding.Type
|
||||
let alias: String
|
||||
let alias: ZcashSynchronizerAlias
|
||||
let endpoint: LightWalletEndpoint
|
||||
let fsBlockDbRoot: URL
|
||||
let dataDbURL: URL
|
||||
|
@ -95,6 +135,7 @@ public class Initializer {
|
|||
let storage: CompactBlockRepository
|
||||
let blockDownloaderService: BlockDownloaderService
|
||||
let network: ZcashNetwork
|
||||
let logger: Logger
|
||||
|
||||
/// The effective birthday of the wallet based on the height provided when initializing and the checkpoints available on this SDK.
|
||||
///
|
||||
|
@ -104,71 +145,20 @@ public class Initializer {
|
|||
/// The purpose of this to migrate from cacheDb to fsBlockDb
|
||||
private var cacheDbURL: URL?
|
||||
|
||||
/// Constructs the Initializer
|
||||
/// - Parameters:
|
||||
/// - fsBlockDbRoot: location of the compact blocks cache
|
||||
/// - dataDbURL: Location of the data db
|
||||
/// - pendingDbURL: location of the pending transactions database
|
||||
/// - endpoint: the endpoint representing the lightwalletd instance you want to point to
|
||||
/// - spendParamsURL: location of the spend parameters
|
||||
/// - outputParamsURL: location of the output parameters
|
||||
convenience public init (
|
||||
fsBlockDbRoot: URL,
|
||||
dataDbURL: URL,
|
||||
pendingDbURL: URL,
|
||||
endpoint: LightWalletEndpoint,
|
||||
network: ZcashNetwork,
|
||||
spendParamsURL: URL,
|
||||
outputParamsURL: URL,
|
||||
saplingParamsSourceURL: SaplingParamsSourceURL,
|
||||
alias: String = "",
|
||||
loggerProxy: Logger? = nil
|
||||
) {
|
||||
self.init(
|
||||
rustBackend: ZcashRustBackend.self,
|
||||
network: network,
|
||||
cacheDbURL: nil,
|
||||
fsBlockDbRoot: fsBlockDbRoot,
|
||||
dataDbURL: dataDbURL,
|
||||
pendingDbURL: pendingDbURL,
|
||||
endpoint: endpoint,
|
||||
service: Self.makeLightWalletServiceFactory(endpoint: endpoint).make(),
|
||||
repository: TransactionRepositoryBuilder.build(dataDbURL: dataDbURL),
|
||||
accountRepository: AccountRepositoryBuilder.build(
|
||||
dataDbURL: dataDbURL,
|
||||
readOnly: true,
|
||||
caching: true
|
||||
),
|
||||
storage: FSCompactBlockRepository(
|
||||
fsBlockDbRoot: fsBlockDbRoot,
|
||||
metadataStore: .live(
|
||||
fsBlockDbRoot: fsBlockDbRoot,
|
||||
rustBackend: ZcashRustBackend.self
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
),
|
||||
spendParamsURL: spendParamsURL,
|
||||
outputParamsURL: outputParamsURL,
|
||||
saplingParamsSourceURL: saplingParamsSourceURL,
|
||||
alias: alias,
|
||||
loggerProxy: loggerProxy
|
||||
)
|
||||
}
|
||||
/// Error that can be created when updating URLs according to alias. If this error is created then it is thrown from `SDKSynchronizer.prepare()`
|
||||
/// or `SDKSynchronizer.wipe()`.
|
||||
var urlsParsingError: InitializerError?
|
||||
|
||||
/// Constructs the Initializer and migrates an old cacheDb to the new file system block cache if
|
||||
/// a `cacheDbURL` is provided.
|
||||
/// Constructs the Initializer and migrates an old cacheDb to the new file system block cache if a `cacheDbURL` is provided.
|
||||
/// - Parameters:
|
||||
/// - cacheDbURL: previous location of the cacheDb. Use this constru
|
||||
/// - cacheDbURL: previous location of the cacheDb. If you don't know what a cacheDb is and you are adopting this SDK for the first time then
|
||||
/// just pass `nil` here.
|
||||
/// - fsBlockDbRoot: location of the compact blocks cache
|
||||
/// - dataDbURL: Location of the data db
|
||||
/// - pendingDbURL: location of the pending transactions database
|
||||
/// - endpoint: the endpoint representing the lightwalletd instance you want to point to
|
||||
/// - spendParamsURL: location of the spend parameters
|
||||
/// - outputParamsURL: location of the output parameters
|
||||
///
|
||||
/// - note: If you don't know what a cacheDb is and you are adopting
|
||||
/// this SDK for the first time then you just need to invoke `convenience init(fsBlockDbRoot: URL, dataDbURL: URL, pendingDbURL: URL, endpoint: LightWalletEndpoint, network: ZcashNetwork, spendParamsURL: URL, outputParamsURL: URL, alias: String = "", loggerProxy: Logger? = nil)` instead
|
||||
convenience public init (
|
||||
cacheDbURL: URL?,
|
||||
fsBlockDbRoot: URL,
|
||||
|
@ -179,71 +169,80 @@ public class Initializer {
|
|||
spendParamsURL: URL,
|
||||
outputParamsURL: URL,
|
||||
saplingParamsSourceURL: SaplingParamsSourceURL,
|
||||
alias: String = "",
|
||||
loggerProxy: Logger? = nil
|
||||
alias: ZcashSynchronizerAlias = .default,
|
||||
logLevel: OSLogger.LogLevel = .debug
|
||||
) {
|
||||
let urls = URLs(
|
||||
fsBlockDbRoot: fsBlockDbRoot,
|
||||
dataDbURL: dataDbURL,
|
||||
pendingDbURL: pendingDbURL,
|
||||
spendParamsURL: spendParamsURL,
|
||||
outputParamsURL: outputParamsURL
|
||||
)
|
||||
|
||||
// It's not possible to fail from constructor. Technically it's possible but it can be pain for the client apps to handle errors thrown
|
||||
// from constructor. So `parsingError` is just stored in initializer and `SDKSynchronizer.prepare()` throw this error if it exists.
|
||||
let (updatedURLs, parsingError) = Self.tryToUpdateURLs(with: alias, urls: urls)
|
||||
|
||||
let logger = OSLogger(logLevel: logLevel, alias: alias)
|
||||
self.init(
|
||||
rustBackend: ZcashRustBackend.self,
|
||||
network: network,
|
||||
cacheDbURL: cacheDbURL,
|
||||
fsBlockDbRoot: fsBlockDbRoot,
|
||||
dataDbURL: dataDbURL,
|
||||
pendingDbURL: pendingDbURL,
|
||||
urls: updatedURLs,
|
||||
endpoint: endpoint,
|
||||
service: Self.makeLightWalletServiceFactory(endpoint: endpoint).make(),
|
||||
repository: TransactionRepositoryBuilder.build(dataDbURL: dataDbURL),
|
||||
accountRepository: AccountRepositoryBuilder.build(
|
||||
dataDbURL: dataDbURL,
|
||||
dataDbURL: updatedURLs.dataDbURL,
|
||||
readOnly: true,
|
||||
caching: true
|
||||
caching: true,
|
||||
logger: logger
|
||||
),
|
||||
storage: FSCompactBlockRepository(
|
||||
fsBlockDbRoot: fsBlockDbRoot,
|
||||
fsBlockDbRoot: updatedURLs.fsBlockDbRoot,
|
||||
metadataStore: .live(
|
||||
fsBlockDbRoot: fsBlockDbRoot,
|
||||
rustBackend: ZcashRustBackend.self
|
||||
fsBlockDbRoot: updatedURLs.fsBlockDbRoot,
|
||||
rustBackend: ZcashRustBackend.self,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
),
|
||||
spendParamsURL: spendParamsURL,
|
||||
outputParamsURL: outputParamsURL,
|
||||
saplingParamsSourceURL: saplingParamsSourceURL,
|
||||
alias: alias,
|
||||
loggerProxy: loggerProxy
|
||||
urlsParsingError: parsingError,
|
||||
logger: logger
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Internal for dependency injection purposes
|
||||
*/
|
||||
/// Internal for dependency injection purposes.
|
||||
///
|
||||
/// !!! It's expected that URLs put here are already update with the Alias.
|
||||
init(
|
||||
rustBackend: ZcashRustBackendWelding.Type,
|
||||
network: ZcashNetwork,
|
||||
cacheDbURL: URL?,
|
||||
fsBlockDbRoot: URL,
|
||||
dataDbURL: URL,
|
||||
pendingDbURL: URL,
|
||||
urls: URLs,
|
||||
endpoint: LightWalletEndpoint,
|
||||
service: LightWalletService,
|
||||
repository: TransactionRepository,
|
||||
accountRepository: AccountRepository,
|
||||
storage: CompactBlockRepository,
|
||||
spendParamsURL: URL,
|
||||
outputParamsURL: URL,
|
||||
saplingParamsSourceURL: SaplingParamsSourceURL,
|
||||
alias: String = "",
|
||||
loggerProxy: Logger? = nil
|
||||
alias: ZcashSynchronizerAlias,
|
||||
urlsParsingError: InitializerError?,
|
||||
logger: Logger
|
||||
) {
|
||||
logger = loggerProxy
|
||||
self.cacheDbURL = cacheDbURL
|
||||
self.rustBackend = rustBackend
|
||||
self.fsBlockDbRoot = fsBlockDbRoot
|
||||
self.dataDbURL = dataDbURL
|
||||
self.pendingDbURL = pendingDbURL
|
||||
self.fsBlockDbRoot = urls.fsBlockDbRoot
|
||||
self.dataDbURL = urls.dataDbURL
|
||||
self.pendingDbURL = urls.pendingDbURL
|
||||
self.endpoint = endpoint
|
||||
self.spendParamsURL = spendParamsURL
|
||||
self.outputParamsURL = outputParamsURL
|
||||
self.spendParamsURL = urls.spendParamsURL
|
||||
self.outputParamsURL = urls.outputParamsURL
|
||||
self.saplingParamsSourceURL = saplingParamsSourceURL
|
||||
self.alias = alias
|
||||
self.lightWalletService = service
|
||||
|
@ -253,21 +252,76 @@ public class Initializer {
|
|||
self.blockDownloaderService = BlockDownloaderServiceImpl(service: service, storage: storage)
|
||||
self.network = network
|
||||
self.walletBirthday = Checkpoint.birthday(with: 0, network: network).height
|
||||
self.urlsParsingError = urlsParsingError
|
||||
self.logger = logger
|
||||
}
|
||||
|
||||
private static func makeLightWalletServiceFactory(endpoint: LightWalletEndpoint) -> LightWalletServiceFactory {
|
||||
return LightWalletServiceFactory(
|
||||
endpoint: endpoint,
|
||||
connectionStateChange: { oldState, newState in
|
||||
NotificationSender.default.post(
|
||||
name: .synchronizerConnectionStateChanged,
|
||||
object: self,
|
||||
userInfo: [
|
||||
SDKSynchronizer.NotificationKeys.previousConnectionState: oldState,
|
||||
SDKSynchronizer.NotificationKeys.currentConnectionState: newState
|
||||
]
|
||||
)
|
||||
}
|
||||
return LightWalletServiceFactory(endpoint: endpoint)
|
||||
}
|
||||
|
||||
/// Try to update URLs with `alias`.
|
||||
///
|
||||
/// If the `default` alias is used then the URLs are changed at all.
|
||||
/// If the `custom("anotherInstance")` is used then last path component or the URL is updated like this:
|
||||
/// - /some/path/to.file -> /some/path/c_anotherInstance_to.file
|
||||
/// - /some/path/to/directory -> /some/path/to/c_anotherInstance_directory
|
||||
///
|
||||
/// If any of the URLs can't be parsed then returned error isn't nil.
|
||||
private static func tryToUpdateURLs(
|
||||
with alias: ZcashSynchronizerAlias,
|
||||
urls: URLs
|
||||
) -> (URLs, InitializerError?) {
|
||||
let updatedURLsResult = Self.updateURLs(with: alias, urls: urls)
|
||||
|
||||
let parsingError: InitializerError?
|
||||
let updatedURLs: URLs
|
||||
switch updatedURLsResult {
|
||||
case let .success(updated):
|
||||
parsingError = nil
|
||||
updatedURLs = updated
|
||||
case let .failure(error):
|
||||
parsingError = error
|
||||
// When failure happens just use original URLs because something must be used. But this shouldn't be a problem because
|
||||
// `SDKSynchronizer.prepare()` handles this error. And the SDK won't work if it isn't switched from `unprepared` state.
|
||||
updatedURLs = urls
|
||||
}
|
||||
|
||||
return (updatedURLs, parsingError)
|
||||
}
|
||||
|
||||
private static func updateURLs(
|
||||
with alias: ZcashSynchronizerAlias,
|
||||
urls: URLs
|
||||
) -> Result<URLs, InitializerError> {
|
||||
guard let updatedFsBlockDbRoot = urls.fsBlockDbRoot.updateLastPathComponent(with: alias) else {
|
||||
return .failure(.cantUpdateURLWithAlias(urls.fsBlockDbRoot))
|
||||
}
|
||||
|
||||
guard let updatedDataDbURL = urls.dataDbURL.updateLastPathComponent(with: alias) else {
|
||||
return .failure(.cantUpdateURLWithAlias(urls.dataDbURL))
|
||||
}
|
||||
|
||||
guard let updatedPendingDbURL = urls.pendingDbURL.updateLastPathComponent(with: alias) else {
|
||||
return .failure(.cantUpdateURLWithAlias(urls.pendingDbURL))
|
||||
}
|
||||
|
||||
guard let updatedSpendParamsURL = urls.spendParamsURL.updateLastPathComponent(with: alias) else {
|
||||
return .failure(.cantUpdateURLWithAlias(urls.spendParamsURL))
|
||||
}
|
||||
|
||||
guard let updateOutputParamsURL = urls.outputParamsURL.updateLastPathComponent(with: alias) else {
|
||||
return .failure(.cantUpdateURLWithAlias(urls.outputParamsURL))
|
||||
}
|
||||
|
||||
return .success(
|
||||
URLs(
|
||||
fsBlockDbRoot: updatedFsBlockDbRoot,
|
||||
dataDbURL: updatedDataDbURL,
|
||||
pendingDbURL: updatedPendingDbURL,
|
||||
spendParamsURL: updatedSpendParamsURL,
|
||||
outputParamsURL: updateOutputParamsURL
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -333,7 +387,8 @@ public class Initializer {
|
|||
|
||||
let migrationManager = MigrationManager(
|
||||
pendingDbConnection: SimpleConnectionProvider(path: pendingDbURL.path),
|
||||
networkType: self.network.networkType
|
||||
networkType: self.network.networkType,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try migrationManager.performMigration()
|
||||
|
@ -415,6 +470,13 @@ extension InitializerError: LocalizedError {
|
|||
return "account table init failed with error: \(error.localizedDescription)"
|
||||
case .fsCacheInitFailed(let error):
|
||||
return "Compact Block Cache failed to initialize with error: \(error.localizedDescription)"
|
||||
case let .aliasAlreadyInUse(alias):
|
||||
return """
|
||||
The Alias \(alias) used for this instance of the SDKSynchronizer is already in use. Each instance of the SDKSynchronizer must use unique \
|
||||
Alias.
|
||||
"""
|
||||
case .cantUpdateURLWithAlias(let url):
|
||||
return "Can't update path URL with alias. \(url)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,8 +31,6 @@ import Foundation
|
|||
///
|
||||
/// We encourage you to check`SDKMetricsTests` and other tests in the Test/PerformanceTests/ folder.
|
||||
public class SDKMetrics {
|
||||
public static let shared = SDKMetrics()
|
||||
|
||||
public struct BlockMetricReport: Equatable {
|
||||
public let startHeight: BlockHeight
|
||||
public let progressHeight: BlockHeight
|
||||
|
@ -62,6 +60,8 @@ public class SDKMetrics {
|
|||
var isEnabled = false
|
||||
var reports: [Operation: [BlockMetricReport]] = [:]
|
||||
|
||||
public init() { }
|
||||
|
||||
/// `SDKMetrics` is disabled by default. Any pushed data are simply ignored until `enableMetrics()` is called.
|
||||
public func enableMetrics() {
|
||||
isEnabled = true
|
||||
|
@ -183,7 +183,7 @@ extension SDKMetrics {
|
|||
let fetchUTXOsReport = summaryFor(reports: reports[.fetchUTXOs])
|
||||
var totalSyncReport: ReportSummary?
|
||||
|
||||
if let duration = SDKMetrics.shared.syncReport?.duration {
|
||||
if let duration = syncReport?.duration {
|
||||
totalSyncReport = ReportSummary(minTime: duration, maxTime: duration, avgTime: duration)
|
||||
}
|
||||
|
||||
|
|
|
@ -61,16 +61,20 @@ class LightWalletGRPCService {
|
|||
let streamingCallTimeout: TimeLimit
|
||||
var latestBlockHeightProvider: LatestBlockHeightProvider = LiveLatestBlockHeightProvider()
|
||||
|
||||
var connectionStateChange: ((_ from: ConnectionState, _ to: ConnectionState) -> Void)? {
|
||||
get { connectionManager.connectionStateChange }
|
||||
set { connectionManager.connectionStateChange = newValue }
|
||||
}
|
||||
|
||||
var queue: DispatchQueue
|
||||
|
||||
convenience init(endpoint: LightWalletEndpoint, connectionStateChange: @escaping (_ from: ConnectionState, _ to: ConnectionState) -> Void) {
|
||||
convenience init(endpoint: LightWalletEndpoint) {
|
||||
self.init(
|
||||
host: endpoint.host,
|
||||
port: endpoint.port,
|
||||
secure: endpoint.secure,
|
||||
singleCallTimeout: endpoint.singleCallTimeoutInMillis,
|
||||
streamingCallTimeout: endpoint.streamingCallTimeoutInMillis,
|
||||
connectionStateChange: connectionStateChange
|
||||
streamingCallTimeout: endpoint.streamingCallTimeoutInMillis
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -86,10 +90,9 @@ class LightWalletGRPCService {
|
|||
port: Int = 9067,
|
||||
secure: Bool = true,
|
||||
singleCallTimeout: Int64,
|
||||
streamingCallTimeout: Int64,
|
||||
connectionStateChange: @escaping (_ from: ConnectionState, _ to: ConnectionState) -> Void
|
||||
streamingCallTimeout: Int64
|
||||
) {
|
||||
self.connectionManager = ConnectionStatusManager(connectionStateChange: connectionStateChange)
|
||||
self.connectionManager = ConnectionStatusManager()
|
||||
self.queue = DispatchQueue.init(label: "LightWalletGRPCService")
|
||||
self.streamingCallTimeout = TimeLimit.timeout(.milliseconds(streamingCallTimeout))
|
||||
self.singleCallTimeout = TimeLimit.timeout(.milliseconds(singleCallTimeout))
|
||||
|
@ -344,12 +347,10 @@ extension LightWalletServiceError {
|
|||
}
|
||||
|
||||
class ConnectionStatusManager: ConnectivityStateDelegate {
|
||||
let connectionStateChange: (_ from: ConnectionState, _ to: ConnectionState) -> Void
|
||||
init(connectionStateChange: @escaping (_ from: ConnectionState, _ to: ConnectionState) -> Void) {
|
||||
self.connectionStateChange = connectionStateChange
|
||||
}
|
||||
var connectionStateChange: ((_ from: ConnectionState, _ to: ConnectionState) -> Void)?
|
||||
init() { }
|
||||
|
||||
func connectivityStateDidChange(from oldState: ConnectivityState, to newState: ConnectivityState) {
|
||||
connectionStateChange(oldState.toConnectionState(), newState.toConnectionState())
|
||||
connectionStateChange?(oldState.toConnectionState(), newState.toConnectionState())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -136,19 +136,20 @@ protocol LightWalletServiceResponse {
|
|||
|
||||
struct LightWalletServiceFactory {
|
||||
let endpoint: LightWalletEndpoint
|
||||
let connectionStateChange: (_ from: ConnectionState, _ to: ConnectionState) -> Void
|
||||
|
||||
init(endpoint: LightWalletEndpoint, connectionStateChange: @escaping (_ from: ConnectionState, _ to: ConnectionState) -> Void) {
|
||||
init(endpoint: LightWalletEndpoint) {
|
||||
self.endpoint = endpoint
|
||||
self.connectionStateChange = connectionStateChange
|
||||
}
|
||||
|
||||
func make() -> LightWalletService {
|
||||
return LightWalletGRPCService(endpoint: endpoint, connectionStateChange: connectionStateChange)
|
||||
return LightWalletGRPCService(endpoint: endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
protocol LightWalletService {
|
||||
protocol LightWalletService: AnyObject {
|
||||
/// Closure which is called when connection state changes.
|
||||
var connectionStateChange: ((_ from: ConnectionState, _ to: ConnectionState) -> Void)? { get set }
|
||||
|
||||
/// Returns the info for this lightwalletd server
|
||||
func getInfo() async throws -> LightWalletdInfo
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ public enum SynchronizerError: Error {
|
|||
case rewindErrorUnknownArchorHeight // ZcashLightClientKit.SynchronizerError error 13.
|
||||
case invalidAccount // ZcashLightClientKit.SynchronizerError error 14.
|
||||
case lightwalletdValidationFailed(underlyingError: Error) // ZcashLightClientKit.SynchronizerError error 8.
|
||||
case wipeAttemptWhileProcessing
|
||||
}
|
||||
|
||||
public enum ShieldFundsError: Error {
|
||||
|
@ -90,12 +89,15 @@ public enum SynchronizerEvent {
|
|||
// Sent when the synchronizer fetched utxos from lightwalletd attempted to store them.
|
||||
case storedUTXOs(_ inserted: [UnspentTransactionOutputEntity], _ skipped: [UnspentTransactionOutputEntity])
|
||||
// Connection state to LightwalletEndpoint changed.
|
||||
case connectionStateChanged
|
||||
case connectionStateChanged(ConnectionState)
|
||||
}
|
||||
|
||||
/// Primary interface for interacting with the SDK. Defines the contract that specific
|
||||
/// implementations like SdkSynchronizer fulfill.
|
||||
public protocol Synchronizer: AnyObject {
|
||||
/// Alias used for this instance.
|
||||
var alias: ZcashSynchronizerAlias { get }
|
||||
|
||||
/// Latest state of the SDK which can be get in synchronous manner.
|
||||
var latestState: SynchronizerState { get }
|
||||
|
||||
|
@ -112,6 +114,9 @@ public protocol Synchronizer: AnyObject {
|
|||
/// This stream is backed by `PassthroughSubject`. Check `SynchronizerEvent` to see which events may be emitted.
|
||||
var eventStream: AnyPublisher<SynchronizerEvent, Never> { get }
|
||||
|
||||
/// An object that when enabled collects mertrics from the synchronizer
|
||||
var metrics: SDKMetrics { get }
|
||||
|
||||
/// Initialize the wallet. The ZIP-32 seed bytes can optionally be passed to perform
|
||||
/// database migrations. most of the times the seed won't be needed. If they do and are
|
||||
/// not provided this will fail with `InitializationResult.seedRequired`. It could
|
||||
|
@ -126,8 +131,13 @@ public protocol Synchronizer: AnyObject {
|
|||
/// - seed: ZIP-32 Seed bytes for the wallet that will be initialized.
|
||||
/// - viewingKeys: Viewing key derived from seed.
|
||||
/// - walletBirthday: Birthday of wallet.
|
||||
/// - Throws: `InitializerError.dataDbInitFailed` if the creation of the dataDb fails
|
||||
/// - Throws:
|
||||
/// `InitializerError.dataDbInitFailed` if the creation of the dataDb fails
|
||||
/// `InitializerError.accountInitFailed` if the account table can't be initialized.
|
||||
/// `InitializerError.aliasAlreadyInUse` if the Alias used to create this instance is already used by other instance
|
||||
/// `InitializerError.cantUpdateURLWithAlias` if the updating of paths in `Initilizer` according to alias fails. When this happens it means that
|
||||
/// some path passed to `Initializer` is invalid. The SDK can't recover from this and this instance
|
||||
/// won't do anything.
|
||||
func prepare(
|
||||
with seed: [UInt8]?,
|
||||
viewingKeys: [UnifiedFullViewingKey],
|
||||
|
@ -163,6 +173,9 @@ public protocol Synchronizer: AnyObject {
|
|||
/// - Parameter zatoshi: the amount to send in Zatoshi.
|
||||
/// - Parameter toAddress: the recipient's address.
|
||||
/// - Parameter memo: an `Optional<Memo>`with the memo to include as part of the transaction. send `nil` when sending to transparent receivers otherwise the function will throw an error
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creating of synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
func sendToAddress(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
|
@ -173,6 +186,9 @@ public protocol Synchronizer: AnyObject {
|
|||
/// Shields transparent funds from the given private key into the best shielded pool of the account associated to the given `UnifiedSpendingKey`.
|
||||
/// - Parameter spendingKey: the `UnifiedSpendingKey` that allows to spend transparent funds
|
||||
/// - Parameter memo: the optional memo to include as part of the transaction.
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creating of synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
func shieldFunds(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
memo: Memo,
|
||||
|
@ -232,8 +248,11 @@ public protocol Synchronizer: AnyObject {
|
|||
/// Returns the latest block height from the provided Lightwallet endpoint
|
||||
/// Blocking
|
||||
func latestHeight() async throws -> BlockHeight
|
||||
|
||||
|
||||
/// Returns the latests UTXOs for the given address from the specified height on
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creating of synchronizer instance or since the last wipe then this method throws
|
||||
/// `SynchronizerErrors.notPrepared`.
|
||||
func refreshUTXOs(address: TransparentAddress, from height: BlockHeight) async throws -> RefreshedUTXOs
|
||||
|
||||
/// Returns the last stored transparent balance
|
||||
|
@ -266,7 +285,10 @@ public protocol Synchronizer: AnyObject {
|
|||
/// - Emits rewindError for other errors
|
||||
///
|
||||
/// `rewind(policy:)` itself doesn't start the sync process when it's done and it doesn't trigger notifications as regorg would. After it is done
|
||||
/// you have start the sync process by calling `start()`.
|
||||
/// you have start the sync process by calling `start()`
|
||||
///
|
||||
/// If `prepare()` hasn't already been called since creating of synchronizer instance or since the last wipe then returned publisher emits
|
||||
/// `SynchronizerErrors.notPrepared` error.
|
||||
///
|
||||
/// - Parameter policy: the rewind policy
|
||||
func rewind(_ policy: RewindPolicy) -> AnyPublisher<Void, Error>
|
||||
|
@ -284,6 +306,12 @@ public protocol Synchronizer: AnyObject {
|
|||
/// fails then something is seriously wrong. If the wipe fails then the SDK may be in inconsistent state. It's suggested to call wipe again until
|
||||
/// it succeed.
|
||||
///
|
||||
/// Returned publisher emits `InitializerError.aliasAlreadyInUse` error if the Alias used to create this instance is already used by other
|
||||
/// instance.
|
||||
///
|
||||
/// Returned publisher emits `InitializerError.cantUpdateURLWithAlias` if the updating of paths in `Initilizer` according to alias fails. When
|
||||
/// this happens it means that some path passed to `Initializer` is invalid. The SDK can't recover from this and this instance won't do anything.
|
||||
///
|
||||
func wipe() -> AnyPublisher<Void, Error>
|
||||
}
|
||||
|
||||
|
@ -331,6 +359,14 @@ public enum SyncStatus: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
public var isPrepared: Bool {
|
||||
if case .unprepared = self {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public var briefDebugDescription: String {
|
||||
switch self {
|
||||
case .unprepared: return "unprepared"
|
||||
|
|
|
@ -23,6 +23,8 @@ public struct ClosureSDKSynchronizer {
|
|||
}
|
||||
|
||||
extension ClosureSDKSynchronizer: ClosureSynchronizer {
|
||||
public var alias: ZcashSynchronizerAlias { synchronizer.alias }
|
||||
|
||||
public var latestState: SynchronizerState { synchronizer.latestState }
|
||||
public var connectionState: ConnectionState { synchronizer.connectionState }
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ public struct CombineSDKSynchronizer {
|
|||
}
|
||||
|
||||
extension CombineSDKSynchronizer: CombineSynchronizer {
|
||||
public var alias: ZcashSynchronizerAlias { synchronizer.alias }
|
||||
|
||||
public var latestState: SynchronizerState { synchronizer.latestState }
|
||||
public var connectionState: ConnectionState { synchronizer.connectionState }
|
||||
|
||||
|
|
|
@ -9,19 +9,12 @@
|
|||
import Foundation
|
||||
import Combine
|
||||
|
||||
extension Notification.Name {
|
||||
static let synchronizerConnectionStateChanged = Notification.Name("SynchronizerConnectionStateChanged")
|
||||
}
|
||||
|
||||
/// Synchronizer implementation for UIKit and iOS 13+
|
||||
// swiftlint:disable type_body_length
|
||||
public class SDKSynchronizer: Synchronizer {
|
||||
public enum NotificationKeys {
|
||||
public static let currentConnectionState = "SDKSynchronizer.currentConnectionState"
|
||||
public static let previousConnectionState = "SDKSynchronizer.previousConnectionState"
|
||||
}
|
||||
public var alias: ZcashSynchronizerAlias { initializer.alias }
|
||||
|
||||
private let streamsUpdateQueue = DispatchQueue(label: "streamsUpdateQueue")
|
||||
private lazy var streamsUpdateQueue = { DispatchQueue(label: "streamsUpdateQueue_\(initializer.alias.description)") }()
|
||||
private let stateSubject = CurrentValueSubject<SynchronizerState, Never>(.zero)
|
||||
public var stateStream: AnyPublisher<SynchronizerState, Never> { stateSubject.eraseToAnyPublisher() }
|
||||
public private(set) var latestState: SynchronizerState = .zero
|
||||
|
@ -29,6 +22,9 @@ public class SDKSynchronizer: Synchronizer {
|
|||
private let eventSubject = PassthroughSubject<SynchronizerEvent, Never>()
|
||||
public var eventStream: AnyPublisher<SynchronizerEvent, Never> { eventSubject.eraseToAnyPublisher() }
|
||||
|
||||
public let metrics: SDKMetrics
|
||||
public let logger: Logger
|
||||
|
||||
// Don't read this variable directly. Use `status` instead. And don't update this variable directly use `updateStatus()` methods instead.
|
||||
private var underlyingStatus: GenericActor<SyncStatus>
|
||||
var status: SyncStatus {
|
||||
|
@ -36,7 +32,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
}
|
||||
|
||||
let blockProcessor: CompactBlockProcessor
|
||||
let blockProcessorEventProcessingQueue = DispatchQueue(label: "blockProcessorEventProcessingQueue")
|
||||
lazy var blockProcessorEventProcessingQueue = { DispatchQueue(label: "blockProcessorEventProcessingQueue_\(initializer.alias.description)") }()
|
||||
|
||||
public private(set) var initializer: Initializer
|
||||
// Valid value is stored here after `prepare` is called.
|
||||
|
@ -52,6 +48,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
/// Creates an SDKSynchronizer instance
|
||||
/// - Parameter initializer: a wallet Initializer object
|
||||
public convenience init(initializer: Initializer) {
|
||||
let metrics = SDKMetrics()
|
||||
self.init(
|
||||
status: .unprepared,
|
||||
initializer: initializer,
|
||||
|
@ -60,8 +57,11 @@ public class SDKSynchronizer: Synchronizer {
|
|||
utxoRepository: UTXORepositoryBuilder.build(initializer: initializer),
|
||||
blockProcessor: CompactBlockProcessor(
|
||||
initializer: initializer,
|
||||
metrics: metrics,
|
||||
logger: initializer.logger,
|
||||
walletBirthdayProvider: { initializer.walletBirthday }
|
||||
)
|
||||
),
|
||||
metrics: metrics
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,8 @@ public class SDKSynchronizer: Synchronizer {
|
|||
transactionManager: OutboundTransactionManager,
|
||||
transactionRepository: TransactionRepository,
|
||||
utxoRepository: UnspentTransactionOutputRepository,
|
||||
blockProcessor: CompactBlockProcessor
|
||||
blockProcessor: CompactBlockProcessor,
|
||||
metrics: SDKMetrics
|
||||
) {
|
||||
self.connectionState = .idle
|
||||
self.underlyingStatus = GenericActor(status)
|
||||
|
@ -81,13 +82,18 @@ public class SDKSynchronizer: Synchronizer {
|
|||
self.utxoRepository = utxoRepository
|
||||
self.blockProcessor = blockProcessor
|
||||
self.network = initializer.network
|
||||
self.metrics = metrics
|
||||
self.logger = initializer.logger
|
||||
|
||||
subscribeToProcessorNotifications(blockProcessor)
|
||||
initializer.lightWalletService.connectionStateChange = { [weak self] oldState, newState in
|
||||
self?.connectivityStateChanged(oldState: oldState, newState: newState)
|
||||
}
|
||||
|
||||
Task(priority: .high) { [weak self] in await self?.subscribeToProcessorEvents(blockProcessor) }
|
||||
}
|
||||
|
||||
deinit {
|
||||
UsedAliasesChecker.stopUsing(alias: initializer.alias, id: initializer.id)
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
Task { [blockProcessor] in
|
||||
await blockProcessor.stop()
|
||||
|
@ -99,6 +105,24 @@ public class SDKSynchronizer: Synchronizer {
|
|||
await notify(oldStatus: oldValue, newStatus: newValue)
|
||||
}
|
||||
|
||||
func throwIfUnprepared() throws {
|
||||
if !latestState.syncStatus.isPrepared {
|
||||
throw SynchronizerError.notPrepared
|
||||
}
|
||||
}
|
||||
|
||||
func checkIfCanContinueInitialisation() -> InitializerError? {
|
||||
if let initialisationError = initializer.urlsParsingError {
|
||||
return initialisationError
|
||||
}
|
||||
|
||||
if !UsedAliasesChecker.tryToUse(alias: initializer.alias, id: initializer.id) {
|
||||
return InitializerError.aliasAlreadyInUse(initializer.alias)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
public func prepare(
|
||||
with seed: [UInt8]?,
|
||||
viewingKeys: [UnifiedFullViewingKey],
|
||||
|
@ -106,6 +130,10 @@ public class SDKSynchronizer: Synchronizer {
|
|||
) async throws -> Initializer.InitializationResult {
|
||||
guard await status == .unprepared else { return .success }
|
||||
|
||||
if let error = checkIfCanContinueInitialisation() {
|
||||
throw error
|
||||
}
|
||||
|
||||
try utxoRepository.initialise()
|
||||
|
||||
if case .seedRequired = try self.initializer.initialize(with: seed, viewingKeys: viewingKeys, walletBirthday: walletBirthday) {
|
||||
|
@ -127,7 +155,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
throw SynchronizerError.notPrepared
|
||||
|
||||
case .syncing, .enhancing, .fetching:
|
||||
LoggerProxy.warn("warning: Synchronizer started when already running. Next sync process will be started when the current one stops.")
|
||||
logger.warn("warning: Synchronizer started when already running. Next sync process will be started when the current one stops.")
|
||||
/// This may look strange but `CompactBlockProcessor` has mechanisms which can handle this situation. So we are fine with calling
|
||||
/// it's start here.
|
||||
await blockProcessor.start(retry: retry)
|
||||
|
@ -143,41 +171,19 @@ public class SDKSynchronizer: Synchronizer {
|
|||
public func stop() async {
|
||||
let status = await self.status
|
||||
guard status != .stopped, status != .disconnected else {
|
||||
LoggerProxy.info("attempted to stop when status was: \(status)")
|
||||
logger.info("attempted to stop when status was: \(status)")
|
||||
return
|
||||
}
|
||||
|
||||
await blockProcessor.stop()
|
||||
}
|
||||
|
||||
private func subscribeToProcessorNotifications(_ processor: CompactBlockProcessor) {
|
||||
let center = NotificationCenter.default
|
||||
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(connectivityStateChanged(_:)),
|
||||
name: Notification.Name.synchronizerConnectionStateChanged,
|
||||
object: nil
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: Connectivity State
|
||||
|
||||
@objc func connectivityStateChanged(_ notification: Notification) {
|
||||
guard
|
||||
let userInfo = notification.userInfo,
|
||||
let current = userInfo[NotificationKeys.currentConnectionState] as? ConnectionState
|
||||
else {
|
||||
LoggerProxy.error(
|
||||
"Found \(notification.name) but lacks dictionary information." +
|
||||
"This is probably a programming error"
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
connectionState = current
|
||||
func connectivityStateChanged(oldState: ConnectionState, newState: ConnectionState) {
|
||||
connectionState = newState
|
||||
streamsUpdateQueue.async { [weak self] in
|
||||
self?.eventSubject.send(.connectionStateChanged)
|
||||
self?.eventSubject.send(.connectionStateChanged(newState))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,7 +238,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
await updateStatus(.synced)
|
||||
|
||||
if let syncStartDate {
|
||||
SDKMetrics.shared.pushSyncReport(
|
||||
metrics.pushSyncReport(
|
||||
start: syncStartDate,
|
||||
end: Date()
|
||||
)
|
||||
|
@ -246,12 +252,12 @@ public class SDKSynchronizer: Synchronizer {
|
|||
}
|
||||
|
||||
private func handledReorg(reorgHeight: BlockHeight, rewindHeight: BlockHeight) {
|
||||
LoggerProxy.debug("handling reorg at: \(reorgHeight) with rewind height: \(rewindHeight)")
|
||||
logger.debug("handling reorg at: \(reorgHeight) with rewind height: \(rewindHeight)")
|
||||
|
||||
do {
|
||||
try transactionManager.handleReorg(at: rewindHeight)
|
||||
} catch {
|
||||
LoggerProxy.debug("error handling reorg: \(error)")
|
||||
logger.debug("error handling reorg: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,12 +280,15 @@ public class SDKSynchronizer: Synchronizer {
|
|||
toAddress: Recipient,
|
||||
memo: Memo?
|
||||
) async throws -> PendingTransactionEntity {
|
||||
try throwIfUnprepared()
|
||||
|
||||
do {
|
||||
try await SaplingParameterDownloader.downloadParamsIfnotPresent(
|
||||
spendURL: initializer.spendParamsURL,
|
||||
spendSourceURL: initializer.saplingParamsSourceURL.spendParamFileURL,
|
||||
outputURL: initializer.outputParamsURL,
|
||||
outputSourceURL: initializer.saplingParamsSourceURL.outputParamFileURL
|
||||
outputSourceURL: initializer.saplingParamsSourceURL.outputParamFileURL,
|
||||
logger: logger
|
||||
)
|
||||
} catch {
|
||||
throw SynchronizerError.parameterMissing(underlyingError: error)
|
||||
|
@ -302,6 +311,8 @@ public class SDKSynchronizer: Synchronizer {
|
|||
memo: Memo,
|
||||
shieldingThreshold: Zatoshi
|
||||
) async throws -> PendingTransactionEntity {
|
||||
try throwIfUnprepared()
|
||||
|
||||
// let's see if there are funds to shield
|
||||
let accountIndex = Int(spendingKey.account)
|
||||
do {
|
||||
|
@ -410,6 +421,8 @@ public class SDKSynchronizer: Synchronizer {
|
|||
}
|
||||
|
||||
public func latestUTXOs(address: String) async throws -> [UnspentTransactionOutputEntity] {
|
||||
try throwIfUnprepared()
|
||||
|
||||
guard initializer.isValidTransparentAddress(address) else {
|
||||
throw SynchronizerError.generalError(message: "invalid t-address")
|
||||
}
|
||||
|
@ -431,7 +444,8 @@ public class SDKSynchronizer: Synchronizer {
|
|||
}
|
||||
|
||||
public func refreshUTXOs(address: TransparentAddress, from height: BlockHeight) async throws -> RefreshedUTXOs {
|
||||
try await blockProcessor.refreshUTXOs(tAddress: address, startHeight: height)
|
||||
try throwIfUnprepared()
|
||||
return try await blockProcessor.refreshUTXOs(tAddress: address, startHeight: height)
|
||||
}
|
||||
@available(*, deprecated, message: "This function will be removed soon, use the one returning a `Zatoshi` value instead")
|
||||
public func getShieldedBalance(accountIndex: Int = 0) -> Int64 {
|
||||
|
@ -472,7 +486,12 @@ public class SDKSynchronizer: Synchronizer {
|
|||
|
||||
public func rewind(_ policy: RewindPolicy) -> AnyPublisher<Void, Error> {
|
||||
let subject = PassthroughSubject<Void, Error>()
|
||||
Task {
|
||||
Task(priority: .high) {
|
||||
if !latestState.syncStatus.isPrepared {
|
||||
subject.send(completion: .failure(SynchronizerError.notPrepared))
|
||||
return
|
||||
}
|
||||
|
||||
let height: BlockHeight?
|
||||
|
||||
switch policy {
|
||||
|
@ -521,6 +540,11 @@ public class SDKSynchronizer: Synchronizer {
|
|||
public func wipe() -> AnyPublisher<Void, Error> {
|
||||
let subject = PassthroughSubject<Void, Error>()
|
||||
Task(priority: .high) {
|
||||
if let error = checkIfCanContinueInitialisation() {
|
||||
subject.send(completion: .failure(error))
|
||||
return
|
||||
}
|
||||
|
||||
let context = AfterSyncHooksManager.WipeContext(
|
||||
pendingDbURL: initializer.pendingDbURL,
|
||||
prewipe: { [weak self] in
|
||||
|
@ -633,7 +657,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
try updateMinedTransactions()
|
||||
try removeConfirmedTransactions()
|
||||
} catch {
|
||||
LoggerProxy.debug("error refreshing pending transactions: \(error)")
|
||||
logger.debug("error refreshing pending transactions: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,18 +25,21 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
|||
var service: LightWalletService
|
||||
var queue: DispatchQueue
|
||||
var network: NetworkType
|
||||
let logger: Logger
|
||||
|
||||
init(
|
||||
encoder: TransactionEncoder,
|
||||
service: LightWalletService,
|
||||
repository: PendingTransactionRepository,
|
||||
networkType: NetworkType
|
||||
networkType: NetworkType,
|
||||
logger: Logger
|
||||
) {
|
||||
self.repository = repository
|
||||
self.encoder = encoder
|
||||
self.service = service
|
||||
self.network = networkType
|
||||
self.queue = DispatchQueue.init(label: "PersistentTransactionManager.serial.queue", qos: .userInitiated)
|
||||
self.logger = logger
|
||||
}
|
||||
|
||||
func initSpend(
|
||||
|
@ -61,7 +64,7 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
|||
zatoshi: zatoshi
|
||||
)
|
||||
}
|
||||
LoggerProxy.debug("pending transaction \(String(describing: insertedTx.id)) created")
|
||||
logger.debug("pending transaction \(String(describing: insertedTx.id)) created")
|
||||
return insertedTx
|
||||
}
|
||||
|
||||
|
@ -162,12 +165,12 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
|||
}
|
||||
|
||||
guard !storedTx.isCancelled else {
|
||||
LoggerProxy.debug("ignoring cancelled transaction \(storedTx)")
|
||||
logger.debug("ignoring cancelled transaction \(storedTx)")
|
||||
throw TransactionManagerError.cancelled(storedTx)
|
||||
}
|
||||
|
||||
guard let raw = storedTx.raw else {
|
||||
LoggerProxy.debug("INCONSISTENCY: attempt to send pending transaction \(txId) that has not raw data")
|
||||
logger.debug("INCONSISTENCY: attempt to send pending transaction \(txId) that has not raw data")
|
||||
throw TransactionManagerError.internalInconsistency(storedTx)
|
||||
}
|
||||
|
||||
|
@ -273,14 +276,18 @@ enum OutboundTransactionManagerBuilder {
|
|||
encoder: TransactionEncoderbuilder.build(initializer: initializer),
|
||||
service: initializer.lightWalletService,
|
||||
repository: PendingTransactionRepositoryBuilder.build(initializer: initializer),
|
||||
networkType: initializer.network.networkType
|
||||
networkType: initializer.network.networkType,
|
||||
logger: initializer.logger
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum PendingTransactionRepositoryBuilder {
|
||||
static func build(initializer: Initializer) -> PendingTransactionRepository {
|
||||
PendingTransactionSQLDAO(dbProvider: SimpleConnectionProvider(path: initializer.pendingDbURL.path, readonly: false))
|
||||
PendingTransactionSQLDAO(
|
||||
dbProvider: SimpleConnectionProvider(path: initializer.pendingDbURL.path, readonly: false),
|
||||
logger: initializer.logger
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import Foundation
|
|||
class WalletTransactionEncoder: TransactionEncoder {
|
||||
var rustBackend: ZcashRustBackendWelding.Type
|
||||
var repository: TransactionRepository
|
||||
let logger: Logger
|
||||
|
||||
private var outputParamsURL: URL
|
||||
private var spendParamsURL: URL
|
||||
|
@ -24,7 +25,8 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
repository: TransactionRepository,
|
||||
outputParams: URL,
|
||||
spendParams: URL,
|
||||
networkType: NetworkType
|
||||
networkType: NetworkType,
|
||||
logger: Logger
|
||||
) {
|
||||
self.rustBackend = rust
|
||||
self.dataDbURL = dataDb
|
||||
|
@ -33,6 +35,7 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
self.outputParamsURL = outputParams
|
||||
self.spendParamsURL = spendParams
|
||||
self.networkType = networkType
|
||||
self.logger = logger
|
||||
}
|
||||
|
||||
convenience init(initializer: Initializer) {
|
||||
|
@ -43,7 +46,8 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
repository: initializer.transactionRepository,
|
||||
outputParams: initializer.outputParamsURL,
|
||||
spendParams: initializer.spendParamsURL,
|
||||
networkType: initializer.network.networkType
|
||||
networkType: initializer.network.networkType,
|
||||
logger: initializer.logger
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -63,7 +67,7 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
)
|
||||
|
||||
do {
|
||||
LoggerProxy.debug("transaction id: \(txId)")
|
||||
logger.debug("transaction id: \(txId)")
|
||||
return try repository.find(id: txId)
|
||||
} catch {
|
||||
throw TransactionEncoderError.notFound(transactionId: txId)
|
||||
|
@ -113,7 +117,7 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
)
|
||||
|
||||
do {
|
||||
LoggerProxy.debug("transaction id: \(txId)")
|
||||
logger.debug("transaction id: \(txId)")
|
||||
return try repository.find(id: txId)
|
||||
} catch {
|
||||
throw TransactionEncoderError.notFound(transactionId: txId)
|
||||
|
|
|
@ -22,26 +22,24 @@ public protocol Logger {
|
|||
func error(_ message: String, file: StaticString, function: StaticString, line: Int)
|
||||
}
|
||||
|
||||
var logger: Logger?
|
||||
|
||||
enum LoggerProxy {
|
||||
static func debug(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
logger?.debug(message, file: file, function: function, line: line)
|
||||
extension Logger {
|
||||
func debug(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
debug(message, file: file, function: function, line: line)
|
||||
}
|
||||
|
||||
static func info(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
logger?.info(message, file: file, function: function, line: line)
|
||||
func info(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
info(message, file: file, function: function, line: line)
|
||||
}
|
||||
|
||||
static func event(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
logger?.event(message, file: file, function: function, line: line)
|
||||
func event(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
event(message, file: file, function: function, line: line)
|
||||
}
|
||||
|
||||
static func warn(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
logger?.warn(message, file: file, function: function, line: line)
|
||||
func warn(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
warn(message, file: file, function: function, line: line)
|
||||
}
|
||||
|
||||
static func error(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
logger?.error(message, file: file, function: function, line: line)
|
||||
func error(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
error(message, file: file, function: function, line: line)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import Foundation
|
|||
import os
|
||||
|
||||
public class OSLogger: Logger {
|
||||
public var alias: ZcashSynchronizerAlias?
|
||||
|
||||
public enum LogLevel: Int {
|
||||
case debug
|
||||
case error
|
||||
|
@ -21,10 +23,13 @@ public class OSLogger: Logger {
|
|||
|
||||
var level: LogLevel
|
||||
|
||||
public init(logLevel: LogLevel, category: String = "logs") {
|
||||
public init(logLevel: LogLevel, category: String = "logs", alias: ZcashSynchronizerAlias? = nil) {
|
||||
self.alias = alias
|
||||
self.level = logLevel
|
||||
if let bundleName = Bundle.main.bundleIdentifier {
|
||||
self.oslog = OSLog(subsystem: bundleName, category: category)
|
||||
var postfix = ""
|
||||
if let alias { postfix = "_\(alias.description)" }
|
||||
self.oslog = OSLog(subsystem: bundleName, category: "\(category)\(postfix)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,8 @@ public enum SaplingParameterDownloader {
|
|||
/// - Parameters:
|
||||
/// - at: The destination URL for the download
|
||||
@discardableResult
|
||||
public static func downloadSpendParameter(_ at: URL, sourceURL: URL) async throws -> URL {
|
||||
let resultURL = try await downloadFileWithRequestWithContinuation(URLRequest(url: sourceURL), at: at)
|
||||
public static func downloadSpendParameter(_ at: URL, sourceURL: URL, logger: Logger) async throws -> URL {
|
||||
let resultURL = try await downloadFileWithRequestWithContinuation(URLRequest(url: sourceURL), logger: logger, at: at)
|
||||
try isSpendParamsSHA1Valid(url: resultURL)
|
||||
return resultURL
|
||||
}
|
||||
|
@ -40,8 +40,8 @@ public enum SaplingParameterDownloader {
|
|||
/// - Parameters:
|
||||
/// - at: The destination URL for the download
|
||||
@discardableResult
|
||||
public static func downloadOutputParameter(_ at: URL, sourceURL: URL) async throws -> URL {
|
||||
let resultURL = try await downloadFileWithRequestWithContinuation(URLRequest(url: sourceURL), at: at)
|
||||
public static func downloadOutputParameter(_ at: URL, sourceURL: URL, logger: Logger) async throws -> URL {
|
||||
let resultURL = try await downloadFileWithRequestWithContinuation(URLRequest(url: sourceURL), logger: logger, at: at)
|
||||
try isOutputParamsSHA1Valid(url: resultURL)
|
||||
return resultURL
|
||||
}
|
||||
|
@ -55,11 +55,12 @@ public enum SaplingParameterDownloader {
|
|||
spendURL: URL,
|
||||
spendSourceURL: URL,
|
||||
outputURL: URL,
|
||||
outputSourceURL: URL
|
||||
outputSourceURL: URL,
|
||||
logger: Logger
|
||||
) async throws -> (spend: URL, output: URL) {
|
||||
do {
|
||||
async let spendResultURL = ensureSpendParameter(at: spendURL, sourceURL: spendSourceURL)
|
||||
async let outputResultURL = ensureOutputParameter(at: outputURL, sourceURL: outputSourceURL)
|
||||
async let spendResultURL = ensureSpendParameter(at: spendURL, sourceURL: spendSourceURL, logger: logger)
|
||||
async let outputResultURL = ensureOutputParameter(at: outputURL, sourceURL: outputSourceURL, logger: logger)
|
||||
|
||||
let results = try await [spendResultURL, outputResultURL]
|
||||
return (spend: results[0], output: results[1])
|
||||
|
@ -68,21 +69,21 @@ public enum SaplingParameterDownloader {
|
|||
}
|
||||
}
|
||||
|
||||
static func ensureSpendParameter(at url: URL, sourceURL: URL) async throws -> URL {
|
||||
static func ensureSpendParameter(at url: URL, sourceURL: URL, logger: Logger) async throws -> URL {
|
||||
if isFilePresent(url: url) {
|
||||
try isSpendParamsSHA1Valid(url: url)
|
||||
return url
|
||||
} else {
|
||||
return try await downloadSpendParameter(url, sourceURL: sourceURL)
|
||||
return try await downloadSpendParameter(url, sourceURL: sourceURL, logger: logger)
|
||||
}
|
||||
}
|
||||
|
||||
static func ensureOutputParameter(at url: URL, sourceURL: URL) async throws -> URL {
|
||||
static func ensureOutputParameter(at url: URL, sourceURL: URL, logger: Logger) async throws -> URL {
|
||||
if isFilePresent(url: url) {
|
||||
try isOutputParamsSHA1Valid(url: url)
|
||||
return url
|
||||
} else {
|
||||
return try await downloadOutputParameter(url, sourceURL: sourceURL)
|
||||
return try await downloadOutputParameter(url, sourceURL: sourceURL, logger: logger)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,10 +114,11 @@ private extension SaplingParameterDownloader {
|
|||
|
||||
static func downloadFileWithRequestWithContinuation(
|
||||
_ request: URLRequest,
|
||||
logger: Logger,
|
||||
at destination: URL
|
||||
) async throws -> URL {
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
downloadFileWithRequest(request, at: destination) { result in
|
||||
downloadFileWithRequest(request, at: destination, logger: logger) { result in
|
||||
switch result {
|
||||
case .success(let outputResultURL):
|
||||
continuation.resume(returning: outputResultURL)
|
||||
|
@ -130,9 +132,10 @@ private extension SaplingParameterDownloader {
|
|||
static func downloadFileWithRequest(
|
||||
_ request: URLRequest,
|
||||
at destination: URL,
|
||||
logger: Logger,
|
||||
result: @escaping (Result<URL, Error>) -> Void
|
||||
) {
|
||||
LoggerProxy.debug("Downloading sapling file from \(String(describing: request.url))")
|
||||
logger.debug("Downloading sapling file from \(String(describing: request.url))")
|
||||
let task = URLSession.shared.downloadTask(with: request) { url, _, error in
|
||||
if let error {
|
||||
result(.failure(Errors.failed(error: error)))
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// UsedAliasesChecker.swift
|
||||
//
|
||||
//
|
||||
// Created by Michal Fousek on 23.03.2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
private var usedAliases: [ZcashSynchronizerAlias: UUID] = [:]
|
||||
private let usedAliasesLock = NSLock()
|
||||
|
||||
// Utility used to track which SDKSynchronizer aliases are already in use.
|
||||
enum UsedAliasesChecker {
|
||||
static func tryToUse(alias: ZcashSynchronizerAlias, id: UUID) -> Bool {
|
||||
usedAliasesLock.lock()
|
||||
defer { usedAliasesLock.unlock() }
|
||||
|
||||
// Using of `id` allows one instance of the SDKSynchronizer to call is any time it wants and still pass the check. When `wipe()` is called it
|
||||
// starts using the alias. Then when `prepare()` is also registers alias. If the check for `id` wouldn't be here one instance of the
|
||||
// `SDKSynchronizer` couldn't call `prepare()` after wipe because this check wouldn't pass.
|
||||
//
|
||||
// `id` is uniquely generated for each instance of the `SDKSynchronizer`.
|
||||
if let idUsingAlias = usedAliases[alias] {
|
||||
return idUsingAlias == id
|
||||
} else {
|
||||
usedAliases[alias] = id
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
static func stopUsing(alias: ZcashSynchronizerAlias, id: UUID) {
|
||||
usedAliasesLock.lock()
|
||||
defer { usedAliasesLock.unlock() }
|
||||
|
||||
// When instance "owns" the alias the alias is removed and it's no longer registered as used.
|
||||
if let idUsingAlias = usedAliases[alias], idUsingAlias == id {
|
||||
usedAliases.removeValue(forKey: alias)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -48,7 +48,7 @@ class AdvancedReOrgTests: XCTestCase {
|
|||
func handleReorg(event: CompactBlockProcessor.Event) {
|
||||
guard case let .handledReorg(reorgHeight, rewindHeight) = event else { return XCTFail("empty reorg event") }
|
||||
|
||||
logger!.debug("--- REORG DETECTED \(reorgHeight)--- RewindHeight: \(rewindHeight)", file: #file, function: #function, line: #line)
|
||||
logger.debug("--- REORG DETECTED \(reorgHeight)--- RewindHeight: \(rewindHeight)", file: #file, function: #function, line: #line)
|
||||
|
||||
XCTAssertEqual(reorgHeight, expectedReorgHeight)
|
||||
reorgExpectation.fulfill()
|
||||
|
|
|
@ -1088,7 +1088,8 @@ class BalanceTests: XCTestCase {
|
|||
let pendingRepo = PendingTransactionSQLDAO(
|
||||
dbProvider: SimpleConnectionProvider(
|
||||
path: coordinator.synchronizer.initializer.pendingDbURL.absoluteString
|
||||
)
|
||||
),
|
||||
logger: logger
|
||||
)
|
||||
|
||||
guard
|
||||
|
|
|
@ -27,13 +27,18 @@ class BlockDownloaderTests: XCTestCase {
|
|||
|
||||
override func setUpWithError() throws {
|
||||
try super.setUpWithError()
|
||||
service = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default, connectionStateChange: { _, _ in }).make()
|
||||
service = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default).make()
|
||||
|
||||
storage = FSCompactBlockRepository(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: FSMetadataStore.live(fsBlockDbRoot: testTempDirectory, rustBackend: ZcashRustBackend.self),
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: ZcashRustBackend.self,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
try storage.create()
|
||||
|
||||
|
|
|
@ -82,7 +82,11 @@ final class InternalStateConsistencyTests: XCTestCase {
|
|||
XCTAssertFalse(isSyncing, "SDKSynchronizer shouldn't be syncing")
|
||||
XCTAssertEqual(status, .stopped)
|
||||
|
||||
let internalSyncState = InternalSyncProgress(storage: UserDefaults.standard)
|
||||
let internalSyncState = InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: UserDefaults.standard,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let latestDownloadHeight = await internalSyncState.latestDownloadedBlockHeight
|
||||
let latestScanHeight = try coordinator.synchronizer.initializer.transactionRepository.lastScannedHeight()
|
||||
|
|
|
@ -58,7 +58,7 @@ final class SynchronizerTests: XCTestCase {
|
|||
func handleReorg(event: CompactBlockProcessor.Event) {
|
||||
guard case let .handledReorg(reorgHeight, rewindHeight) = event else { return XCTFail("empty reorg notification") }
|
||||
|
||||
logger!.debug("--- REORG DETECTED \(reorgHeight)--- RewindHeight: \(rewindHeight)", file: #file, function: #function, line: #line)
|
||||
logger.debug("--- REORG DETECTED \(reorgHeight)--- RewindHeight: \(rewindHeight)", file: #file, function: #function, line: #line)
|
||||
|
||||
XCTAssertEqual(reorgHeight, expectedReorgHeight)
|
||||
reorgExpectation.fulfill()
|
||||
|
@ -227,7 +227,11 @@ final class SynchronizerTests: XCTestCase {
|
|||
XCTAssertTrue(fm.fileExists(atPath: storage.blocksDirectory.path), "FS Cache directory should exist")
|
||||
XCTAssertEqual(try fm.contentsOfDirectory(atPath: storage.blocksDirectory.path), [], "FS Cache directory should be empty")
|
||||
|
||||
let internalSyncProgress = InternalSyncProgress(storage: UserDefaults.standard)
|
||||
let internalSyncProgress = InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: UserDefaults.standard,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let latestDownloadedBlockHeight = await internalSyncProgress.load(.latestDownloadedBlockHeight)
|
||||
let latestEnhancedHeight = await internalSyncProgress.load(.latestEnhancedHeight)
|
||||
|
|
|
@ -43,7 +43,13 @@ class TransactionEnhancementTests: XCTestCase {
|
|||
override func setUpWithError() throws {
|
||||
try super.setUpWithError()
|
||||
try self.testFileManager.createDirectory(at: self.testTempDirectory, withIntermediateDirectories: false)
|
||||
XCTestCase.wait { await InternalSyncProgress(storage: UserDefaults.standard).rewind(to: 0) }
|
||||
XCTestCase.wait {
|
||||
await InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: UserDefaults.standard,
|
||||
logger: logger
|
||||
).rewind(to: 0)
|
||||
}
|
||||
|
||||
logger = OSLogger(logLevel: .debug)
|
||||
|
||||
|
@ -62,6 +68,7 @@ class TransactionEnhancementTests: XCTestCase {
|
|||
|
||||
let pathProvider = DefaultResourceProvider(network: network)
|
||||
processorConfig = CompactBlockProcessor.Configuration(
|
||||
alias: .default,
|
||||
fsBlockCacheRoot: testTempDirectory,
|
||||
dataDb: pathProvider.dataDbURL,
|
||||
spendParamsURL: pathProvider.spendParamsURL,
|
||||
|
@ -116,10 +123,12 @@ class TransactionEnhancementTests: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: rustBackend
|
||||
rustBackend: rustBackend,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
try! storage.create()
|
||||
|
||||
|
@ -128,7 +137,9 @@ class TransactionEnhancementTests: XCTestCase {
|
|||
service: service,
|
||||
storage: storage,
|
||||
backend: rustBackend,
|
||||
config: processorConfig
|
||||
config: processorConfig,
|
||||
metrics: SDKMetrics(),
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let eventClosure: CompactBlockProcessor.EventClosure = { [weak self] event in
|
||||
|
|
|
@ -71,7 +71,7 @@ class BlockScanTests: XCTestCase {
|
|||
XCTAssertNoThrow(try rustWelding.initDataDb(dbData: dataDbURL, seed: nil, networkType: network.networkType))
|
||||
|
||||
let endpoint = LightWalletEndpoint(address: "lightwalletd.testnet.electriccoin.co", port: 9067)
|
||||
let service = LightWalletServiceFactory(endpoint: endpoint, connectionStateChange: { _, _ in }).make()
|
||||
let service = LightWalletServiceFactory(endpoint: endpoint).make()
|
||||
let blockCount = 100
|
||||
let range = network.constants.saplingActivationHeight ... network.constants.saplingActivationHeight + blockCount
|
||||
|
||||
|
@ -82,15 +82,18 @@ class BlockScanTests: XCTestCase {
|
|||
fsBlockDbRoot: fsDbRootURL,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: fsDbRootURL,
|
||||
rustBackend: rustBackend
|
||||
rustBackend: rustBackend,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: ZcashCompactBlockDescriptor.live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try fsBlockRepository.create()
|
||||
|
||||
let processorConfig = CompactBlockProcessor.Configuration(
|
||||
alias: .default,
|
||||
fsBlockCacheRoot: fsDbRootURL,
|
||||
dataDb: dataDbURL,
|
||||
spendParamsURL: spendParamsURL,
|
||||
|
@ -104,7 +107,9 @@ class BlockScanTests: XCTestCase {
|
|||
service: service,
|
||||
storage: fsBlockRepository,
|
||||
backend: rustBackend,
|
||||
config: processorConfig
|
||||
config: processorConfig,
|
||||
metrics: SDKMetrics(),
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let repository = BlockSQLDAO(dbProvider: SimpleConnectionProvider.init(path: self.dataDbURL.absoluteString, readonly: true))
|
||||
|
@ -118,8 +123,8 @@ class BlockScanTests: XCTestCase {
|
|||
XCTAssertEqual(latestScannedheight, range.upperBound)
|
||||
}
|
||||
|
||||
func observeBenchmark() {
|
||||
let reports = SDKMetrics.shared.popAllBlockReports(flush: true)
|
||||
func observeBenchmark(_ metrics: SDKMetrics) {
|
||||
let reports = metrics.popAllBlockReports(flush: true)
|
||||
|
||||
reports.forEach {
|
||||
print("observed benchmark: \($0)")
|
||||
|
@ -131,7 +136,8 @@ class BlockScanTests: XCTestCase {
|
|||
|
||||
logger = OSLogger(logLevel: .debug)
|
||||
|
||||
SDKMetrics.shared.enableMetrics()
|
||||
let metrics = SDKMetrics()
|
||||
metrics.enableMetrics()
|
||||
|
||||
guard try self.rustWelding.initDataDb(dbData: dataDbURL, seed: nil, networkType: network.networkType) == .success else {
|
||||
XCTFail("Seed should not be required for this test")
|
||||
|
@ -163,7 +169,7 @@ class BlockScanTests: XCTestCase {
|
|||
networkType: network.networkType
|
||||
)
|
||||
|
||||
let service = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet, connectionStateChange: { _, _ in }).make()
|
||||
let service = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make()
|
||||
|
||||
let fsDbRootURL = self.testTempDirectory
|
||||
|
||||
|
@ -171,15 +177,18 @@ class BlockScanTests: XCTestCase {
|
|||
fsBlockDbRoot: fsDbRootURL,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: fsDbRootURL,
|
||||
rustBackend: rustWelding
|
||||
rustBackend: rustWelding,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: ZcashCompactBlockDescriptor.live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try fsBlockRepository.create()
|
||||
|
||||
var processorConfig = CompactBlockProcessor.Configuration(
|
||||
alias: .default,
|
||||
fsBlockCacheRoot: fsDbRootURL,
|
||||
dataDb: dataDbURL,
|
||||
spendParamsURL: spendParamsURL,
|
||||
|
@ -194,12 +203,14 @@ class BlockScanTests: XCTestCase {
|
|||
service: service,
|
||||
storage: fsBlockRepository,
|
||||
backend: rustWelding,
|
||||
config: processorConfig
|
||||
config: processorConfig,
|
||||
metrics: metrics,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let eventClosure: CompactBlockProcessor.EventClosure = { [weak self] event in
|
||||
switch event {
|
||||
case .progressUpdated: self?.observeBenchmark()
|
||||
case .progressUpdated: self?.observeBenchmark(metrics)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
@ -242,6 +253,6 @@ class BlockScanTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
SDKMetrics.shared.disableMetrics()
|
||||
metrics.disableMetrics()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ class BlockStreamingTest: XCTestCase {
|
|||
singleCallTimeoutInMillis: 1000,
|
||||
streamingCallTimeoutInMillis: 100000
|
||||
)
|
||||
let service = LightWalletServiceFactory(endpoint: endpoint, connectionStateChange: { _, _ in }).make()
|
||||
let service = LightWalletServiceFactory(endpoint: endpoint).make()
|
||||
|
||||
let latestHeight = try await service.latestBlockHeight()
|
||||
|
||||
|
@ -66,7 +66,7 @@ class BlockStreamingTest: XCTestCase {
|
|||
singleCallTimeoutInMillis: 10000,
|
||||
streamingCallTimeoutInMillis: 10000
|
||||
)
|
||||
let service = LightWalletServiceFactory(endpoint: endpoint, connectionStateChange: { _, _ in }).make()
|
||||
let service = LightWalletServiceFactory(endpoint: endpoint).make()
|
||||
|
||||
let realRustBackend = ZcashRustBackend.self
|
||||
|
||||
|
@ -74,10 +74,12 @@ class BlockStreamingTest: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: realRustBackend
|
||||
rustBackend: realRustBackend,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try storage.create()
|
||||
|
@ -93,7 +95,9 @@ class BlockStreamingTest: XCTestCase {
|
|||
service: service,
|
||||
storage: storage,
|
||||
backend: realRustBackend,
|
||||
config: processorConfig
|
||||
config: processorConfig,
|
||||
metrics: SDKMetrics(),
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let cancelableTask = Task {
|
||||
|
@ -126,7 +130,7 @@ class BlockStreamingTest: XCTestCase {
|
|||
singleCallTimeoutInMillis: 1000,
|
||||
streamingCallTimeoutInMillis: 1000
|
||||
)
|
||||
let service = LightWalletServiceFactory(endpoint: endpoint, connectionStateChange: { _, _ in }).make()
|
||||
let service = LightWalletServiceFactory(endpoint: endpoint).make()
|
||||
|
||||
let realRustBackend = ZcashRustBackend.self
|
||||
|
||||
|
@ -134,10 +138,12 @@ class BlockStreamingTest: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: realRustBackend
|
||||
rustBackend: realRustBackend,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try storage.create()
|
||||
|
@ -155,7 +161,9 @@ class BlockStreamingTest: XCTestCase {
|
|||
service: service,
|
||||
storage: storage,
|
||||
backend: realRustBackend,
|
||||
config: processorConfig
|
||||
config: processorConfig,
|
||||
metrics: SDKMetrics(),
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let date = Date()
|
||||
|
|
|
@ -15,6 +15,7 @@ class CompactBlockProcessorTests: XCTestCase {
|
|||
lazy var processorConfig = {
|
||||
let pathProvider = DefaultResourceProvider(network: network)
|
||||
return CompactBlockProcessor.Configuration(
|
||||
alias: .default,
|
||||
fsBlockCacheRoot: testTempDirectory,
|
||||
dataDb: pathProvider.dataDbURL,
|
||||
spendParamsURL: pathProvider.spendParamsURL,
|
||||
|
@ -46,9 +47,15 @@ class CompactBlockProcessorTests: XCTestCase {
|
|||
logger = OSLogger(logLevel: .debug)
|
||||
try self.testFileManager.createDirectory(at: self.testTempDirectory, withIntermediateDirectories: false)
|
||||
|
||||
XCTestCase.wait { await InternalSyncProgress(storage: UserDefaults.standard).rewind(to: 0) }
|
||||
XCTestCase.wait {
|
||||
await InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: UserDefaults.standard,
|
||||
logger: logger
|
||||
).rewind(to: 0)
|
||||
}
|
||||
|
||||
let liveService = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet, connectionStateChange: { _, _ in }).make()
|
||||
let liveService = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make()
|
||||
let service = MockLightWalletService(
|
||||
latestBlockHeight: mockLatestHeight,
|
||||
service: liveService
|
||||
|
@ -71,10 +78,12 @@ class CompactBlockProcessorTests: XCTestCase {
|
|||
fsBlockDbRoot: processorConfig.fsBlockCacheRoot,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: processorConfig.fsBlockCacheRoot,
|
||||
rustBackend: realRustBackend
|
||||
rustBackend: realRustBackend,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try storage.create()
|
||||
|
@ -83,7 +92,9 @@ class CompactBlockProcessorTests: XCTestCase {
|
|||
service: service,
|
||||
storage: storage,
|
||||
backend: realRustBackend,
|
||||
config: processorConfig
|
||||
config: processorConfig,
|
||||
metrics: SDKMetrics(),
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let dbInit = try realRustBackend.initDataDb(dbData: processorConfig.dataDb, seed: nil, networkType: .testnet)
|
||||
|
@ -185,7 +196,11 @@ class CompactBlockProcessorTests: XCTestCase {
|
|||
latestDownloadedBlockHeight: latestDownloadedHeight
|
||||
)
|
||||
|
||||
var internalSyncProgress = InternalSyncProgress(storage: InternalSyncProgressMemoryStorage())
|
||||
var internalSyncProgress = InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: InternalSyncProgressMemoryStorage(),
|
||||
logger: logger
|
||||
)
|
||||
await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadedHeight)
|
||||
|
||||
var syncRanges = await internalSyncProgress.computeSyncRanges(
|
||||
|
@ -214,7 +229,11 @@ class CompactBlockProcessorTests: XCTestCase {
|
|||
latestDownloadedBlockHeight: latestDownloadedHeight
|
||||
)
|
||||
|
||||
internalSyncProgress = InternalSyncProgress(storage: InternalSyncProgressMemoryStorage())
|
||||
internalSyncProgress = InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: InternalSyncProgressMemoryStorage(),
|
||||
logger: logger
|
||||
)
|
||||
await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadedHeight)
|
||||
|
||||
syncRanges = await internalSyncProgress.computeSyncRanges(
|
||||
|
@ -244,7 +263,11 @@ class CompactBlockProcessorTests: XCTestCase {
|
|||
latestDownloadedBlockHeight: latestDownloadedHeight
|
||||
)
|
||||
|
||||
internalSyncProgress = InternalSyncProgress(storage: InternalSyncProgressMemoryStorage())
|
||||
internalSyncProgress = InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: InternalSyncProgressMemoryStorage(),
|
||||
logger: logger
|
||||
)
|
||||
await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadedHeight)
|
||||
|
||||
syncRanges = await internalSyncProgress.computeSyncRanges(
|
||||
|
|
|
@ -15,6 +15,7 @@ class CompactBlockReorgTests: XCTestCase {
|
|||
lazy var processorConfig = {
|
||||
let pathProvider = DefaultResourceProvider(network: network)
|
||||
return CompactBlockProcessor.Configuration(
|
||||
alias: .default,
|
||||
fsBlockCacheRoot: testTempDirectory,
|
||||
dataDb: pathProvider.dataDbURL,
|
||||
spendParamsURL: pathProvider.spendParamsURL,
|
||||
|
@ -48,9 +49,15 @@ class CompactBlockReorgTests: XCTestCase {
|
|||
logger = OSLogger(logLevel: .debug)
|
||||
try self.testFileManager.createDirectory(at: self.testTempDirectory, withIntermediateDirectories: false)
|
||||
|
||||
XCTestCase.wait { await InternalSyncProgress(storage: UserDefaults.standard).rewind(to: 0) }
|
||||
XCTestCase.wait {
|
||||
await InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: UserDefaults.standard,
|
||||
logger: logger
|
||||
).rewind(to: 0)
|
||||
}
|
||||
|
||||
let liveService = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet, connectionStateChange: { _, _ in }).make()
|
||||
let liveService = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make()
|
||||
let service = MockLightWalletService(
|
||||
latestBlockHeight: mockLatestHeight,
|
||||
service: liveService
|
||||
|
@ -74,10 +81,12 @@ class CompactBlockReorgTests: XCTestCase {
|
|||
fsBlockDbRoot: processorConfig.fsBlockCacheRoot,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: processorConfig.fsBlockCacheRoot,
|
||||
rustBackend: realRustBackend
|
||||
rustBackend: realRustBackend,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try realCache.create()
|
||||
|
@ -96,7 +105,9 @@ class CompactBlockReorgTests: XCTestCase {
|
|||
service: service,
|
||||
storage: realCache,
|
||||
backend: mockBackend,
|
||||
config: processorConfig
|
||||
config: processorConfig,
|
||||
metrics: SDKMetrics(),
|
||||
logger: logger
|
||||
)
|
||||
|
||||
syncStartedExpect = XCTestExpectation(description: "\(self.description) syncStartedExpect")
|
||||
|
|
|
@ -33,7 +33,7 @@ class DownloadTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testSingleDownload() async throws {
|
||||
let service = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet, connectionStateChange: { _, _ in }).make()
|
||||
let service = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make()
|
||||
|
||||
let realRustBackend = ZcashRustBackend.self
|
||||
|
||||
|
@ -41,10 +41,12 @@ class DownloadTests: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: realRustBackend
|
||||
rustBackend: realRustBackend,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try storage.create()
|
||||
|
@ -62,7 +64,9 @@ class DownloadTests: XCTestCase {
|
|||
service: service,
|
||||
storage: storage,
|
||||
backend: realRustBackend,
|
||||
config: processorConfig
|
||||
config: processorConfig,
|
||||
metrics: SDKMetrics(),
|
||||
logger: logger
|
||||
)
|
||||
|
||||
do {
|
||||
|
|
|
@ -19,7 +19,7 @@ class LightWalletServiceTests: XCTestCase {
|
|||
override func setUp() {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
super.setUp()
|
||||
service = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet, connectionStateChange: { _, _ in }).make()
|
||||
service = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make()
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
|
|
|
@ -31,7 +31,7 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
let network = ZcashNetworkBuilder.network(for: .mainnet)
|
||||
let service = MockLightWalletService(
|
||||
latestBlockHeight: 1210000,
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default, connectionStateChange: { _, _ in }).make()
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default).make()
|
||||
)
|
||||
|
||||
let realRustBackend = ZcashRustBackend.self
|
||||
|
@ -40,10 +40,12 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: realRustBackend
|
||||
rustBackend: realRustBackend,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try storage.create()
|
||||
|
@ -51,6 +53,7 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000)
|
||||
let downloaderService = BlockDownloaderServiceImpl(service: service, storage: repository)
|
||||
let config = CompactBlockProcessor.Configuration(
|
||||
alias: .default,
|
||||
fsBlockCacheRoot: testTempDirectory,
|
||||
dataDb: try! __dataDbURL(),
|
||||
spendParamsURL: try! __spendParamsURL(),
|
||||
|
@ -81,7 +84,9 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
service: service,
|
||||
storage: storage,
|
||||
backend: mockRust,
|
||||
config: config
|
||||
config: config,
|
||||
metrics: SDKMetrics(),
|
||||
logger: logger
|
||||
)
|
||||
|
||||
do {
|
||||
|
@ -101,7 +106,7 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
let network = ZcashNetworkBuilder.network(for: .mainnet)
|
||||
let service = MockLightWalletService(
|
||||
latestBlockHeight: 1210000,
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default, connectionStateChange: { _, _ in }).make()
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default).make()
|
||||
)
|
||||
|
||||
let realRustBackend = ZcashRustBackend.self
|
||||
|
@ -110,10 +115,12 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: realRustBackend
|
||||
rustBackend: realRustBackend,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try storage.create()
|
||||
|
@ -121,6 +128,7 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000)
|
||||
let downloaderService = BlockDownloaderServiceImpl(service: service, storage: repository)
|
||||
let config = CompactBlockProcessor.Configuration(
|
||||
alias: .default,
|
||||
fsBlockCacheRoot: testTempDirectory,
|
||||
dataDb: try! __dataDbURL(),
|
||||
spendParamsURL: try! __spendParamsURL(),
|
||||
|
@ -151,7 +159,9 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
service: service,
|
||||
storage: storage,
|
||||
backend: mockRust,
|
||||
config: config
|
||||
config: config,
|
||||
metrics: SDKMetrics(),
|
||||
logger: logger
|
||||
)
|
||||
|
||||
do {
|
||||
|
@ -171,7 +181,7 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
let network = ZcashNetworkBuilder.network(for: .testnet)
|
||||
let service = MockLightWalletService(
|
||||
latestBlockHeight: 1210000,
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default, connectionStateChange: { _, _ in }).make()
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default).make()
|
||||
)
|
||||
|
||||
let realRustBackend = ZcashRustBackend.self
|
||||
|
@ -180,10 +190,12 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: realRustBackend
|
||||
rustBackend: realRustBackend,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try storage.create()
|
||||
|
@ -191,6 +203,7 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000)
|
||||
let downloaderService = BlockDownloaderServiceImpl(service: service, storage: repository)
|
||||
let config = CompactBlockProcessor.Configuration(
|
||||
alias: .default,
|
||||
fsBlockCacheRoot: testTempDirectory,
|
||||
dataDb: try! __dataDbURL(),
|
||||
spendParamsURL: try! __spendParamsURL(),
|
||||
|
@ -221,7 +234,9 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
service: service,
|
||||
storage: storage,
|
||||
backend: mockRust,
|
||||
config: config
|
||||
config: config,
|
||||
metrics: SDKMetrics(),
|
||||
logger: logger
|
||||
)
|
||||
|
||||
do {
|
||||
|
@ -241,7 +256,7 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
let network = ZcashNetworkBuilder.network(for: .mainnet)
|
||||
let service = MockLightWalletService(
|
||||
latestBlockHeight: 1210000,
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default, connectionStateChange: { _, _ in }).make()
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default).make()
|
||||
)
|
||||
|
||||
let realRustBackend = ZcashRustBackend.self
|
||||
|
@ -250,10 +265,12 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: realRustBackend
|
||||
rustBackend: realRustBackend,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try storage.create()
|
||||
|
@ -261,6 +278,7 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000)
|
||||
let downloaderService = BlockDownloaderServiceImpl(service: service, storage: repository)
|
||||
let config = CompactBlockProcessor.Configuration(
|
||||
alias: .default,
|
||||
fsBlockCacheRoot: testTempDirectory,
|
||||
dataDb: try! __dataDbURL(),
|
||||
spendParamsURL: try! __spendParamsURL(),
|
||||
|
@ -292,7 +310,9 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
service: service,
|
||||
storage: storage,
|
||||
backend: mockRust,
|
||||
config: config
|
||||
config: config,
|
||||
metrics: SDKMetrics(),
|
||||
logger: logger
|
||||
)
|
||||
|
||||
do {
|
||||
|
@ -317,7 +337,7 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
let expectedLatestHeight = BlockHeight(1210000)
|
||||
let service = MockLightWalletService(
|
||||
latestBlockHeight: expectedLatestHeight,
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default, connectionStateChange: { _, _ in }).make()
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default).make()
|
||||
)
|
||||
let expectedStoredLatestHeight = BlockHeight(1220000)
|
||||
let expectedResult = CompactBlockProcessor.NextState.wait(
|
||||
|
@ -329,6 +349,7 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
let downloaderService = BlockDownloaderServiceImpl(service: service, storage: repository)
|
||||
|
||||
let config = CompactBlockProcessor.Configuration(
|
||||
alias: .default,
|
||||
fsBlockCacheRoot: testTempDirectory,
|
||||
dataDb: try! __dataDbURL(),
|
||||
spendParamsURL: try! __spendParamsURL(),
|
||||
|
@ -372,7 +393,11 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
transactionRepository: transactionRepository,
|
||||
config: config,
|
||||
rustBackend: mockRust,
|
||||
internalSyncProgress: InternalSyncProgress(storage: InternalSyncProgressMemoryStorage())
|
||||
internalSyncProgress: InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: InternalSyncProgressMemoryStorage(),
|
||||
logger: logger
|
||||
)
|
||||
)
|
||||
XCTAssertFalse(Task.isCancelled)
|
||||
} catch {
|
||||
|
@ -402,7 +427,7 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
let expectedLatestHeight = BlockHeight(1230000)
|
||||
let service = MockLightWalletService(
|
||||
latestBlockHeight: expectedLatestHeight,
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default, connectionStateChange: { _, _ in }).make()
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default).make()
|
||||
)
|
||||
let expectedStoreLatestHeight = BlockHeight(1220000)
|
||||
let walletBirthday = BlockHeight(1210000)
|
||||
|
@ -421,6 +446,7 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
let repository = ZcashConsoleFakeStorage(latestBlockHeight: expectedStoreLatestHeight)
|
||||
let downloaderService = BlockDownloaderServiceImpl(service: service, storage: repository)
|
||||
let config = CompactBlockProcessor.Configuration(
|
||||
alias: .default,
|
||||
fsBlockCacheRoot: testTempDirectory,
|
||||
dataDb: try! __dataDbURL(),
|
||||
spendParamsURL: try! __spendParamsURL(),
|
||||
|
@ -464,7 +490,11 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
transactionRepository: transactionRepository,
|
||||
config: config,
|
||||
rustBackend: mockRust,
|
||||
internalSyncProgress: InternalSyncProgress(storage: InternalSyncProgressMemoryStorage())
|
||||
internalSyncProgress: InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: InternalSyncProgressMemoryStorage(),
|
||||
logger: logger
|
||||
)
|
||||
)
|
||||
XCTAssertFalse(Task.isCancelled)
|
||||
} catch {
|
||||
|
@ -494,7 +524,7 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
let expectedLatestHeight = BlockHeight(1230000)
|
||||
let service = MockLightWalletService(
|
||||
latestBlockHeight: expectedLatestHeight,
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default, connectionStateChange: { _, _ in }).make()
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default).make()
|
||||
)
|
||||
let expectedStoreLatestHeight = BlockHeight(1230000)
|
||||
let walletBirthday = BlockHeight(1210000)
|
||||
|
@ -502,6 +532,7 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
let repository = ZcashConsoleFakeStorage(latestBlockHeight: expectedStoreLatestHeight)
|
||||
let downloaderService = BlockDownloaderServiceImpl(service: service, storage: repository)
|
||||
let config = CompactBlockProcessor.Configuration(
|
||||
alias: .default,
|
||||
fsBlockCacheRoot: testTempDirectory,
|
||||
dataDb: try! __dataDbURL(),
|
||||
spendParamsURL: try! __spendParamsURL(),
|
||||
|
@ -516,7 +547,11 @@ class BlockBatchValidationTests: XCTestCase {
|
|||
network: network
|
||||
)
|
||||
|
||||
let internalSyncProgress = InternalSyncProgress(storage: InternalSyncProgressMemoryStorage())
|
||||
let internalSyncProgress = InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: InternalSyncProgressMemoryStorage(),
|
||||
logger: logger
|
||||
)
|
||||
await internalSyncProgress.set(expectedStoreLatestHeight, .latestEnhancedHeight)
|
||||
await internalSyncProgress.set(expectedStoreLatestHeight, .latestUTXOFetchedHeight)
|
||||
|
||||
|
|
|
@ -33,6 +33,11 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
|
|||
data = nil
|
||||
}
|
||||
|
||||
func testAliasIsAsExpected() {
|
||||
synchronizerMock.underlyingAlias = .custom("some_alias")
|
||||
XCTAssertEqual(synchronizer.alias, .custom("some_alias"))
|
||||
}
|
||||
|
||||
func testStateStreamEmitsAsExpected() {
|
||||
let state = SynchronizerState(
|
||||
shieldedBalance: WalletBalance(verified: Zatoshi(100), total: Zatoshi(200)),
|
||||
|
@ -76,7 +81,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testEventStreamEmitsAsExpected() {
|
||||
synchronizerMock.underlyingEventStream = Just(.connectionStateChanged).eraseToAnyPublisher()
|
||||
synchronizerMock.underlyingEventStream = Just(.connectionStateChanged(.connecting)).eraseToAnyPublisher()
|
||||
|
||||
let expectation = XCTestExpectation()
|
||||
|
||||
|
|
|
@ -31,6 +31,11 @@ class CombineSynchronizerOfflineTests: XCTestCase {
|
|||
data = nil
|
||||
}
|
||||
|
||||
func testAliasIsAsExpected() {
|
||||
synchronizerMock.underlyingAlias = .custom("some_alias")
|
||||
XCTAssertEqual(synchronizer.alias, .custom("some_alias"))
|
||||
}
|
||||
|
||||
func testStateStreamEmitsAsExpected() {
|
||||
let state = SynchronizerState(
|
||||
shieldedBalance: WalletBalance(verified: Zatoshi(100), total: Zatoshi(200)),
|
||||
|
@ -74,7 +79,7 @@ class CombineSynchronizerOfflineTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testEventStreamEmitsAsExpected() {
|
||||
synchronizerMock.underlyingEventStream = Just(.connectionStateChanged).eraseToAnyPublisher()
|
||||
synchronizerMock.underlyingEventStream = Just(.connectionStateChanged(.connecting)).eraseToAnyPublisher()
|
||||
|
||||
let expectation = XCTestExpectation()
|
||||
|
||||
|
|
|
@ -37,20 +37,29 @@ class CompactBlockProcessorOfflineTests: XCTestCase {
|
|||
|
||||
let service = MockLightWalletService(
|
||||
latestBlockHeight: 690000,
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet, connectionStateChange: { _, _ in }).make()
|
||||
service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make()
|
||||
)
|
||||
|
||||
let storage = FSCompactBlockRepository(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: realRustBackend
|
||||
rustBackend: realRustBackend,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let processor = CompactBlockProcessor(service: service, storage: storage, backend: ZcashRustBackend.self, config: processorConfig)
|
||||
let processor = CompactBlockProcessor(
|
||||
service: service,
|
||||
storage: storage,
|
||||
backend: ZcashRustBackend.self,
|
||||
config: processorConfig,
|
||||
metrics: SDKMetrics(),
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let fullRange = 0...1000
|
||||
|
||||
|
|
|
@ -36,16 +36,17 @@ class CompactBlockRepositoryTests: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: ZcashRustBackend.self
|
||||
rustBackend: ZcashRustBackend.self,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try compactBlockRepository.create()
|
||||
|
||||
let latestHeight = await compactBlockRepository.latestHeight()
|
||||
|
||||
XCTAssertEqual(latestHeight, BlockHeight.empty())
|
||||
}
|
||||
|
||||
|
@ -54,10 +55,12 @@ class CompactBlockRepositoryTests: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: ZcashRustBackend.self
|
||||
rustBackend: ZcashRustBackend.self,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try compactBlockRepository.create()
|
||||
|
@ -79,10 +82,12 @@ class CompactBlockRepositoryTests: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: ZcashRustBackend.self
|
||||
rustBackend: ZcashRustBackend.self,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try compactBlockRepository.create()
|
||||
|
@ -103,10 +108,12 @@ class CompactBlockRepositoryTests: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: ZcashRustBackend.self
|
||||
rustBackend: ZcashRustBackend.self,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try compactBlockRepository.create()
|
||||
|
|
|
@ -61,7 +61,8 @@ final class FsBlockStorageTests: XCTestCase {
|
|||
describe: { _ in blockNameFixture },
|
||||
compare: { _, _ in nil }
|
||||
),
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try freshCache.create()
|
||||
|
@ -82,7 +83,8 @@ final class FsBlockStorageTests: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: .mock,
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try freshCache.create()
|
||||
|
@ -107,7 +109,8 @@ final class FsBlockStorageTests: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: .mock,
|
||||
blockDescriptor: .live,
|
||||
contentProvider: contentProvider
|
||||
contentProvider: contentProvider,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try freshCache.create()
|
||||
|
@ -167,9 +170,10 @@ final class FsBlockStorageTests: XCTestCase {
|
|||
func testGetLatestHeight() async throws {
|
||||
let freshCache = FSCompactBlockRepository(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: .live(fsBlockDbRoot: testTempDirectory, rustBackend: ZcashRustBackend.self),
|
||||
metadataStore: .live(fsBlockDbRoot: testTempDirectory, rustBackend: ZcashRustBackend.self, logger: logger),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try freshCache.create()
|
||||
|
@ -278,9 +282,10 @@ final class FsBlockStorageTests: XCTestCase {
|
|||
func testClearTheCache() async throws {
|
||||
let fsBlockCache = FSCompactBlockRepository(
|
||||
fsBlockDbRoot: self.testTempDirectory,
|
||||
metadataStore: .live(fsBlockDbRoot: testTempDirectory, rustBackend: ZcashRustBackend.self),
|
||||
metadataStore: .live(fsBlockDbRoot: testTempDirectory, rustBackend: ZcashRustBackend.self, logger: logger),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.naive
|
||||
contentProvider: DirectoryListingProviders.naive,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try fsBlockCache.create()
|
||||
|
@ -305,7 +310,8 @@ final class FsBlockStorageTests: XCTestCase {
|
|||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: .mock,
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try freshCache.create()
|
||||
|
@ -313,14 +319,15 @@ final class FsBlockStorageTests: XCTestCase {
|
|||
XCTAssertNoThrow(try freshCache.create())
|
||||
}
|
||||
|
||||
func testStoringTenSandblastedBlocks() async throws {
|
||||
func disabled_testStoringTenSandblastedBlocks() async throws {
|
||||
let realRustBackend = ZcashRustBackend.self
|
||||
|
||||
let realCache = FSCompactBlockRepository(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: .live(fsBlockDbRoot: testTempDirectory, rustBackend: realRustBackend),
|
||||
metadataStore: .live(fsBlockDbRoot: testTempDirectory, rustBackend: realRustBackend, logger: logger),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try realCache.create()
|
||||
|
@ -348,10 +355,11 @@ final class FsBlockStorageTests: XCTestCase {
|
|||
|
||||
let realCache = FSCompactBlockRepository(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: .live(fsBlockDbRoot: testTempDirectory, rustBackend: realRustBackend),
|
||||
metadataStore: .live(fsBlockDbRoot: testTempDirectory, rustBackend: realRustBackend, logger: logger),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
fileWriter: FSBlockFileWriter(writeToURL: { _, _ in throw FixtureError.arbitraryError })
|
||||
fileWriter: FSBlockFileWriter(writeToURL: { _, _ in throw FixtureError.arbitraryError }),
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try realCache.create()
|
||||
|
@ -374,9 +382,10 @@ final class FsBlockStorageTests: XCTestCase {
|
|||
|
||||
let realCache = FSCompactBlockRepository(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: .live(fsBlockDbRoot: testTempDirectory, rustBackend: realRustBackend),
|
||||
metadataStore: .live(fsBlockDbRoot: testTempDirectory, rustBackend: realRustBackend, logger: logger),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try realCache.create()
|
||||
|
@ -421,7 +430,12 @@ final class FsBlockStorageTests: XCTestCase {
|
|||
MockRustBackend.writeBlocksMetadataResult = { false }
|
||||
|
||||
do {
|
||||
try await FSMetadataStore.saveBlocksMeta(sandblastedBlocks, fsBlockDbRoot: testTempDirectory, rustBackend: MockRustBackend.self)
|
||||
try await FSMetadataStore.saveBlocksMeta(
|
||||
sandblastedBlocks,
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: MockRustBackend.self,
|
||||
logger: logger
|
||||
)
|
||||
} catch CompactBlockRepositoryError.failedToWriteMetadata {
|
||||
// this is fine
|
||||
} catch {
|
||||
|
@ -438,7 +452,12 @@ final class FsBlockStorageTests: XCTestCase {
|
|||
MockRustBackend.writeBlocksMetadataResult = { throw RustWeldingError.genericError(message: "oops") }
|
||||
|
||||
do {
|
||||
try await FSMetadataStore.saveBlocksMeta(sandblastedBlocks, fsBlockDbRoot: testTempDirectory, rustBackend: MockRustBackend.self)
|
||||
try await FSMetadataStore.saveBlocksMeta(
|
||||
sandblastedBlocks,
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: MockRustBackend.self,
|
||||
logger: logger
|
||||
)
|
||||
} catch CompactBlockRepositoryError.failedToWriteMetadata {
|
||||
// this is fine
|
||||
} catch {
|
||||
|
@ -453,7 +472,8 @@ final class FsBlockStorageTests: XCTestCase {
|
|||
XCTAssertThrowsError(
|
||||
try FSMetadataStore.live(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
rustBackend: MockRustBackend.self
|
||||
rustBackend: MockRustBackend.self,
|
||||
logger: logger
|
||||
)
|
||||
.rewindToHeight(expectedHeight)
|
||||
) { error in
|
||||
|
@ -476,9 +496,10 @@ final class FsBlockStorageTests: XCTestCase {
|
|||
// NOTE: performance tests don't work with async code. Thanks Apple!
|
||||
let freshCache = FSCompactBlockRepository(
|
||||
fsBlockDbRoot: testTempDirectory,
|
||||
metadataStore: .live(fsBlockDbRoot: testTempDirectory, rustBackend: ZcashRustBackend.self),
|
||||
metadataStore: .live(fsBlockDbRoot: testTempDirectory, rustBackend: ZcashRustBackend.self, logger: logger),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
try freshCache.create()
|
||||
|
@ -516,7 +537,8 @@ extension FSCompactBlockRepository {
|
|||
contentProvider: SortedDirectoryContentProvider(
|
||||
fileManager: FileManager.default,
|
||||
sorting: { _, _ in false }
|
||||
)
|
||||
),
|
||||
logger: OSLogger(logLevel: .debug)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
//
|
||||
// InitializerOfflineTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Michal Fousek on 24.03.2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@testable import TestUtils
|
||||
import XCTest
|
||||
@testable import ZcashLightClientKit
|
||||
|
||||
class InitializerOfflineTests: XCTestCase {
|
||||
let validFileURL = URL(fileURLWithPath: "/some/valid/path/to.file")
|
||||
let validDirectoryURL = URL(fileURLWithPath: "/some/valid/path/to/directory")
|
||||
let invalidPathURL = URL(string: "https://whatever")!
|
||||
|
||||
// MARK: - Utils
|
||||
|
||||
private func makeInitializer(
|
||||
fsBlockDbRoot: URL,
|
||||
dataDbURL: URL,
|
||||
pendingDbURL: URL,
|
||||
spendParamsURL: URL,
|
||||
outputParamsURL: URL,
|
||||
alias: ZcashSynchronizerAlias
|
||||
) -> Initializer {
|
||||
return Initializer(
|
||||
cacheDbURL: nil,
|
||||
fsBlockDbRoot: fsBlockDbRoot,
|
||||
dataDbURL: dataDbURL,
|
||||
pendingDbURL: pendingDbURL,
|
||||
endpoint: LightWalletEndpointBuilder.default,
|
||||
network: ZcashNetworkBuilder.network(for: .testnet),
|
||||
spendParamsURL: spendParamsURL,
|
||||
outputParamsURL: outputParamsURL,
|
||||
saplingParamsSourceURL: .default,
|
||||
alias: alias,
|
||||
logLevel: .debug
|
||||
)
|
||||
}
|
||||
|
||||
private func update(url: URL, with alias: ZcashSynchronizerAlias) -> URL {
|
||||
guard alias != .default else { return url }
|
||||
let lastPathComponent = url.lastPathComponent
|
||||
guard !lastPathComponent.isEmpty else { return url }
|
||||
return url
|
||||
.deletingLastPathComponent()
|
||||
.appendingPathComponent("\(alias.description)_\(lastPathComponent)")
|
||||
}
|
||||
|
||||
// MARK: - Tests
|
||||
|
||||
private func genericTestForURLsParsingFailures(
|
||||
fsBlockDbRoot: URL,
|
||||
dataDbURL: URL,
|
||||
pendingDbURL: URL,
|
||||
spendParamsURL: URL,
|
||||
outputParamsURL: URL,
|
||||
alias: ZcashSynchronizerAlias,
|
||||
function: String = #function
|
||||
) {
|
||||
let initializer = makeInitializer(
|
||||
fsBlockDbRoot: fsBlockDbRoot,
|
||||
dataDbURL: dataDbURL,
|
||||
pendingDbURL: pendingDbURL,
|
||||
spendParamsURL: spendParamsURL,
|
||||
outputParamsURL: outputParamsURL,
|
||||
alias: alias
|
||||
)
|
||||
|
||||
if let error = initializer.urlsParsingError, case let .cantUpdateURLWithAlias(failedURL) = error {
|
||||
XCTAssertEqual(failedURL, invalidPathURL, "Failing \(function)")
|
||||
} else {
|
||||
XCTFail("URLs parsing error expected. Failing \(function)")
|
||||
}
|
||||
|
||||
XCTAssertEqual(initializer.fsBlockDbRoot, fsBlockDbRoot, "Failing \(function)")
|
||||
XCTAssertEqual(initializer.dataDbURL, dataDbURL, "Failing \(function)")
|
||||
XCTAssertEqual(initializer.pendingDbURL, pendingDbURL, "Failing \(function)")
|
||||
XCTAssertEqual(initializer.spendParamsURL, spendParamsURL, "Failing \(function)")
|
||||
XCTAssertEqual(initializer.outputParamsURL, outputParamsURL, "Failing \(function)")
|
||||
}
|
||||
|
||||
func test__defaultAlias__validURLs__updatedURLsAreBackwardsCompatible() {
|
||||
let initializer = makeInitializer(
|
||||
fsBlockDbRoot: validDirectoryURL,
|
||||
dataDbURL: validFileURL,
|
||||
pendingDbURL: validFileURL,
|
||||
spendParamsURL: validFileURL,
|
||||
outputParamsURL: validFileURL,
|
||||
alias: .default
|
||||
)
|
||||
|
||||
XCTAssertNil(initializer.urlsParsingError)
|
||||
XCTAssertEqual(initializer.fsBlockDbRoot, validDirectoryURL)
|
||||
XCTAssertEqual(initializer.dataDbURL, validFileURL)
|
||||
XCTAssertEqual(initializer.pendingDbURL, validFileURL)
|
||||
XCTAssertEqual(initializer.spendParamsURL, validFileURL)
|
||||
XCTAssertEqual(initializer.outputParamsURL, validFileURL)
|
||||
}
|
||||
|
||||
func test__defaultAlias__invalidFsBlockDbRootURL__errorIsGenerated() {
|
||||
genericTestForURLsParsingFailures(
|
||||
fsBlockDbRoot: invalidPathURL,
|
||||
dataDbURL: validFileURL,
|
||||
pendingDbURL: validFileURL,
|
||||
spendParamsURL: validFileURL,
|
||||
outputParamsURL: validFileURL,
|
||||
alias: .default
|
||||
)
|
||||
}
|
||||
|
||||
func test__defaultAlias__invalidDataDbURL__errorIsGenerated() {
|
||||
genericTestForURLsParsingFailures(
|
||||
fsBlockDbRoot: validDirectoryURL,
|
||||
dataDbURL: invalidPathURL,
|
||||
pendingDbURL: validFileURL,
|
||||
spendParamsURL: validFileURL,
|
||||
outputParamsURL: validFileURL,
|
||||
alias: .default
|
||||
)
|
||||
}
|
||||
|
||||
func test__defaultAlias__invalidPendingDbURL__errorIsGenerated() {
|
||||
genericTestForURLsParsingFailures(
|
||||
fsBlockDbRoot: validDirectoryURL,
|
||||
dataDbURL: validFileURL,
|
||||
pendingDbURL: invalidPathURL,
|
||||
spendParamsURL: validFileURL,
|
||||
outputParamsURL: validFileURL,
|
||||
alias: .default
|
||||
)
|
||||
}
|
||||
|
||||
func test__defaultAlias__invalidSpendParamsURL__errorIsGenerated() {
|
||||
genericTestForURLsParsingFailures(
|
||||
fsBlockDbRoot: validDirectoryURL,
|
||||
dataDbURL: validFileURL,
|
||||
pendingDbURL: validFileURL,
|
||||
spendParamsURL: invalidPathURL,
|
||||
outputParamsURL: validFileURL,
|
||||
alias: .default
|
||||
)
|
||||
}
|
||||
|
||||
func test__defaultAlias__invalidOutputParamsURL__errorIsGenerated() {
|
||||
genericTestForURLsParsingFailures(
|
||||
fsBlockDbRoot: validDirectoryURL,
|
||||
dataDbURL: validFileURL,
|
||||
pendingDbURL: validFileURL,
|
||||
spendParamsURL: validFileURL,
|
||||
outputParamsURL: invalidPathURL,
|
||||
alias: .default
|
||||
)
|
||||
}
|
||||
|
||||
func test__customAlias__validURLs__updatedURLsAreAsExpected() {
|
||||
let alias: ZcashSynchronizerAlias = .custom("alias")
|
||||
let initializer = makeInitializer(
|
||||
fsBlockDbRoot: validDirectoryURL,
|
||||
dataDbURL: validFileURL,
|
||||
pendingDbURL: validFileURL,
|
||||
spendParamsURL: validFileURL,
|
||||
outputParamsURL: validFileURL,
|
||||
alias: alias
|
||||
)
|
||||
|
||||
XCTAssertNil(initializer.urlsParsingError)
|
||||
XCTAssertEqual(initializer.fsBlockDbRoot, update(url: validDirectoryURL, with: alias))
|
||||
XCTAssertEqual(initializer.dataDbURL, update(url: validFileURL, with: alias))
|
||||
XCTAssertEqual(initializer.pendingDbURL, update(url: validFileURL, with: alias))
|
||||
XCTAssertEqual(initializer.spendParamsURL, update(url: validFileURL, with: alias))
|
||||
XCTAssertEqual(initializer.outputParamsURL, update(url: validFileURL, with: alias))
|
||||
}
|
||||
|
||||
func test__customAlias__invalidFsBlockDbRootURL__errorIsGenerated() {
|
||||
genericTestForURLsParsingFailures(
|
||||
fsBlockDbRoot: invalidPathURL,
|
||||
dataDbURL: validFileURL,
|
||||
pendingDbURL: validFileURL,
|
||||
spendParamsURL: validFileURL,
|
||||
outputParamsURL: validFileURL,
|
||||
alias: .custom("alias")
|
||||
)
|
||||
}
|
||||
|
||||
func test__customAlias__invalidDataDbURL__errorIsGenerated() {
|
||||
genericTestForURLsParsingFailures(
|
||||
fsBlockDbRoot: validDirectoryURL,
|
||||
dataDbURL: invalidPathURL,
|
||||
pendingDbURL: validFileURL,
|
||||
spendParamsURL: validFileURL,
|
||||
outputParamsURL: validFileURL,
|
||||
alias: .custom("alias")
|
||||
)
|
||||
}
|
||||
|
||||
func test__customAlias__invalidPendingDbURL__errorIsGenerated() {
|
||||
genericTestForURLsParsingFailures(
|
||||
fsBlockDbRoot: validDirectoryURL,
|
||||
dataDbURL: validFileURL,
|
||||
pendingDbURL: invalidPathURL,
|
||||
spendParamsURL: validFileURL,
|
||||
outputParamsURL: validFileURL,
|
||||
alias: .custom("alias")
|
||||
)
|
||||
}
|
||||
|
||||
func test__customAlias__invalidSpendParamsURL__errorIsGenerated() {
|
||||
genericTestForURLsParsingFailures(
|
||||
fsBlockDbRoot: validDirectoryURL,
|
||||
dataDbURL: validFileURL,
|
||||
pendingDbURL: validFileURL,
|
||||
spendParamsURL: invalidPathURL,
|
||||
outputParamsURL: validFileURL,
|
||||
alias: .custom("alias")
|
||||
)
|
||||
}
|
||||
|
||||
func test__customAlias__invalidOutputParamsURL__errorIsGenerated() {
|
||||
genericTestForURLsParsingFailures(
|
||||
fsBlockDbRoot: validDirectoryURL,
|
||||
dataDbURL: validFileURL,
|
||||
pendingDbURL: validFileURL,
|
||||
spendParamsURL: validFileURL,
|
||||
outputParamsURL: invalidPathURL,
|
||||
alias: .custom("alias")
|
||||
)
|
||||
}
|
||||
}
|
|
@ -10,13 +10,13 @@ import XCTest
|
|||
@testable import ZcashLightClientKit
|
||||
|
||||
class InternalSyncProgressTests: XCTestCase {
|
||||
var storage: InternalSyncProgressStorage!
|
||||
var storage: InternalSyncProgressMemoryStorage!
|
||||
var internalSyncProgress: InternalSyncProgress!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
storage = InternalSyncProgressMemoryStorage()
|
||||
internalSyncProgress = InternalSyncProgress(storage: storage)
|
||||
internalSyncProgress = InternalSyncProgress(alias: .default, storage: storage, logger: logger)
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
|
@ -129,4 +129,40 @@ class InternalSyncProgressTests: XCTestCase {
|
|||
await internalSyncProgress.set(519000, .latestUTXOFetchedHeight)
|
||||
XCTAssertEqual(storage.integer(forKey: "latestUTXOFetchedHeight"), 519000)
|
||||
}
|
||||
|
||||
func test__whenUsingDefaultAliasKeysAreBackwardsCompatible() async {
|
||||
await internalSyncProgress.set(630000, .latestDownloadedBlockHeight)
|
||||
await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
|
||||
await internalSyncProgress.set(630000, .latestEnhancedHeight)
|
||||
|
||||
XCTAssertEqual(storage.integer(forKey: InternalSyncProgress.Key.latestDownloadedBlockHeight.rawValue), 630000)
|
||||
XCTAssertEqual(storage.integer(forKey: InternalSyncProgress.Key.latestUTXOFetchedHeight.rawValue), 630000)
|
||||
XCTAssertEqual(storage.integer(forKey: InternalSyncProgress.Key.latestEnhancedHeight.rawValue), 630000)
|
||||
}
|
||||
|
||||
func test__usingDifferentAliasesStoreValuesIndependently() async {
|
||||
let internalSyncProgress1 = InternalSyncProgress(alias: .custom("alias1"), storage: storage, logger: logger)
|
||||
await internalSyncProgress1.set(121000, .latestDownloadedBlockHeight)
|
||||
await internalSyncProgress1.set(121000, .latestUTXOFetchedHeight)
|
||||
await internalSyncProgress1.set(121000, .latestEnhancedHeight)
|
||||
|
||||
let internalSyncProgress2 = InternalSyncProgress(alias: .custom("alias2"), storage: storage, logger: logger)
|
||||
await internalSyncProgress2.set(630000, .latestDownloadedBlockHeight)
|
||||
await internalSyncProgress2.set(630000, .latestUTXOFetchedHeight)
|
||||
await internalSyncProgress2.set(630000, .latestEnhancedHeight)
|
||||
|
||||
let latestDownloadedBlockHeight1 = await internalSyncProgress1.load(.latestDownloadedBlockHeight)
|
||||
let latestUTXOFetchedHeigh1 = await internalSyncProgress1.load(.latestUTXOFetchedHeight)
|
||||
let latestEnhancedHeight1 = await internalSyncProgress1.load(.latestEnhancedHeight)
|
||||
XCTAssertEqual(latestDownloadedBlockHeight1, 121000)
|
||||
XCTAssertEqual(latestUTXOFetchedHeigh1, 121000)
|
||||
XCTAssertEqual(latestEnhancedHeight1, 121000)
|
||||
|
||||
let latestDownloadedBlockHeight2 = await internalSyncProgress2.load(.latestDownloadedBlockHeight)
|
||||
let latestUTXOFetchedHeigh2 = await internalSyncProgress2.load(.latestUTXOFetchedHeight)
|
||||
let latestEnhancedHeight2 = await internalSyncProgress2.load(.latestEnhancedHeight)
|
||||
XCTAssertEqual(latestDownloadedBlockHeight2, 630000)
|
||||
XCTAssertEqual(latestUTXOFetchedHeigh2, 630000)
|
||||
XCTAssertEqual(latestEnhancedHeight2, 630000)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@ class PendingTransactionRepositoryTests: XCTestCase {
|
|||
super.setUp()
|
||||
cleanUpDb()
|
||||
let pendingDbProvider = SimpleConnectionProvider(path: try! TestDbBuilder.pendingTransactionsDbURL().absoluteString)
|
||||
let migrations = MigrationManager(pendingDbConnection: pendingDbProvider, networkType: .testnet)
|
||||
let migrations = MigrationManager(pendingDbConnection: pendingDbProvider, networkType: .testnet, logger: logger)
|
||||
try! migrations.performMigration()
|
||||
pendingRepository = PendingTransactionSQLDAO(dbProvider: pendingDbProvider)
|
||||
pendingRepository = PendingTransactionSQLDAO(dbProvider: pendingDbProvider, logger: logger)
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
|
|
|
@ -0,0 +1,322 @@
|
|||
//
|
||||
// SynchronizerOfflineTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Michal Fousek on 23.03.2023.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
@testable import TestUtils
|
||||
import XCTest
|
||||
@testable import ZcashLightClientKit
|
||||
|
||||
class SynchronizerOfflineTests: XCTestCase {
|
||||
let data = AlternativeSynchronizerAPITestsData()
|
||||
var network: ZcashNetwork!
|
||||
var cancellables: [AnyCancellable] = []
|
||||
|
||||
override func setUp() async throws {
|
||||
try await super.setUp()
|
||||
network = ZcashNetworkBuilder.network(for: .testnet)
|
||||
cancellables = []
|
||||
}
|
||||
|
||||
override func tearDown() async throws {
|
||||
try await super.tearDown()
|
||||
network = nil
|
||||
cancellables = []
|
||||
}
|
||||
|
||||
func testCallPrepareWithAlreadyUsedAliasThrowsError() async throws {
|
||||
let firstTestCoordinator = try await TestCoordinator(
|
||||
alias: .custom("alias"),
|
||||
walletBirthday: 10,
|
||||
network: network,
|
||||
callPrepareInConstructor: false
|
||||
)
|
||||
|
||||
let secondTestCoordinator = try await TestCoordinator(
|
||||
alias: .custom("alias"),
|
||||
walletBirthday: 10,
|
||||
network: network,
|
||||
callPrepareInConstructor: false
|
||||
)
|
||||
|
||||
do {
|
||||
_ = try await firstTestCoordinator.prepare(seed: Environment.seedBytes)
|
||||
} catch {
|
||||
XCTFail("Unpected fail. Prepare should succeed. \(error)")
|
||||
}
|
||||
|
||||
do {
|
||||
_ = try await secondTestCoordinator.prepare(seed: Environment.seedBytes)
|
||||
XCTFail("Prepare should fail.")
|
||||
} catch { }
|
||||
}
|
||||
|
||||
func testWhenSynchronizerIsDeallocatedAliasIsntUsedAnymore() async throws {
|
||||
var testCoordinator: TestCoordinator! = try await TestCoordinator(
|
||||
alias: .default,
|
||||
walletBirthday: 10,
|
||||
network: network,
|
||||
callPrepareInConstructor: false
|
||||
)
|
||||
|
||||
do {
|
||||
_ = try await testCoordinator.prepare(seed: Environment.seedBytes)
|
||||
} catch {
|
||||
XCTFail("Unpected fail. Prepare should succeed. \(error)")
|
||||
}
|
||||
|
||||
testCoordinator = try await TestCoordinator(
|
||||
alias: .default,
|
||||
walletBirthday: 10,
|
||||
network: network,
|
||||
callPrepareInConstructor: false
|
||||
)
|
||||
|
||||
do {
|
||||
_ = try await testCoordinator.prepare(seed: Environment.seedBytes)
|
||||
} catch {
|
||||
XCTFail("Unpected fail. Prepare should succeed. \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func testCallWipeWithAlreadyUsedAliasThrowsError() async throws {
|
||||
let firstTestCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false)
|
||||
let secondTestCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false)
|
||||
|
||||
let firstWipeExpectation = XCTestExpectation(description: "First wipe expectation")
|
||||
|
||||
firstTestCoordinator.synchronizer.wipe()
|
||||
.sink(
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
case .finished:
|
||||
firstWipeExpectation.fulfill()
|
||||
case let .failure(error):
|
||||
XCTFail("Unexpected error when calling wipe \(error)")
|
||||
}
|
||||
},
|
||||
receiveValue: { _ in }
|
||||
)
|
||||
.store(in: &cancellables)
|
||||
|
||||
wait(for: [firstWipeExpectation], timeout: 1)
|
||||
|
||||
let secondWipeExpectation = XCTestExpectation(description: "Second wipe expectation")
|
||||
|
||||
secondTestCoordinator.synchronizer.wipe()
|
||||
.sink(
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
case .finished:
|
||||
XCTFail("Second wipe should fail with error.")
|
||||
case let .failure(error):
|
||||
if let error = error as? InitializerError, case .aliasAlreadyInUse = error {
|
||||
secondWipeExpectation.fulfill()
|
||||
} else {
|
||||
XCTFail("Wipe failed with unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
},
|
||||
receiveValue: { _ in }
|
||||
)
|
||||
.store(in: &cancellables)
|
||||
|
||||
wait(for: [secondWipeExpectation], timeout: 1)
|
||||
}
|
||||
|
||||
func testPrepareCanBeCalledAfterWipeWithSameInstanceOfSDKSynchronizer() async throws {
|
||||
let testCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false)
|
||||
|
||||
let expectation = XCTestExpectation(description: "Wipe expectation")
|
||||
|
||||
testCoordinator.synchronizer.wipe()
|
||||
.sink(
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
case .finished:
|
||||
expectation.fulfill()
|
||||
case let .failure(error):
|
||||
XCTFail("Unexpected error when calling wipe \(error)")
|
||||
}
|
||||
},
|
||||
receiveValue: { _ in }
|
||||
)
|
||||
.store(in: &cancellables)
|
||||
|
||||
wait(for: [expectation], timeout: 1)
|
||||
|
||||
do {
|
||||
_ = try await testCoordinator.prepare(seed: Environment.seedBytes)
|
||||
} catch {
|
||||
XCTFail("Prepare after wipe should succeed.")
|
||||
}
|
||||
}
|
||||
|
||||
func testSendToAddressCalledWithoutPrepareThrowsError() async throws {
|
||||
let testCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false)
|
||||
|
||||
do {
|
||||
_ = try await testCoordinator.synchronizer.sendToAddress(
|
||||
spendingKey: testCoordinator.spendingKey,
|
||||
zatoshi: Zatoshi(1),
|
||||
toAddress: .transparent(data.transparentAddress),
|
||||
memo: nil
|
||||
)
|
||||
XCTFail("Send to address should fail.")
|
||||
} catch {
|
||||
if let error = error as? SynchronizerError, case .notPrepared = error {
|
||||
} else {
|
||||
XCTFail("Send to address failed with unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testShieldFundsCalledWithoutPrepareThrowsError() async throws {
|
||||
let testCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false)
|
||||
|
||||
do {
|
||||
_ = try await testCoordinator.synchronizer.shieldFunds(
|
||||
spendingKey: testCoordinator.spendingKey,
|
||||
memo: Memo(string: "memo"),
|
||||
shieldingThreshold: Zatoshi(1)
|
||||
)
|
||||
XCTFail("Shield funds should fail.")
|
||||
} catch {
|
||||
if let error = error as? SynchronizerError, case .notPrepared = error {
|
||||
} else {
|
||||
XCTFail("Shield funds failed with unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRefreshUTXOCalledWithoutPrepareThrowsError() async throws {
|
||||
let testCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false)
|
||||
|
||||
do {
|
||||
_ = try await testCoordinator.synchronizer.refreshUTXOs(address: data.transparentAddress, from: 1)
|
||||
XCTFail("Shield funds should fail.")
|
||||
} catch {
|
||||
if let error = error as? SynchronizerError, case .notPrepared = error {
|
||||
} else {
|
||||
XCTFail("Shield funds failed with unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRewindCalledWithoutPrepareThrowsError() async throws {
|
||||
let testCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false)
|
||||
|
||||
let expectation = XCTestExpectation()
|
||||
|
||||
testCoordinator.synchronizer.rewind(.quick)
|
||||
.sink(
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
case .finished:
|
||||
XCTFail("Rewind should fail with error.")
|
||||
case let .failure(error):
|
||||
if let error = error as? SynchronizerError, case .notPrepared = error {
|
||||
expectation.fulfill()
|
||||
} else {
|
||||
XCTFail("Rewind failed with unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
},
|
||||
receiveValue: { _ in }
|
||||
)
|
||||
.store(in: &cancellables)
|
||||
|
||||
wait(for: [expectation], timeout: 1)
|
||||
}
|
||||
|
||||
func testURLsParsingFailsInInitialierPrepareThenThrowsError() async throws {
|
||||
let validFileURL = URL(fileURLWithPath: "/some/valid/path/to.file")
|
||||
let validDirectoryURL = URL(fileURLWithPath: "/some/valid/path/to/directory")
|
||||
let invalidPathURL = URL(string: "https://whatever")!
|
||||
|
||||
let initializer = Initializer(
|
||||
cacheDbURL: nil,
|
||||
fsBlockDbRoot: validDirectoryURL,
|
||||
dataDbURL: validFileURL,
|
||||
pendingDbURL: invalidPathURL,
|
||||
endpoint: LightWalletEndpointBuilder.default,
|
||||
network: ZcashNetworkBuilder.network(for: .testnet),
|
||||
spendParamsURL: validFileURL,
|
||||
outputParamsURL: validFileURL,
|
||||
saplingParamsSourceURL: .default,
|
||||
alias: .default,
|
||||
logLevel: .debug
|
||||
)
|
||||
|
||||
XCTAssertNotNil(initializer.urlsParsingError)
|
||||
|
||||
let synchronizer = SDKSynchronizer(initializer: initializer)
|
||||
|
||||
do {
|
||||
let derivationTool = DerivationTool(networkType: network.networkType)
|
||||
let spendingKey = try derivationTool.deriveUnifiedSpendingKey(
|
||||
seed: Environment.seedBytes,
|
||||
accountIndex: 0
|
||||
)
|
||||
let viewingKey = try derivationTool.deriveUnifiedFullViewingKey(from: spendingKey)
|
||||
_ = try await synchronizer.prepare(with: Environment.seedBytes, viewingKeys: [viewingKey], walletBirthday: 123000)
|
||||
XCTFail("Failure of prepare is expected.")
|
||||
} catch {
|
||||
if let error = error as? InitializerError, case let .cantUpdateURLWithAlias(failedURL) = error {
|
||||
XCTAssertEqual(failedURL, invalidPathURL)
|
||||
} else {
|
||||
XCTFail("Failed with unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testURLsParsingFailsInInitialierWipeThenThrowsError() async throws {
|
||||
let validFileURL = URL(fileURLWithPath: "/some/valid/path/to.file")
|
||||
let validDirectoryURL = URL(fileURLWithPath: "/some/valid/path/to/directory")
|
||||
let invalidPathURL = URL(string: "https://whatever")!
|
||||
|
||||
let initializer = Initializer(
|
||||
cacheDbURL: nil,
|
||||
fsBlockDbRoot: validDirectoryURL,
|
||||
dataDbURL: validFileURL,
|
||||
pendingDbURL: invalidPathURL,
|
||||
endpoint: LightWalletEndpointBuilder.default,
|
||||
network: ZcashNetworkBuilder.network(for: .testnet),
|
||||
spendParamsURL: validFileURL,
|
||||
outputParamsURL: validFileURL,
|
||||
saplingParamsSourceURL: .default,
|
||||
alias: .default,
|
||||
logLevel: .debug
|
||||
)
|
||||
|
||||
XCTAssertNotNil(initializer.urlsParsingError)
|
||||
|
||||
let synchronizer = SDKSynchronizer(initializer: initializer)
|
||||
let expectation = XCTestExpectation()
|
||||
|
||||
synchronizer.wipe()
|
||||
.sink(
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
case .finished:
|
||||
XCTFail("Failure of wipe is expected.")
|
||||
case let .failure(error):
|
||||
if let error = error as? InitializerError, case let .cantUpdateURLWithAlias(failedURL) = error {
|
||||
XCTAssertEqual(failedURL, invalidPathURL)
|
||||
expectation.fulfill()
|
||||
} else {
|
||||
XCTFail("Failed with unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
},
|
||||
receiveValue: { _ in }
|
||||
)
|
||||
.store(in: &cancellables)
|
||||
|
||||
wait(for: [expectation], timeout: 1)
|
||||
}
|
||||
}
|
|
@ -45,6 +45,7 @@ class WalletTests: XCTestCase {
|
|||
.map({ try derivationTool.deriveUnifiedFullViewingKey(from: $0) })
|
||||
|
||||
let wallet = Initializer(
|
||||
cacheDbURL: nil,
|
||||
fsBlockDbRoot: self.testTempDirectory,
|
||||
dataDbURL: try __dataDbURL(),
|
||||
pendingDbURL: try TestDbBuilder.pendingTransactionsDbURL(),
|
||||
|
@ -64,7 +65,7 @@ class WalletTests: XCTestCase {
|
|||
} catch {
|
||||
XCTFail("shouldn't fail here. Got error: \(error)")
|
||||
}
|
||||
|
||||
|
||||
// fileExists actually sucks, so attempting to delete the file and checking what happens is far better :)
|
||||
XCTAssertNoThrow( try FileManager.default.removeItem(at: dbData!) )
|
||||
}
|
||||
|
|
|
@ -93,6 +93,8 @@ class ZcashRustBackendTests: XCTestCase {
|
|||
let tempDBs = TemporaryDbBuilder.build()
|
||||
let seed = testVector[0].root_seed!
|
||||
|
||||
try? FileManager.default.removeItem(at: tempDBs.dataDB)
|
||||
|
||||
XCTAssertEqual(
|
||||
try ZcashRustBackend.initDataDb(
|
||||
dbData: tempDBs.dataDB,
|
||||
|
@ -159,8 +161,6 @@ class ZcashRustBackendTests: XCTestCase {
|
|||
expectedReceivers.sorted(),
|
||||
actualReceivers.sorted()
|
||||
)
|
||||
|
||||
try? FileManager.default.removeItem(at: tempDBs.dataDB)
|
||||
}
|
||||
|
||||
func testGetMetadataFromAddress() throws {
|
||||
|
|
|
@ -10,9 +10,10 @@ import XCTest
|
|||
|
||||
final class SDKMetricsTests: XCTestCase {
|
||||
func testPushDownloadBlocksReport() throws {
|
||||
SDKMetrics.shared.enableMetrics()
|
||||
let metrics = SDKMetrics()
|
||||
metrics.enableMetrics()
|
||||
|
||||
SDKMetrics.shared.pushProgressReport(
|
||||
metrics.pushProgressReport(
|
||||
progress: BlockProgress(
|
||||
startHeight: 1_730_000,
|
||||
targetHeight: 1_730_099,
|
||||
|
@ -24,35 +25,37 @@ final class SDKMetricsTests: XCTestCase {
|
|||
operation: .downloadBlocks
|
||||
)
|
||||
|
||||
XCTAssertTrue(SDKMetrics.shared.popBlock(operation: .downloadBlocks)?.count == 1)
|
||||
XCTAssertTrue(metrics.popBlock(operation: .downloadBlocks)?.count == 1)
|
||||
|
||||
if let reports = SDKMetrics.shared.reports[.downloadBlocks], let report = reports.first {
|
||||
if let reports = metrics.reports[.downloadBlocks], let report = reports.first {
|
||||
XCTAssertEqual(report, SDKMetrics.BlockMetricReport.placeholderA)
|
||||
} else {
|
||||
XCTFail("Not expected to fail.")
|
||||
}
|
||||
|
||||
SDKMetrics.shared.disableMetrics()
|
||||
metrics.disableMetrics()
|
||||
}
|
||||
|
||||
func testPopDownloadBlocksReport() throws {
|
||||
SDKMetrics.shared.enableMetrics()
|
||||
|
||||
SDKMetrics.shared.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
let metrics = SDKMetrics()
|
||||
metrics.enableMetrics()
|
||||
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
|
||||
if let reports = SDKMetrics.shared.popBlock(operation: .downloadBlocks), let report = reports.first {
|
||||
if let reports = metrics.popBlock(operation: .downloadBlocks), let report = reports.first {
|
||||
XCTAssertEqual(report, SDKMetrics.BlockMetricReport.placeholderA)
|
||||
} else {
|
||||
XCTFail("Not expected to fail.")
|
||||
}
|
||||
|
||||
SDKMetrics.shared.disableMetrics()
|
||||
metrics.disableMetrics()
|
||||
}
|
||||
|
||||
func testCumulativeSummary() throws {
|
||||
SDKMetrics.shared.enableMetrics()
|
||||
|
||||
SDKMetrics.shared.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
let metrics = SDKMetrics()
|
||||
metrics.enableMetrics()
|
||||
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
|
||||
let summary = SDKMetrics.CumulativeSummary(
|
||||
downloadedBlocksReport: SDKMetrics.ReportSummary(minTime: 1.0, maxTime: 1.0, avgTime: 1.0),
|
||||
|
@ -63,24 +66,25 @@ final class SDKMetricsTests: XCTestCase {
|
|||
totalSyncReport: nil
|
||||
)
|
||||
|
||||
XCTAssertEqual(summary, SDKMetrics.shared.cumulativeSummary())
|
||||
XCTAssertEqual(summary, metrics.cumulativeSummary())
|
||||
|
||||
SDKMetrics.shared.disableMetrics()
|
||||
metrics.disableMetrics()
|
||||
}
|
||||
|
||||
func testCumulateAndStartNewSet() throws {
|
||||
SDKMetrics.shared.enableMetrics()
|
||||
|
||||
SDKMetrics.shared.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
SDKMetrics.shared.cumulateReportsAndStartNewSet()
|
||||
let metrics = SDKMetrics()
|
||||
metrics.enableMetrics()
|
||||
|
||||
SDKMetrics.shared.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
SDKMetrics.shared.cumulateReportsAndStartNewSet()
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
metrics.cumulateReportsAndStartNewSet()
|
||||
|
||||
SDKMetrics.shared.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
SDKMetrics.shared.cumulateReportsAndStartNewSet()
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
metrics.cumulateReportsAndStartNewSet()
|
||||
|
||||
XCTAssertTrue(SDKMetrics.shared.cumulativeSummaries.count == 3)
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
metrics.cumulateReportsAndStartNewSet()
|
||||
|
||||
XCTAssertTrue(metrics.cumulativeSummaries.count == 3)
|
||||
|
||||
let summary = SDKMetrics.CumulativeSummary(
|
||||
downloadedBlocksReport: SDKMetrics.ReportSummary(minTime: 1.0, maxTime: 1.0, avgTime: 1.0),
|
||||
|
@ -92,15 +96,16 @@ final class SDKMetricsTests: XCTestCase {
|
|||
)
|
||||
let summaries = [summary, summary, summary]
|
||||
|
||||
XCTAssertEqual(summaries, SDKMetrics.shared.cumulativeSummaries)
|
||||
XCTAssertEqual(summaries, metrics.cumulativeSummaries)
|
||||
|
||||
SDKMetrics.shared.disableMetrics()
|
||||
metrics.disableMetrics()
|
||||
}
|
||||
|
||||
func testCumulativeSummaryMinMaxAvg() throws {
|
||||
SDKMetrics.shared.enableMetrics()
|
||||
|
||||
SDKMetrics.shared.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA, SDKMetrics.BlockMetricReport.placeholderB]
|
||||
let metrics = SDKMetrics()
|
||||
metrics.enableMetrics()
|
||||
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA, SDKMetrics.BlockMetricReport.placeholderB]
|
||||
|
||||
let summary = SDKMetrics.CumulativeSummary(
|
||||
downloadedBlocksReport: SDKMetrics.ReportSummary(minTime: 1.0, maxTime: 6.0, avgTime: 3.5),
|
||||
|
@ -111,19 +116,20 @@ final class SDKMetricsTests: XCTestCase {
|
|||
totalSyncReport: nil
|
||||
)
|
||||
|
||||
XCTAssertEqual(summary, SDKMetrics.shared.cumulativeSummary())
|
||||
XCTAssertEqual(summary, metrics.cumulativeSummary())
|
||||
|
||||
SDKMetrics.shared.disableMetrics()
|
||||
metrics.disableMetrics()
|
||||
}
|
||||
|
||||
func testSummarizedCumulativeReports() throws {
|
||||
SDKMetrics.shared.enableMetrics()
|
||||
|
||||
SDKMetrics.shared.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
SDKMetrics.shared.cumulateReportsAndStartNewSet()
|
||||
let metrics = SDKMetrics()
|
||||
metrics.enableMetrics()
|
||||
|
||||
SDKMetrics.shared.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderB]
|
||||
SDKMetrics.shared.cumulateReportsAndStartNewSet()
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
metrics.cumulateReportsAndStartNewSet()
|
||||
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderB]
|
||||
metrics.cumulateReportsAndStartNewSet()
|
||||
|
||||
let summary = SDKMetrics.CumulativeSummary(
|
||||
downloadedBlocksReport: SDKMetrics.ReportSummary(minTime: 1.0, maxTime: 6.0, avgTime: 3.5),
|
||||
|
@ -134,9 +140,9 @@ final class SDKMetricsTests: XCTestCase {
|
|||
totalSyncReport: nil
|
||||
)
|
||||
|
||||
XCTAssertEqual(SDKMetrics.shared.summarizedCumulativeReports(), summary)
|
||||
XCTAssertEqual(metrics.summarizedCumulativeReports(), summary)
|
||||
|
||||
SDKMetrics.shared.disableMetrics()
|
||||
metrics.disableMetrics()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,11 +56,11 @@ class SynchronizerTests: XCTestCase {
|
|||
let network = ZcashNetworkBuilder.network(for: .mainnet)
|
||||
let endpoint = LightWalletEndpoint(address: "lightwalletd.electriccoin.co", port: 9067, secure: true)
|
||||
|
||||
SDKMetrics.shared.enableMetrics()
|
||||
|
||||
var synchronizer: SDKSynchronizer?
|
||||
for _ in 1...5 {
|
||||
let databases = TemporaryDbBuilder.build()
|
||||
let initializer = Initializer(
|
||||
cacheDbURL: nil,
|
||||
fsBlockDbRoot: databases.fsCacheDbRoot,
|
||||
dataDbURL: databases.dataDB,
|
||||
pendingDbURL: databases.pendingDB,
|
||||
|
@ -69,21 +69,29 @@ class SynchronizerTests: XCTestCase {
|
|||
spendParamsURL: try __spendParamsURL(),
|
||||
outputParamsURL: try __outputParamsURL(),
|
||||
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
|
||||
alias: "",
|
||||
loggerProxy: OSLogger(logLevel: .debug)
|
||||
alias: .default,
|
||||
logLevel: .debug
|
||||
)
|
||||
|
||||
try? FileManager.default.removeItem(at: databases.fsCacheDbRoot)
|
||||
try? FileManager.default.removeItem(at: databases.dataDB)
|
||||
try? FileManager.default.removeItem(at: databases.pendingDB)
|
||||
|
||||
let synchronizer = SDKSynchronizer(initializer: initializer)
|
||||
synchronizer = SDKSynchronizer(initializer: initializer)
|
||||
|
||||
guard let synchronizer else { fatalError("Synchronizer not initialized.") }
|
||||
|
||||
synchronizer.metrics.enableMetrics()
|
||||
_ = try await synchronizer.prepare(with: seedBytes, viewingKeys: [ufvk], walletBirthday: birthday)
|
||||
|
||||
let syncSyncedExpectation = XCTestExpectation(description: "synchronizerSynced Expectation")
|
||||
sdkSynchronizerSyncStatusHandler.subscribe(to: synchronizer.stateStream, expectations: [.synced: syncSyncedExpectation])
|
||||
|
||||
let internalSyncProgress = InternalSyncProgress(storage: UserDefaults.standard)
|
||||
let internalSyncProgress = InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: UserDefaults.standard,
|
||||
logger: logger
|
||||
)
|
||||
await internalSyncProgress.rewind(to: birthday)
|
||||
await (synchronizer.blockProcessor.service as? LightWalletGRPCService)?.latestBlockHeightProvider = MockLatestBlockHeightProvider(
|
||||
birthday: self.birthday + 99
|
||||
|
@ -93,10 +101,10 @@ class SynchronizerTests: XCTestCase {
|
|||
|
||||
wait(for: [syncSyncedExpectation], timeout: 100)
|
||||
|
||||
SDKMetrics.shared.cumulateReportsAndStartNewSet()
|
||||
synchronizer.metrics.cumulateReportsAndStartNewSet()
|
||||
}
|
||||
|
||||
if let cumulativeSummary = SDKMetrics.shared.summarizedCumulativeReports() {
|
||||
if let cumulativeSummary = synchronizer?.metrics.summarizedCumulativeReports() {
|
||||
let downloadedBlocksReport = cumulativeSummary.downloadedBlocksReport ?? .zero
|
||||
let validatedBlocksReport = cumulativeSummary.validatedBlocksReport ?? .zero
|
||||
let scannedBlocksReport = cumulativeSummary.scannedBlocksReport ?? .zero
|
||||
|
@ -116,6 +124,6 @@ class SynchronizerTests: XCTestCase {
|
|||
""")
|
||||
}
|
||||
|
||||
SDKMetrics.shared.disableMetrics()
|
||||
synchronizer?.metrics.disableMetrics()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,13 +38,17 @@ enum DarksideDataset: String {
|
|||
}
|
||||
|
||||
class DarksideWalletService: LightWalletService {
|
||||
var connectionStateChange: ((ZcashLightClientKit.ConnectionState, ZcashLightClientKit.ConnectionState) -> Void)? {
|
||||
get { service.connectionStateChange }
|
||||
set { service.connectionStateChange = newValue }
|
||||
}
|
||||
var channel: Channel
|
||||
var service: LightWalletService
|
||||
var darksideService: DarksideStreamerClient
|
||||
|
||||
init(endpoint: LightWalletEndpoint) {
|
||||
self.channel = ChannelProvider().channel()
|
||||
self.service = LightWalletServiceFactory(endpoint: endpoint, connectionStateChange: { _, _ in }).make()
|
||||
self.service = LightWalletServiceFactory(endpoint: endpoint).make()
|
||||
self.darksideService = DarksideStreamerClient(channel: channel)
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,10 @@ struct MockCancellable: CancellableCall {
|
|||
}
|
||||
|
||||
class MockLightWalletService: LightWalletService {
|
||||
var connectionStateChange: ((ZcashLightClientKit.ConnectionState, ZcashLightClientKit.ConnectionState) -> Void)? {
|
||||
get { service.connectionStateChange }
|
||||
set { service.connectionStateChange = newValue }
|
||||
}
|
||||
var mockLightDInfo: LightWalletdInfo?
|
||||
var queue = DispatchQueue(label: "mock service queue")
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// LoggerProxy.swift
|
||||
//
|
||||
//
|
||||
// Created by Lukáš Korba on 27.03.2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ZcashLightClientKit
|
||||
|
||||
var logger = OSLogger(logLevel: .debug)
|
||||
|
||||
enum LoggerProxy {
|
||||
static func debug(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
logger.debug(message, file: file, function: function, line: line)
|
||||
}
|
||||
|
||||
static func info(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
logger.info(message, file: file, function: function, line: line)
|
||||
}
|
||||
|
||||
static func event(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
logger.event(message, file: file, function: function, line: line)
|
||||
}
|
||||
|
||||
static func warn(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
logger.warn(message, file: file, function: function, line: line)
|
||||
}
|
||||
|
||||
static func error(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
logger.error(message, file: file, function: function, line: line)
|
||||
}
|
||||
}
|
|
@ -404,9 +404,14 @@ extension SaplingParamsSourceURL {
|
|||
|
||||
extension CompactBlockProcessor.Configuration {
|
||||
/// Standard configuration for most compact block processors
|
||||
static func standard(for network: ZcashNetwork, walletBirthday: BlockHeight) -> CompactBlockProcessor.Configuration {
|
||||
static func standard(
|
||||
alias: ZcashSynchronizerAlias = .default,
|
||||
for network: ZcashNetwork,
|
||||
walletBirthday: BlockHeight
|
||||
) -> CompactBlockProcessor.Configuration {
|
||||
let pathProvider = DefaultResourceProvider(network: network)
|
||||
return CompactBlockProcessor.Configuration(
|
||||
alias: alias,
|
||||
fsBlockCacheRoot: pathProvider.fsCacheURL,
|
||||
dataDb: pathProvider.dataDbURL,
|
||||
spendParamsURL: pathProvider.spendParamsURL,
|
||||
|
|
|
@ -12,6 +12,9 @@ import Foundation
|
|||
class SynchronizerMock: Synchronizer {
|
||||
init() { }
|
||||
|
||||
var underlyingAlias: ZcashSynchronizerAlias! = nil
|
||||
var alias: ZcashLightClientKit.ZcashSynchronizerAlias { underlyingAlias }
|
||||
|
||||
var underlyingStateStream: AnyPublisher<SynchronizerState, Never>! = nil
|
||||
var stateStream: AnyPublisher<SynchronizerState, Never> { underlyingStateStream }
|
||||
|
||||
|
@ -24,6 +27,8 @@ class SynchronizerMock: Synchronizer {
|
|||
var underlyingConnectionState: ConnectionState! = nil
|
||||
var connectionState: ConnectionState { underlyingConnectionState }
|
||||
|
||||
let metrics = SDKMetrics()
|
||||
|
||||
var prepareWithSeedViewingKeysWalletBirthdayClosure: (
|
||||
([UInt8]?, [UnifiedFullViewingKey], BlockHeight) async throws -> Initializer.InitializationResult
|
||||
)! = nil
|
||||
|
|
|
@ -45,35 +45,51 @@ class TestCoordinator {
|
|||
var databases: TemporaryTestDatabases
|
||||
let network: ZcashNetwork
|
||||
|
||||
static func make(walletBirthday: BlockHeight, network: ZcashNetwork) -> TestCoordinator {
|
||||
static func make(
|
||||
alias: ZcashSynchronizerAlias = .default,
|
||||
walletBirthday: BlockHeight,
|
||||
network: ZcashNetwork,
|
||||
callPrepareInConstructor: Bool = true
|
||||
) -> TestCoordinator {
|
||||
var coordinator: TestCoordinator!
|
||||
XCTestCase.wait {
|
||||
coordinator = try await TestCoordinator(walletBirthday: walletBirthday, network: network)
|
||||
coordinator = try await TestCoordinator(
|
||||
alias: alias,
|
||||
walletBirthday: walletBirthday,
|
||||
network: network,
|
||||
callPrepareInConstructor: callPrepareInConstructor
|
||||
)
|
||||
}
|
||||
return coordinator
|
||||
}
|
||||
|
||||
static func make(
|
||||
alias: ZcashSynchronizerAlias = .default,
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
unifiedFullViewingKey: UnifiedFullViewingKey,
|
||||
walletBirthday: BlockHeight,
|
||||
network: ZcashNetwork
|
||||
network: ZcashNetwork,
|
||||
callPrepareInConstructor: Bool = true
|
||||
) -> TestCoordinator {
|
||||
var coordinator: TestCoordinator!
|
||||
XCTestCase.wait {
|
||||
coordinator = try await TestCoordinator(
|
||||
alias: alias,
|
||||
spendingKey: spendingKey,
|
||||
unifiedFullViewingKey: unifiedFullViewingKey,
|
||||
walletBirthday: walletBirthday,
|
||||
network: network
|
||||
network: network,
|
||||
callPrepareInConstructor: callPrepareInConstructor
|
||||
)
|
||||
}
|
||||
return coordinator
|
||||
}
|
||||
|
||||
convenience init(
|
||||
alias: ZcashSynchronizerAlias = .default,
|
||||
walletBirthday: BlockHeight,
|
||||
network: ZcashNetwork
|
||||
network: ZcashNetwork,
|
||||
callPrepareInConstructor: Bool = true
|
||||
) async throws {
|
||||
let derivationTool = DerivationTool(networkType: network.networkType)
|
||||
|
||||
|
@ -85,20 +101,24 @@ class TestCoordinator {
|
|||
let ufvk = try derivationTool.deriveUnifiedFullViewingKey(from: spendingKey)
|
||||
|
||||
try await self.init(
|
||||
alias: alias,
|
||||
spendingKey: spendingKey,
|
||||
unifiedFullViewingKey: ufvk,
|
||||
walletBirthday: walletBirthday,
|
||||
network: network
|
||||
network: network,
|
||||
callPrepareInConstructor: callPrepareInConstructor
|
||||
)
|
||||
}
|
||||
|
||||
required init(
|
||||
alias: ZcashSynchronizerAlias = .default,
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
unifiedFullViewingKey: UnifiedFullViewingKey,
|
||||
walletBirthday: BlockHeight,
|
||||
network: ZcashNetwork
|
||||
network: ZcashNetwork,
|
||||
callPrepareInConstructor: Bool = true
|
||||
) async throws {
|
||||
await InternalSyncProgress(storage: UserDefaults.standard).rewind(to: 0)
|
||||
await InternalSyncProgress(alias: alias, storage: UserDefaults.standard, logger: logger).rewind(to: 0)
|
||||
|
||||
self.spendingKey = spendingKey
|
||||
self.viewingKey = unifiedFullViewingKey
|
||||
|
@ -113,7 +133,7 @@ class TestCoordinator {
|
|||
singleCallTimeoutInMillis: 10000,
|
||||
streamingCallTimeoutInMillis: 1000000
|
||||
)
|
||||
let liveService = LightWalletServiceFactory(endpoint: endpoint, connectionStateChange: { _, _ in }).make()
|
||||
let liveService = LightWalletServiceFactory(endpoint: endpoint).make()
|
||||
self.service = DarksideWalletService(service: liveService)
|
||||
|
||||
let realRustBackend = ZcashRustBackend.self
|
||||
|
@ -122,13 +142,16 @@ class TestCoordinator {
|
|||
fsBlockDbRoot: self.databases.fsCacheDbRoot,
|
||||
metadataStore: .live(
|
||||
fsBlockDbRoot: self.databases.fsCacheDbRoot,
|
||||
rustBackend: ZcashRustBackend.self
|
||||
rustBackend: ZcashRustBackend.self,
|
||||
logger: logger
|
||||
),
|
||||
blockDescriptor: .live,
|
||||
contentProvider: DirectoryListingProviders.defaultSorted
|
||||
contentProvider: DirectoryListingProviders.defaultSorted,
|
||||
logger: logger
|
||||
)
|
||||
|
||||
let synchronizer = TestSynchronizerBuilder.build(
|
||||
alias: alias,
|
||||
rustBackend: realRustBackend,
|
||||
fsBlockDbRoot: databases.fsCacheDbRoot,
|
||||
dataDbURL: databases.dataDB,
|
||||
|
@ -138,19 +161,23 @@ class TestCoordinator {
|
|||
repository: TransactionSQLDAO(dbProvider: SimpleConnectionProvider(path: databases.dataDB.absoluteString)),
|
||||
accountRepository: AccountRepositoryBuilder.build(
|
||||
dataDbURL: databases.dataDB,
|
||||
readOnly: true
|
||||
readOnly: true,
|
||||
logger: logger
|
||||
),
|
||||
storage: storage,
|
||||
spendParamsURL: try __spendParamsURL(),
|
||||
outputParamsURL: try __outputParamsURL(),
|
||||
network: network,
|
||||
loggerProxy: OSLogger(logLevel: .debug)
|
||||
logLevel: .debug
|
||||
)
|
||||
|
||||
self.synchronizer = synchronizer
|
||||
subscribeToState(synchronizer: self.synchronizer)
|
||||
if case .seedRequired = try await prepare(seed: Environment.seedBytes) {
|
||||
throw TestCoordinator.CoordinatorError.seedRequiredForMigration
|
||||
|
||||
if callPrepareInConstructor {
|
||||
if case .seedRequired = try await prepare(seed: Environment.seedBytes) {
|
||||
throw TestCoordinator.CoordinatorError.seedRequiredForMigration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,6 +296,7 @@ extension TestCoordinator {
|
|||
let config = await self.synchronizer.blockProcessor.config
|
||||
|
||||
let newConfig = CompactBlockProcessor.Configuration(
|
||||
alias: config.alias,
|
||||
fsBlockCacheRoot: config.fsBlockCacheRoot,
|
||||
dataDb: config.dataDb,
|
||||
spendParamsURL: config.spendParamsURL,
|
||||
|
@ -315,6 +343,7 @@ enum TemporaryDbBuilder {
|
|||
|
||||
enum TestSynchronizerBuilder {
|
||||
static func build(
|
||||
alias: ZcashSynchronizerAlias = .default,
|
||||
rustBackend: ZcashRustBackendWelding.Type,
|
||||
fsBlockDbRoot: URL,
|
||||
dataDbURL: URL,
|
||||
|
@ -327,9 +356,10 @@ enum TestSynchronizerBuilder {
|
|||
spendParamsURL: URL,
|
||||
outputParamsURL: URL,
|
||||
network: ZcashNetwork,
|
||||
loggerProxy: Logger? = nil
|
||||
logLevel: OSLogger.LogLevel
|
||||
) -> SDKSynchronizer {
|
||||
let initializer = Initializer(
|
||||
cacheDbURL: nil,
|
||||
fsBlockDbRoot: fsBlockDbRoot,
|
||||
dataDbURL: dataDbURL,
|
||||
pendingDbURL: pendingDbURL,
|
||||
|
@ -338,8 +368,8 @@ enum TestSynchronizerBuilder {
|
|||
spendParamsURL: spendParamsURL,
|
||||
outputParamsURL: outputParamsURL,
|
||||
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
|
||||
alias: "",
|
||||
loggerProxy: loggerProxy
|
||||
alias: alias,
|
||||
logLevel: logLevel
|
||||
)
|
||||
|
||||
return SDKSynchronizer(initializer: initializer)
|
||||
|
|
Loading…
Reference in New Issue