Skip to content

Commit

Permalink
[#1385] Adopt AccountBalance fields in Kotlin layer
Browse files Browse the repository at this point in the history
* [#1385] Adopt `AccountBalance` fields in Kotlin layer

- Adds the Kotlin side changes for #1380
- Changed WalletBalance to contain these three fields: available, changePending, and valuePending, instead of total and available, and change the transparent flow to StateFlow<Zatoshi>, as we don't distinguish there.
- Related connected APIs changed
- Closes #1385

* Add WalletBalanceFixture

Placed in the public directory to be visible for clients as well

* Changelog update

* Remove `getVerifiedTransparentBalance ` API entirely

Co-authored-by: str4d <[email protected]>
  • Loading branch information
HonzaR and str4d authored Feb 7, 2024
1 parent b617eb1 commit 19cca51
Show file tree
Hide file tree
Showing 21 changed files with 117 additions and 146 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ and this library adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed
- `WalletBalance` now contains new fields `changePending` and `valuePending`. Fields `total` and `pending` are
still provided. See more in the class documentation
`sdk-lib/src/main/java/cash/z/ecc/android/sdk/model/WalletBalance.kt`
- `Synchronizer.transparentBalances: WalletBalance` to `Synchronizer.transparentBalance: Zatoshi`
- `WalletSnapshot.transparentBalance: WalletBalance` to `WalletSnapshot.transparentBalance: Zatoshi`

### Added
- `WalletBalanceFixture` class with mock values that are supposed to be used only for testing purposes

## [2.0.6] - 2024-01-30

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,6 @@ interface Backend {

suspend fun rewindBlockMetadataToHeight(height: Long)

suspend fun getVerifiedTransparentBalance(address: String): Long

suspend fun getTotalTransparentBalance(address: String): Long

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,15 +161,6 @@ class RustBackend private constructor(
)
}

override suspend fun getVerifiedTransparentBalance(address: String): Long =
withContext(SdkDispatchers.DATABASE_IO) {
getVerifiedTransparentBalance(
dataDbFile.absolutePath,
address,
networkId = networkId
)
}

override suspend fun getTotalTransparentBalance(address: String): Long =
withContext(SdkDispatchers.DATABASE_IO) {
getTotalTransparentBalance(
Expand Down Expand Up @@ -628,13 +619,6 @@ class RustBackend private constructor(
networkId: Int
)

@JvmStatic
private external fun getVerifiedTransparentBalance(
pathDataDb: String,
taddr: String,
networkId: Int
): Long

@JvmStatic
private external fun getTotalTransparentBalance(
pathDataDb: String,
Expand Down
41 changes: 0 additions & 41 deletions backend-lib/src/main/rust/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -671,47 +671,6 @@ pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_isValidUn
unwrap_exc_or(&mut env, res, JNI_FALSE)
}

#[no_mangle]
pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getVerifiedTransparentBalance<
'local,
>(
mut env: JNIEnv<'local>,
_: JClass<'local>,
db_data: JString<'local>,
address: JString<'local>,
network_id: jint,
) -> jlong {
let res = catch_unwind(&mut env, |env| {
let _span = tracing::info_span!("RustBackend.getVerifiedTransparentBalance").entered();
let network = parse_network(network_id as u32)?;
let db_data = wallet_db(env, network, db_data)?;
let addr = utils::java_string_to_rust(env, &address);
let taddr = TransparentAddress::decode(&network, &addr).unwrap();

let amount = (&db_data)
.get_target_and_anchor_heights(ANCHOR_OFFSET)
.map_err(|e| format_err!("Error while fetching anchor height: {}", e))
.and_then(|opt_anchor| {
opt_anchor
.map(|(_, a)| a)
.ok_or(format_err!("Anchor height not available; scan required."))
})
.and_then(|anchor| {
(&db_data)
.get_unspent_transparent_outputs(&taddr, anchor, &[])
.map_err(|e| format_err!("Error while fetching verified balance: {}", e))
})?
.iter()
.map(|utxo| utxo.txout().value)
.sum::<Option<NonNegativeAmount>>()
.ok_or_else(|| format_err!("Balance overflowed MAX_MONEY."))?;

Ok(Amount::from(amount).into())
});

unwrap_exc_or(&mut env, res, -1)
}

#[no_mangle]
pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getTotalTransparentBalance<
'local,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import cash.z.ecc.android.sdk.ext.Darkside
import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.model.Account
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.Zatoshi
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.tool.DerivationTool
Expand Down Expand Up @@ -81,7 +80,7 @@ class TestWallet(
val birthdayHeight get() = synchronizer.latestBirthdayHeight
val networkName get() = synchronizer.network.networkName

suspend fun transparentBalance(): WalletBalance {
suspend fun transparentBalance(): Zatoshi {
synchronizer.refreshUtxos(account, synchronizer.latestBirthdayHeight)
return synchronizer.getTransparentBalance(transparentAddress)
}
Expand Down Expand Up @@ -121,7 +120,7 @@ class TestWallet(
}

synchronizer.getTransparentBalance(transparentAddress).let { walletBalance ->
if (walletBalance.available.value > 0L) {
if (walletBalance.value > 0L) {
synchronizer.shieldFunds(shieldedSpendingKey)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
launch {
sharedViewModel.synchronizerFlow
.filterNotNull()
.flatMapLatest { it.transparentBalances }
.flatMapLatest { it.transparentBalance }
.collect { onTransparentBalance(it) }
}
}
Expand All @@ -124,7 +124,7 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
}
}

private fun onTransparentBalance(transparentBalance: WalletBalance?) {
private fun onTransparentBalance(transparentBalance: Zatoshi?) {
binding.transparentBalance.apply {
text = transparentBalance.humanString()
}
Expand All @@ -133,7 +133,7 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
// TODO [#776]: Support variable fees
// TODO [#776]: https://github.com/zcash/zcash-android-wallet-sdk/issues/776
visibility =
if ((transparentBalance?.available ?: Zatoshi(0)) > ZcashSdk.MINERS_FEE) {
if ((transparentBalance ?: Zatoshi(0)) > ZcashSdk.MINERS_FEE) {
View.VISIBLE
} else {
View.GONE
Expand All @@ -160,7 +160,7 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
sharedViewModel.synchronizerFlow.value?.let { synchronizer ->
onOrchardBalance(synchronizer.orchardBalances.value)
onSaplingBalance(synchronizer.saplingBalances.value)
onTransparentBalance(synchronizer.transparentBalances.value)
onTransparentBalance(synchronizer.transparentBalance.value)
}
}

Expand All @@ -183,3 +183,13 @@ private fun WalletBalance?.humanString() =
Total balance: ${total.convertZatoshiToZecString(12)}
""".trimIndent()
}

@Suppress("MagicNumber")
private fun Zatoshi?.humanString() =
if (null == this) {
"Calculating balance"
} else {
"""
Balance: ${convertZatoshiToZecString(12)}
""".trimIndent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor
import cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel.SynchronizerError
import cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel.WalletSnapshot
import cash.z.ecc.android.sdk.fixture.WalletBalanceFixture
import cash.z.ecc.android.sdk.model.PercentDecimal
import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.Zatoshi
Expand All @@ -12,9 +13,9 @@ import cash.z.ecc.android.sdk.model.Zatoshi
object WalletSnapshotFixture {
val STATUS = Synchronizer.Status.SYNCED
val PROGRESS = PercentDecimal.ZERO_PERCENT
val TRANSPARENT_BALANCE: WalletBalance = WalletBalance(Zatoshi(8), Zatoshi(1))
val ORCHARD_BALANCE: WalletBalance = WalletBalance(Zatoshi(5), Zatoshi(2))
val SAPLING_BALANCE: WalletBalance = WalletBalance(Zatoshi(4), Zatoshi(4))
val TRANSPARENT_BALANCE: Zatoshi = Zatoshi(8)
val ORCHARD_BALANCE: WalletBalance = WalletBalanceFixture.new(Zatoshi(5), Zatoshi(2), Zatoshi(1))
val SAPLING_BALANCE: WalletBalance = WalletBalanceFixture.new(Zatoshi(4), Zatoshi(4), Zatoshi(2))

// Should fill in with non-empty values for better example values in tests and UI previews
@Suppress("LongParameterList")
Expand All @@ -28,7 +29,7 @@ object WalletSnapshotFixture {
),
orchardBalance: WalletBalance = ORCHARD_BALANCE,
saplingBalance: WalletBalance = SAPLING_BALANCE,
transparentBalance: WalletBalance = TRANSPARENT_BALANCE,
transparentBalance: Zatoshi = TRANSPARENT_BALANCE,
progress: PercentDecimal = PROGRESS,
synchronizerError: SynchronizerError? = null
) = WalletSnapshot(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,20 +148,14 @@ private fun BalanceMainContent(
Text(
stringResource(
id = R.string.balance_available_amount_format,
walletSnapshot.transparentBalance.available.toZecString()
)
)
Text(
stringResource(
id = R.string.balance_pending_amount_format,
walletSnapshot.transparentBalance.pending.toZecString()
walletSnapshot.transparentBalance.toZecString()
)
)

// TODO [#776]: Support variable fees
// TODO [#776]: https://github.com/zcash/zcash-android-wallet-sdk/issues/776
// This check will not be correct with variable fees
if (walletSnapshot.transparentBalance.available > ZcashSdk.MINERS_FEE) {
if (walletSnapshot.transparentBalance > ZcashSdk.MINERS_FEE) {
// Note this implementation does not guard against multiple clicks
Button(onClick = onShieldFunds) {
Text(stringResource(id = R.string.action_shield))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ data class WalletSnapshot(
val processorInfo: CompactBlockProcessor.ProcessorInfo,
val orchardBalance: WalletBalance,
val saplingBalance: WalletBalance,
val transparentBalance: WalletBalance,
val transparentBalance: Zatoshi,
val progress: PercentDecimal,
val synchronizerError: SynchronizerError?
) {
Expand All @@ -27,7 +27,7 @@ data class WalletSnapshot(
val isSendEnabled: Boolean get() = status == Synchronizer.Status.SYNCED && hasFunds
}

fun WalletSnapshot.totalBalance() = orchardBalance.total + saplingBalance.total + transparentBalance.total
fun WalletSnapshot.totalBalance() = orchardBalance.total + saplingBalance.total + transparentBalance

// Note that considering both to be spendable is subject to change.
// The user experience could be confusing, and in the future we might prefer to ask users
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,23 +377,23 @@ private fun Synchronizer.toWalletSnapshot() =
// 3
saplingBalances,
// 4
transparentBalances,
transparentBalance,
// 5
progress,
// 6
toCommonError()
) { flows ->
val orchardBalance = flows[2] as WalletBalance?
val saplingBalance = flows[3] as WalletBalance?
val transparentBalance = flows[4] as WalletBalance?
val transparentBalance = flows[4] as Zatoshi?
val progressPercentDecimal = (flows[5] as PercentDecimal)

WalletSnapshot(
flows[0] as Synchronizer.Status,
flows[1] as CompactBlockProcessor.ProcessorInfo,
orchardBalance ?: WalletBalance(Zatoshi(0), Zatoshi(0)),
saplingBalance ?: WalletBalance(Zatoshi(0), Zatoshi(0)),
transparentBalance ?: WalletBalance(Zatoshi(0), Zatoshi(0)),
orchardBalance ?: WalletBalance(Zatoshi(0), Zatoshi(0), Zatoshi(0)),
saplingBalance ?: WalletBalance(Zatoshi(0), Zatoshi(0), Zatoshi(0)),
transparentBalance ?: Zatoshi(0),
progressPercentDecimal,
flows[6] as SynchronizerError?
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ class TransparentRestoreSample {
val address = wallet.transparentAddress

Assert.assertTrue(
"Not enough funds to run sample. Expected some Zatoshi but found ${tbalance.available}. " +
"Not enough funds to run sample. Expected some Zatoshi but found $tbalance. " +
"Try adding funds to $address",
tbalance.available.value > 0
tbalance.value > 0
)

// wallet.shieldFunds()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import cash.z.ecc.android.sdk.internal.deriveUnifiedSpendingKey
import cash.z.ecc.android.sdk.internal.jni.RustDerivationTool
import cash.z.ecc.android.sdk.model.Account
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.Zatoshi
import cash.z.ecc.android.sdk.model.ZcashNetwork
import co.electriccoin.lightwallet.client.model.LightWalletEndpoint
Expand Down Expand Up @@ -82,7 +81,7 @@ class TestWallet(
val birthdayHeight get() = synchronizer.latestBirthdayHeight
val networkName get() = synchronizer.network.networkName

suspend fun transparentBalance(): WalletBalance {
suspend fun transparentBalance(): Zatoshi {
synchronizer.refreshUtxos(account, synchronizer.latestBirthdayHeight)
return synchronizer.getTransparentBalance(transparentAddress)
}
Expand Down Expand Up @@ -122,9 +121,9 @@ class TestWallet(
}

synchronizer.getTransparentBalance(transparentAddress).let { walletBalance ->
Twig.debug { "FOUND utxo balance of total: ${walletBalance.total} available: ${walletBalance.available}" }
Twig.debug { "FOUND utxo balance of total: $walletBalance" }

if (walletBalance.available.value > 0L) {
if (walletBalance.value > 0L) {
synchronizer.shieldFunds(spendingKey)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ internal class FakeRustBackend(

override suspend fun getLatestCacheHeight(): Long = metadata.maxOf { it.height }

override suspend fun getVerifiedTransparentBalance(address: String): Long {
TODO("Not yet implemented")
}

override suspend fun getTotalTransparentBalance(address: String): Long {
TODO("Not yet implemented")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ import cash.z.ecc.android.sdk.model.PercentDecimal
import cash.z.ecc.android.sdk.model.TransactionOverview
import cash.z.ecc.android.sdk.model.TransactionRecipient
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.Zatoshi
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.type.AddressType
Expand Down Expand Up @@ -185,7 +184,7 @@ class SdkSynchronizer private constructor(

override val orchardBalances = processor.orchardBalances.asStateFlow()
override val saplingBalances = processor.saplingBalances.asStateFlow()
override val transparentBalances = processor.transparentBalances.asStateFlow()
override val transparentBalance = processor.transparentBalance.asStateFlow()

override val transactions
get() =
Expand Down Expand Up @@ -359,7 +358,7 @@ class SdkSynchronizer private constructor(

/**
* Calculate the latest balance based on the blocks that have been scanned and transmit this information into the
* [transparentBalances] and [saplingBalances] flow. The [orchardBalances] flow is still not filled with proper data
* [transparentBalance] and [saplingBalances] flow. The [orchardBalances] flow is still not filled with proper data
* because of the current limited Orchard support.
*/
suspend fun refreshAllBalances() {
Expand Down Expand Up @@ -587,7 +586,7 @@ class SdkSynchronizer private constructor(
val encodedTx =
txManager.encode(
usk,
tBalance.available,
tBalance,
TransactionRecipient.Account(usk.account),
memo,
usk.account
Expand All @@ -607,7 +606,7 @@ class SdkSynchronizer private constructor(
return processor.refreshUtxos(account, since)
}

override suspend fun getTransparentBalance(tAddr: String): WalletBalance {
override suspend fun getTransparentBalance(tAddr: String): Zatoshi {
return processor.getUtxoCacheBalance(tAddr)
}

Expand Down
Loading

0 comments on commit 19cca51

Please sign in to comment.