Implement versioning and other cleanup.

- Correct typo and compiler warning in Rust.
'trait objects without an explicity dyn' are deprecated and this is a warning as of Rust 1.37
- update dependencies
- update documentation

docs
This commit is contained in:
Kevin Gorham 2019-08-30 13:05:02 -04:00
parent 636cbb59c0
commit 1f18042d52
No known key found for this signature in database
GPG Key ID: CCA55602DF49FC38
90 changed files with 719 additions and 902 deletions

View File

@ -49,6 +49,7 @@ This lightweight SDK connects Android to Zcash. It welds together Rust and Kotli
- [Components](#components)
- [Quickstart](#quickstart)
- [Compiling Sources](#compiling-sources)
- [Versioning](#versioning)
## Structure
@ -84,20 +85,20 @@ To accomplish this, these responsibilities of the SDK are divided into separate
#### Components
| Component | Summary | Input | Output |
| :--------- | :------------ | :--- | :--- |
| **Downloader** | Downloads compact blocks | Server host:port | Stream of compact blocks |
| **Processor** | Processes compact blocks | Stream of compact blocks | Decoded wallet data |
| **Repository** | Source of data derived from processing blocks | Decoded wallet data | UI Data |
| **Active Transaction Manager** | Manages the lifecycle of pending transactions | Decoded wallet data | Transaction state |
| **Wallet** | Wraps the Zcash rust libraries, insulating SDK users from changes in that layer | Configuration | Configuration |
| Component | Summary |
| :--------- | :------ |
| **LightWalletService** | Service used for requesting compact blocks |
| **CompactBlockStore** | Stores compact blocks that have been downloaded from the `LightWalletService` |
| **CompactBlockProcessor** | Validates and scans the compact blocks in the `CompactBlockStore` for transaction details |
| **TransactionManager** | Creates, Submits and manages transactions for spending funds |
| **Wallet** | Wraps the Zcash rust libraries, insulating SDK users from changes in that layer |
[Back to contents](#contents)
## Quickstart
Add the SDK dependency
```gradle
implementation "cash.z.android.wallet:zcash-android-testnet:1.7.5-alpha@aar"
implementation "cash.z.android.wallet:zcash-android-testnet:1.0.0-alpha01@aar"
```
Start the [Synchronizer](docs/-synchronizer/README.md)
@ -127,4 +128,18 @@ Compilation requires `Cargo` and has been tested on Ubuntu, MacOS and Windows. T
```
This creates a `testnet` build of the SDK that can be used to preview basic functionality for sending and receiving shielded transactions. If you do not have `Rust` and `Cargo` installed, the build script will let you know and provide further instructions for installation. Note that merely using the SDK does not require installing Rust or Cargo--that is only required for compilation.
[Back to contents](#contents)
## Versioning
This project follows [semantic versioning](https://semver.org/) with pre-release versions. An example of a valid version number is `1.0.4-alpha11` denoting the `11th` iteration of the `alpha` pre-release of version `1.0.4`. Stable releases, such as `1.0.4` will not contain any pre-release identifiers. Pre-releases include the following, in order of stability: `alpha`, `beta`, `rc`. Version codes offer a numeric representation of the build name that always increases. The first six significant digits represent the major, minor and patch number (two digits each) and the last 3 significant digits represent the pre-release identifier. The first digit of the identifier signals the build type. Lastly, each new build has a higher version code than all previous builds. The following table breaks this down:
#### Build Types
| Type | Purpose | Stability | Audience | Identifier | Example Version |
| :---- | :--------- | :---------- | :-------- | :------- | :--- |
| **alpha** | **Sandbox.** For developers to verify behavior and try features. Things seen here might never go to production. Most bugs here can be ignored.| Unstable: Expect bugs | Internal developers | 0XX | 1.2.3-alpha04 (10203004) |
| **beta** | **Hand-off.** For developers to present finished features. Bugs found here should be reported and immediately addressed, if they relate to recent changes. | Unstable: Report bugs | Internal stakeholders | 2XX | 1.2.3-beta04 (10203204) |
| **release candidate** | **Hardening.** Final testing for an app release that we believe is ready to go live. The focus here is regression testing to ensure that new changes have not introduced instability in areas that were previously working. | Stable: Hunt for bugs | External testers | 4XX | 1.2.3-rc04 (10203404) |
| **production** | **Dellivery.** Deliver new features to end users. Any bugs found here need to be prioritized. Some will require immediate attention but most can be worked into a future release. | Stable: Prioritize bugs | Public | 8XX | 1.2.3 (10203800) |
[Back to contents](#contents)

View File

@ -1,16 +1,16 @@
buildscript {
ext.buildConfig = [
'compileSdkVersion': 28,
'compileSdkVersion': 29,
'minSdkVersion': 16,
'targetSdkVersion': 28
'targetSdkVersion': 29
]
ext.versions = [
'architectureComponents': [
'lifecycle': '2.2.0-alpha02',
'room': '2.1.0'
'lifecycle': '2.2.0-alpha04',
'room': '2.1.0-rc01'
],
'grpc':'1.21.0',
'kotlin': '1.3.41',
'kotlin': '1.3.50',
'coroutines': '1.3.0-M1',
'junitJupiter': '5.5.0-M1'
]
@ -22,7 +22,7 @@ buildscript {
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0-beta05'
classpath 'com.android.tools.build:gradle:3.6.0-alpha11'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "org.jetbrains.kotlin:kotlin-allopen:${versions.kotlin}"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:0.9.18"
@ -31,13 +31,14 @@ buildscript {
classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.8"
classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.6'
classpath 'com.github.str4d:rust-android-gradle:68b4ecc053'
classpath 'org.owasp:dependency-check-gradle:5.2.1'
}
}
apply from: 'custom-tasks.gradle'
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-allopen'
apply plugin: 'org.jetbrains.dokka'
@ -49,7 +50,7 @@ apply plugin: 'org.mozilla.rust-android-gradle.rust-android'
apply plugin: 'org.owasp.dependencycheck'
group = 'cash.z.android.wallet'
version = '1.9.1'
version = '1.0.0-alpha01'
repositories {
google()
@ -64,8 +65,8 @@ android {
defaultConfig {
minSdkVersion buildConfig.minSdkVersion
targetSdkVersion buildConfig.targetSdkVersion
versionCode = 1_09_01_00 // last digits are alpha(0X) beta(1X) rc(2X) release(3X). Ex: 1_08_04_20 is a RC build
versionName = "$version-alpha"
versionCode = 1_00_00_001 // last digits are alpha(0XX) beta(2XX) rc(4XX) release(8XX). Ex: 1_08_04_401 is an release candidate build of version 1.8.4 and 1_08_04_800 would be the final release.
versionName = "$version"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments clearPackageData: 'true'
multiDexEnabled true
@ -184,7 +185,7 @@ cargo {
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
implementation 'androidx.appcompat:appcompat:1.1.0'
// Architecture Components: Lifecycle
implementation "androidx.lifecycle:lifecycle-runtime:${versions.architectureComponents.lifecycle}"
@ -232,7 +233,7 @@ dependencies {
androidTestImplementation 'org.mockito:mockito-android:2.25.1'
androidTestImplementation "androidx.test:runner:1.2.0"
androidTestImplementation "androidx.test:core:1.2.0"
androidTestImplementation "androidx.arch.core:core-testing:2.0.1"
androidTestImplementation "androidx.arch.core:core-testing:2.1.0-rc01"
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:runner:1.2.0'
}

View File

@ -2,32 +2,23 @@
# <init>
`SdkSynchronizer(downloader: `[`CompactBlockStream`](../-compact-block-stream/index.md)`, processor: `[`CompactBlockProcessor`](../-compact-block-processor/index.md)`, repository: `[`TransactionRepository`](../-transaction-repository/index.md)`, activeTransactionManager: `[`ActiveTransactionManager`](../-active-transaction-manager/index.md)`, wallet: `[`Wallet`](../../cash.z.wallet.sdk.secure/-wallet/index.md)`, batchSize: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = 1000, staleTolerance: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = 10, blockPollFrequency: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)` = CompactBlockStream.DEFAULT_POLL_INTERVAL)`
`SdkSynchronizer(wallet: `[`Wallet`](../../cash.z.wallet.sdk.secure/-wallet/index.md)`, ledger: `[`TransactionRepository`](../-transaction-repository/index.md)`, sender: `[`TransactionSender`](../-transaction-sender/index.md)`, processor: `[`CompactBlockProcessor`](../../cash.z.wallet.sdk.block/-compact-block-processor/index.md)`, encoder: `[`TransactionEncoder`](../-transaction-encoder/index.md)`)`
The glue. Downloads compact blocks to the database and then scans them for transactions. In order to serve that
purpose, this class glues together a variety of key components. Each component contributes to the team effort of
providing a simple source of truth to interact with.
Another way of thinking about this class is the reference that demonstrates how all the pieces can be tied
together.
A synchronizer that attempts to remain operational, despite any number of errors that can occur. It acts as the glue
that ties all the pieces of the SDK together. Each component of the SDK is designed for the potential of stand-alone
usage but coordinating all the interactions is non-trivial. So the synchronizer facilitates this, acting as reference
that demonstrates how all the pieces can be tied together. Its goal is to allow a developer to focus on their app
rather than the nuances of how Zcash works.
### Parameters
`downloader` - the component that downloads compact blocks and exposes them as a stream
`wallet` - the component that wraps the JNI layer that interacts with the rust backend and manages wallet config.
`repository` - the component that exposes streams of wallet transaction information.
`sender` - the component responsible for sending transactions to lightwalletd in order to spend funds.
`processor` - the component that saves the downloaded compact blocks to the cache and then scans those blocks for
data related to this wallet.
`repository` - the component that exposes streams of wallet transaction information.
`activeTransactionManager` - the component that manages the lifecycle of active transactions. This includes sent
transactions that have not been mined.
`wallet` - the component that wraps the JNI layer that interacts with librustzcash and manages wallet config.
`batchSize` - the number of compact blocks to download at a time.
`staleTolerance` - the number of blocks to allow before considering our data to be stale
`blockPollFrequency` - how often to poll for compact blocks. Once all missing blocks have been downloaded, this
number represents the number of milliseconds the synchronizer will wait before checking for newly mined blocks.
`encoder` - the component that creates a signed transaction, used for spending funds.

View File

@ -0,0 +1,10 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [balances](./balances.md)
# balances
`fun balances(): ReceiveChannel<`[`Wallet.WalletBalance`](../../cash.z.wallet.sdk.secure/-wallet/-wallet-balance/index.md)`>`
Overrides [Synchronizer.balances](../-synchronizer/balances.md)
A stream of balance values, separately reflecting both the available and total balance.

View File

@ -2,13 +2,12 @@
# cancelSend
`fun cancelSend(transaction: `[`ActiveSendTransaction`](../-active-send-transaction/index.md)`): `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)
`fun cancelSend(transaction: `[`SentTransaction`](../../cash.z.wallet.sdk.entity/-sent-transaction/index.md)`): `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)
Overrides [Synchronizer.cancelSend](../-synchronizer/cancel-send.md)
Attempts to cancel a previously sent transaction. Transactions can only be cancelled during the calculation phase
before they've been submitted to the server. This method will return false when it is too late to cancel. This
logic is delegated to the activeTransactionManager, which knows the state of the given transaction.
Attempts to cancel a previously sent transaction. Typically, cancellation is only an option if the transaction
has not yet been submitted to the server.
### Parameters

View File

@ -0,0 +1,11 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [clearedTransactions](./cleared-transactions.md)
# clearedTransactions
`fun clearedTransactions(): ReceiveChannel<`[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`ClearedTransaction`](../../cash.z.wallet.sdk.entity/-cleared-transaction/index.md)`>>`
Overrides [Synchronizer.clearedTransactions](../-synchronizer/cleared-transactions.md)
A stream of all the transactions that are on the blockchain. Implementations should consider only returning a
subset like the most recent 100 transactions, perhaps through paging the underlying database.

View File

@ -0,0 +1,12 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [coroutineScope](./coroutine-scope.md)
# coroutineScope
`lateinit var coroutineScope: CoroutineScope`
The lifespan of this Synchronizer. This scope is initialized once the Synchronizer starts because it will be a
child of the parentScope that gets passed into the [start](start.md) function. Everything launched by this Synchronizer
will be cancelled once the Synchronizer or its parentScope stops. This is a lateinit rather than nullable
property so that it fails early rather than silently, whenever the scope is used before the Synchronizer has been
started.

View File

@ -2,7 +2,7 @@
# getAddress
`fun getAddress(accountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)`): `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)
`suspend fun getAddress(accountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)`): `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)
Overrides [Synchronizer.getAddress](../-synchronizer/get-address.md)
@ -10,4 +10,4 @@ Gets the address for the given account.
### Parameters
`accountId` - the optional accountId whose address of interest. By default, the first account is used.
`accountId` - the optional accountId whose address is of interest. By default, the first account is used.

View File

@ -0,0 +1,61 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](./index.md)
# SdkSynchronizer
`@ExperimentalCoroutinesApi class SdkSynchronizer : `[`Synchronizer`](../-synchronizer/index.md)
A synchronizer that attempts to remain operational, despite any number of errors that can occur. It acts as the glue
that ties all the pieces of the SDK together. Each component of the SDK is designed for the potential of stand-alone
usage but coordinating all the interactions is non-trivial. So the synchronizer facilitates this, acting as reference
that demonstrates how all the pieces can be tied together. Its goal is to allow a developer to focus on their app
rather than the nuances of how Zcash works.
### Parameters
`wallet` - the component that wraps the JNI layer that interacts with the rust backend and manages wallet config.
`repository` - the component that exposes streams of wallet transaction information.
`sender` - the component responsible for sending transactions to lightwalletd in order to spend funds.
`processor` - the component that saves the downloaded compact blocks to the cache and then scans those blocks for
data related to this wallet.
`encoder` - the component that creates a signed transaction, used for spending funds.
### Constructors
| Name | Summary |
|---|---|
| [&lt;init&gt;](-init-.md) | `SdkSynchronizer(wallet: `[`Wallet`](../../cash.z.wallet.sdk.secure/-wallet/index.md)`, ledger: `[`TransactionRepository`](../-transaction-repository/index.md)`, sender: `[`TransactionSender`](../-transaction-sender/index.md)`, processor: `[`CompactBlockProcessor`](../../cash.z.wallet.sdk.block/-compact-block-processor/index.md)`, encoder: `[`TransactionEncoder`](../-transaction-encoder/index.md)`)`<br>A synchronizer that attempts to remain operational, despite any number of errors that can occur. It acts as the glue that ties all the pieces of the SDK together. Each component of the SDK is designed for the potential of stand-alone usage but coordinating all the interactions is non-trivial. So the synchronizer facilitates this, acting as reference that demonstrates how all the pieces can be tied together. Its goal is to allow a developer to focus on their app rather than the nuances of how Zcash works. |
### Properties
| Name | Summary |
|---|---|
| [coroutineScope](coroutine-scope.md) | `lateinit var coroutineScope: CoroutineScope`<br>The lifespan of this Synchronizer. This scope is initialized once the Synchronizer starts because it will be a child of the parentScope that gets passed into the [start](start.md) function. Everything launched by this Synchronizer will be cancelled once the Synchronizer or its parentScope stops. This is a lateinit rather than nullable property so that it fails early rather than silently, whenever the scope is used before the Synchronizer has been started. |
| [isConnected](is-connected.md) | `val isConnected: `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)<br>A property that is true while a connection to the lightwalletd server exists. |
| [isScanning](is-scanning.md) | `val isScanning: `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)<br>A property that is true while actively scanning the cache of compact blocks for transactions. |
| [isSyncing](is-syncing.md) | `val isSyncing: `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)<br>A property that is true while actively downloading blocks from lightwalletd. |
| [onCriticalErrorHandler](on-critical-error-handler.md) | `var onCriticalErrorHandler: ((`[`Throwable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-throwable/index.html)`?) -> `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)`)?`<br>A callback to invoke whenever an uncaught error is encountered. By definition, the return value of the function is ignored because this error is unrecoverable. The only reason the function has a return value is so that all error handlers work with the same signature which allows one function to handle all errors in simple apps. This callback is not called on the main thread so any UI work would need to switch context to the main thread. |
| [onProcessorErrorHandler](on-processor-error-handler.md) | `var onProcessorErrorHandler: ((`[`Throwable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-throwable/index.html)`?) -> `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)`)?`<br>A callback to invoke whenver a processor error is encountered. Returning true signals that the error was handled and a retry attempt should be made, if possible. This callback is not called on the main thread so any UI work would need to switch context to the main thread. |
| [onSubmissionErrorHandler](on-submission-error-handler.md) | `var onSubmissionErrorHandler: ((`[`Throwable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-throwable/index.html)`?) -> `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)`)?`<br>A callback to invoke whenever a server error is encountered while submitting a transaction to lightwalletd. Returning true signals that the error was handled and a retry attempt should be made, if possible. This callback is not called on the main thread so any UI work would need to switch context to the main thread. |
### Functions
| Name | Summary |
|---|---|
| [balances](balances.md) | `fun balances(): ReceiveChannel<`[`Wallet.WalletBalance`](../../cash.z.wallet.sdk.secure/-wallet/-wallet-balance/index.md)`>`<br>A stream of balance values, separately reflecting both the available and total balance. |
| [cancelSend](cancel-send.md) | `fun cancelSend(transaction: `[`SentTransaction`](../../cash.z.wallet.sdk.entity/-sent-transaction/index.md)`): `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)<br>Attempts to cancel a previously sent transaction. Typically, cancellation is only an option if the transaction has not yet been submitted to the server. |
| [clearedTransactions](cleared-transactions.md) | `fun clearedTransactions(): ReceiveChannel<`[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`ClearedTransaction`](../../cash.z.wallet.sdk.entity/-cleared-transaction/index.md)`>>`<br>A stream of all the transactions that are on the blockchain. Implementations should consider only returning a subset like the most recent 100 transactions, perhaps through paging the underlying database. |
| [getAddress](get-address.md) | `suspend fun getAddress(accountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)`): `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)<br>Gets the address for the given account. |
| [lastBalance](last-balance.md) | `fun lastBalance(): `[`Wallet.WalletBalance`](../../cash.z.wallet.sdk.secure/-wallet/-wallet-balance/index.md)<br>Holds the most recent value that was transmitted through the [balances](../-synchronizer/balances.md) channel. Typically, if the underlying channel is a BroadcastChannel (and it should be), then this value is simply [balanceChannel.value](#) |
| [lastCleared](last-cleared.md) | `fun lastCleared(): `[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`ClearedTransaction`](../../cash.z.wallet.sdk.entity/-cleared-transaction/index.md)`>`<br>Holds the most recent value that was transmitted through the [clearedTransactions](../-synchronizer/cleared-transactions.md) channel. Typically, if the underlying channel is a BroadcastChannel (and it should be), then this value is simply [clearedChannel.value](#) |
| [lastPending](last-pending.md) | `fun lastPending(): `[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`PendingTransaction`](../../cash.z.wallet.sdk.entity/-pending-transaction/index.md)`>`<br>Holds the most recent value that was transmitted through the [pendingTransactions](../-synchronizer/pending-transactions.md) channel. Typically, if the underlying channel is a BroadcastChannel (and it should be),then this value is simply [pendingChannel.value](#) |
| [onTransactionsChanged](on-transactions-changed.md) | `fun onTransactionsChanged(): `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html) |
| [pendingTransactions](pending-transactions.md) | `fun pendingTransactions(): ReceiveChannel<`[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`PendingTransaction`](../../cash.z.wallet.sdk.entity/-pending-transaction/index.md)`>>`<br>A stream of all the outbound pending transaction that have been sent but are awaiting confirmations. |
| [progress](progress.md) | `fun progress(): ReceiveChannel<`[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)`>`<br>A stream of progress values, typically corresponding to this Synchronizer downloading blocks. Typically, any non- zero value below 100 indicates that progress indicators can be shown and a value of 100 signals that progress is complete and any progress indicators can be hidden. |
| [refreshBalance](refresh-balance.md) | `suspend fun refreshBalance(): `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html) |
| [sendToAddress](send-to-address.md) | `suspend fun sendToAddress(zatoshi: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)`, toAddress: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, memo: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, fromAccountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)`): `[`PendingTransaction`](../../cash.z.wallet.sdk.entity/-pending-transaction/index.md)<br>Sends zatoshi. |
| [start](start.md) | `fun start(parentScope: CoroutineScope): `[`Synchronizer`](../-synchronizer/index.md)<br>Starts this synchronizer within the given scope. For simplicity, attempting to start an instance that has already been started will throw a [SynchronizerException.FalseStart](../../cash.z.wallet.sdk.exception/-synchronizer-exception/-false-start.md) exception. This reduces the complexity of managing resources that must be recycled. Instead, each synchronizer is designed to have a long lifespan and should be started from an activity, application or session. |
| [stop](stop.md) | `fun stop(): `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html)<br>Stop this synchronizer and all of its child jobs. Once a synchronizer has been stopped it should not be restarted and attempting to do so will result in an error. Also, this function will throw an exception if the synchronizer was never previously started. |

View File

@ -0,0 +1,10 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [isConnected](./is-connected.md)
# isConnected
`val isConnected: `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)
Overrides [Synchronizer.isConnected](../-synchronizer/is-connected.md)
A property that is true while a connection to the lightwalletd server exists.

View File

@ -0,0 +1,10 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [isScanning](./is-scanning.md)
# isScanning
`val isScanning: `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)
Overrides [Synchronizer.isScanning](../-synchronizer/is-scanning.md)
A property that is true while actively scanning the cache of compact blocks for transactions.

View File

@ -0,0 +1,10 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [isSyncing](./is-syncing.md)
# isSyncing
`val isSyncing: `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)
Overrides [Synchronizer.isSyncing](../-synchronizer/is-syncing.md)
A property that is true while actively downloading blocks from lightwalletd.

View File

@ -0,0 +1,11 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [lastBalance](./last-balance.md)
# lastBalance
`fun lastBalance(): `[`Wallet.WalletBalance`](../../cash.z.wallet.sdk.secure/-wallet/-wallet-balance/index.md)
Overrides [Synchronizer.lastBalance](../-synchronizer/last-balance.md)
Holds the most recent value that was transmitted through the [balances](../-synchronizer/balances.md) channel. Typically, if the
underlying channel is a BroadcastChannel (and it should be), then this value is simply [balanceChannel.value](#)

View File

@ -0,0 +1,11 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [lastCleared](./last-cleared.md)
# lastCleared
`fun lastCleared(): `[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`ClearedTransaction`](../../cash.z.wallet.sdk.entity/-cleared-transaction/index.md)`>`
Overrides [Synchronizer.lastCleared](../-synchronizer/last-cleared.md)
Holds the most recent value that was transmitted through the [clearedTransactions](../-synchronizer/cleared-transactions.md) channel. Typically, if the
underlying channel is a BroadcastChannel (and it should be), then this value is simply [clearedChannel.value](#)

View File

@ -0,0 +1,11 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [lastPending](./last-pending.md)
# lastPending
`fun lastPending(): `[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`PendingTransaction`](../../cash.z.wallet.sdk.entity/-pending-transaction/index.md)`>`
Overrides [Synchronizer.lastPending](../-synchronizer/last-pending.md)
Holds the most recent value that was transmitted through the [pendingTransactions](../-synchronizer/pending-transactions.md) channel. Typically, if the
underlying channel is a BroadcastChannel (and it should be),then this value is simply [pendingChannel.value](#)

View File

@ -0,0 +1,13 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [onCriticalErrorHandler](./on-critical-error-handler.md)
# onCriticalErrorHandler
`var onCriticalErrorHandler: ((`[`Throwable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-throwable/index.html)`?) -> `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)`)?`
Overrides [Synchronizer.onCriticalErrorHandler](../-synchronizer/on-critical-error-handler.md)
A callback to invoke whenever an uncaught error is encountered. By definition, the return value of the function
is ignored because this error is unrecoverable. The only reason the function has a return value is so that all
error handlers work with the same signature which allows one function to handle all errors in simple apps. This
callback is not called on the main thread so any UI work would need to switch context to the main thread.

View File

@ -0,0 +1,12 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [onProcessorErrorHandler](./on-processor-error-handler.md)
# onProcessorErrorHandler
`var onProcessorErrorHandler: ((`[`Throwable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-throwable/index.html)`?) -> `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)`)?`
Overrides [Synchronizer.onProcessorErrorHandler](../-synchronizer/on-processor-error-handler.md)
A callback to invoke whenver a processor error is encountered. Returning true signals that the error was handled
and a retry attempt should be made, if possible. This callback is not called on the main thread so any UI work
would need to switch context to the main thread.

View File

@ -0,0 +1,12 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [onSubmissionErrorHandler](./on-submission-error-handler.md)
# onSubmissionErrorHandler
`var onSubmissionErrorHandler: ((`[`Throwable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-throwable/index.html)`?) -> `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)`)?`
Overrides [Synchronizer.onSubmissionErrorHandler](../-synchronizer/on-submission-error-handler.md)
A callback to invoke whenever a server error is encountered while submitting a transaction to lightwalletd.
Returning true signals that the error was handled and a retry attempt should be made, if possible. This callback
is not called on the main thread so any UI work would need to switch context to the main thread.

View File

@ -0,0 +1,5 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [onTransactionsChanged](./on-transactions-changed.md)
# onTransactionsChanged
`fun onTransactionsChanged(): `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html)

View File

@ -0,0 +1,10 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [pendingTransactions](./pending-transactions.md)
# pendingTransactions
`fun pendingTransactions(): ReceiveChannel<`[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`PendingTransaction`](../../cash.z.wallet.sdk.entity/-pending-transaction/index.md)`>>`
Overrides [Synchronizer.pendingTransactions](../-synchronizer/pending-transactions.md)
A stream of all the outbound pending transaction that have been sent but are awaiting confirmations.

View File

@ -6,8 +6,7 @@
Overrides [Synchronizer.progress](../-synchronizer/progress.md)
A stream of progress values, corresponding to this Synchronizer downloading blocks, delegated to the
[downloader](#). Any non-zero value below 100 indicates that progress indicators can be shown and a value of 100
signals that progress is complete and any progress indicators can be hidden. At that point, the synchronizer
switches from catching up on missed blocks to periodically monitoring for newly mined blocks.
A stream of progress values, typically corresponding to this Synchronizer downloading blocks. Typically, any non-
zero value below 100 indicates that progress indicators can be shown and a value of 100 signals that progress is
complete and any progress indicators can be hidden.

View File

@ -0,0 +1,5 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [SdkSynchronizer](index.md) / [refreshBalance](./refresh-balance.md)
# refreshBalance
`suspend fun refreshBalance(): `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html)

View File

@ -2,7 +2,7 @@
# sendToAddress
`suspend fun sendToAddress(zatoshi: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)`, toAddress: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, memo: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, fromAccountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)`): `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html)
`suspend fun sendToAddress(zatoshi: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)`, toAddress: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, memo: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, fromAccountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)`): `[`PendingTransaction`](../../cash.z.wallet.sdk.entity/-pending-transaction/index.md)
Overrides [Synchronizer.sendToAddress](../-synchronizer/send-to-address.md)

View File

@ -6,7 +6,7 @@
Overrides [Synchronizer.stop](../-synchronizer/stop.md)
Stops this synchronizer by stopping the downloader, repository, and activeTransactionManager, then cancelling the
parent job. Note that we do not cancel the parent scope that was passed into [start](start.md) because the synchronizer
does not own that scope, it just uses it for launching children.
Stop this synchronizer and all of its child jobs. Once a synchronizer has been stopped it should not be restarted
and attempting to do so will result in an error. Also, this function will throw an exception if the synchronizer
was never previously started.

View File

@ -0,0 +1,8 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [Synchronizer](index.md) / [balances](./balances.md)
# balances
`abstract fun balances(): ReceiveChannel<`[`Wallet.WalletBalance`](../../cash.z.wallet.sdk.secure/-wallet/-wallet-balance/index.md)`>`
A stream of balance values, separately reflecting both the available and total balance.

View File

@ -2,7 +2,7 @@
# cancelSend
`abstract fun cancelSend(transaction: `[`ActiveSendTransaction`](../-active-send-transaction/index.md)`): `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)
`abstract fun cancelSend(transaction: `[`SentTransaction`](../../cash.z.wallet.sdk.entity/-sent-transaction/index.md)`): `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)
Attempts to cancel a previously sent transaction. Typically, cancellation is only an option if the transaction
has not yet been submitted to the server.

View File

@ -0,0 +1,9 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [Synchronizer](index.md) / [clearedTransactions](./cleared-transactions.md)
# clearedTransactions
`abstract fun clearedTransactions(): ReceiveChannel<`[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`ClearedTransaction`](../../cash.z.wallet.sdk.entity/-cleared-transaction/index.md)`>>`
A stream of all the transactions that are on the blockchain. Implementations should consider only returning a
subset like the most recent 100 transactions, perhaps through paging the underlying database.

View File

@ -2,10 +2,10 @@
# getAddress
`abstract fun getAddress(accountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = 0): `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)
`abstract suspend fun getAddress(accountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = 0): `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)
Gets the address for the given account.
### Parameters
`accountId` - the optional accountId whose address of interest. By default, the first account is used.
`accountId` - the optional accountId whose address is of interest. By default, the first account is used.

View File

@ -0,0 +1,44 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [Synchronizer](./index.md)
# Synchronizer
`interface Synchronizer`
Primary interface for interacting with the SDK. Defines the contract that specific implementations like
[MockSynchronizer](../-mock-synchronizer/index.md) and [SdkSynchronizer](../-sdk-synchronizer/index.md) fulfill. Given the language-level support for coroutines, we favor their
use in the SDK and incorporate that choice into this contract.
### Properties
| Name | Summary |
|---|---|
| [isConnected](is-connected.md) | `abstract val isConnected: `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)<br>A flag indicating whether this Synchronizer is connected to its lightwalletd server. When false, a UI element may want to turn red. |
| [isScanning](is-scanning.md) | `abstract val isScanning: `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)<br>A flag indicating whether this Synchronizer is actively decrypting compact blocks, searching for transactions. When true, a UI element may want to turn yellow. |
| [isSyncing](is-syncing.md) | `abstract val isSyncing: `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)<br>A flag indicating whether this Synchronizer is actively downloading compact blocks. When true, a UI element may want to turn yellow. |
| [onCriticalErrorHandler](on-critical-error-handler.md) | `abstract var onCriticalErrorHandler: ((`[`Throwable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-throwable/index.html)`?) -> `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)`)?`<br>Gets or sets a global error handler. This is a useful hook for handling unexpected critical errors. |
| [onProcessorErrorHandler](on-processor-error-handler.md) | `abstract var onProcessorErrorHandler: ((`[`Throwable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-throwable/index.html)`?) -> `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)`)?`<br>An error handler for exceptions during processing. For instance, a block might be missing or a reorg may get mishandled or the database may get corrupted. |
| [onSubmissionErrorHandler](on-submission-error-handler.md) | `abstract var onSubmissionErrorHandler: ((`[`Throwable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-throwable/index.html)`?) -> `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)`)?`<br>An error handler for exceptions while submitting transactions to lightwalletd. For instance, a transaction may get rejected because it would be a double-spend or the user might lose their cellphone signal. |
### Functions
| Name | Summary |
|---|---|
| [balances](balances.md) | `abstract fun balances(): ReceiveChannel<`[`Wallet.WalletBalance`](../../cash.z.wallet.sdk.secure/-wallet/-wallet-balance/index.md)`>`<br>A stream of balance values, separately reflecting both the available and total balance. |
| [cancelSend](cancel-send.md) | `abstract fun cancelSend(transaction: `[`SentTransaction`](../../cash.z.wallet.sdk.entity/-sent-transaction/index.md)`): `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)<br>Attempts to cancel a previously sent transaction. Typically, cancellation is only an option if the transaction has not yet been submitted to the server. |
| [clearedTransactions](cleared-transactions.md) | `abstract fun clearedTransactions(): ReceiveChannel<`[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`ClearedTransaction`](../../cash.z.wallet.sdk.entity/-cleared-transaction/index.md)`>>`<br>A stream of all the transactions that are on the blockchain. Implementations should consider only returning a subset like the most recent 100 transactions, perhaps through paging the underlying database. |
| [getAddress](get-address.md) | `abstract suspend fun getAddress(accountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = 0): `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)<br>Gets the address for the given account. |
| [lastBalance](last-balance.md) | `abstract fun lastBalance(): `[`Wallet.WalletBalance`](../../cash.z.wallet.sdk.secure/-wallet/-wallet-balance/index.md)<br>Holds the most recent value that was transmitted through the [balances](balances.md) channel. Typically, if the underlying channel is a BroadcastChannel (and it should be), then this value is simply [balanceChannel.value](#) |
| [lastCleared](last-cleared.md) | `abstract fun lastCleared(): `[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`ClearedTransaction`](../../cash.z.wallet.sdk.entity/-cleared-transaction/index.md)`>`<br>Holds the most recent value that was transmitted through the [clearedTransactions](cleared-transactions.md) channel. Typically, if the underlying channel is a BroadcastChannel (and it should be), then this value is simply [clearedChannel.value](#) |
| [lastPending](last-pending.md) | `abstract fun lastPending(): `[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`PendingTransaction`](../../cash.z.wallet.sdk.entity/-pending-transaction/index.md)`>`<br>Holds the most recent value that was transmitted through the [pendingTransactions](pending-transactions.md) channel. Typically, if the underlying channel is a BroadcastChannel (and it should be),then this value is simply [pendingChannel.value](#) |
| [pendingTransactions](pending-transactions.md) | `abstract fun pendingTransactions(): ReceiveChannel<`[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`PendingTransaction`](../../cash.z.wallet.sdk.entity/-pending-transaction/index.md)`>>`<br>A stream of all the outbound pending transaction that have been sent but are awaiting confirmations. |
| [progress](progress.md) | `abstract fun progress(): ReceiveChannel<`[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)`>`<br>A stream of progress values, typically corresponding to this Synchronizer downloading blocks. Typically, any non- zero value below 100 indicates that progress indicators can be shown and a value of 100 signals that progress is complete and any progress indicators can be hidden. |
| [sendToAddress](send-to-address.md) | `abstract suspend fun sendToAddress(zatoshi: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)`, toAddress: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, memo: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)` = "", fromAccountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = 0): `[`PendingTransaction`](../../cash.z.wallet.sdk.entity/-pending-transaction/index.md)<br>Sends zatoshi. |
| [start](start.md) | `abstract fun start(parentScope: CoroutineScope): `[`Synchronizer`](./index.md)<br>Starts this synchronizer within the given scope. |
| [stop](stop.md) | `abstract fun stop(): `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html)<br>Stop this synchronizer. Implementations should ensure that calling this method cancels all jobs that were created by this instance. |
### Inheritors
| Name | Summary |
|---|---|
| [MockSynchronizer](../-mock-synchronizer/index.md) | `abstract class MockSynchronizer : `[`Synchronizer`](./index.md)`, CoroutineScope`<br>Utility for building UIs. It does the best it can to mock the SDKSynchronizer so that it can be dropped into any project and drive the UI. It generates active transactions in response to funds being sent and generates random received transactions, periodically. |
| [SdkSynchronizer](../-sdk-synchronizer/index.md) | `class SdkSynchronizer : `[`Synchronizer`](./index.md)<br>A synchronizer that attempts to remain operational, despite any number of errors that can occur. It acts as the glue that ties all the pieces of the SDK together. Each component of the SDK is designed for the potential of stand-alone usage but coordinating all the interactions is non-trivial. So the synchronizer facilitates this, acting as reference that demonstrates how all the pieces can be tied together. Its goal is to allow a developer to focus on their app rather than the nuances of how Zcash works. |

View File

@ -0,0 +1,9 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [Synchronizer](index.md) / [isConnected](./is-connected.md)
# isConnected
`abstract val isConnected: `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)
A flag indicating whether this Synchronizer is connected to its lightwalletd server. When false, a UI element
may want to turn red.

View File

@ -0,0 +1,9 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [Synchronizer](index.md) / [isScanning](./is-scanning.md)
# isScanning
`abstract val isScanning: `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)
A flag indicating whether this Synchronizer is actively decrypting compact blocks, searching for transactions.
When true, a UI element may want to turn yellow.

View File

@ -0,0 +1,9 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [Synchronizer](index.md) / [isSyncing](./is-syncing.md)
# isSyncing
`abstract val isSyncing: `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)
A flag indicating whether this Synchronizer is actively downloading compact blocks. When true, a UI element
may want to turn yellow.

View File

@ -0,0 +1,9 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [Synchronizer](index.md) / [lastBalance](./last-balance.md)
# lastBalance
`abstract fun lastBalance(): `[`Wallet.WalletBalance`](../../cash.z.wallet.sdk.secure/-wallet/-wallet-balance/index.md)
Holds the most recent value that was transmitted through the [balances](balances.md) channel. Typically, if the
underlying channel is a BroadcastChannel (and it should be), then this value is simply [balanceChannel.value](#)

View File

@ -0,0 +1,9 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [Synchronizer](index.md) / [lastCleared](./last-cleared.md)
# lastCleared
`abstract fun lastCleared(): `[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`ClearedTransaction`](../../cash.z.wallet.sdk.entity/-cleared-transaction/index.md)`>`
Holds the most recent value that was transmitted through the [clearedTransactions](cleared-transactions.md) channel. Typically, if the
underlying channel is a BroadcastChannel (and it should be), then this value is simply [clearedChannel.value](#)

View File

@ -0,0 +1,9 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [Synchronizer](index.md) / [lastPending](./last-pending.md)
# lastPending
`abstract fun lastPending(): `[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`PendingTransaction`](../../cash.z.wallet.sdk.entity/-pending-transaction/index.md)`>`
Holds the most recent value that was transmitted through the [pendingTransactions](pending-transactions.md) channel. Typically, if the
underlying channel is a BroadcastChannel (and it should be),then this value is simply [pendingChannel.value](#)

View File

@ -0,0 +1,12 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [Synchronizer](index.md) / [onCriticalErrorHandler](./on-critical-error-handler.md)
# onCriticalErrorHandler
`abstract var onCriticalErrorHandler: ((`[`Throwable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-throwable/index.html)`?) -> `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)`)?`
Gets or sets a global error handler. This is a useful hook for handling unexpected critical errors.
**Return**
true when the error has been handled and the Synchronizer should attempt to continue. False when the
error is unrecoverable and the Synchronizer should [stop](stop.md).

View File

@ -0,0 +1,13 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [Synchronizer](index.md) / [onProcessorErrorHandler](./on-processor-error-handler.md)
# onProcessorErrorHandler
`abstract var onProcessorErrorHandler: ((`[`Throwable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-throwable/index.html)`?) -> `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)`)?`
An error handler for exceptions during processing. For instance, a block might be missing or a reorg may get
mishandled or the database may get corrupted.
**Return**
true when the error has been handled and the processor should attempt to continue. False when the
error is unrecoverable and the processor should [stop](stop.md).

View File

@ -0,0 +1,13 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [Synchronizer](index.md) / [onSubmissionErrorHandler](./on-submission-error-handler.md)
# onSubmissionErrorHandler
`abstract var onSubmissionErrorHandler: ((`[`Throwable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-throwable/index.html)`?) -> `[`Boolean`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html)`)?`
An error handler for exceptions while submitting transactions to lightwalletd. For instance, a transaction may
get rejected because it would be a double-spend or the user might lose their cellphone signal.
**Return**
true when the error has been handled and the sender should attempt to resend. False when the
error is unrecoverable and the sender should [stop](stop.md).

View File

@ -0,0 +1,8 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.data](../index.md) / [Synchronizer](index.md) / [pendingTransactions](./pending-transactions.md)
# pendingTransactions
`abstract fun pendingTransactions(): ReceiveChannel<`[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)`<`[`PendingTransaction`](../../cash.z.wallet.sdk.entity/-pending-transaction/index.md)`>>`
A stream of all the outbound pending transaction that have been sent but are awaiting confirmations.

View File

@ -2,7 +2,7 @@
# sendToAddress
`abstract suspend fun sendToAddress(zatoshi: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)`, toAddress: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, memo: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)` = "", fromAccountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = 0): `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html)
`abstract suspend fun sendToAddress(zatoshi: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)`, toAddress: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, memo: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)` = "", fromAccountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = 0): `[`PendingTransaction`](../../cash.z.wallet.sdk.entity/-pending-transaction/index.md)
Sends zatoshi.

View File

@ -4,5 +4,6 @@
`abstract fun stop(): `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html)
Stop this synchronizer.
Stop this synchronizer. Implementations should ensure that calling this method cancels all jobs that were created
by this instance.

View File

@ -1,8 +0,0 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.secure](../index.md) / [Wallet](index.md) / [BIRTHDAY_DIRECTORY](./-b-i-r-t-h-d-a-y_-d-i-r-e-c-t-o-r-y.md)
# BIRTHDAY_DIRECTORY
`const val BIRTHDAY_DIRECTORY: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)
Directory within the assets folder where birthday data (i.e. sapling trees for a given height) can be found.

View File

@ -1,10 +0,0 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.secure](../index.md) / [Wallet](index.md) / [CLOUD_PARAM_DIR_URL](./-c-l-o-u-d_-p-a-r-a-m_-d-i-r_-u-r-l.md)
# CLOUD_PARAM_DIR_URL
`const val CLOUD_PARAM_DIR_URL: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)
The Url that is used by default in zcashd.
We'll want to make this externally configurable, rather than baking it into the SDK but this will do for now,
since we're using a cloudfront URL that already redirects.

View File

@ -1,12 +0,0 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.secure](../index.md) / [Wallet](index.md) / [&lt;init&gt;](./-init-.md)
# &lt;init&gt;
`Wallet(context: Context, converter: `[`JniConverter`](../../cash.z.wallet.sdk.jni/-jni-converter/index.md)`, dataDbPath: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, paramDestinationDir: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, accountIds: `[`Array`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-array/index.html)`<`[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)`> = arrayOf(0), seedProvider: `[`ReadOnlyProperty`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-read-only-property/index.html)`<`[`Any`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html)`?, `[`ByteArray`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-byte-array/index.html)`>, spendingKeyProvider: `[`ReadWriteProperty`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-read-write-property/index.html)`<`[`Any`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html)`?, `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`>)``Wallet(birthday: `[`Wallet.WalletBirthday`](-wallet-birthday/index.md)`, converter: `[`JniConverter`](../../cash.z.wallet.sdk.jni/-jni-converter/index.md)`, dataDbPath: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, paramDestinationDir: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, accountIds: `[`Array`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-array/index.html)`<`[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)`> = arrayOf(0), seedProvider: `[`ReadOnlyProperty`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-read-only-property/index.html)`<`[`Any`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html)`?, `[`ByteArray`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-byte-array/index.html)`>, spendingKeyProvider: `[`ReadWriteProperty`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-read-write-property/index.html)`<`[`Any`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html)`?, `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`>)`
Wrapper for the converter. This class basically represents all the Rust-wallet capabilities and the supporting data
required to exercise those abilities.
### Parameters
`birthday` - the birthday of this wallet. See [WalletBirthday](-wallet-birthday/index.md) for more info.

View File

@ -1,8 +0,0 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.secure](../index.md) / [Wallet](index.md) / [OUTPUT_PARAM_FILE_NAME](./-o-u-t-p-u-t_-p-a-r-a-m_-f-i-l-e_-n-a-m-e.md)
# OUTPUT_PARAM_FILE_NAME
`const val OUTPUT_PARAM_FILE_NAME: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)
File name for the sapling output params

View File

@ -1,8 +0,0 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.secure](../index.md) / [Wallet](index.md) / [SPEND_PARAM_FILE_NAME](./-s-p-e-n-d_-p-a-r-a-m_-f-i-l-e_-n-a-m-e.md)
# SPEND_PARAM_FILE_NAME
`const val SPEND_PARAM_FILE_NAME: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)
File name for the sappling spend params

View File

@ -1,17 +0,0 @@
[zcash-android-wallet-sdk](../../../index.md) / [cash.z.wallet.sdk.secure](../../index.md) / [Wallet](../index.md) / [WalletBalance](index.md) / [&lt;init&gt;](./-init-.md)
# &lt;init&gt;
`WalletBalance(total: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)` = -1, available: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)` = -1)`
Data structure to hold the total and available balance of the wallet. This is what is received on the balance
channel.
### Parameters
`total` - the total balance, ignoring funds that cannot be used.
`available` - the amount of funds that are available for use. Typical reasons that funds may be unavailable
include fairly new transactions that do not have enough confirmations or notes that are tied up because we are
awaiting change from a transaction. When a note has been spent, its change cannot be used until there are enough
confirmations.

View File

@ -1,30 +0,0 @@
[zcash-android-wallet-sdk](../../../index.md) / [cash.z.wallet.sdk.secure](../../index.md) / [Wallet](../index.md) / [WalletBalance](./index.md)
# WalletBalance
`data class WalletBalance`
Data structure to hold the total and available balance of the wallet. This is what is received on the balance
channel.
### Parameters
`total` - the total balance, ignoring funds that cannot be used.
`available` - the amount of funds that are available for use. Typical reasons that funds may be unavailable
include fairly new transactions that do not have enough confirmations or notes that are tied up because we are
awaiting change from a transaction. When a note has been spent, its change cannot be used until there are enough
confirmations.
### Constructors
| Name | Summary |
|---|---|
| [&lt;init&gt;](-init-.md) | `WalletBalance(total: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)` = -1, available: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)` = -1)`<br>Data structure to hold the total and available balance of the wallet. This is what is received on the balance channel. |
### Properties
| Name | Summary |
|---|---|
| [available](available.md) | `val available: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)<br>the amount of funds that are available for use. Typical reasons that funds may be unavailable include fairly new transactions that do not have enough confirmations or notes that are tied up because we are awaiting change from a transaction. When a note has been spent, its change cannot be used until there are enough confirmations. |
| [total](total.md) | `val total: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)<br>the total balance, ignoring funds that cannot be used. |

View File

@ -1,11 +0,0 @@
[zcash-android-wallet-sdk](../../../index.md) / [cash.z.wallet.sdk.secure](../../index.md) / [Wallet](../index.md) / [WalletBalance](index.md) / [available](./available.md)
# available
`val available: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)
the amount of funds that are available for use. Typical reasons that funds may be unavailable
include fairly new transactions that do not have enough confirmations or notes that are tied up because we are
awaiting change from a transaction. When a note has been spent, its change cannot be used until there are enough
confirmations.

View File

@ -1,8 +0,0 @@
[zcash-android-wallet-sdk](../../../index.md) / [cash.z.wallet.sdk.secure](../../index.md) / [Wallet](../index.md) / [WalletBalance](index.md) / [total](./total.md)
# total
`val total: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)
the total balance, ignoring funds that cannot be used.

View File

@ -1,32 +0,0 @@
[zcash-android-wallet-sdk](../../../index.md) / [cash.z.wallet.sdk.secure](../../index.md) / [Wallet](../index.md) / [WalletBirthday](index.md) / [&lt;init&gt;](./-init-.md)
# &lt;init&gt;
`WalletBirthday(height: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = -1, time: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)` = -1, tree: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)` = "")`
Represents the wallet's birthday which can be thought of as a checkpoint at the earliest moment in history where
transactions related to this wallet could exist. Ideally, this would correspond to the latest block height at the
time the wallet key was created. Worst case, the height of Sapling activation could be used (280000).
Knowing a wallet's birthday can significantly reduce the amount of data that it needs to download because none of
the data before that height needs to be scanned for transactions. However, we do need the Sapling tree data in
order to construct valid transactions from that point forward. This birthday contains that tree data, allowing us
to avoid downloading all the compact blocks required in order to generate it.
Currently, the data for this is generated by running `cargo run --release --features=updater` with the SDK and
saving the resulting JSON to the `src/main/assets/zcash` folder. That script simply builds a Sapling tree from
the start of Sapling activation up to the latest block height. In the future, this data could be exposed as a
service on the lightwalletd server because every zcashd node already maintains the sapling tree for each block.
For now, we just include the latest checkpoint in each SDK release.
New wallets can ignore any blocks created before their birthday.
### Parameters
`height` - the height at the time the wallet was born
`time` - the time the wallet was born, in seconds
`tree` - the sapling tree corresponding to the given height. This takes around 15 minutes of processing to
generate from scratch because all blocks since activation need to be considered. So when it is calculated in
advance it can save the user a lot of time.

View File

@ -1,46 +0,0 @@
[zcash-android-wallet-sdk](../../../index.md) / [cash.z.wallet.sdk.secure](../../index.md) / [Wallet](../index.md) / [WalletBirthday](./index.md)
# WalletBirthday
`data class WalletBirthday`
Represents the wallet's birthday which can be thought of as a checkpoint at the earliest moment in history where
transactions related to this wallet could exist. Ideally, this would correspond to the latest block height at the
time the wallet key was created. Worst case, the height of Sapling activation could be used (280000).
Knowing a wallet's birthday can significantly reduce the amount of data that it needs to download because none of
the data before that height needs to be scanned for transactions. However, we do need the Sapling tree data in
order to construct valid transactions from that point forward. This birthday contains that tree data, allowing us
to avoid downloading all the compact blocks required in order to generate it.
Currently, the data for this is generated by running `cargo run --release --features=updater` with the SDK and
saving the resulting JSON to the `src/main/assets/zcash` folder. That script simply builds a Sapling tree from
the start of Sapling activation up to the latest block height. In the future, this data could be exposed as a
service on the lightwalletd server because every zcashd node already maintains the sapling tree for each block.
For now, we just include the latest checkpoint in each SDK release.
New wallets can ignore any blocks created before their birthday.
### Parameters
`height` - the height at the time the wallet was born
`time` - the time the wallet was born, in seconds
`tree` - the sapling tree corresponding to the given height. This takes around 15 minutes of processing to
generate from scratch because all blocks since activation need to be considered. So when it is calculated in
advance it can save the user a lot of time.
### Constructors
| Name | Summary |
|---|---|
| [&lt;init&gt;](-init-.md) | `WalletBirthday(height: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = -1, time: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)` = -1, tree: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)` = "")`<br>Represents the wallet's birthday which can be thought of as a checkpoint at the earliest moment in history where transactions related to this wallet could exist. Ideally, this would correspond to the latest block height at the time the wallet key was created. Worst case, the height of Sapling activation could be used (280000). |
### Properties
| Name | Summary |
|---|---|
| [height](height.md) | `val height: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)<br>the height at the time the wallet was born |
| [time](time.md) | `val time: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)<br>the time the wallet was born, in seconds |
| [tree](tree.md) | `val tree: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)<br>the sapling tree corresponding to the given height. This takes around 15 minutes of processing to generate from scratch because all blocks since activation need to be considered. So when it is calculated in advance it can save the user a lot of time. |

View File

@ -1,8 +0,0 @@
[zcash-android-wallet-sdk](../../../index.md) / [cash.z.wallet.sdk.secure](../../index.md) / [Wallet](../index.md) / [WalletBirthday](index.md) / [height](./height.md)
# height
`val height: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)
the height at the time the wallet was born

View File

@ -1,8 +0,0 @@
[zcash-android-wallet-sdk](../../../index.md) / [cash.z.wallet.sdk.secure](../../index.md) / [Wallet](../index.md) / [WalletBirthday](index.md) / [time](./time.md)
# time
`val time: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)
the time the wallet was born, in seconds

View File

@ -1,10 +0,0 @@
[zcash-android-wallet-sdk](../../../index.md) / [cash.z.wallet.sdk.secure](../../index.md) / [Wallet](../index.md) / [WalletBirthday](index.md) / [tree](./tree.md)
# tree
`val tree: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)
the sapling tree corresponding to the given height. This takes around 15 minutes of processing to
generate from scratch because all blocks since activation need to be considered. So when it is calculated in
advance it can save the user a lot of time.

View File

@ -1,51 +0,0 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.secure](../index.md) / [Wallet](./index.md)
# Wallet
`class Wallet`
Wrapper for the converter. This class basically represents all the Rust-wallet capabilities and the supporting data
required to exercise those abilities.
### Parameters
`birthday` - the birthday of this wallet. See [WalletBirthday](-wallet-birthday/index.md) for more info.
### Types
| Name | Summary |
|---|---|
| [WalletBalance](-wallet-balance/index.md) | `data class WalletBalance`<br>Data structure to hold the total and available balance of the wallet. This is what is received on the balance channel. |
| [WalletBirthday](-wallet-birthday/index.md) | `data class WalletBirthday`<br>Represents the wallet's birthday which can be thought of as a checkpoint at the earliest moment in history where transactions related to this wallet could exist. Ideally, this would correspond to the latest block height at the time the wallet key was created. Worst case, the height of Sapling activation could be used (280000). |
### Constructors
| Name | Summary |
|---|---|
| [&lt;init&gt;](-init-.md) | `Wallet(context: Context, converter: `[`JniConverter`](../../cash.z.wallet.sdk.jni/-jni-converter/index.md)`, dataDbPath: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, paramDestinationDir: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, accountIds: `[`Array`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-array/index.html)`<`[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)`> = arrayOf(0), seedProvider: `[`ReadOnlyProperty`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-read-only-property/index.html)`<`[`Any`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html)`?, `[`ByteArray`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-byte-array/index.html)`>, spendingKeyProvider: `[`ReadWriteProperty`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-read-write-property/index.html)`<`[`Any`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html)`?, `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`>)``Wallet(birthday: `[`Wallet.WalletBirthday`](-wallet-birthday/index.md)`, converter: `[`JniConverter`](../../cash.z.wallet.sdk.jni/-jni-converter/index.md)`, dataDbPath: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, paramDestinationDir: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, accountIds: `[`Array`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-array/index.html)`<`[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)`> = arrayOf(0), seedProvider: `[`ReadOnlyProperty`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-read-only-property/index.html)`<`[`Any`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html)`?, `[`ByteArray`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-byte-array/index.html)`>, spendingKeyProvider: `[`ReadWriteProperty`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-read-write-property/index.html)`<`[`Any`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html)`?, `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`>)`<br>Wrapper for the converter. This class basically represents all the Rust-wallet capabilities and the supporting data required to exercise those abilities. |
### Functions
| Name | Summary |
|---|---|
| [balance](balance.md) | `fun balance(): ReceiveChannel<`[`Wallet.WalletBalance`](-wallet-balance/index.md)`>`<br>Stream of balances. |
| [createRawSendTransaction](create-raw-send-transaction.md) | `suspend fun createRawSendTransaction(value: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)`, toAddress: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, memo: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)` = "", fromAccountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = accountIds[0]): `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)<br>Does the proofs and processing required to create a raw transaction and inserts the result in the database. On average, this call takes over 10 seconds. |
| [fetchParams](fetch-params.md) | `suspend fun fetchParams(destinationDir: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`): `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html)<br>Download and store the params into the given directory. |
| [getAddress](get-address.md) | `fun getAddress(accountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = accountIds[0]): `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)<br>Gets the address for this wallet, defaulting to the first account. |
| [initialize](initialize.md) | `fun initialize(firstRunStartHeight: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = SAPLING_ACTIVATION_HEIGHT): `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)<br>Initializes the wallet by creating the DataDb and pre-populating it with data corresponding to the birthday for this wallet. |
| [sendBalanceInfo](send-balance-info.md) | `suspend fun sendBalanceInfo(accountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = accountIds[0]): `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html)<br>Calculates the latest balance info and emits it into the balance channel. Defaults to the first account. |
### Companion Object Properties
| Name | Summary |
|---|---|
| [BIRTHDAY_DIRECTORY](-b-i-r-t-h-d-a-y_-d-i-r-e-c-t-o-r-y.md) | `const val BIRTHDAY_DIRECTORY: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)<br>Directory within the assets folder where birthday data (i.e. sapling trees for a given height) can be found. |
| [CLOUD_PARAM_DIR_URL](-c-l-o-u-d_-p-a-r-a-m_-d-i-r_-u-r-l.md) | `const val CLOUD_PARAM_DIR_URL: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)<br>The Url that is used by default in zcashd. We'll want to make this externally configurable, rather than baking it into the SDK but this will do for now, since we're using a cloudfront URL that already redirects. |
| [OUTPUT_PARAM_FILE_NAME](-o-u-t-p-u-t_-p-a-r-a-m_-f-i-l-e_-n-a-m-e.md) | `const val OUTPUT_PARAM_FILE_NAME: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)<br>File name for the sapling output params |
| [SPEND_PARAM_FILE_NAME](-s-p-e-n-d_-p-a-r-a-m_-f-i-l-e_-n-a-m-e.md) | `const val SPEND_PARAM_FILE_NAME: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)<br>File name for the sappling spend params |
### Companion Object Functions
| Name | Summary |
|---|---|
| [loadBirthdayFromAssets](load-birthday-from-assets.md) | `fun loadBirthdayFromAssets(context: Context, birthdayHeight: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)`? = null): `[`Wallet.WalletBirthday`](-wallet-birthday/index.md)<br>Load the given birthday file from the assets of the given context. When no height is specified, we default to the file with the greatest name. |

View File

@ -1,8 +0,0 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.secure](../index.md) / [Wallet](index.md) / [balance](./balance.md)
# balance
`fun balance(): ReceiveChannel<`[`Wallet.WalletBalance`](-wallet-balance/index.md)`>`
Stream of balances.

View File

@ -1,20 +0,0 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.secure](../index.md) / [Wallet](index.md) / [createRawSendTransaction](./create-raw-send-transaction.md)
# createRawSendTransaction
`suspend fun createRawSendTransaction(value: `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)`, toAddress: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`, memo: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)` = "", fromAccountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = accountIds[0]): `[`Long`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/index.html)
Does the proofs and processing required to create a raw transaction and inserts the result in the database. On
average, this call takes over 10 seconds.
### Parameters
`value` - the zatoshi value to send
`toAddress` - the destination address
`memo` - the memo, which is not augmented in any way
**Return**
the row id in the transactions table that contains the raw transaction or -1 if it failed

View File

@ -1,13 +0,0 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.secure](../index.md) / [Wallet](index.md) / [fetchParams](./fetch-params.md)
# fetchParams
`suspend fun fetchParams(destinationDir: `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)`): `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html)
Download and store the params into the given directory.
### Parameters
`destinationDir` - the directory where the params will be stored. It's assumed that we have write access to
this directory. Typically, this should be the app's cache directory because it is not harmful if these files are
cleared by the user since they are downloaded on-demand.

View File

@ -1,8 +0,0 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.secure](../index.md) / [Wallet](index.md) / [getAddress](./get-address.md)
# getAddress
`fun getAddress(accountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = accountIds[0]): `[`String`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)
Gets the address for this wallet, defaulting to the first account.

View File

@ -1,9 +0,0 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.secure](../index.md) / [Wallet](index.md) / [initialize](./initialize.md)
# initialize
`fun initialize(firstRunStartHeight: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = SAPLING_ACTIVATION_HEIGHT): `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)
Initializes the wallet by creating the DataDb and pre-populating it with data corresponding to the birthday for
this wallet.

View File

@ -1,18 +0,0 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.secure](../index.md) / [Wallet](index.md) / [loadBirthdayFromAssets](./load-birthday-from-assets.md)
# loadBirthdayFromAssets
`fun loadBirthdayFromAssets(context: Context, birthdayHeight: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)`? = null): `[`Wallet.WalletBirthday`](-wallet-birthday/index.md)
Load the given birthday file from the assets of the given context. When no height is specified, we default to
the file with the greatest name.
### Parameters
`context` - the context from which to load assets.
`birthdayHeight` - the height file to look for among the file names.
**Return**
a WalletBirthday that reflects the contents of the file or an exception when parsing fails.

View File

@ -1,11 +0,0 @@
[zcash-android-wallet-sdk](../../index.md) / [cash.z.wallet.sdk.secure](../index.md) / [Wallet](index.md) / [sendBalanceInfo](./send-balance-info.md)
# sendBalanceInfo
`suspend fun sendBalanceInfo(accountId: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)` = accountIds[0]): `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html)
Calculates the latest balance info and emits it into the balance channel. Defaults to the first account.
### Parameters
`accountId` - the account to check for balance info.

View File

@ -1,6 +1,6 @@
#Fri Jun 07 02:04:27 EDT 2019
#Wed Sep 11 23:39:49 EDT 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip

View File

@ -3,11 +3,11 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 28
compileSdkVersion 29
defaultConfig {
applicationId "cash.z.wallet.sdk.sample.address"
minSdkVersion 21
targetSdkVersion 28
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -23,11 +23,11 @@ android {
}
dependencies {
compile project(path: ':sdk')
implementation project(path: ':sdk')
// api project(path: ':sdk', configuration: 'default')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.2'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutines}"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutines}"

View File

@ -1,15 +1,14 @@
buildscript {
ext.versions = [
'kotlin': '1.3.31',
'coroutines': '1.3.0-M1'
'kotlin': '1.3.50',
'coroutines': '1.3.0-M1'
]
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0-beta04'
classpath 'com.android.tools.build:gradle:3.6.0-alpha11'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
}
}

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip

View File

@ -5,11 +5,11 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 28
compileSdkVersion 29
defaultConfig {
applicationId "cash.z.wallet.sdk.sample.memo"
minSdkVersion 21
targetSdkVersion 28
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -27,13 +27,13 @@ android {
dependencies {
implementation project(path: ':sdk', configuration: 'default')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.2'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutines}"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutines}"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
compile project(path: ':sdk')
implementation project(path: ':sdk')
}

View File

@ -6,6 +6,8 @@ import cash.z.wallet.sdk.block.CompactBlockDownloader
import cash.z.wallet.sdk.block.CompactBlockProcessor
import cash.z.wallet.sdk.block.ProcessorConfig
import cash.z.wallet.sdk.data.*
import cash.z.wallet.sdk.ext.SampleSeedProvider
import cash.z.wallet.sdk.ext.SimpleProvider
import cash.z.wallet.sdk.jni.RustBackend
import cash.z.wallet.sdk.secure.Wallet
import cash.z.wallet.sdk.service.LightWalletGrpcService
@ -18,12 +20,21 @@ object Injection {
private val rustBackend = RustBackend()
fun provideSynchronizer(appContext: Context): Synchronizer {
val config = ProcessorConfig(cacheDbName.toDbPath(appContext), dataDbName.toDbPath(appContext), downloadBatchSize = 1_000) // settings
val service = LightWalletGrpcService(appContext, host, port) // connects to lightwalletd
val blockStore = CompactBlockDbStore(appContext, cacheDbName) // enables compact block storage in cache
val downloader = CompactBlockDownloader(service, blockStore) // downloads blocks an puts them in storage
val repository = PollingTransactionRepository(appContext, dataDbName, rustBackend) // provides access to txs
val processor = CompactBlockProcessor(config, downloader, repository, rustBackend) // decrypts compact blocks
// ledger
val ledger = PollingTransactionRepository(appContext, dataDbName)
// sender
val manager = PersistentTransactionManager(appContext)
val service = LightWalletGrpcService(appContext, host, port)
val sender = PersistentTransactionSender(manager, service, ledger)
// processor
val config = ProcessorConfig(cacheDbName.toDbPath(appContext), dataDbName.toDbPath(appContext))
val blockStore = CompactBlockDbStore(appContext, cacheDbName)
val downloader = CompactBlockDownloader(service, blockStore)
val processor = CompactBlockProcessor(config, downloader, ledger, rustBackend)
// wrapper for rustbackend
val wallet = Wallet(
context = appContext,
@ -33,10 +44,18 @@ object Injection {
seedProvider = SampleSeedProvider("testreferencecarol"),
spendingKeyProvider = SimpleProvider("dummyValue")
)
val activeTransactionManager = ActiveTransactionManager(repository, service, wallet) // monitors active txs
// Encoder
val encoder = WalletTransactionEncoder(wallet, ledger)
// ties everything together
return SdkSynchronizer(processor, repository, activeTransactionManager, wallet)
return SdkSynchronizer(
wallet,
ledger,
sender,
processor,
encoder
)
}
private fun String.toDbPath(context: Context): String {

View File

@ -11,6 +11,7 @@ import cash.z.wallet.sdk.secure.Wallet
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.launch
import kotlin.properties.Delegates
@ -20,13 +21,11 @@ import kotlin.reflect.KProperty
class MainActivity : ScopedActivity() {
private lateinit var synchronizer: Synchronizer
private var progressJob: Job? = null
private var balanceJob: Job? = null
private var activeTransaction: TransactionInfo = TransactionInfo()
private var loaded: Boolean by observable(false) {_, old: Boolean, new: Boolean ->
if (!old && new) {
launch {
onBalance(synchronizer.getBalance())
onBalance(synchronizer.lastBalance())
}
}
}
@ -40,23 +39,14 @@ class MainActivity : ScopedActivity() {
synchronizer = Injection.provideSynchronizer(this.applicationContext)
synchronizer.start(this)
}
override fun onResume() {
super.onResume()
progressJob = launchProgressMonitor(synchronizer.progress())
balanceJob = launchBalanceMonitor(synchronizer.balances())
}
override fun onPause() {
super.onPause()
progressJob?.cancel().also { progressJob = null }
balanceJob?.cancel().also { balanceJob = null }
launchProgressMonitor(synchronizer.progress())
launchBalanceMonitor(synchronizer.balances())
}
override fun onDestroy() {
super.onDestroy()
cancel()
synchronizer.stop()
}
@ -66,11 +56,11 @@ class MainActivity : ScopedActivity() {
}
}
private fun CoroutineScope.launchTransactionMonitor(channel: ReceiveChannel<Map<ActiveTransaction, TransactionState>>) = launch {
for (i in channel) {
onUpdate(i)
}
}
// private fun CoroutineScope.launchTransactionMonitor(channel: ReceiveChannel<Map<ActiveTransaction, TransactionState>>) = launch {
// for (i in channel) {
// onUpdate(i)
// }
// }
private fun CoroutineScope.launchBalanceMonitor(channel: ReceiveChannel<Wallet.WalletBalance>) = launch {
for (i in channel) {
@ -98,23 +88,23 @@ class MainActivity : ScopedActivity() {
}
}
private fun onUpdate(transactions: Map<ActiveTransaction, TransactionState>) {
if (transactions.isNotEmpty()) {
// primary is the last one that was inserted
val primaryEntry =
transactions.entries.toTypedArray()[transactions.size - 1]
updatePrimaryTransaction(primaryEntry.key, primaryEntry.value)
}
}
// private fun onUpdate(transactions: Map<ActiveTransaction, TransactionState>) {
// if (transactions.isNotEmpty()) {
// // primary is the last one that was inserted
// val primaryEntry =
// transactions.entries.toTypedArray()[transactions.size - 1]
// updatePrimaryTransaction(primaryEntry.key, primaryEntry.value)
// }
// }
private fun updatePrimaryTransaction(transaction: ActiveTransaction,
transactionState: TransactionState) {
val status = transactionState.toString()
text_status.text = "Memo Sent!\nAwaiting confirmation...\nstatus: $status"
}
// private fun updatePrimaryTransaction(transaction: ActiveTransaction,
// transactionState: TransactionState) {
// val status = transactionState.toString()
// text_status.text = "Memo Sent!\nAwaiting confirmation...\nstatus: $status"
// }
fun onSendMemo(v: View) {
launchTransactionMonitor(synchronizer.activeTransactions())
// launchTransactionMonitor(synchronizer.activeTransactions())
activeTransaction = TransactionInfo(memo = input_memo.text.toString())

View File

@ -1,15 +1,14 @@
buildscript {
ext.versions = [
'kotlin': '1.3.31',
'kotlin': '1.3.50',
'coroutines': '1.3.0-M1'
]
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0-beta04'
classpath 'com.android.tools.build:gradle:3.6.0-alpha11'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
}
}
@ -18,7 +17,6 @@ allprojects {
repositories {
google()
jcenter()
}
}

View File

@ -1,6 +1,6 @@
#Thu Jun 20 02:36:23 EDT 2019
#Wed Sep 11 21:21:29 EDT 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip

View File

@ -6,7 +6,6 @@ import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.test.core.app.ApplicationProvider
import cash.z.wallet.sdk.entity.CompactBlock
import cash.z.wallet.sdk.ext.toBlockHeight
import cash.z.wallet.sdk.jni.RustBackend
import cash.z.wallet.sdk.jni.RustBackendWelding
import cash.z.wallet.sdk.rpc.CompactTxStreamerGrpc
@ -42,8 +41,8 @@ class GlueIntegrationTest {
private fun addData() {
val result = blockingStub.getBlockRange(
BlockRange.newBuilder()
.setStart(373070.toBlockHeight())
.setEnd(373085.toBlockHeight())
.setStart(BlockID.newBuilder().setHeight(373070L).build())
.setEnd(BlockID.newBuilder().setHeight(373085L).build())
.build()
)
while (result.hasNext()) {

View File

@ -6,7 +6,6 @@ import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.test.core.app.ApplicationProvider
import cash.z.wallet.sdk.entity.CompactBlock
import cash.z.wallet.sdk.ext.toBlockHeight
import cash.z.wallet.sdk.jni.RustBackend
import cash.z.wallet.sdk.jni.RustBackendWelding
import cash.z.wallet.sdk.rpc.CompactTxStreamerGrpc
@ -44,8 +43,8 @@ class GlueSetupIntegrationTest {
private fun addData() {
val result = blockingStub.getBlockRange(
BlockRange.newBuilder()
.setStart(373070.toBlockHeight())
.setEnd(373085.toBlockHeight())
.setStart(BlockID.newBuilder().setHeight(373070L).build())
.setEnd(BlockID.newBuilder().setHeight(373085L).build())
.build()
)
while (result.hasNext()) {

View File

@ -71,7 +71,7 @@ class IntegrationTest {
)
// repository.start(this)
// synchronizer = StableSynchronizer(wallet, repository, , processor)
// synchronizer = SdkSynchronizer(wallet, repository, , processor)
// processor,
// repository,
// ActiveTransactionManager(repository, lightwalletService, wallet),

View File

@ -1,6 +1,5 @@
package cash.z.wallet.sdk.jni
import android.text.format.DateUtils
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.BeforeClass
@ -38,7 +37,7 @@ class RustBackendTest {
@Test
fun testSend() {
rustBackend.sendToAddress(
rustBackend.createToAddress(
"/data/user/0/cash.z.wallet.sdk.test/databases/data2.db",
0,
"dummykey",
@ -52,12 +51,6 @@ class RustBackendTest {
companion object {
val rustBackend: RustBackendWelding = RustBackend()
@BeforeClass
@JvmStatic
fun setup() {
rustBackend.initLogs()
}
}
}

View File

@ -13,10 +13,14 @@ import kotlinx.coroutines.Dispatchers.IO
* Repository that does polling for simplicity. We will implement an alternative version that uses live data as well as
* one that creates triggers and then reference them here. For now this is the most basic example of keeping track of
* changes.
*
* @param limit the max number of transactions to return when polling for changes. After a wallet has been a round for a
* long time, returning ALL transactions might be overkill. Instead, it may be more efficient to only return a limited
* number of transactions, like the most recent transaction that has changed.
*/
open class PollingTransactionRepository(
private val derivedDataDb: DerivedDataDb,
private val pollFrequencyMillis: Long = 2000L,
private val pollFrequencyMillis: Long = DEFAULT_TX_POLL_INTERVAL_MS,
private val limit: Int = Int.MAX_VALUE
) : TransactionRepository {
@ -26,7 +30,7 @@ open class PollingTransactionRepository(
constructor(
context: Context,
dataDbName: String,
pollFrequencyMillis: Long = 2000L
pollFrequencyMillis: Long = DEFAULT_TX_POLL_INTERVAL_MS
) : this(
Room.databaseBuilder(context, DerivedDataDb::class.java, dataDbName)
.setJournalMode(RoomDatabase.JournalMode.TRUNCATE)
@ -89,7 +93,7 @@ open class PollingTransactionRepository(
twig("Notifying listener that changes have been detected in transactions!")
listener.invoke()
} else {
twig("No changes detected in transactions.")
//twig("No changes detected in transactions.")
}
delay(pollFrequencyMillis)
}
@ -101,6 +105,9 @@ open class PollingTransactionRepository(
derivedDataDb.close()
}
companion object {
const val DEFAULT_TX_POLL_INTERVAL_MS = 5_000L
}
}
/**

View File

@ -1,305 +0,0 @@
// This has been replaced by "StableSynchronizer" We keep it around for the docs
//package cash.z.wallet.sdk.data
//
//import cash.z.wallet.sdk.block.CompactBlockProcessor
//import cash.z.wallet.sdk.entity.ClearedTransaction
//import cash.z.wallet.sdk.exception.SynchronizerException
//import cash.z.wallet.sdk.exception.WalletException
//import cash.z.wallet.sdk.secure.Wallet
//import kotlinx.coroutines.*
//import kotlinx.coroutines.Dispatchers.IO
//import kotlinx.coroutines.channels.ConflatedBroadcastChannel
//import kotlinx.coroutines.channels.ReceiveChannel
//import kotlinx.coroutines.channels.distinct
//import kotlin.coroutines.CoroutineContext
//
///**
// * The glue. Downloads compact blocks to the database and then scans them for transactions. In order to serve that
// * purpose, this class glues together a variety of key components. Each component contributes to the team effort of
// * providing a simple source of truth to interact with.
// *
// * Another way of thinking about this class is the reference that demonstrates how all the pieces can be tied
// * together.
// *
// * @param processor the component that saves the downloaded compact blocks to the cache and then scans those blocks for
// * data related to this wallet.
// * @param repository the component that exposes streams of wallet transaction information.
// * @param activeTransactionManager the component that manages the lifecycle of active transactions. This includes sent
// * transactions that have not been mined.
// * @param wallet the component that wraps the JNI layer that interacts with librustzcash and manages wallet config.
// * @param batchSize the number of compact blocks to download at a time.
// * @param staleTolerance the number of blocks to allow before considering our data to be stale
// * @param blockPollFrequency how often to poll for compact blocks. Once all missing blocks have been downloaded, this
// * number represents the number of milliseconds the synchronizer will wait before checking for newly mined blocks.
// */
//class SdkSynchronizer(
// private val processor: CompactBlockProcessor,
// private val repository: TransactionRepository,
// private val activeTransactionManager: ActiveTransactionManager,
// private val wallet: Wallet,
// private val staleTolerance: Int = 10
//) : Synchronizer {
//
// /**
// * The primary job for this Synchronizer. It leverages structured concurrency to cancel all work when the
// * `parentScope` provided to the [start] method ends.
// */
// private lateinit var blockJob: Job
//
// /**
// * The state this Synchronizer was in when it started. This is helpful because the conditions that lead to FirstRun
// * or isStale being detected can change quickly so retaining the initial state is useful for walkthroughs or other
// * elements of an app that need to rely on this information later, rather than in realtime.
// */
// private lateinit var initialState: SyncState
//
// /**
// * Returns true when `start` has been called on this synchronizer.
// */
// private val wasPreviouslyStarted
// get() = ::blockJob.isInitialized
//
// /**
// * Retains the error that caused this synchronizer to fail for future error handling or reporting.
// */
// private var failure: Throwable? = null
//
// /**
// * The default exception handler for the block job. Calls [onException].
// */
// private val exceptionHandler: (c: CoroutineContext, t: Throwable) -> Unit = { _, t -> onException(t) }
//
// /**
// * Sets a listener to be notified of uncaught Synchronizer errors. When null, errors will only be logged.
// */
// override var onSynchronizerErrorListener: ((Throwable?) -> Boolean)? = null
// set(value) {
// field = value
// if (failure != null) value?.invoke(failure)
// }
//
// /**
// * Channel of transactions from the repository.
// */
// private val transactionChannel = ConflatedBroadcastChannel<List<ClearedTransaction>>()
//
// /**
// * Channel of balance information.
// */
// private val balanceChannel = ConflatedBroadcastChannel<Wallet.WalletBalance>()
//
// //
// // Public API
// //
//
// /* Lifecycle */
//
// /**
// * Starts this synchronizer within the given scope. For simplicity, attempting to start an instance that has already
// * been started will throw a [SynchronizerException.FalseStart] exception. This reduces the complexity of managing
// * resources that must be recycled. Instead, each synchronizer is designed to have a long lifespan and should be
// * started from an activity, application or session.
// *
// * @param parentScope the scope to use for this synchronizer, typically something with a lifecycle such as an
// * Activity for single-activity apps or a logged in user session. This scope is only used for launching this
// * synchronzer's job as a child.
// */
// override fun start(parentScope: CoroutineScope): Synchronizer {
// // prevent restarts so the behavior of this class is easier to reason about
// if (wasPreviouslyStarted) throw SynchronizerException.FalseStart
// twig("starting")
// failure = null
// blockJob = parentScope.launch(CoroutineExceptionHandler(exceptionHandler)) {
// supervisorScope {
// try {
// wallet.initialize()
// } catch (e: WalletException.AlreadyInitializedException) {
// twig("Warning: wallet already initialized but this is safe to ignore " +
// "because the SDK now automatically detects where to start downloading.")
// }
// onReady()
// }
// }
// return this
// }
//
// /**
// * Stops this synchronizer by stopping the downloader, repository, and activeTransactionManager, then cancelling the
// * parent job. Note that we do not cancel the parent scope that was passed into [start] because the synchronizer
// * does not own that scope, it just uses it for launching children.
// */
// override fun stop() {
// twig("stopping")
// (repository as? PollingTransactionRepository)?.stop().also { twig("repository stopped") }
// activeTransactionManager.stop().also { twig("activeTransactionManager stopped") }
// // TODO: investigate whether this is necessary and remove or improve, accordingly
// Thread.sleep(5000L)
// blockJob.cancel().also { twig("blockJob cancelled") }
// }
//
//
// /* Channels */
//
// /**
// * A stream of all the wallet transactions, delegated to the [activeTransactionManager].
// */
// override fun activeTransactions() = activeTransactionManager.subscribe()
//
// /**
// * A stream of all the wallet transactions, delegated to the [repository].
// */
// override fun allTransactions(): ReceiveChannel<List<ClearedTransaction>> {
// return transactionChannel.openSubscription()
// }
//
// /**
// * A stream of progress values, corresponding to this Synchronizer downloading blocks, delegated to the
// * [downloader]. Any non-zero value below 100 indicates that progress indicators can be shown and a value of 100
// * signals that progress is complete and any progress indicators can be hidden. At that point, the synchronizer
// * switches from catching up on missed blocks to periodically monitoring for newly mined blocks.
// */
// override fun progress(): ReceiveChannel<Int> {
// return processor.progress()
// }
//
// /**
// * A stream of balance values, delegated to the [wallet].
// */
// override fun balances(): ReceiveChannel<Wallet.WalletBalance> {
// return balanceChannel.openSubscription()
// }
//
//
// /* Status */
//
// /**
// * A flag to indicate that this Synchronizer is significantly out of sync with it's server. This is determined by
// * the delta between the current block height reported by the server and the latest block we have stored in cache.
// * Whenever this delta is greater than the [staleTolerance], this function returns true. This is intended for
// * showing progress indicators when the user returns to the app after having not used it for a long period.
// * Typically, this means the user may have to wait for downloading to occur and the current balance and transaction
// * information cannot be trusted as 100% accurate.
// *
// * @return true when the local data is significantly out of sync with the remote server and the app data is stale.
// */
// override suspend fun isStale(): Boolean = withContext(IO) {
// val latestBlockHeight = processor.downloader.getLatestBlockHeight()
// val ourHeight = processor.downloader.getLastDownloadedHeight()
// val tolerance = staleTolerance
// val delta = latestBlockHeight - ourHeight
// twig("checking whether out of sync. " +
// "LatestHeight: $latestBlockHeight ourHeight: $ourHeight Delta: $delta tolerance: $tolerance")
// delta > tolerance
// }
//
// /* Operations */
//
// /**
// * Gets the address for the given account.
// *
// * @param accountId the optional accountId whose address of interest. Typically, this value is zero.
// */
// override fun getAddress(accountId: Int): String = wallet.getAddress()
//
// override suspend fun getBalance(accountId: Int): Wallet.WalletBalance = wallet.getBalanceInfo(accountId)
//
// /**
// * Sends zatoshi.
// *
// * @param zatoshi the amount of zatoshi to send.
// * @param toAddress the recipient's address.
// * @param memo the optional memo to include as part of the transaction.
// * @param fromAccountId the optional account id to use. By default, the first account is used.
// */
// override suspend fun sendToAddress(zatoshi: Long, toAddress: String, memo: String, fromAccountId: Int) =
// activeTransactionManager.sendToAddress(zatoshi, toAddress, memo, fromAccountId)
//
// /**
// * Attempts to cancel a previously sent transaction. Transactions can only be cancelled during the calculation phase
// * before they've been submitted to the server. This method will return false when it is too late to cancel. This
// * logic is delegated to the activeTransactionManager, which knows the state of the given transaction.
// *
// * @param transaction the transaction to cancel.
// * @return true when the cancellation request was successful. False when it is too late to cancel.
// */
// override fun cancelSend(transaction: ActiveSendTransaction): Boolean = activeTransactionManager.cancel(transaction)
//
//
// //
// // Private API
// //
//
//
// /**
// * Logic for starting the Synchronizer once it is ready for processing. All starts eventually end with this method.
// */
// private fun CoroutineScope.onReady() = launch {
// twig("synchronization is ready to begin!")
// launch { monitorTransactions(transactionChannel.openSubscription().distinct()) }
//
// activeTransactionManager.start()
// repository.poll(transactionChannel)
// processor.start()
// }
//
// /**
// * Monitors transactions and recalculates the balance any time transactions have changed.
// */
// private suspend fun monitorTransactions(transactionChannel: ReceiveChannel<List<ClearedTransaction>>) =
// withContext(IO) {
// twig("beginning to monitor transactions in order to update the balance")
// launch {
// for (i in transactionChannel) {
// twig("triggering a balance update because transactions have changed")
// balanceChannel.send(wallet.getBalanceInfo())
// twig("done triggering balance check!")
// }
// }
// twig("done monitoring transactions in order to update the balance")
// }
//
// /**
// * Wraps exceptions, logs them and then invokes the [onSynchronizerErrorListener], if it exists.
// */
// private fun onException(throwable: Throwable) {
// twig("********")
// twig("******** ERROR: $throwable")
// if (throwable.cause != null) twig("******** caused by ${throwable.cause}")
// if (throwable.cause?.cause != null) twig("******** caused by ${throwable.cause?.cause}")
// twig("********")
//
// val hasRecovered = onSynchronizerErrorListener?.invoke(throwable)
// if (hasRecovered != true) stop().also { failure = throwable }
// }
//
// /**
// * Represents the initial state of the Synchronizer.
// */
// sealed class SyncState {
// /**
// * State for the first run of the Synchronizer, when the database has not been initialized.
// */
// object FirstRun : SyncState()
//
// /**
// * State for when compact blocks have been downloaded but not scanned. This state is typically achieved when the
// * app was previously started but killed before the first scan took place. In this case, we do not need to
// * download compact blocks that we already have.
// *
// * @param startingBlockHeight the last block that has been downloaded into the cache. We do not need to download
// * any blocks before this height because we already have them.
// */
// class CacheOnly(val startingBlockHeight: Int = Int.MAX_VALUE) : SyncState()
//
// /**
// * The final state of the Synchronizer, when all initialization is complete and the starting block is known.
// *
// * @param startingBlockHeight the height that will be fed to the downloader. In most cases, it will represent
// * either the wallet birthday or the last block that was processed in the previous session.
// */
// class ReadyToProcess(val startingBlockHeight: Int = Int.MAX_VALUE) : SyncState()
// }
//
//}

View File

@ -4,6 +4,7 @@ import cash.z.wallet.sdk.block.CompactBlockProcessor
import cash.z.wallet.sdk.entity.ClearedTransaction
import cash.z.wallet.sdk.entity.PendingTransaction
import cash.z.wallet.sdk.entity.SentTransaction
import cash.z.wallet.sdk.exception.SynchronizerException
import cash.z.wallet.sdk.exception.WalletException
import cash.z.wallet.sdk.secure.Wallet
import kotlinx.coroutines.*
@ -13,10 +14,21 @@ import kotlinx.coroutines.channels.ReceiveChannel
import kotlin.coroutines.CoroutineContext
/**
* A synchronizer that attempts to remain operational, despite any number of errors that can occur.
* A synchronizer that attempts to remain operational, despite any number of errors that can occur. It acts as the glue
* that ties all the pieces of the SDK together. Each component of the SDK is designed for the potential of stand-alone
* usage but coordinating all the interactions is non-trivial. So the synchronizer facilitates this, acting as reference
* that demonstrates how all the pieces can be tied together. Its goal is to allow a developer to focus on their app
* rather than the nuances of how Zcash works.
*
* @param wallet the component that wraps the JNI layer that interacts with the rust backend and manages wallet config.
* @param repository the component that exposes streams of wallet transaction information.
* @param sender the component responsible for sending transactions to lightwalletd in order to spend funds.
* @param processor the component that saves the downloaded compact blocks to the cache and then scans those blocks for
* data related to this wallet.
* @param encoder the component that creates a signed transaction, used for spending funds.
*/
@ExperimentalCoroutinesApi
class StableSynchronizer (
class SdkSynchronizer (
private val wallet: Wallet,
private val ledger: TransactionRepository,
private val sender: TransactionSender,
@ -34,39 +46,86 @@ class StableSynchronizer (
lateinit var coroutineScope: CoroutineScope
//
// Communication Primitives
//
private val balanceChannel = ConflatedBroadcastChannel(Wallet.WalletBalance())
private val progressChannel = ConflatedBroadcastChannel(0)
private val pendingChannel = ConflatedBroadcastChannel<List<PendingTransaction>>(listOf())
private val clearedChannel = ConflatedBroadcastChannel<List<ClearedTransaction>>(listOf())
//
// Status
//
/**
* A property that is true while a connection to the lightwalletd server exists.
*/
override val isConnected: Boolean get() = processor.isConnected
/**
* A property that is true while actively downloading blocks from lightwalletd.
*/
override val isSyncing: Boolean get() = processor.isSyncing
/**
* A property that is true while actively scanning the cache of compact blocks for transactions.
*/
override val isScanning: Boolean get() = processor.isScanning
//
// Communication Primitives
//
/**
* Channel of balance information.
*/
private val balanceChannel = ConflatedBroadcastChannel(Wallet.WalletBalance())
/**
* Channel of data representing transactions that are pending.
*/
private val pendingChannel = ConflatedBroadcastChannel<List<PendingTransaction>>(listOf())
/**
* Channel of data representing transactions that have been mined.
*/
private val clearedChannel = ConflatedBroadcastChannel<List<ClearedTransaction>>(listOf())
//
// Error Handling
//
/*
* These listeners will not be called on the main thread.
* So they will need to switch to do anything with UI, like dialogs
/**
* A callback to invoke whenever an uncaught error is encountered. By definition, the return value of the function
* is ignored because this error is unrecoverable. The only reason the function has a return value is so that all
* error handlers work with the same signature which allows one function to handle all errors in simple apps. This
* callback is not called on the main thread so any UI work would need to switch context to the main thread.
*/
override var onCriticalErrorHandler: ((Throwable?) -> Boolean)? = null
/**
* A callback to invoke whenver a processor error is encountered. Returning true signals that the error was handled
* and a retry attempt should be made, if possible. This callback is not called on the main thread so any UI work
* would need to switch context to the main thread.
*/
override var onProcessorErrorHandler: ((Throwable?) -> Boolean)? = null
/**
* A callback to invoke whenever a server error is encountered while submitting a transaction to lightwalletd.
* Returning true signals that the error was handled and a retry attempt should be made, if possible. This callback
* is not called on the main thread so any UI work would need to switch context to the main thread.
*/
override var onSubmissionErrorHandler: ((Throwable?) -> Boolean)? = null
/**
* Starts this synchronizer within the given scope. For simplicity, attempting to start an instance that has already
* been started will throw a [SynchronizerException.FalseStart] exception. This reduces the complexity of managing
* resources that must be recycled. Instead, each synchronizer is designed to have a long lifespan and should be
* started from an activity, application or session.
*
* @param parentScope the scope to use for this synchronizer, typically something with a lifecycle such as an
* Activity for single-activity apps or a logged in user session. This scope is only used for launching this
* synchronzer's job as a child.
*/
override fun start(parentScope: CoroutineScope): Synchronizer {
if (::coroutineScope.isInitialized) throw SynchronizerException.FalseStart
// base this scope on the parent so that when the parent's job cancels, everything here cancels as well
// also use a supervisor job so that one failure doesn't bring down the whole synchronizer
coroutineScope = CoroutineScope(SupervisorJob(parentScope.coroutineContext[Job]!!) + Dispatchers.Main)
@ -84,12 +143,18 @@ class StableSynchronizer (
return this
}
/**
* Initializes the sender such that it can initiate requests within the scope of this synchronizer.
*/
private fun startSender(parentScope: CoroutineScope) {
sender.onSubmissionError = ::onFailedSend
sender.start(parentScope)
}
private suspend fun initWallet() = withContext(Dispatchers.IO) {
/**
* Initialize the wallet, which involves populating data tables based on the latest state of the wallet.
*/
private suspend fun initWallet() = withContext(IO) {
try {
wallet.initialize()
} catch (e: WalletException.AlreadyInitializedException) {
@ -104,6 +169,8 @@ class StableSynchronizer (
}
}
// TODO: this is a work in progress. We could take drastic measures to automatically recover from certain critical
// errors and alert the user but this might be better to do at the app level, rather than SDK level.
private fun recoverFrom(error: WalletException.FalseStart): Boolean {
if (error.message?.contains("unable to open database file") == true
|| error.message?.contains("table blocks has no column named") == true) {
@ -113,6 +180,11 @@ class StableSynchronizer (
return false
}
/**
* Stop this synchronizer and all of its child jobs. Once a synchronizer has been stopped it should not be restarted
* and attempting to do so will result in an error. Also, this function will throw an exception if the synchronizer
* was never previously started.
*/
override fun stop() {
coroutineScope.cancel()
}
@ -157,6 +229,7 @@ class StableSynchronizer (
fun onTransactionsChanged() {
coroutineScope.launch {
twig("triggering a balance update because transactions have changed")
refreshBalance()
clearedChannel.send(ledger.getClearedTransactions())
}
@ -165,7 +238,6 @@ class StableSynchronizer (
suspend fun refreshBalance() = withContext(IO) {
if (!balanceChannel.isClosedForSend) {
twig("triggering a balance update because transactions have changed")
balanceChannel.send(wallet.getBalanceInfo())
} else {
twig("WARNING: noticed new transactions but the balance channel was closed for send so ignoring!")
@ -212,9 +284,7 @@ class StableSynchronizer (
return balanceChannel.openSubscription()
}
override fun progress(): ReceiveChannel<Int> {
return progressChannel.openSubscription()
}
override fun progress(): ReceiveChannel<Int> = processor.progress()
override fun pendingTransactions(): ReceiveChannel<List<PendingTransaction>> {
return pendingChannel.openSubscription()

View File

@ -9,7 +9,7 @@ import kotlinx.coroutines.channels.ReceiveChannel
/**
* Primary interface for interacting with the SDK. Defines the contract that specific implementations like
* [MockSynchronizer] and [StableSynchronizer] fulfill. Given the language-level support for coroutines, we favor their
* [MockSynchronizer] and [SdkSynchronizer] fulfill. Given the language-level support for coroutines, we favor their
* use in the SDK and incorporate that choice into this contract.
*/
interface Synchronizer {

View File

@ -18,7 +18,7 @@ class WalletTransactionEncoder(
* double-bangs for things).
*/
override suspend fun create(zatoshi: Long, toAddress: String, memo: String): EncodedTransaction = withContext(IO) {
val transactionId = wallet.createRawSendTransaction(zatoshi, toAddress, memo)
val transactionId = wallet.createSpend(zatoshi, toAddress, memo)
val transaction = repository.findTransactionById(transactionId)
?: throw TransactionNotFoundException(transactionId)
EncodedTransaction(transaction.transactionId, transaction.raw

View File

@ -1,8 +1,5 @@
package cash.z.wallet.sdk.exception
import java.lang.Exception
import java.lang.RuntimeException
/**
* Exceptions thrown in the Rust layer of the SDK. We may not always be able to surface details about this
* exception so it's important for the SDK to provide helpful messages whenever these errors are encountered.
@ -19,8 +16,9 @@ sealed class RepositoryException(message: String, cause: Throwable? = null) : Ru
}
sealed class SynchronizerException(message: String, cause: Throwable? = null) : RuntimeException(message, cause) {
object FalseStart: SynchronizerException("Once a synchronizer has stopped it cannot be restarted. Instead, a new " +
"instance should be created.")
object FalseStart: SynchronizerException("This synchronizer was already started. Multiple calls to start are not" +
"allowed and once a synchronizer has stopped it cannot be restarted."
)
}
sealed class CompactBlockProcessorException(message: String, cause: Throwable? = null) : RuntimeException(message, cause) {

View File

@ -2,13 +2,10 @@ package cash.z.wallet.sdk.ext
import android.content.Context
import cash.z.wallet.sdk.data.twig
import cash.z.wallet.sdk.rpc.Service
import kotlinx.coroutines.delay
import java.io.File
import kotlin.random.Random
inline fun Int.toBlockHeight(): Service.BlockID = Service.BlockID.newBuilder().setHeight(this.toLong()).build()
suspend inline fun retryUpTo(retries: Int, initialDelay: Int = 10, block: () -> Unit) {
var failedAttempts = 0
while (failedAttempts < retries) {

View File

@ -67,4 +67,9 @@ const val DEFAULT_REWIND_DISTANCE = 10
* The number of blocks to allow before considering our data to be stale. This usually helps with what to do when
* returning from the background and is exposed via the Synchronizer's isStale function.
*/
const val DEFAULT_STALE_TOLERANCE = 10
const val DEFAULT_STALE_TOLERANCE = 10
/**
* The default port to use for connecting to lightwalletd instances.
*/
const val DEFAULT_LIGHTWALLETD_PORT = 9067

View File

@ -9,49 +9,49 @@ import cash.z.wallet.sdk.data.twig
*/
class RustBackend : RustBackendWelding {
external override fun initDataDb(dbData: String): Boolean
external override fun initDataDb(dbDataPath: String): Boolean
external override fun initAccountsTable(
dbData: String,
dbDataPath: String,
seed: ByteArray,
accounts: Int): Array<String>
external override fun initBlocksTable(
dbData: String,
dbDataPath: String,
height: Int,
hash: String,
time: Long,
saplingTree: String): Boolean
external override fun getAddress(dbData: String, account: Int): String
external override fun getAddress(dbDataPath: String, account: Int): String
external override fun isValidShieldedAddress(addr: String): Boolean
external override fun isValidTransparentAddress(addr: String): Boolean
external override fun getBalance(dbData: String, account: Int): Long
external override fun getBalance(dbDataPath: String, account: Int): Long
external override fun getVerifiedBalance(dbData: String, account: Int): Long
external override fun getVerifiedBalance(dbDataPath: String, account: Int): Long
external override fun getReceivedMemoAsUtf8(dbData: String, idNote: Long): String
external override fun getReceivedMemoAsUtf8(dbDataPath: String, idNote: Long): String
external override fun getSentMemoAsUtf8(dbData: String, idNote: Long): String
external override fun getSentMemoAsUtf8(dbDataPath: String, idNote: Long): String
external override fun validateCombinedChain(db_cache: String, db_data: String): Int
external override fun validateCombinedChain(dbCachePath: String, dbDataPath: String): Int
external override fun rewindToHeight(db_data: String, height: Int): Boolean
external override fun rewindToHeight(dbDataPath: String, height: Int): Boolean
external override fun scanBlocks(db_cache: String, db_data: String): Boolean
external override fun scanBlocks(dbCachePath: String, dbDataPath: String): Boolean
external override fun sendToAddress(
dbData: String,
external override fun createToAddress(
dbDataPath: String,
account: Int,
extsk: String,
to: String,
value: Long,
memo: String,
spendParams: String,
outputParams: String
spendParamsPath: String,
outputParamsPath: String
): Long
external override fun initLogs()

View File

@ -6,49 +6,49 @@ package cash.z.wallet.sdk.jni
*/
interface RustBackendWelding {
fun initDataDb(dbData: String): Boolean
fun initDataDb(dbDataPath: String): Boolean
fun initAccountsTable(
dbData: String,
dbDataPath: String,
seed: ByteArray,
accounts: Int): Array<String>
fun initBlocksTable(
dbData: String,
dbDataPath: String,
height: Int,
hash: String,
time: Long,
saplingTree: String): Boolean
fun getAddress(dbData: String, account: Int): String
fun getAddress(dbDataPath: String, account: Int): String
fun isValidShieldedAddress(addr: String): Boolean
fun isValidTransparentAddress(addr: String): Boolean
fun getBalance(dbData: String, account: Int): Long
fun getBalance(dbDataPath: String, account: Int): Long
fun getVerifiedBalance(dbData: String, account: Int): Long
fun getVerifiedBalance(dbDataPath: String, account: Int): Long
fun getReceivedMemoAsUtf8(dbData: String, idNote: Long): String
fun getReceivedMemoAsUtf8(dbDataPath: String, idNote: Long): String
fun getSentMemoAsUtf8(dbData: String, idNote: Long): String
fun getSentMemoAsUtf8(dbDataPath: String, idNote: Long): String
fun validateCombinedChain(db_cache: String, db_data: String): Int
fun validateCombinedChain(dbCachePath: String, dbDataPath: String): Int
fun rewindToHeight(db_data: String, height: Int): Boolean
fun rewindToHeight(dbDataPath: String, height: Int): Boolean
fun scanBlocks(db_cache: String, db_data: String): Boolean
fun scanBlocks(dbCachePath: String, dbDataPath: String): Boolean
fun sendToAddress(
dbData: String,
fun createToAddress(
dbDataPath: String,
account: Int,
extsk: String,
to: String,
value: Long,
memo: String,
spendParams: String,
outputParams: String
spendParamsPath: String,
outputParamsPath: String
): Long
fun initLogs()

View File

@ -138,40 +138,45 @@ class Wallet(
}
/**
* Does the proofs and processing required to create a raw transaction and inserts the result in the database. On
* average, this call takes over 10 seconds.
* Does the proofs and processing required to create a transaction to spend funds and inserts
* the result in the database. On average, this call takes over 10 seconds.
*
* @param value the zatoshi value to send
* @param toAddress the destination address
* @param memo the memo, which is not augmented in any way
*
* @return the row id in the transactions table that contains the raw transaction or -1 if it failed
* @return the row id in the transactions table that contains the spend transaction
* or -1 if it failed
*/
suspend fun createRawSendTransaction(value: Long, toAddress: String, memo: String = "", fromAccountId: Int = accountIds[0]): Long =
withContext(IO) {
twigTask("creating raw transaction to send $value zatoshi to ${toAddress.masked()} with memo $memo") {
try {
ensureParams(paramDestinationDir)
twig("params exist at $paramDestinationDir! attempting to send...")
rustBackend.sendToAddress(
dataDbPath,
fromAccountId,
spendingKeyStore,
toAddress,
value,
memo,
// using names here so it's easier to avoid transposing them, if the function signature changes
spendParams = SPEND_PARAM_FILE_NAME.toPath(),
outputParams = OUTPUT_PARAM_FILE_NAME.toPath()
)
} catch (t: Throwable) {
twig("${t.message}")
throw t
}
}.also { result ->
twig("result of sendToAddress: $result")
suspend fun createSpend(
value: Long,
toAddress: String,
memo: String = "",
fromAccountId: Int = accountIds[0]
): Long = withContext(IO) {
twigTask("creating transaction to spend $value zatoshi to ${toAddress.masked()} with memo $memo") {
try {
ensureParams(paramDestinationDir)
twig("params exist at $paramDestinationDir! attempting to send...")
rustBackend.createToAddress(
dataDbPath,
fromAccountId,
spendingKeyStore,
toAddress,
value,
memo,
// using names here so it's easier to avoid transposing them, if the function signature changes
spendParamsPath = SPEND_PARAM_FILE_NAME.toPath(),
outputParamsPath = OUTPUT_PARAM_FILE_NAME.toPath()
)
} catch (t: Throwable) {
twig("${t.message}")
throw t
}
}.also { result ->
twig("result of sendToAddress: $result")
}
}
/**
* Download and store the params into the given directory.
@ -284,8 +289,8 @@ class Wallet(
* @return a WalletBirthday that reflects the contents of the file or an exception when parsing fails.
*/
fun loadBirthdayFromAssets(context: Context, birthdayHeight: Int? = null): WalletBirthday {
val treeFiles = context.assets.list(Wallet.BIRTHDAY_DIRECTORY).apply { sortDescending() }
if (treeFiles.isEmpty()) throw WalletException.MissingBirthdayFilesException(BIRTHDAY_DIRECTORY)
val treeFiles = context.assets.list(Wallet.BIRTHDAY_DIRECTORY)?.apply { sortDescending() }
if (treeFiles.isNullOrEmpty()) throw WalletException.MissingBirthdayFilesException(BIRTHDAY_DIRECTORY)
try {
val file = treeFiles.first {
if (birthdayHeight == null) true

View File

@ -1,8 +1,10 @@
package cash.z.wallet.sdk.service
import android.content.Context
import cash.z.wallet.sdk.R
import cash.z.wallet.sdk.entity.CompactBlock
import cash.z.wallet.sdk.ext.toBlockHeight
import cash.z.wallet.sdk.exception.LightwalletException
import cash.z.wallet.sdk.ext.DEFAULT_LIGHTWALLETD_PORT
import cash.z.wallet.sdk.rpc.CompactFormats
import cash.z.wallet.sdk.rpc.CompactTxStreamerGrpc
import cash.z.wallet.sdk.rpc.Service
@ -12,8 +14,20 @@ import io.grpc.ManagedChannel
import io.grpc.android.AndroidChannelBuilder
import java.util.concurrent.TimeUnit
class LightWalletGrpcService private constructor(private val channel: ManagedChannel)
: LightWalletService {
/**
* Implementation of LightwalletService using gRPC for requests to lightwalletd.
*
* @param channel the channel to use for communicating with the lightwalletd server.
* @param singleRequestTimeoutSec the timeout to use for non-streaming requests. When a new stub is
* created, it will use a deadline that is after the given duration from now.
* @param streamingRequestTimeoutSec the timeout to use for streaming requests. When a new stub is
* created for streaming requests, it will use a deadline that is after the given duration from now.
*/
class LightWalletGrpcService private constructor(
private val channel: ManagedChannel,
private val singleRequestTimeoutSec: Long = 10L,
private val streamingRequestTimeoutSec: Long = 90L
) : LightWalletService {
constructor(
appContext: Context,
@ -26,12 +40,12 @@ class LightWalletGrpcService private constructor(private val channel: ManagedCha
override fun getBlockRange(heightRange: IntRange): List<CompactBlock> {
channel.resetConnectBackoff()
return channel.createStub(90L).getBlockRange(heightRange.toBlockRange()).toList()
return channel.createStub(streamingRequestTimeoutSec).getBlockRange(heightRange.toBlockRange()).toList()
}
override fun getLatestBlockHeight(): Int {
channel.resetConnectBackoff()
return channel.createStub(10L).getLatestBlock(Service.ChainSpec.newBuilder().build()).height.toInt()
return channel.createStub(singleRequestTimeoutSec).getLatestBlock(Service.ChainSpec.newBuilder().build()).height.toInt()
}
override fun submitTransaction(raw: ByteArray): Service.SendResponse {
@ -50,7 +64,9 @@ class LightWalletGrpcService private constructor(private val channel: ManagedCha
.newBlockingStub(this)
.withDeadlineAfter(timeoutSec, TimeUnit.SECONDS)
private fun IntRange.toBlockRange(): Service.BlockRange =
private inline fun Int.toBlockHeight(): Service.BlockID = Service.BlockID.newBuilder().setHeight(this.toLong()).build()
private inline fun IntRange.toBlockRange(): Service.BlockRange =
Service.BlockRange.newBuilder()
.setStart(this.first.toBlockHeight())
.setEnd(this.last.toBlockHeight())

View File

@ -373,7 +373,7 @@ pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_RustBackend_scanBlocks(
}
#[no_mangle]
pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_RustBackend_sendToAddress(
pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_RustBackend_createToAddress(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@ -396,7 +396,7 @@ pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_RustBackend_sendToAddress(
let to = utils::java_string_to_rust(&env, to);
let value = Amount::from_i64(value).map_err(|()| format_err!("Invalid amount, out of range"))?;
if value.is_negative() {
return Err(ormat_err!("Amount is negative"));
return Err(format_err!("Amount is negative"));
}
let memo = utils::java_string_to_rust(&env, memo);
let spend_params = utils::java_string_to_rust(&env, spend_params);

View File

@ -72,12 +72,12 @@ fn throw(env: &JNIEnv, description: &str) {
}
// Tries to get meaningful description from panic-error.
pub fn any_to_string(any: &Box<Any + Send>) -> String {
pub fn any_to_string(any: &Box<dyn Any + Send>) -> String {
if let Some(s) = any.downcast_ref::<&str>() {
s.to_string()
} else if let Some(s) = any.downcast_ref::<String>() {
s.clone()
} else if let Some(error) = any.downcast_ref::<Box<std::error::Error + Send>>() {
} else if let Some(error) = any.downcast_ref::<Box<dyn std::error::Error + Send>>() {
error.description().to_string()
} else {
"Unknown error occurred".to_string()