Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#1632] Refactor Account.DEFAULT #1645

Merged
merged 25 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
09f4489
[#1632] Remove `Account.DEFAULT`
HonzaR Nov 22, 2024
edf12e0
Update Account related APIs
HonzaR Nov 25, 2024
7ba8578
Refactor balances APIs
HonzaR Nov 25, 2024
e8be36a
Adopt `getAccounts` in fragment-based Demo app
HonzaR Nov 25, 2024
15b886f
Add init to `JniUnifiedSpendingKey.kt`
HonzaR Nov 28, 2024
1ed11c0
File and link followup
HonzaR Nov 28, 2024
2a09067
Update deprecated Fragment-based Demo app
HonzaR Nov 28, 2024
1a9662c
Remove deprecated functions from Synchronizer
HonzaR Nov 28, 2024
12125de
Update WalletSnapshot and WalletVM APIs in Demo
HonzaR Nov 28, 2024
5958dd8
Update newer Compose-based Demo app
HonzaR Nov 28, 2024
dc59e2c
Static code analysis warnings
HonzaR Nov 29, 2024
ed14aa1
Changelog update
HonzaR Nov 29, 2024
c4a53e1
Update fixture values + tests
HonzaR Nov 29, 2024
7c1e40a
Fix some Demo app tests
HonzaR Dec 1, 2024
731f2d5
Fix shield funds sample test
HonzaR Dec 1, 2024
77d80b0
Hide `Synchronizer.createAccount` form public API
HonzaR Dec 2, 2024
5dd8ec6
Add all accounts flow API
HonzaR Dec 2, 2024
9f66836
Refactor AccountFixture
HonzaR Dec 3, 2024
9a7dbbd
Move Account changes to account-uuids
HonzaR Dec 3, 2024
496ba8c
Move AccountTest changes to account-uuids
HonzaR Dec 3, 2024
a412b9a
Move FakeRustBackend changes to account-uuids
HonzaR Dec 3, 2024
5f7cac1
Revert TransactionRecipient.Account parameter change
HonzaR Dec 3, 2024
152d1ae
Fix static code analysis warnings
HonzaR Dec 3, 2024
6f51c74
Changelog cleanup
HonzaR Dec 3, 2024
2f4ac4c
Final 1640 changes stirp out
HonzaR Dec 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ and this library adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- `Synchronizer.getAccounts()`
- `Synchronizer.walletBalances: StateFlow<Map<Account, AccountBalance>?>` that is replacement for the removed
`orchardBalances`, `saplingBalances`, and `transparentBalance`

### Changed
- `Synchronizer.orchardBalances`, `Synchronizer.saplingBalances`, and `Synchronizer.transparentBalance` have been
replaced by `Synchronizer.walletBalances` that provides these balances based on `Account`

### Removed
- `Synchronizer.sendToAddress` and `Synchronizer.shieldFunds` have been removed, use
`Synchronizer.createProposedTransactions` and `Synchronizer.proposeShielding` instead
- `Synchronizer.orchardBalances`, `Synchronizer.saplingBalances`, and `Synchronizer.transparentBalance`

## [2.2.6] - 2024-11-16

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import androidx.annotation.Keep
* @throws IllegalArgumentException if the values are inconsistent.
*/
@Keep
@Suppress("LongParameterList")
class JniAccount(
val accountIndex: Int,
val ufvk: String?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ package cash.z.ecc.android.sdk.internal.fixture

import cash.z.ecc.android.sdk.internal.model.JniAccountBalance

/**
* This is a test fixture for [JniAccountBalance] class. It holds mocked values that are only used within
* [JniWalletSummaryTest].
*/
object JniAccountBalanceFixture {
const val ACCOUNT_ID: Int = 0
val ACCOUNT_UUID: ByteArray = "random_uuid_16_b".toByteArray()
const val SAPLING_VERIFIED_BALANCE: Long = 0L
const val SAPLING_CHANGE_PENDING: Long = 0L
const val SAPLING_VALUE_PENDING: Long = 0L
Expand All @@ -14,7 +18,7 @@ object JniAccountBalanceFixture {

@Suppress("LongParameterList")
fun new(
account: Int = ACCOUNT_ID,
account: ByteArray = ACCOUNT_UUID,
saplingVerifiedBalance: Long = SAPLING_VERIFIED_BALANCE,
saplingChangePending: Long = SAPLING_CHANGE_PENDING,
saplingValuePending: Long = SAPLING_VALUE_PENDING,
Expand Down
2 changes: 1 addition & 1 deletion darkside-test-lib/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ dependencies {
implementation(libs.androidx.multidex)
implementation(libs.bundles.grpc)

androidTestImplementation(projects.sdkIncubatorLib)
androidTestImplementation(libs.bundles.androidx.test)

androidTestImplementation(libs.zcashwalletplgn)
androidTestImplementation(libs.bip39)
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ class DarksideTestCoordinator(val wallet: TestWallet) {
available: Long = -1,
total: Long = -1
) {
val balance = synchronizer.saplingBalances.value
val balance = synchronizer.walletBalances.value?.get(wallet.account)?.sapling
if (available > 0) {
assertTrue(
"invalid available balance. Expected a minimum of $available but found ${balance?.available}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import cash.z.ecc.android.sdk.SdkSynchronizer
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.WalletInitMode
import cash.z.ecc.android.sdk.ext.Darkside
import cash.z.ecc.android.sdk.fixture.AccountFixture
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.Zatoshi
import cash.z.ecc.android.sdk.model.ZcashNetwork
Expand Down Expand Up @@ -55,11 +55,17 @@ class TestWallet(
// Although runBlocking isn't great, this usage is OK because this is only used within the
// automated tests

private val account = Account.DEFAULT
internal val account = AccountFixture.new()
private val context = InstrumentationRegistry.getInstrumentation().context
private val seed: ByteArray = Mnemonics.MnemonicCode(seedPhrase).toSeed()
private val shieldedSpendingKey =
runBlocking { DerivationTool.getInstance().deriveUnifiedSpendingKey(seed, network = network, account) }
runBlocking {
DerivationTool.getInstance().deriveUnifiedSpendingKey(
seed = seed,
network = network,
account = AccountFixture.new()
)
}
val synchronizer: SdkSynchronizer =
Synchronizer.newBlocking(
context,
Expand All @@ -72,7 +78,8 @@ class TestWallet(
walletInitMode = WalletInitMode.ExistingWallet
) as SdkSynchronizer

val available get() = synchronizer.saplingBalances.value?.available
val available
get() = synchronizer.walletBalances.value?.get(account)?.sapling?.available
val unifiedAddress =
runBlocking { synchronizer.getUnifiedAddress(account) }
val transparentAddress =
Expand Down Expand Up @@ -123,7 +130,7 @@ class TestWallet(
}

suspend fun shieldFunds(): TestWallet {
synchronizer.refreshUtxos(Account.DEFAULT, BlockHeight.new(935000L)).let { count ->
synchronizer.refreshUtxos(account, BlockHeight.new(935000L)).let { count ->
Twig.debug { "FOUND $count new UTXOs" }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import cash.z.ecc.android.sdk.demoapp.ext.defaultForNetwork
import cash.z.ecc.android.sdk.demoapp.util.fromResources
import cash.z.ecc.android.sdk.ext.convertZecToZatoshi
import cash.z.ecc.android.sdk.ext.toHex
import cash.z.ecc.android.sdk.fixture.AccountFixture
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.ZcashNetwork
import cash.z.ecc.android.sdk.tool.DerivationTool
Expand Down Expand Up @@ -72,7 +72,7 @@ class SampleCodeTest {
@Test
fun getAddress() =
runBlocking {
val address = synchronizer.getUnifiedAddress(Account.DEFAULT)
val address = synchronizer.getUnifiedAddress(AccountFixture.new())
assertFalse(address.isBlank())
log("Address: $address")
}
Expand Down Expand Up @@ -170,7 +170,7 @@ class SampleCodeTest {
// ///////////////////////////////////////////////////
// Create a signed transaction (with memo) and broadcast
@Test
fun submitTransaction() =
fun submitTransaction(): Unit =
runBlocking {
val amount = 0.123.convertZecToZatoshi()
val address = "ztestsapling1tklsjr0wyw0d58f3p7wufvrj2cyfv6q6caumyueadq8qvqt8lda6v6tpx474rfru9y6u75u7qnw"
Expand All @@ -179,7 +179,7 @@ class SampleCodeTest {
DerivationTool.getInstance().deriveUnifiedSpendingKey(
seed,
ZcashNetwork.Mainnet,
Account.DEFAULT
AccountFixture.new()
)
synchronizer.createProposedTransactions(
synchronizer.proposeTransfer(
Expand All @@ -197,7 +197,7 @@ class SampleCodeTest {
// ////////////////////////////////////////////////////

companion object {
private val seed = "Insert seed for testing".toByteArray()
private val seed = "Inserting seed for test purposes".toByteArray()
private val lightwalletdHost = LightWalletEndpoint.Mainnet

private val context = InstrumentationRegistry.getInstrumentation().targetContext
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package cash.z.ecc.android.sdk.demoapp.ui.common
package cash.z.ecc.android.sdk.demoapp

import kotlin.time.Duration.Companion.seconds

Expand All @@ -9,3 +9,7 @@ val ANDROID_STATE_FLOW_TIMEOUT = 5.seconds
* A tiny weight, useful for spacers to fill an empty space.
*/
const val MINIMAL_WEIGHT = 0.0001f

// TODO [#1644]: Refactor Account ZIP32 index across SDK
// TODO [#1644]: https://github.com/Electric-Coin-Company/zcash-android-wallet-sdk/issues/1644
const val CURRENT_ZIP_32_ACCOUNT_INDEX = 0
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import cash.z.ecc.android.sdk.model.Proposal
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.type.ServerValidation
import co.electriccoin.lightwallet.client.model.LightWalletEndpoint
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

Expand All @@ -76,11 +77,14 @@ internal fun ComposeActivity.Navigation() {
NavHost(navController = navController, startDestination = HOME) {
composable(HOME) {
val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value
val allAccounts = walletViewModel.accounts.collectAsStateWithLifecycle().value
if (null == walletSnapshot) {
// Display loading indicator
} else {
Home(
walletSnapshot,
currentAccount = walletViewModel.getCurrentAccount(),
allAccounts = allAccounts.toImmutableList(),
walletSnapshot = walletSnapshot,
goBalance = { navController.navigateJustOnce(BALANCE) },
goSend = { navController.navigateJustOnce(SEND) },
goAddressDetails = { navController.navigateJustOnce(WALLET_ADDRESS_DETAILS) },
Expand All @@ -106,9 +110,11 @@ internal fun ComposeActivity.Navigation() {
if (null == synchronizer || null == walletSnapshot) {
// Display loading indicator
} else {
val balance = walletSnapshot.balanceByAccount(walletViewModel.getCurrentAccount())
val scope = rememberCoroutineScope()
Balance(
walletSnapshot,
exchangeRateUsd = walletSnapshot.exchangeRateUsd,
accountBalance = balance,
onShieldFunds = { walletViewModel.shieldFunds() },
sendState = walletViewModel.sendState.collectAsStateWithLifecycle().value,
onBack = {
Expand All @@ -129,11 +135,13 @@ internal fun ComposeActivity.Navigation() {
if (null == synchronizer) {
// Display loading indicator
} else {
val currentAccount = walletViewModel.getCurrentAccount()
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
// I don't like giving synchronizer directly over to the view, but for now it isolates each of the
// demo app views
Addresses(
account = currentAccount,
synchronizer = synchronizer,
copyToClipboard = { tag, textToCopy ->
copyToClipboard(
Expand Down Expand Up @@ -161,8 +169,9 @@ internal fun ComposeActivity.Navigation() {
if (null == synchronizer || null == walletSnapshot || null == spendingKey) {
// Display loading indicator
} else {
val currentAccount = walletViewModel.getCurrentAccount()
Send(
walletSnapshot = walletSnapshot,
accountBalance = walletSnapshot.balanceByAccount(currentAccount),
sendState = walletViewModel.sendState.collectAsStateWithLifecycle().value,
onSend = {
walletViewModel.send(it)
Expand Down Expand Up @@ -292,7 +301,9 @@ internal fun ComposeActivity.Navigation() {
if (null == synchronizer) {
// Display loading indicator
} else {
val currentAccount = walletViewModel.getCurrentAccount()
Transactions(
account = currentAccount,
synchronizer = synchronizer,
onBack = { navController.popBackStackJustOnce(TRANSACTIONS) }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
import cash.z.ecc.android.sdk.demoapp.CURRENT_ZIP_32_ACCOUNT_INDEX
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetAddressBinding
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
import cash.z.ecc.android.sdk.demoapp.util.ProvideAddressBenchmarkTrace
import cash.z.ecc.android.sdk.demoapp.util.fromResources
import cash.z.ecc.android.sdk.model.Account
import cash.z.ecc.android.sdk.model.UnifiedFullViewingKey
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.tool.DerivationTool
Expand All @@ -30,23 +30,25 @@ class GetAddressFragment : BaseDemoFragment<FragmentGetAddressBinding>() {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
sharedViewModel.synchronizerFlow.filterNotNull().collect { synchronizer ->
val account = synchronizer.getAccounts()[CURRENT_ZIP_32_ACCOUNT_INDEX]

binding.unifiedAddress.apply {
reportTraceEvent(ProvideAddressBenchmarkTrace.Event.UNIFIED_ADDRESS_START)
val uaddress = synchronizer.getUnifiedAddress(Account.DEFAULT)
val uaddress = synchronizer.getUnifiedAddress(account)
reportTraceEvent(ProvideAddressBenchmarkTrace.Event.UNIFIED_ADDRESS_END)
text = uaddress
setOnClickListener { copyToClipboard(uaddress) }
}
binding.saplingAddress.apply {
reportTraceEvent(ProvideAddressBenchmarkTrace.Event.SAPLING_ADDRESS_START)
val sapling = synchronizer.getSaplingAddress(Account.DEFAULT)
val sapling = synchronizer.getSaplingAddress(account)
reportTraceEvent(ProvideAddressBenchmarkTrace.Event.SAPLING_ADDRESS_END)
text = sapling
setOnClickListener { copyToClipboard(sapling) }
}
binding.transparentAddress.apply {
reportTraceEvent(ProvideAddressBenchmarkTrace.Event.TRANSPARENT_ADDRESS_START)
val transparent = synchronizer.getTransparentAddress(Account.DEFAULT)
val transparent = synchronizer.getTransparentAddress(account)
reportTraceEvent(ProvideAddressBenchmarkTrace.Event.TRANSPARENT_ADDRESS_END)
text = transparent
setOnClickListener { copyToClipboard(transparent) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import cash.z.ecc.android.bip39.Mnemonics
import cash.z.ecc.android.bip39.toSeed
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
import cash.z.ecc.android.sdk.demoapp.CURRENT_ZIP_32_ACCOUNT_INDEX
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetBalanceBinding
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
import cash.z.ecc.android.sdk.demoapp.util.SyncBlockchainBenchmarkTrace
Expand Down Expand Up @@ -78,7 +79,7 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
DerivationTool.getInstance().deriveUnifiedSpendingKey(
seed,
network,
Account.DEFAULT
Account(CURRENT_ZIP_32_ACCOUNT_INDEX)
)
sharedViewModel.synchronizerFlow.value?.let { synchronizer ->
synchronizer.proposeShielding(usk.account, Zatoshi(100000))?.let { it1 ->
Expand Down Expand Up @@ -116,10 +117,12 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
sharedViewModel.synchronizerFlow
.filterNotNull()
.flatMapLatest {
it.saplingBalances.combine(it.exchangeRateUsd) { b, r ->
b?.let {
b to
r.currencyConversion
val account = it.getAccounts()[CURRENT_ZIP_32_ACCOUNT_INDEX]
it.walletBalances.combine(it.exchangeRateUsd) { balances, rate ->
balances?.let {
val walletBalance = balances[account]!!.sapling
walletBalance to
rate.currencyConversion
?.priceOfZec
?.toBigDecimal()
}
Expand All @@ -131,23 +134,28 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
sharedViewModel.synchronizerFlow
.filterNotNull()
.flatMapLatest {
it.orchardBalances.combine(it.exchangeRateUsd) { b, r ->
b?.let {
b to
r.currencyConversion?.priceOfZec?.toBigDecimal()
val account = it.getAccounts()[CURRENT_ZIP_32_ACCOUNT_INDEX]
it.walletBalances.combine(it.exchangeRateUsd) { balances, rate ->
balances?.let {
val walletBalance = balances[account]!!.orchard
walletBalance to
rate.currencyConversion
?.priceOfZec
?.toBigDecimal()
}
}
}
.collect { onOrchardBalance(it) }
}
launch {

sharedViewModel.synchronizerFlow
.filterNotNull()
.flatMapLatest {
it.transparentBalance.combine(it.exchangeRateUsd) { b, r ->
b?.let {
b to
r.currencyConversion
val account = it.getAccounts()[CURRENT_ZIP_32_ACCOUNT_INDEX]
it.walletBalances.combine(it.exchangeRateUsd) { balances, rate ->
balances?.let {
val walletBalance = balances[account]!!.unshielded
walletBalance to
rate.currencyConversion
?.priceOfZec
?.toBigDecimal()
}
Expand Down Expand Up @@ -208,9 +216,12 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
binding.textStatus.text = "Status: $status"
sharedViewModel.synchronizerFlow.value?.let { synchronizer ->
val rate = synchronizer.exchangeRateUsd.value.currencyConversion?.priceOfZec?.toBigDecimal()
onOrchardBalance(synchronizer.orchardBalances.value?.let { Pair(it, rate) })
onSaplingBalance(synchronizer.saplingBalances.value?.let { Pair(it, rate) })
onTransparentBalance(synchronizer.transparentBalance.value?.let { Pair(it, rate) })
viewLifecycleOwner.lifecycleScope.launch {
val account = synchronizer.getAccounts()[CURRENT_ZIP_32_ACCOUNT_INDEX]
onOrchardBalance(synchronizer.walletBalances.value?.let { Pair(it[account]!!.orchard, rate) })
onSaplingBalance(synchronizer.walletBalances.value?.let { Pair(it[account]!!.sapling, rate) })
onTransparentBalance(synchronizer.walletBalances.value?.let { Pair(it[account]!!.unshielded, rate) })
}
}
}

Expand Down
Loading
Loading