[#1160] New SyncAlgorithm type API
This commit is contained in:
parent
54d016e401
commit
6b487e979e
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -1,8 +1,18 @@
|
|||
# Change Log
|
||||
|
||||
## Unreleased
|
||||
- `CompactBlockProcessor` now processes compact blocks from the lightwalletd server in a non-linear order. This
|
||||
feature shortens the time after which a wallet's spendable balance can be used.
|
||||
|
||||
### Added
|
||||
- New `syncAlgorithm` parameter of `Synchronizer.new()` and `WalletCoordinator()` to select preferred
|
||||
`CompactBlockProcessor` block synchronizing algorithm. It can be of `CompactBlockProcessor.SyncAlgorithm.LINEAR`
|
||||
or `NON_LINEAR`. The LINEAR type is automatically used if the client app does not specify otherwise. Please note
|
||||
that the NON_LINEAR type is currently unstable and still under development.
|
||||
|
||||
## 1.21.0-beta01
|
||||
Note: This is the last _1.x_ version release. The upcoming version _2.0_ brings the **Spend-before-Sync** feature,
|
||||
which speeds up discovering the wallet's spendable balance.
|
||||
Note: This is the last _1.x_ version release. The upcoming version _2.0_ brings the **Spend-before-Sync** feature,
|
||||
which speeds up discovering the wallet's spendable balance.
|
||||
|
||||
### Changed
|
||||
- Updated dependencies:
|
||||
|
|
|
@ -53,3 +53,12 @@ Note that we aim for the main branch of this repository to be stable and releasa
|
|||
1. Intel-based machines may have trouble building in Android Studio. The workaround is to add the following line to `~/.gradle/gradle.properties`: `ZCASH_IS_DEPENDENCY_LOCKING_ENABLED=false`
|
||||
1. During builds, a warning will be printed that says "Unable to detect AGP versions for included builds. All projects in the build should use the same AGP version." This can be safely ignored. The version under build-conventions is the same as the version used elsewhere in the application.
|
||||
1. Android Studio will warn about the Gradle checksum. This is a [known issue](https://github.com/gradle/gradle/issues/9361) and can be safely ignored.
|
||||
|
||||
## Unstable Features
|
||||
### Non-linear compact blocks synchronization
|
||||
- CompactBlockProcessor now processes compact blocks from the lightwalletd server in a **non-linear** order. This
|
||||
feature shortens the time after which a wallet's spendable balance can be used.
|
||||
- Use the new `syncAlgorithm` parameter of `Synchronizer.new()` or `WalletCoordinator()` to select preferred
|
||||
`CompactBlockProcessor` block synchronizing algorithm. It can be of `CompactBlockProcessor.SyncAlgorithm.LINEAR`
|
||||
or `NON_LINEAR`. The LINEAR type is automatically used if the client app does not specify otherwise. Please note
|
||||
that the NON_LINEAR type is currently unstable and still under development.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cash.z.ecc.android.sdk
|
||||
|
||||
import android.content.Context
|
||||
import cash.z.ecc.android.sdk.block.CompactBlockProcessor
|
||||
import cash.z.ecc.android.sdk.ext.onFirst
|
||||
import cash.z.ecc.android.sdk.internal.Twig
|
||||
import cash.z.ecc.android.sdk.model.PersistableWallet
|
||||
|
@ -33,6 +34,8 @@ import java.util.UUID
|
|||
|
||||
/**
|
||||
* @param persistableWallet flow of the user's stored wallet. Null indicates that no wallet has been stored.
|
||||
*
|
||||
* @param syncAlgorithm The CompactBlockProcess's type of block syncing algorithm
|
||||
*/
|
||||
/*
|
||||
* One area where this class needs to change before it can be moved out of the incubator is that we need to be able to
|
||||
|
@ -43,7 +46,8 @@ import java.util.UUID
|
|||
*/
|
||||
class WalletCoordinator(
|
||||
context: Context,
|
||||
val persistableWallet: Flow<PersistableWallet?>
|
||||
val persistableWallet: Flow<PersistableWallet?>,
|
||||
val syncAlgorithm: CompactBlockProcessor.SyncAlgorithm = CompactBlockProcessor.SyncAlgorithm.NON_LINEAR
|
||||
) {
|
||||
|
||||
private val applicationContext = context.applicationContext
|
||||
|
@ -80,6 +84,7 @@ class WalletCoordinator(
|
|||
lightWalletEndpoint = LightWalletEndpoint.defaultForNetwork(persistableWallet.network),
|
||||
birthday = persistableWallet.birthday,
|
||||
seed = persistableWallet.seedPhrase.toByteArray(),
|
||||
syncAlgorithm = syncAlgorithm
|
||||
)
|
||||
|
||||
trySend(InternalSynchronizerStatus.Available(closeableSynchronizer))
|
||||
|
|
|
@ -109,6 +109,10 @@ class SdkSynchronizer private constructor(
|
|||
private val mutex = Mutex()
|
||||
|
||||
/**
|
||||
* Convenience method to create new SdkSynchronizer instance.
|
||||
*
|
||||
* @return Synchronizer instance as CloseableSynchronizer
|
||||
*
|
||||
* @throws IllegalStateException If multiple instances of synchronizer with the same network+alias are
|
||||
* active at the same time. Call `close` to finish one synchronizer before starting another one with the same
|
||||
* network+alias.
|
||||
|
@ -708,12 +712,14 @@ internal object DefaultSynchronizerFactory {
|
|||
backend: TypesafeBackend,
|
||||
downloader: CompactBlockDownloader,
|
||||
repository: DerivedDataRepository,
|
||||
birthdayHeight: BlockHeight
|
||||
birthdayHeight: BlockHeight,
|
||||
syncAlgorithm: CompactBlockProcessor.SyncAlgorithm
|
||||
): CompactBlockProcessor = CompactBlockProcessor(
|
||||
downloader,
|
||||
repository,
|
||||
backend,
|
||||
birthdayHeight
|
||||
downloader = downloader,
|
||||
repository = repository,
|
||||
backend = backend,
|
||||
minimumHeight = birthdayHeight,
|
||||
syncAlgorithm = syncAlgorithm
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -398,20 +398,28 @@ interface Synchronizer {
|
|||
* Primary method that SDK clients will use to construct a synchronizer.
|
||||
*
|
||||
* @param zcashNetwork the network to use.
|
||||
*
|
||||
* @param alias A string used to segregate multiple wallets in the filesystem. This implies the string
|
||||
* should not contain characters unsuitable for the platform's filesystem. The default value is
|
||||
* generally used unless an SDK client needs to support multiple wallets.
|
||||
*
|
||||
* @param lightWalletEndpoint Server endpoint. See [cash.z.ecc.android.sdk.model.defaultForNetwork]. If a
|
||||
* client wishes to change the server endpoint, the active synchronizer will need to be stopped and a new
|
||||
* instance created with a new value.
|
||||
*
|
||||
* @param seed the wallet's seed phrase. This is required the first time a new wallet is set up. For
|
||||
* subsequent calls, seed is only needed if [InitializerException.SeedRequired] is thrown.
|
||||
*
|
||||
* @param birthday Block height representing the "birthday" of the wallet. When creating a new wallet, see
|
||||
* [BlockHeight.ofLatestCheckpoint]. When restoring an existing wallet, use block height that was first used
|
||||
* to create the wallet. If that value is unknown, null is acceptable but will result in longer
|
||||
* sync times. After sync completes, the birthday can be determined from [Synchronizer.latestBirthdayHeight].
|
||||
*
|
||||
* @param syncAlgorithm The CompactBlockProcess's type of block syncing algorithm
|
||||
*
|
||||
* @throws InitializerException.SeedRequired Indicates clients need to call this method again, providing the
|
||||
* seed bytes.
|
||||
*
|
||||
* @throws IllegalStateException If multiple instances of synchronizer with the same network+alias are
|
||||
* active at the same time. Call `close` to finish one synchronizer before starting another one with the same
|
||||
* network+alias.
|
||||
|
@ -427,7 +435,8 @@ interface Synchronizer {
|
|||
alias: String = ZcashSdk.DEFAULT_ALIAS,
|
||||
lightWalletEndpoint: LightWalletEndpoint,
|
||||
seed: ByteArray?,
|
||||
birthday: BlockHeight?
|
||||
birthday: BlockHeight?,
|
||||
syncAlgorithm: CompactBlockProcessor.SyncAlgorithm = CompactBlockProcessor.SyncAlgorithm.LINEAR
|
||||
): CloseableSynchronizer {
|
||||
val applicationContext = context.applicationContext
|
||||
|
||||
|
@ -482,20 +491,20 @@ interface Synchronizer {
|
|||
service
|
||||
)
|
||||
val processor = DefaultSynchronizerFactory.defaultProcessor(
|
||||
backend,
|
||||
downloader,
|
||||
repository,
|
||||
birthday
|
||||
?: zcashNetwork.saplingActivationHeight
|
||||
backend = backend,
|
||||
downloader = downloader,
|
||||
repository = repository,
|
||||
birthdayHeight = birthday ?: zcashNetwork.saplingActivationHeight,
|
||||
syncAlgorithm = syncAlgorithm
|
||||
)
|
||||
|
||||
return SdkSynchronizer.new(
|
||||
zcashNetwork,
|
||||
alias,
|
||||
repository,
|
||||
txManager,
|
||||
processor,
|
||||
backend
|
||||
zcashNetwork = zcashNetwork,
|
||||
alias = alias,
|
||||
repository = repository,
|
||||
txManager = txManager,
|
||||
processor = processor,
|
||||
backend = backend
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -513,9 +522,10 @@ interface Synchronizer {
|
|||
alias: String = ZcashSdk.DEFAULT_ALIAS,
|
||||
lightWalletEndpoint: LightWalletEndpoint,
|
||||
seed: ByteArray?,
|
||||
birthday: BlockHeight?
|
||||
birthday: BlockHeight?,
|
||||
syncAlgorithm: CompactBlockProcessor.SyncAlgorithm = CompactBlockProcessor.SyncAlgorithm.LINEAR
|
||||
): CloseableSynchronizer = runBlocking {
|
||||
new(context, zcashNetwork, alias, lightWalletEndpoint, seed, birthday)
|
||||
new(context, zcashNetwork, alias, lightWalletEndpoint, seed, birthday, syncAlgorithm)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -85,6 +85,8 @@ import kotlin.time.toDuration
|
|||
* reorgs as a backstop to make sure we do not rewind beyond sapling activation. It also is factored
|
||||
* in when considering initial range to download. In most cases, this should be the birthday height
|
||||
* of the current wallet--the height before which we do not need to scan for transactions.
|
||||
*
|
||||
* @property syncAlgorithm The type of block syncing algorithm which should be preferably used
|
||||
*/
|
||||
@OpenForTesting
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
|
@ -92,7 +94,8 @@ class CompactBlockProcessor internal constructor(
|
|||
val downloader: CompactBlockDownloader,
|
||||
private val repository: DerivedDataRepository,
|
||||
private val backend: TypesafeBackend,
|
||||
minimumHeight: BlockHeight
|
||||
minimumHeight: BlockHeight,
|
||||
private val syncAlgorithm: SyncAlgorithm
|
||||
) {
|
||||
/**
|
||||
* Callback for any non-trivial errors that occur while processing compact blocks.
|
||||
|
@ -196,8 +199,12 @@ class CompactBlockProcessor internal constructor(
|
|||
)
|
||||
|
||||
// Download note commitment tree data from lightwalletd to decide if we communicate with linear
|
||||
// or non-linear node
|
||||
val subTreeRootList = getSubtreeRoots(downloader, network)
|
||||
// or non-linear node. It depends on the syncAlgorithm property on the first place.
|
||||
val subTreeRootList = if (syncAlgorithm == SyncAlgorithm.LINEAR) {
|
||||
emptyList()
|
||||
} else {
|
||||
getSubtreeRoots(downloader, network)
|
||||
}
|
||||
Twig.info { "Fetched SubTreeRoot list size: ${subTreeRootList?.size ?: 0}" }
|
||||
|
||||
Twig.debug { "Setup verified. Processor starting..." }
|
||||
|
@ -205,7 +212,10 @@ class CompactBlockProcessor internal constructor(
|
|||
// Using do/while makes it easier to execute exactly one loop which helps with testing this processor quickly
|
||||
// (because you can start and then immediately set isStopped=true to always get precisely one loop)
|
||||
do {
|
||||
retryWithBackoff(::onProcessorError, maxDelayMillis = MAX_BACKOFF_INTERVAL) {
|
||||
retryWithBackoff(
|
||||
onErrorListener = ::onProcessorError,
|
||||
maxDelayMillis = MAX_BACKOFF_INTERVAL
|
||||
) {
|
||||
val result = processingMutex.withLockLogged("processNewBlocks") {
|
||||
if (subTreeRootList.isNullOrEmpty()) {
|
||||
processNewBlocksInLinearOrder()
|
||||
|
@ -366,7 +376,7 @@ class CompactBlockProcessor internal constructor(
|
|||
firstUnenhancedHeight: BlockHeight?
|
||||
): BlockProcessingResult {
|
||||
Twig.debug {
|
||||
"Beginning to process new blocks with DAG approach (with roots: $subTreeRootList, and lower " +
|
||||
"Beginning to process new blocks with Non-linear approach (with roots: $subTreeRootList, and lower " +
|
||||
"bound: $lastValidHeight)..."
|
||||
}
|
||||
|
||||
|
@ -952,7 +962,7 @@ class CompactBlockProcessor internal constructor(
|
|||
|
||||
retryUpToAndContinue(GET_SUBTREE_ROOTS_RETRIES) {
|
||||
subTreeRootList = downloader.getSubtreeRoots(
|
||||
// TODO [#1133]: DAG: Set the correct getSubtreeRoots inputs
|
||||
// TODO [#1133]: SbS: Set the correct getSubtreeRoots inputs
|
||||
// TODO [#1133]: https://github.com/zcash/zcash-android-wallet-sdk/issues/1133
|
||||
startIndex = 0,
|
||||
maxEntries = if (network.isTestnet()) {
|
||||
|
@ -2043,6 +2053,11 @@ class CompactBlockProcessor internal constructor(
|
|||
val hash: String?
|
||||
)
|
||||
|
||||
enum class SyncAlgorithm {
|
||||
LINEAR,
|
||||
NON_LINEAR
|
||||
}
|
||||
|
||||
//
|
||||
// Helper Extensions
|
||||
//
|
||||
|
|
Loading…
Reference in New Issue