[#367] Fix Kotlin compiler warnings
* Fix missing room schema export directory * Fix returned unused fields from cursor * Fix missing db column index * Improved TODO. Added suppress warning * Changed parameters name to correspond to their supertype * Changed type to Kotlin variant * Use priority parameter * Unified parameter names * Suppress unchecked type warning * Removed inline function * Suppress obsolete coroutine warnings * Improve previous commit * Fix unnecessary safe call warning * Remove unused parameter * Unreachable code * toLowerCase where possible * Changed parameter name * Suppress several "unused" warnings * Fixed fromHtml() deprecation * Suppress intentionally unused parameter warning * Remove redundant initializer * Remove inline function * Suppress intentionally used deprecated code * Unreachable code * Suppress obsolete coroutine warnings * Suppress intentionally unused parameter * Remove unused expression * Supertype parameter name * Warnings of GetBlockRangeFragment.kt * Deprecated onActivityCreated * Suppress obsolete coroutine/flow warnings * Unnecessary null check * Suppress intentionally unused parameter * Suppress intentionally unused parameters * Deprecated onActivityCreated * Predetermined type * ListUtxosFragment clean code * Suppress intentionally unused parameter * Lint checks warnings fix * Add data db migration * Enable treating Kotlin compiler warnings as errors * Solve several darkside-test-lib tests warnings * Solve several demo-app tests warnings * Solve several sdk-lib tests warnings * Ktlint check result fix * Remove parentheses now that Synchronizer is not cast * Remove wildcard imports for java.util * Revert "Add data db migration" * Revert "Fix missing db column index" * Suppress missing indexes on data db entities Co-authored-by: Carter Jernigan <git@carterjernigan.com>
This commit is contained in:
parent
10d0652a8d
commit
150778f008
|
@ -1,6 +1,11 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
|
|
|
@ -43,9 +43,9 @@ class InboundTxTests : ScopedTest() {
|
|||
}
|
||||
|
||||
private fun addTransactions(targetHeight: BlockHeight, vararg txs: String) {
|
||||
val overwriteBlockCount = 5
|
||||
// val overwriteBlockCount = 5
|
||||
chainMaker
|
||||
// .stageEmptyBlocks(targetHeight, overwriteBlockCount)
|
||||
// .stageEmptyBlocks(targetHeight, overwriteBlockCount)
|
||||
.stageTransactions(targetHeight, *txs)
|
||||
.applyTipHeight(targetHeight)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package cash.z.ecc.android.sdk.darkside.test
|
||||
|
||||
open class DarksideTest(name: String = javaClass.simpleName) : ScopedTest() {
|
||||
open class DarksideTest : ScopedTest() {
|
||||
val sithLord = DarksideTestCoordinator()
|
||||
val validator = sithLord.validator
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package cash.z.ecc.android.sdk.darkside.test
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import cash.z.ecc.android.sdk.SdkSynchronizer
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.internal.twig
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
|
@ -101,13 +100,13 @@ class DarksideTestCoordinator(val wallet: TestWallet) {
|
|||
twig("got processor status $it")
|
||||
if (it == Synchronizer.Status.DISCONNECTED) {
|
||||
twig("waiting a bit before giving up on connection...")
|
||||
} else if (targetHeight != null && (synchronizer as SdkSynchronizer).processor.getLastScannedHeight() < targetHeight) {
|
||||
} else if (targetHeight != null && synchronizer.processor.getLastScannedHeight() < targetHeight) {
|
||||
twig("awaiting new blocks from server...")
|
||||
}
|
||||
}.map {
|
||||
// whenever we're waiting for a target height, for simplicity, if we're sleeping,
|
||||
// and in between polls, then consider it that we're not synced
|
||||
if (targetHeight != null && (synchronizer as SdkSynchronizer).processor.getLastScannedHeight() < targetHeight) {
|
||||
if (targetHeight != null && synchronizer.processor.getLastScannedHeight() < targetHeight) {
|
||||
twig("switching status to DOWNLOADING because we're still waiting for height $targetHeight")
|
||||
Synchronizer.Status.DOWNLOADING
|
||||
} else {
|
||||
|
@ -145,8 +144,8 @@ class DarksideTestCoordinator(val wallet: TestWallet) {
|
|||
|
||||
fun validateHasBlock(height: BlockHeight) {
|
||||
runBlocking {
|
||||
assertTrue((synchronizer as SdkSynchronizer).findBlockHashAsHex(height) != null)
|
||||
assertTrue((synchronizer as SdkSynchronizer).findBlockHash(height)?.size ?: 0 > 0)
|
||||
assertTrue(synchronizer.findBlockHashAsHex(height) != null)
|
||||
assertTrue(synchronizer.findBlockHash(height)?.size ?: 0 > 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,7 +192,7 @@ class DarksideTestCoordinator(val wallet: TestWallet) {
|
|||
}
|
||||
|
||||
fun validateBlockHash(height: BlockHeight, expectedHash: String) {
|
||||
val hash = runBlocking { (synchronizer as SdkSynchronizer).findBlockHashAsHex(height) }
|
||||
val hash = runBlocking { synchronizer.findBlockHashAsHex(height) }
|
||||
assertEquals(expectedHash, hash)
|
||||
}
|
||||
|
||||
|
@ -202,7 +201,7 @@ class DarksideTestCoordinator(val wallet: TestWallet) {
|
|||
}
|
||||
|
||||
fun validateTxCount(count: Int) {
|
||||
val txCount = runBlocking { (synchronizer as SdkSynchronizer).getTransactionCount() }
|
||||
val txCount = runBlocking { synchronizer.getTransactionCount() }
|
||||
assertEquals("Expected $count transactions but found $txCount instead!", count, txCount)
|
||||
}
|
||||
|
||||
|
@ -216,7 +215,7 @@ class DarksideTestCoordinator(val wallet: TestWallet) {
|
|||
}
|
||||
}
|
||||
suspend fun validateBalance(available: Long = -1, total: Long = -1, accountIndex: Int = 0) {
|
||||
val balance = (synchronizer as SdkSynchronizer).processor.getBalanceInfo(accountIndex)
|
||||
val balance = synchronizer.processor.getBalanceInfo(accountIndex)
|
||||
if (available > 0) {
|
||||
assertEquals("invalid available balance", available, balance.available)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import cash.z.ecc.android.sdk.internal.TroubleshootingTwig
|
|||
import cash.z.ecc.android.sdk.internal.Twig
|
||||
import cash.z.ecc.android.sdk.internal.twig
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
|
@ -19,6 +20,7 @@ import org.junit.Before
|
|||
import org.junit.BeforeClass
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
open class ScopedTest(val defaultTimeout: Long = 2000L) : DarksideTestPrerequisites() {
|
||||
protected lateinit var testScope: CoroutineScope
|
||||
|
||||
|
@ -60,7 +62,7 @@ open class ScopedTest(val defaultTimeout: Long = 2000L) : DarksideTestPrerequisi
|
|||
fun createScope() {
|
||||
twig("======================= CLASS STARTED ===============================")
|
||||
classScope = CoroutineScope(
|
||||
SupervisorJob() + newFixedThreadPoolContext(2, this.javaClass.simpleName)
|
||||
SupervisorJob() + newFixedThreadPoolContext(2, this::class.java.simpleName)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@ class SimpleMnemonics : MnemonicPlugin {
|
|||
override fun fullWordList(languageCode: String) = Mnemonics.getCachedWords(Locale.ENGLISH.language)
|
||||
override fun nextEntropy(): ByteArray = WordCount.COUNT_24.toEntropy()
|
||||
override fun nextMnemonic(): CharArray = MnemonicCode(WordCount.COUNT_24).chars
|
||||
override fun nextMnemonic(entropy: ByteArray): CharArray = MnemonicCode(entropy).chars
|
||||
override fun nextMnemonic(seed: ByteArray): CharArray = MnemonicCode(seed).chars
|
||||
override fun nextMnemonicList(): List<CharArray> = MnemonicCode(WordCount.COUNT_24).words
|
||||
override fun nextMnemonicList(entropy: ByteArray): List<CharArray> = MnemonicCode(entropy).words
|
||||
override fun nextMnemonicList(seed: ByteArray): List<CharArray> = MnemonicCode(seed).words
|
||||
override fun toSeed(mnemonic: CharArray): ByteArray = MnemonicCode(mnemonic).toSeed()
|
||||
override fun toWordList(mnemonic: CharArray): List<CharArray> = MnemonicCode(mnemonic).words
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import cash.z.ecc.android.sdk.model.Zatoshi
|
|||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.catch
|
||||
|
@ -34,6 +35,7 @@ import java.util.concurrent.TimeoutException
|
|||
* A simple wallet that connects to testnet for integration testing. The intention is that it is
|
||||
* easy to drive and nice to use.
|
||||
*/
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
class TestWallet(
|
||||
val seedPhrase: String,
|
||||
val alias: String = "TestWallet",
|
||||
|
|
|
@ -70,14 +70,14 @@ class SampleCodeTest {
|
|||
)
|
||||
}
|
||||
assertEquals(1, spendingKeys.size)
|
||||
log("Spending Key: ${spendingKeys?.get(0)}")
|
||||
log("Spending Key: ${spendingKeys[0]}")
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////////
|
||||
// Get Address
|
||||
@Test fun getAddress() = runBlocking {
|
||||
val address = synchronizer.getAddress()
|
||||
assertFalse(address.isNullOrBlank())
|
||||
assertFalse(address.isBlank())
|
||||
log("Address: $address")
|
||||
}
|
||||
|
||||
|
|
|
@ -17,11 +17,11 @@ import com.google.android.material.snackbar.Snackbar
|
|||
abstract class BaseDemoFragment<T : ViewBinding> : Fragment() {
|
||||
|
||||
/**
|
||||
* Since the lightwalletservice is not a component that apps typically use, directly, we provide
|
||||
* Since the lightWalletService is not a component that apps typically use, directly, we provide
|
||||
* this from one place. Everything that can be done with the service can/should be done with the
|
||||
* synchronizer because it wraps the service.
|
||||
*/
|
||||
val lightwalletService get() = mainActivity()?.lightwalletService
|
||||
val lightWalletService get() = mainActivity()?.lightWalletService
|
||||
|
||||
// contains view information provided by the user
|
||||
val sharedViewModel: SharedViewModel by activityViewModels()
|
||||
|
@ -76,9 +76,8 @@ abstract class BaseDemoFragment<T : ViewBinding> : Fragment() {
|
|||
* Convenience function to the given text to the clipboard.
|
||||
*/
|
||||
open fun copyToClipboard(text: String, description: String = "Copied to clipboard!") {
|
||||
(activity?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager)?.let { cm ->
|
||||
cm.setPrimaryClip(ClipData.newPlainText("DemoAppClip", text))
|
||||
}
|
||||
(activity?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager)
|
||||
.setPrimaryClip(ClipData.newPlainText("DemoAppClip", text))
|
||||
toast(description)
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ class MainActivity :
|
|||
* this object because it would utilize the synchronizer, instead, which exposes APIs that
|
||||
* automatically sync with the server.
|
||||
*/
|
||||
var lightwalletService: LightWalletService? = null
|
||||
var lightWalletService: LightWalletService? = null
|
||||
private set
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -53,8 +53,8 @@ class MainActivity :
|
|||
setSupportActionBar(toolbar)
|
||||
|
||||
val fab: FloatingActionButton = findViewById(R.id.fab)
|
||||
fab.setOnClickListener { view ->
|
||||
onFabClicked(view)
|
||||
fab.setOnClickListener {
|
||||
onFabClicked()
|
||||
}
|
||||
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
|
||||
val navView: NavigationView = findViewById(R.id.nav_view)
|
||||
|
@ -78,7 +78,7 @@ class MainActivity :
|
|||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
lightwalletService?.shutdown()
|
||||
lightWalletService?.shutdown()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
|
@ -107,17 +107,17 @@ class MainActivity :
|
|||
//
|
||||
|
||||
private fun initService() {
|
||||
if (lightwalletService != null) {
|
||||
lightwalletService?.shutdown()
|
||||
if (lightWalletService != null) {
|
||||
lightWalletService?.shutdown()
|
||||
}
|
||||
val network = ZcashNetwork.fromResources(applicationContext)
|
||||
lightwalletService = LightWalletGrpcService.new(
|
||||
lightWalletService = LightWalletGrpcService.new(
|
||||
applicationContext,
|
||||
LightWalletEndpoint.defaultForNetwork(network)
|
||||
)
|
||||
}
|
||||
|
||||
private fun onFabClicked(view: View) {
|
||||
private fun onFabClicked() {
|
||||
fabListener?.onActionButtonClicked()
|
||||
}
|
||||
|
||||
|
@ -126,8 +126,10 @@ class MainActivity :
|
|||
//
|
||||
|
||||
fun getClipboardText(): String? {
|
||||
return with(clipboard) {
|
||||
if (!hasPrimaryClip()) return null
|
||||
with(clipboard) {
|
||||
if (!hasPrimaryClip()) {
|
||||
return null
|
||||
}
|
||||
return primaryClip!!.getItemAt(0)?.coerceToText(this@MainActivity)?.toString()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package cash.z.ecc.android.sdk.demoapp.demos.getblock
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.Html
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.text.HtmlCompat
|
||||
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
|
||||
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetBlockBinding
|
||||
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
|
||||
|
@ -26,10 +26,10 @@ class GetBlockFragment : BaseDemoFragment<FragmentGetBlockBinding>() {
|
|||
|
||||
private fun setBlockHeight(blockHeight: BlockHeight) {
|
||||
val blocks =
|
||||
lightwalletService?.getBlockRange(blockHeight..blockHeight)
|
||||
lightWalletService?.getBlockRange(blockHeight..blockHeight)
|
||||
val block = blocks?.firstOrNull()
|
||||
binding.textInfo.visibility = View.VISIBLE
|
||||
binding.textInfo.text = Html.fromHtml(
|
||||
binding.textInfo.text = HtmlCompat.fromHtml(
|
||||
"""
|
||||
<b>block height:</b> ${block?.height.withCommas()}
|
||||
<br/><b>block time:</b> ${block?.time.toRelativeTime(requireApplicationContext())}
|
||||
|
@ -37,10 +37,12 @@ class GetBlockFragment : BaseDemoFragment<FragmentGetBlockBinding>() {
|
|||
<br/><b>hash:</b> ${block?.hash?.toByteArray()?.toHex()}
|
||||
<br/><b>prevHash:</b> ${block?.prevHash?.toByteArray()?.toHex()}
|
||||
${block?.vtxList.toHtml()}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun onApply(_unused: View? = null) {
|
||||
val network = ZcashNetwork.fromResources(requireApplicationContext())
|
||||
val newHeight = min(binding.textBlockHeight.text.toString().toLongOrNull() ?: network.saplingActivationHeight.value, network.saplingActivationHeight.value)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package cash.z.ecc.android.sdk.demoapp.demos.getblockrange
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.Html
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.text.HtmlCompat
|
||||
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
|
||||
import cash.z.ecc.android.sdk.demoapp.R
|
||||
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetBlockRangeBinding
|
||||
|
@ -27,13 +27,13 @@ class GetBlockRangeFragment : BaseDemoFragment<FragmentGetBlockRangeBinding>() {
|
|||
private fun setBlockRange(blockRange: ClosedRange<BlockHeight>) {
|
||||
val start = System.currentTimeMillis()
|
||||
val blocks =
|
||||
lightwalletService?.getBlockRange(blockRange)
|
||||
lightWalletService?.getBlockRange(blockRange)
|
||||
val fetchDelta = System.currentTimeMillis() - start
|
||||
|
||||
// Note: This is a demo so we won't worry about iterating efficiently over these blocks
|
||||
// Note: Converting the blocks sequence to a list can consume a lot of memory and may
|
||||
// cause OOM.
|
||||
binding.textInfo.text = Html.fromHtml(
|
||||
binding.textInfo.text = HtmlCompat.fromHtml(
|
||||
blocks?.toList()?.run {
|
||||
val count = size
|
||||
val emptyCount = count { it.vtxCount == 0 }
|
||||
|
@ -66,13 +66,15 @@ class GetBlockRangeFragment : BaseDemoFragment<FragmentGetBlockRangeBinding>() {
|
|||
<br/><b>avg OUTs [per block / per TX]:</b> ${"%.1f / %.1f".format(outCount.toDouble() / (count - emptyCount), outCount.toDouble() / txCount)}
|
||||
<br/><b>avg INs [per block / per TX]:</b> ${"%.1f / %.1f".format(inCount.toDouble() / (count - emptyCount), inCount.toDouble() / txCount)}
|
||||
<br/><b>most shielded TXs:</b> ${if (maxTxs == null) "none" else "${maxTxs.vtxCount} in block ${maxTxs.height.withCommas()}"}
|
||||
<br/><b>most shielded INs:</b> ${if (maxInTx == null) "none" else "${maxInTx.spendsCount} in block ${maxIns?.height.withCommas()} at tx index ${maxInTx.index}"}
|
||||
<br/><b>most shielded OUTs:</b> ${if (maxOutTx == null) "none" else "${maxOutTx?.outputsCount} in block ${maxOuts?.height.withCommas()} at tx index ${maxOutTx?.index}"}
|
||||
<br/><b>most shielded INs:</b> ${if (maxInTx == null) "none" else "${maxInTx.spendsCount} in block ${maxIns.height.withCommas()} at tx index ${maxInTx.index}"}
|
||||
<br/><b>most shielded OUTs:</b> ${if (maxOutTx == null) "none" else "${maxOutTx.outputsCount} in block ${maxOuts.height.withCommas()} at tx index ${maxOutTx.index}"}
|
||||
""".trimIndent()
|
||||
} ?: "No blocks found in that range."
|
||||
} ?: "No blocks found in that range.",
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun onApply(_unused: View) {
|
||||
val network = ZcashNetwork.fromResources(requireApplicationContext())
|
||||
val start = max(binding.textStartHeight.text.toString().toLongOrNull() ?: network.saplingActivationHeight.value, network.saplingActivationHeight.value)
|
||||
|
|
|
@ -15,7 +15,7 @@ class GetLatestHeightFragment : BaseDemoFragment<FragmentGetLatestHeightBinding>
|
|||
private fun displayLatestHeight() {
|
||||
// note: this is a blocking call, a real app wouldn't do this on the main thread
|
||||
// instead, a production app would leverage the synchronizer like in the other demos
|
||||
binding.textInfo.text = lightwalletService?.getLatestBlockHeight().toString()
|
||||
binding.textInfo.text = lightWalletService?.getLatestBlockHeight().toString()
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -2,6 +2,8 @@ package cash.z.ecc.android.sdk.demoapp.demos.getprivatekey
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import cash.z.ecc.android.bip39.Mnemonics
|
||||
import cash.z.ecc.android.bip39.toSeed
|
||||
|
@ -61,9 +63,14 @@ class GetPrivateKeyFragment : BaseDemoFragment<FragmentGetPrivateKeyBinding>() {
|
|||
// Android Lifecycle overrides
|
||||
//
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||
setup()
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
|
@ -49,12 +49,14 @@ class HomeFragment : BaseDemoFragment<FragmentHomeBinding>() {
|
|||
mainActivity()?.removeClipboardListener()
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun onEditSeedPhrase(unused: View) {
|
||||
setEditShown(true)
|
||||
binding.inputSeedPhrase.setText(sharedViewModel.seedPhrase.value)
|
||||
binding.textLayoutSeedPhrase.helperText = ""
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun onAcceptSeedPhrase(unused: View) {
|
||||
if (applySeedPhrase()) {
|
||||
setEditShown(false)
|
||||
|
@ -62,10 +64,12 @@ class HomeFragment : BaseDemoFragment<FragmentHomeBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun onCancelSeedPhrase(unused: View) {
|
||||
setEditShown(false)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun onPasteSeedPhrase(unused: View) {
|
||||
mainActivity()?.getClipboardText().let { clipboardText ->
|
||||
binding.inputSeedPhrase.setText(clipboardText)
|
||||
|
|
|
@ -3,6 +3,7 @@ package cash.z.ecc.android.sdk.demoapp.demos.listtransactions
|
|||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import cash.z.ecc.android.bip39.Mnemonics
|
||||
|
@ -33,7 +34,7 @@ import kotlinx.coroutines.runBlocking
|
|||
class ListTransactionsFragment : BaseDemoFragment<FragmentListTransactionsBinding>() {
|
||||
private lateinit var initializer: Initializer
|
||||
private lateinit var synchronizer: Synchronizer
|
||||
private lateinit var adapter: TransactionAdapter<ConfirmedTransaction>
|
||||
private lateinit var adapter: TransactionAdapter
|
||||
private lateinit var address: String
|
||||
private var status: Synchronizer.Status? = null
|
||||
private val isSynced get() = status == Synchronizer.Status.SYNCED
|
||||
|
@ -134,9 +135,14 @@ class ListTransactionsFragment : BaseDemoFragment<FragmentListTransactionsBindin
|
|||
// Android Lifecycle overrides
|
||||
//
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||
setup()
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
|
|
@ -10,30 +10,29 @@ import cash.z.ecc.android.sdk.demoapp.R
|
|||
/**
|
||||
* Simple adapter implementation that knows how to bind a recyclerview to ClearedTransactions.
|
||||
*/
|
||||
class TransactionAdapter<T : ConfirmedTransaction> :
|
||||
ListAdapter<T, TransactionViewHolder<T>>(
|
||||
object : DiffUtil.ItemCallback<T>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: T,
|
||||
newItem: T
|
||||
) = oldItem.minedHeight == newItem.minedHeight
|
||||
class TransactionAdapter : ListAdapter<ConfirmedTransaction, TransactionViewHolder>(
|
||||
object : DiffUtil.ItemCallback<ConfirmedTransaction>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: ConfirmedTransaction,
|
||||
newItem: ConfirmedTransaction
|
||||
) = oldItem.minedHeight == newItem.minedHeight
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: T,
|
||||
newItem: T
|
||||
) = oldItem == newItem
|
||||
}
|
||||
) {
|
||||
override fun areContentsTheSame(
|
||||
oldItem: ConfirmedTransaction,
|
||||
newItem: ConfirmedTransaction
|
||||
) = oldItem == newItem
|
||||
}
|
||||
) {
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
) = TransactionViewHolder<T>(
|
||||
) = TransactionViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(R.layout.item_transaction, parent, false)
|
||||
)
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: TransactionViewHolder<T>,
|
||||
holder: TransactionViewHolder,
|
||||
position: Int
|
||||
) = holder.bindTo(getItem(position))
|
||||
}
|
||||
|
|
|
@ -15,18 +15,18 @@ import java.util.Locale
|
|||
/**
|
||||
* Simple view holder for displaying confirmed transactions in the recyclerview.
|
||||
*/
|
||||
class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
class TransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
private val amountText = itemView.findViewById<TextView>(R.id.text_transaction_amount)
|
||||
private val infoText = itemView.findViewById<TextView>(R.id.text_transaction_info)
|
||||
private val timeText = itemView.findViewById<TextView>(R.id.text_transaction_timestamp)
|
||||
private val icon = itemView.findViewById<ImageView>(R.id.image_transaction_type)
|
||||
private val formatter = SimpleDateFormat("M/d h:mma", Locale.getDefault())
|
||||
|
||||
fun bindTo(transaction: T?) {
|
||||
fun bindTo(transaction: ConfirmedTransaction?) {
|
||||
val isInbound = transaction?.toAddress.isNullOrEmpty()
|
||||
amountText.text = transaction?.valueInZatoshi.convertZatoshiToZecString()
|
||||
timeText.text =
|
||||
if (transaction == null || transaction?.blockTimeInSeconds == 0L) "Pending"
|
||||
if (transaction == null || transaction.blockTimeInSeconds == 0L) "Pending"
|
||||
else formatter.format(transaction.blockTimeInSeconds * 1000L)
|
||||
infoText.text = getMemoString(transaction)
|
||||
|
||||
|
@ -35,7 +35,7 @@ class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : Recycler
|
|||
icon.setColorFilter(ContextCompat.getColor(itemView.context, if (isInbound) R.color.tx_inbound else R.color.tx_outbound))
|
||||
}
|
||||
|
||||
private fun getMemoString(transaction: T?): String {
|
||||
private fun getMemoString(transaction: ConfirmedTransaction?): String {
|
||||
return transaction?.memo?.takeUnless { it[0] < 0 }?.let { String(it) } ?: "no memo"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
|
|||
private lateinit var seed: ByteArray
|
||||
private lateinit var initializer: Initializer
|
||||
private lateinit var synchronizer: Synchronizer
|
||||
private lateinit var adapter: UtxoAdapter<ConfirmedTransaction>
|
||||
private lateinit var adapter: UtxoAdapter
|
||||
private val address: String = "t1RwbKka1CnktvAJ1cSqdn7c6PXWG4tZqgd"
|
||||
private var status: Synchronizer.Status? = null
|
||||
|
||||
|
@ -86,7 +86,7 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
|
|||
setup()
|
||||
}
|
||||
|
||||
fun initUi() {
|
||||
private fun initUi() {
|
||||
binding.inputAddress.setText(address)
|
||||
binding.inputRangeStart.setText(ZcashNetwork.fromResources(requireApplicationContext()).saplingActivationHeight.toString())
|
||||
binding.inputRangeEnd.setText(getUxtoEndHeight(requireApplicationContext()).value.toString())
|
||||
|
@ -99,7 +99,7 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
|
|||
initTransactionUi()
|
||||
}
|
||||
|
||||
fun downloadTransactions() {
|
||||
private fun downloadTransactions() {
|
||||
binding.textStatus.text = "loading..."
|
||||
binding.textStatus.post {
|
||||
val network = ZcashNetwork.fromResources(requireApplicationContext())
|
||||
|
@ -114,7 +114,7 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
|
|||
?: getUxtoEndHeight(requireApplicationContext()).value
|
||||
var allStart = now
|
||||
twig("loading transactions in range $startToUse..$endToUse")
|
||||
val txids = lightwalletService?.getTAddressTransactions(
|
||||
val txids = lightWalletService?.getTAddressTransactions(
|
||||
addressToUse,
|
||||
BlockHeight.new(network, startToUse)..BlockHeight.new(network, endToUse)
|
||||
)
|
||||
|
@ -131,18 +131,16 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
|
|||
// twig("failed to decrypt and store transaction due to: $t")
|
||||
// }
|
||||
// }
|
||||
}?.let { txData ->
|
||||
}?.let { _ ->
|
||||
// Disabled during migration to newer SDK version; this appears to have been
|
||||
// leveraging non-public APIs in the SDK so perhaps should be removed
|
||||
// val parseStart = now
|
||||
// val tList = LocalRpcTypes.TransactionDataList.newBuilder().addAllData(txData).build()
|
||||
// val parsedTransactions = initializer.rustBackend.parseTransactionDataList(tList)
|
||||
// delta = now - parseStart
|
||||
// updateStatus("parsed txs in ${delta}ms.")
|
||||
// val parseStart = now
|
||||
// val tList = LocalRpcTypes.TransactionDataList.newBuilder().addAllData(txData).build()
|
||||
// val parsedTransactions = initializer.rustBackend.parseTransactionDataList(tList)
|
||||
// delta = now - parseStart
|
||||
// updateStatus("parsed txs in ${delta}ms.")
|
||||
}
|
||||
(synchronizer as SdkSynchronizer).refreshTransactions()
|
||||
// val finalCount = (synchronizer as SdkSynchronizer).getTransactionCount()
|
||||
// "found ${finalCount - initialCount} shielded outputs.
|
||||
delta = now - allStart
|
||||
updateStatus("Total time ${delta}ms.")
|
||||
|
||||
|
@ -199,7 +197,7 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
|
|||
}
|
||||
}
|
||||
synchronizer.clearedTransactions.collectWith(lifecycleScope, ::onTransactionsUpdated)
|
||||
// synchronizer.receivedTransactions.collectWith(lifecycleScope, ::onTransactionsUpdated)
|
||||
// synchronizer.receivedTransactions.collectWith(lifecycleScope, ::onTransactionsUpdated)
|
||||
} catch (t: Throwable) {
|
||||
twig("failed to start the synchronizer!!! due to : $t")
|
||||
}
|
||||
|
@ -220,12 +218,12 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
|
|||
LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
|
||||
adapter = UtxoAdapter()
|
||||
binding.recyclerTransactions.adapter = adapter
|
||||
// lifecycleScope.launch {
|
||||
// // address = synchronizer.getAddress()
|
||||
// synchronizer.receivedTransactions.onEach {
|
||||
// onTransactionsUpdated(it)
|
||||
// }.launchIn(this)
|
||||
// }
|
||||
// lifecycleScope.launch {
|
||||
// address = synchronizer.getAddress()
|
||||
// synchronizer.receivedTransactions.onEach {
|
||||
// onTransactionsUpdated(it)
|
||||
// }.launchIn(this)
|
||||
// }
|
||||
}
|
||||
|
||||
private fun startSynchronizer() {
|
||||
|
|
|
@ -10,30 +10,29 @@ import cash.z.ecc.android.sdk.demoapp.R
|
|||
/**
|
||||
* Simple adapter implementation that knows how to bind a recyclerview to ClearedTransactions.
|
||||
*/
|
||||
class UtxoAdapter<T : ConfirmedTransaction> :
|
||||
ListAdapter<T, UtxoViewHolder<T>>(
|
||||
object : DiffUtil.ItemCallback<T>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: T,
|
||||
newItem: T
|
||||
) = oldItem.minedHeight == newItem.minedHeight
|
||||
class UtxoAdapter : ListAdapter<ConfirmedTransaction, UtxoViewHolder>(
|
||||
object : DiffUtil.ItemCallback<ConfirmedTransaction>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: ConfirmedTransaction,
|
||||
newItem: ConfirmedTransaction
|
||||
) = oldItem.minedHeight == newItem.minedHeight
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: T,
|
||||
newItem: T
|
||||
) = oldItem == newItem
|
||||
}
|
||||
) {
|
||||
override fun areContentsTheSame(
|
||||
oldItem: ConfirmedTransaction,
|
||||
newItem: ConfirmedTransaction
|
||||
) = oldItem == newItem
|
||||
}
|
||||
) {
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
) = UtxoViewHolder<T>(
|
||||
) = UtxoViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(R.layout.item_transaction, parent, false)
|
||||
)
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: UtxoViewHolder<T>,
|
||||
holder: UtxoViewHolder,
|
||||
position: Int
|
||||
) = holder.bindTo(getItem(position))
|
||||
}
|
||||
|
|
|
@ -13,21 +13,21 @@ import java.util.Locale
|
|||
/**
|
||||
* Simple view holder for displaying confirmed transactions in the recyclerview.
|
||||
*/
|
||||
class UtxoViewHolder<T : ConfirmedTransaction>(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
class UtxoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
private val amountText = itemView.findViewById<TextView>(R.id.text_transaction_amount)
|
||||
private val infoText = itemView.findViewById<TextView>(R.id.text_transaction_info)
|
||||
private val timeText = itemView.findViewById<TextView>(R.id.text_transaction_timestamp)
|
||||
private val formatter = SimpleDateFormat("M/d h:mma", Locale.getDefault())
|
||||
|
||||
fun bindTo(transaction: T?) {
|
||||
fun bindTo(transaction: ConfirmedTransaction?) {
|
||||
amountText.text = transaction?.valueInZatoshi.convertZatoshiToZecString()
|
||||
timeText.text =
|
||||
if (transaction == null || transaction?.blockTimeInSeconds == 0L) "Pending"
|
||||
if (transaction == null || transaction.blockTimeInSeconds == 0L) "Pending"
|
||||
else formatter.format(transaction.blockTimeInSeconds * 1000L)
|
||||
infoText.text = getMemoString(transaction)
|
||||
}
|
||||
|
||||
private fun getMemoString(transaction: T?): String {
|
||||
private fun getMemoString(transaction: ConfirmedTransaction?): String {
|
||||
return transaction?.memo?.takeUnless { it[0] < 0 }?.let { String(it) } ?: "no memo"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package cash.z.ecc.android.sdk.demoapp.demos.send
|
|||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import cash.z.ecc.android.bip39.Mnemonics
|
||||
|
@ -162,6 +163,7 @@ class SendFragment : BaseDemoFragment<FragmentSendBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun onSend(unused: View) {
|
||||
isSending = true
|
||||
val amount = amountInput.text.toString().toDouble().convertZecToZatoshi()
|
||||
|
@ -221,9 +223,14 @@ class SendFragment : BaseDemoFragment<FragmentSendBinding>() {
|
|||
// Android Lifecycle overrides
|
||||
//
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||
setup()
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
|
|
@ -5,7 +5,7 @@ package cash.z.ecc.android.sdk.demoapp.util
|
|||
import android.content.Context
|
||||
import cash.z.ecc.android.sdk.demoapp.R
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
fun ZcashNetwork.Companion.fromResources(context: Context): ZcashNetwork {
|
||||
val networkNameFromResources = context.getString(R.string.network_name).lowercase(Locale.ROOT)
|
||||
|
|
|
@ -31,6 +31,7 @@ class SampleStorage(context: Context) {
|
|||
* the SDK. This class delegates to the storage object. For demo purposes, we're using an insecure
|
||||
* SampleStorage implementation but this can easily be swapped for a truly secure storage solution.
|
||||
*/
|
||||
@Suppress("deprecation")
|
||||
class SampleStorageBridge(context: Context) {
|
||||
private val delegate = SampleStorage(context.applicationContext)
|
||||
|
||||
|
|
|
@ -19,9 +19,9 @@ class SimpleMnemonics : MnemonicPlugin {
|
|||
override fun fullWordList(languageCode: String) = Mnemonics.getCachedWords(Locale.ENGLISH.language)
|
||||
override fun nextEntropy(): ByteArray = WordCount.COUNT_24.toEntropy()
|
||||
override fun nextMnemonic(): CharArray = MnemonicCode(WordCount.COUNT_24).chars
|
||||
override fun nextMnemonic(entropy: ByteArray): CharArray = MnemonicCode(entropy).chars
|
||||
override fun nextMnemonic(seed: ByteArray): CharArray = MnemonicCode(seed).chars
|
||||
override fun nextMnemonicList(): List<CharArray> = MnemonicCode(WordCount.COUNT_24).words
|
||||
override fun nextMnemonicList(entropy: ByteArray): List<CharArray> = MnemonicCode(entropy).words
|
||||
override fun nextMnemonicList(seed: ByteArray): List<CharArray> = MnemonicCode(seed).words
|
||||
override fun toSeed(mnemonic: CharArray): ByteArray = MnemonicCode(mnemonic).toSeed()
|
||||
override fun toWordList(mnemonic: CharArray): List<CharArray> = MnemonicCode(mnemonic).words
|
||||
}
|
||||
|
|
|
@ -25,8 +25,7 @@ IS_SNAPSHOT=true
|
|||
LIBRARY_VERSION=1.9.0-beta03
|
||||
|
||||
# Kotlin compiler warnings can be considered errors, failing the build.
|
||||
# Currently set to false, because this project has a lot of warnings to fix first.
|
||||
ZCASH_IS_TREAT_WARNINGS_AS_ERRORS=false
|
||||
ZCASH_IS_TREAT_WARNINGS_AS_ERRORS=true
|
||||
|
||||
# Optionally configure code coverage, as historically Jacoco has at times been buggy with respect to new Kotlin versions
|
||||
IS_ANDROID_INSTRUMENTATION_TEST_COVERAGE_ENABLED=false
|
||||
|
|
|
@ -122,10 +122,8 @@ android {
|
|||
useLibrary("android.test.runner")
|
||||
|
||||
defaultConfig {
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
argument("room.schemaLocation", "$projectDir/schemas")
|
||||
}
|
||||
ksp {
|
||||
arg("room.schemaLocation", "$projectDir/schemas")
|
||||
}
|
||||
|
||||
consumerProguardFiles("proguard-consumer.txt")
|
||||
|
@ -301,6 +299,7 @@ dependencies {
|
|||
androidTestImplementation(libs.androidx.test.junit)
|
||||
androidTestImplementation(libs.androidx.test.core)
|
||||
androidTestImplementation(libs.coroutines.okhttp)
|
||||
androidTestImplementation(libs.kotlin.test)
|
||||
androidTestImplementation(libs.kotlinx.coroutines.test)
|
||||
// used by 'ru.gildor.corutines.okhttp.await' (to make simple suspended requests) and breaks on versions higher than 3.8.0
|
||||
androidTestImplementation(libs.okhttp)
|
||||
|
|
|
@ -8,6 +8,7 @@ import okhttp3.OkHttpClient
|
|||
import okhttp3.Request
|
||||
import org.json.JSONObject
|
||||
import ru.gildor.coroutines.okhttp.await
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
fun Initializer.Config.seedPhrase(seedPhrase: String, network: ZcashNetwork) {
|
||||
runBlocking { setSeed(SimpleMnemonics().toSeed(seedPhrase.toCharArray()), network) }
|
||||
|
@ -21,6 +22,7 @@ object BlockExplorer {
|
|||
.build()
|
||||
val result = client.newCall(request).await()
|
||||
val body = result.body?.string()
|
||||
assertNotNull(body, "Body can not be null.")
|
||||
return JSONObject(body).getJSONArray("data").getJSONObject(0).getLong("id")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,11 @@ import cash.z.ecc.android.sdk.model.BlockHeight
|
|||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.android.sdk.util.TestWallet
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import kotlin.test.DefaultAsserter.assertEquals
|
||||
import kotlin.test.DefaultAsserter.assertTrue
|
||||
|
||||
// TODO [#650]: https://github.com/zcash/zcash-android-wallet-sdk/issues/650
|
||||
|
||||
|
@ -110,7 +110,7 @@ class SanityTest(
|
|||
twig(it)
|
||||
}.getOrElse { return@runBlocking }
|
||||
|
||||
val downloaderHeight = runCatching {
|
||||
runCatching {
|
||||
wallet.service.getLatestBlockHeight()
|
||||
}.getOrNull() ?: return@runBlocking
|
||||
assertTrue("$networkName failed to return a proper block. Height was ${block.height} but we expected $height", block.height == height.value)
|
||||
|
|
|
@ -25,12 +25,12 @@ import kotlinx.coroutines.flow.filterNotNull
|
|||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class TestnetIntegrationTest : ScopedTest() {
|
||||
|
||||
|
@ -50,7 +50,7 @@ class TestnetIntegrationTest : ScopedTest() {
|
|||
|
||||
@Test
|
||||
fun testLoadBirthday() {
|
||||
val (height, hash, time, tree) = runBlocking {
|
||||
val (height) = runBlocking {
|
||||
CheckpointTool.loadNearest(
|
||||
context,
|
||||
synchronizer.network,
|
||||
|
@ -81,8 +81,8 @@ class TestnetIntegrationTest : ScopedTest() {
|
|||
}
|
||||
|
||||
assertTrue(
|
||||
"No funds available when we expected a balance greater than zero!",
|
||||
availableBalance!!.value > 0
|
||||
availableBalance!!.value > 0,
|
||||
"No funds available when we expected a balance greater than zero!"
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ class TestnetIntegrationTest : ScopedTest() {
|
|||
ZcashSdk.MINERS_FEE,
|
||||
toAddress,
|
||||
"first mainnet tx from the SDK"
|
||||
).filter { it?.isSubmitSuccess() == true }.onFirst {
|
||||
).filter { it.isSubmitSuccess() }.onFirst {
|
||||
log("DONE SENDING!!!")
|
||||
}
|
||||
log("returning true from sendFunds")
|
||||
|
|
|
@ -24,10 +24,6 @@ import kotlinx.coroutines.flow.launchIn
|
|||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Test
|
||||
|
@ -35,6 +31,10 @@ import org.junit.runner.RunWith
|
|||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import java.io.File
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@MaintainedTest(TestPurpose.REGRESSION)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
|
@ -69,11 +69,11 @@ class PersistentTransactionManagerTest : ScopedTest() {
|
|||
}
|
||||
|
||||
private fun initMocks() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
MockitoAnnotations.openMocks(this)
|
||||
mockEncoder.stub {
|
||||
onBlocking {
|
||||
createTransaction(any(), any(), any(), any(), any())
|
||||
}.thenAnswer { invocation ->
|
||||
}.thenAnswer {
|
||||
runBlocking {
|
||||
delay(200)
|
||||
EncodedTransaction(byteArrayOf(1, 2, 3), byteArrayOf(8, 9), 5_000_000)
|
||||
|
@ -100,7 +100,7 @@ class PersistentTransactionManagerTest : ScopedTest() {
|
|||
|
||||
txFlow.drop(2).onEach {
|
||||
twig("found tx: $it")
|
||||
assertTrue("Expected the encoded tx to be cancelled but it wasn't", it.isCancelled())
|
||||
assertTrue(it.isCancelled(), "Expected the encoded tx to be cancelled but it wasn't")
|
||||
twig("found it to be successfully cancelled")
|
||||
testScope.cancel()
|
||||
}.launchIn(testScope).join()
|
||||
|
@ -112,16 +112,16 @@ class PersistentTransactionManagerTest : ScopedTest() {
|
|||
assertFalse(tx.isCancelled())
|
||||
manager.cancel(tx.id)
|
||||
tx = manager.findById(tx.id)!!
|
||||
assertTrue("Transaction was not cancelled", tx.isCancelled())
|
||||
assertTrue(tx.isCancelled(), "Transaction was not cancelled")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAbort() = runBlocking {
|
||||
var tx: PendingTransaction? = manager.initSpend(Zatoshi(1234), "a", "b", 0)
|
||||
assertNotNull(tx)
|
||||
manager.abort(tx!!)
|
||||
manager.abort(tx)
|
||||
tx = manager.findById(tx.id)
|
||||
assertNull("Transaction was not removed from the DB", tx)
|
||||
assertNull(tx, "Transaction was not removed from the DB")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -6,6 +6,7 @@ import cash.z.ecc.android.sdk.internal.TroubleshootingTwig
|
|||
import cash.z.ecc.android.sdk.internal.Twig
|
||||
import cash.z.ecc.android.sdk.internal.twig
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
|
@ -19,6 +20,7 @@ import org.junit.Before
|
|||
import org.junit.BeforeClass
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
open class ScopedTest(val defaultTimeout: Long = 2000L) {
|
||||
protected lateinit var testScope: CoroutineScope
|
||||
|
||||
|
@ -60,7 +62,7 @@ open class ScopedTest(val defaultTimeout: Long = 2000L) {
|
|||
fun createScope() {
|
||||
twig("======================= CLASS STARTED ===============================")
|
||||
classScope = CoroutineScope(
|
||||
SupervisorJob() + newFixedThreadPoolContext(2, this.javaClass.simpleName)
|
||||
SupervisorJob() + newFixedThreadPoolContext(2, this::class.java.simpleName)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@ class SimpleMnemonics : MnemonicPlugin {
|
|||
override fun fullWordList(languageCode: String) = Mnemonics.getCachedWords(Locale.ENGLISH.language)
|
||||
override fun nextEntropy(): ByteArray = WordCount.COUNT_24.toEntropy()
|
||||
override fun nextMnemonic(): CharArray = MnemonicCode(WordCount.COUNT_24).chars
|
||||
override fun nextMnemonic(entropy: ByteArray): CharArray = MnemonicCode(entropy).chars
|
||||
override fun nextMnemonic(seed: ByteArray): CharArray = MnemonicCode(seed).chars
|
||||
override fun nextMnemonicList(): List<CharArray> = MnemonicCode(WordCount.COUNT_24).words
|
||||
override fun nextMnemonicList(entropy: ByteArray): List<CharArray> = MnemonicCode(entropy).words
|
||||
override fun nextMnemonicList(seed: ByteArray): List<CharArray> = MnemonicCode(seed).words
|
||||
override fun toSeed(mnemonic: CharArray): ByteArray = MnemonicCode(mnemonic).toSeed()
|
||||
override fun toWordList(mnemonic: CharArray): List<CharArray> = MnemonicCode(mnemonic).words
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import cash.z.ecc.android.sdk.model.Zatoshi
|
|||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.catch
|
||||
|
@ -34,6 +35,7 @@ import java.util.concurrent.TimeoutException
|
|||
* A simple wallet that connects to testnet for integration testing. The intention is that it is
|
||||
* easy to drive and nice to use.
|
||||
*/
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
class TestWallet(
|
||||
val seedPhrase: String,
|
||||
val alias: String = "TestWallet",
|
||||
|
|
|
@ -20,7 +20,7 @@ import java.io.File
|
|||
/**
|
||||
* Simplified Initializer focused on starting from a ViewingKey.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "unused")
|
||||
class Initializer private constructor(
|
||||
val context: Context,
|
||||
internal val rustBackend: RustBackend,
|
||||
|
@ -147,12 +147,10 @@ class Initializer private constructor(
|
|||
/**
|
||||
* Set the server and the network property at the same time to prevent them from getting out
|
||||
* of sync. Ultimately, this determines which host a synchronizer will use in order to
|
||||
* connect to lightwalletd. In most cases, the default host is sufficient but an override
|
||||
* can be provided. The host cannot be changed without explicitly setting the network.
|
||||
* connect to lightwalletd.
|
||||
*
|
||||
* @param network the Zcash network to use. Either testnet or mainnet.
|
||||
* @param host the lightwalletd host to use.
|
||||
* @param port the lightwalletd port to use.
|
||||
* @param lightWalletEndpoint the light wallet endpoint to use.
|
||||
*/
|
||||
fun setNetwork(
|
||||
network: ZcashNetwork,
|
||||
|
@ -311,6 +309,7 @@ class Initializer private constructor(
|
|||
block: (Config) -> Unit
|
||||
) = new(appContext, onCriticalErrorHandler, Config(block))
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
suspend fun new(
|
||||
context: Context,
|
||||
onCriticalErrorHandler: ((Throwable?) -> Boolean)?,
|
||||
|
|
|
@ -62,7 +62,6 @@ import io.grpc.ManagedChannel
|
|||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
|
@ -96,7 +95,7 @@ import kotlin.coroutines.EmptyCoroutineContext
|
|||
* @property processor saves the downloaded compact blocks to the cache and then scans those blocks for
|
||||
* data related to this wallet.
|
||||
*/
|
||||
@ExperimentalCoroutinesApi
|
||||
@OptIn(kotlinx.coroutines.ObsoleteCoroutinesApi::class)
|
||||
@FlowPreview
|
||||
class SdkSynchronizer internal constructor(
|
||||
private val storage: TransactionRepository,
|
||||
|
@ -109,6 +108,8 @@ class SdkSynchronizer internal constructor(
|
|||
private val _saplingBalances = MutableStateFlow<WalletBalance?>(null)
|
||||
private val _transparentBalances = MutableStateFlow<WalletBalance?>(null)
|
||||
|
||||
// TODO [#288]: Remove Deprecated Usage of ConflatedBroadcastChannel
|
||||
// TODO [#288]: https://github.com/zcash/zcash-android-wallet-sdk/issues/288
|
||||
private val _status = ConflatedBroadcastChannel<Synchronizer.Status>(DISCONNECTED)
|
||||
|
||||
/**
|
||||
|
@ -171,6 +172,9 @@ class SdkSynchronizer internal constructor(
|
|||
* processor is finished scanning, the synchronizer updates transaction and balance info and
|
||||
* then emits a [SYNCED] status.
|
||||
*/
|
||||
// TODO [#658] Replace ComputableFlow and asFlow() obsolete Coroutine usage
|
||||
// TODO [#658] https://github.com/zcash/zcash-android-wallet-sdk/issues/658
|
||||
@Suppress("DEPRECATION")
|
||||
override val status = _status.asFlow()
|
||||
|
||||
/**
|
||||
|
@ -414,6 +418,7 @@ class SdkSynchronizer internal constructor(
|
|||
twig("Synchronizer onReady complete. Processor start has exited!")
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun onCriticalError(unused: CoroutineContext?, error: Throwable) {
|
||||
twig("********")
|
||||
twig("******** ERROR: $error")
|
||||
|
@ -641,8 +646,9 @@ class SdkSynchronizer internal constructor(
|
|||
// only submit if it wasn't cancelled. Otherwise cleanup, immediately for best UX.
|
||||
if (encodedTx.isCancelled()) {
|
||||
twig("[cleanup] this tx has been cancelled so we will cleanup instead of submitting")
|
||||
if (cleanupCancelledTx(encodedTx)) refreshAllBalances()
|
||||
encodedTx
|
||||
if (cleanupCancelledTx(encodedTx)) {
|
||||
refreshAllBalances()
|
||||
}
|
||||
} else {
|
||||
txManager.submit(encodedTx)
|
||||
}
|
||||
|
@ -673,8 +679,9 @@ class SdkSynchronizer internal constructor(
|
|||
// only submit if it wasn't cancelled. Otherwise cleanup, immediately for best UX.
|
||||
if (encodedTx.isCancelled()) {
|
||||
twig("[cleanup] this shielding tx has been cancelled so we will cleanup instead of submitting")
|
||||
if (cleanupCancelledTx(encodedTx)) refreshAllBalances()
|
||||
encodedTx
|
||||
if (cleanupCancelledTx(encodedTx)) {
|
||||
refreshAllBalances()
|
||||
}
|
||||
} else {
|
||||
txManager.submit(encodedTx)
|
||||
}
|
||||
|
@ -685,8 +692,8 @@ class SdkSynchronizer internal constructor(
|
|||
txManager.monitorById(it.id)
|
||||
}.distinctUntilChanged()
|
||||
|
||||
override suspend fun refreshUtxos(address: String, startHeight: BlockHeight): Int? {
|
||||
return processor.refreshUtxos(address, startHeight)
|
||||
override suspend fun refreshUtxos(tAddr: String, since: BlockHeight): Int? {
|
||||
return processor.refreshUtxos(tAddr, since)
|
||||
}
|
||||
|
||||
override suspend fun getTransparentBalance(tAddr: String): WalletBalance {
|
||||
|
|
|
@ -76,6 +76,7 @@ import kotlin.math.roundToInt
|
|||
* in when considering initial range to download. In most cases, this should be the birthday height
|
||||
* of the current wallet--the height before which we do not need to scan for transactions.
|
||||
*/
|
||||
@OptIn(kotlinx.coroutines.ObsoleteCoroutinesApi::class)
|
||||
@OpenForTesting
|
||||
class CompactBlockProcessor internal constructor(
|
||||
val downloader: CompactBlockDownloader,
|
||||
|
@ -126,6 +127,8 @@ class CompactBlockProcessor internal constructor(
|
|||
)
|
||||
)
|
||||
|
||||
// TODO [#288]: Remove Deprecated Usage of ConflatedBroadcastChannel
|
||||
// TODO [#288]: https://github.com/zcash/zcash-android-wallet-sdk/issues/288
|
||||
private val _state: ConflatedBroadcastChannel<State> = ConflatedBroadcastChannel(Initialized)
|
||||
private val _progress = ConflatedBroadcastChannel(0)
|
||||
private val _processorInfo =
|
||||
|
@ -161,18 +164,27 @@ class CompactBlockProcessor internal constructor(
|
|||
* The flow of state values so that a wallet can monitor the state of this class without needing
|
||||
* to poll.
|
||||
*/
|
||||
// TODO [#658] Replace ComputableFlow and asFlow() obsolete Coroutine usage
|
||||
// TODO [#658] https://github.com/zcash/zcash-android-wallet-sdk/issues/658
|
||||
@Suppress("DEPRECATION")
|
||||
val state = _state.asFlow()
|
||||
|
||||
/**
|
||||
* The flow of progress values so that a wallet can monitor how much downloading remains
|
||||
* without needing to poll.
|
||||
*/
|
||||
// TODO [#658] Replace ComputableFlow and asFlow() obsolete Coroutine usage
|
||||
// TODO [#658] https://github.com/zcash/zcash-android-wallet-sdk/issues/658
|
||||
@Suppress("DEPRECATION")
|
||||
val progress = _progress.asFlow()
|
||||
|
||||
/**
|
||||
* The flow of detailed processorInfo like the range of blocks that shall be downloaded and
|
||||
* scanned. This gives the wallet a lot of insight into the work of this processor.
|
||||
*/
|
||||
// TODO [#658] Replace ComputableFlow and asFlow() obsolete Coroutine usage
|
||||
// TODO [#658] https://github.com/zcash/zcash-android-wallet-sdk/issues/658
|
||||
@Suppress("DEPRECATION")
|
||||
val processorInfo = _processorInfo.asFlow()
|
||||
|
||||
/**
|
||||
|
@ -393,12 +405,8 @@ class CompactBlockProcessor internal constructor(
|
|||
}
|
||||
}
|
||||
|
||||
newTxs?.onEach { newTransaction ->
|
||||
if (newTransaction == null) {
|
||||
twig("somehow, new transaction was null!!!")
|
||||
} else {
|
||||
enhance(newTransaction)
|
||||
}
|
||||
newTxs.onEach { newTransaction ->
|
||||
enhance(newTransaction)
|
||||
}
|
||||
twig("Done enhancing transaction details")
|
||||
BlockProcessingResult.Success
|
||||
|
@ -864,6 +872,7 @@ class CompactBlockProcessor internal constructor(
|
|||
* when we unexpectedly lose server connection or are waiting for an event to happen on the
|
||||
* chain. We can pass this desire along now and later figure out how to handle it, privately.
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun calculatePollInterval(fastIntervalDesired: Boolean = false): Long {
|
||||
val interval = POLL_INTERVAL
|
||||
val now = System.currentTimeMillis()
|
||||
|
@ -1119,7 +1128,7 @@ class CompactBlockProcessor internal constructor(
|
|||
}
|
||||
|
||||
private fun Service.LightdInfo.matchingNetwork(network: String): Boolean {
|
||||
fun String.toId() = toLowerCase(Locale.US).run {
|
||||
fun String.toId() = lowercase(Locale.US).run {
|
||||
when {
|
||||
contains("main") -> "mainnet"
|
||||
contains("test") -> "testnet"
|
||||
|
@ -1140,7 +1149,6 @@ class CompactBlockProcessor internal constructor(
|
|||
twig("$name MUTEX: releasing lock", -1)
|
||||
}
|
||||
}
|
||||
twig("$name MUTEX: withLock complete", -1)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package cash.z.ecc.android.sdk.db.entity
|
|||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.RoomWarnings
|
||||
|
||||
@Entity(
|
||||
tableName = "received_notes",
|
||||
|
@ -25,6 +26,7 @@ import androidx.room.ForeignKey
|
|||
)
|
||||
]
|
||||
)
|
||||
@SuppressWarnings(RoomWarnings.MISSING_INDEX_ON_FOREIGN_KEY_CHILD)
|
||||
data class Received(
|
||||
@ColumnInfo(name = "id_note")
|
||||
val id: Int? = 0,
|
||||
|
|
|
@ -3,6 +3,7 @@ package cash.z.ecc.android.sdk.db.entity
|
|||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.RoomWarnings
|
||||
|
||||
@Entity(
|
||||
tableName = "sent_notes",
|
||||
|
@ -19,6 +20,7 @@ import androidx.room.ForeignKey
|
|||
)
|
||||
]
|
||||
)
|
||||
@SuppressWarnings(RoomWarnings.MISSING_INDEX_ON_FOREIGN_KEY_CHILD)
|
||||
data class Sent(
|
||||
@ColumnInfo(name = "id_note")
|
||||
val id: Int? = 0,
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.room.ColumnInfo
|
|||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.RoomWarnings
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
|
||||
|
@ -23,6 +24,7 @@ import cash.z.ecc.android.sdk.model.Zatoshi
|
|||
)
|
||||
]
|
||||
)
|
||||
@SuppressWarnings(RoomWarnings.MISSING_INDEX_ON_FOREIGN_KEY_CHILD)
|
||||
data class TransactionEntity(
|
||||
@ColumnInfo(name = "id_tx")
|
||||
val id: Long?,
|
||||
|
|
|
@ -3,6 +3,7 @@ package cash.z.ecc.android.sdk.db.entity
|
|||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.RoomWarnings
|
||||
|
||||
@Entity(
|
||||
tableName = "utxos",
|
||||
|
@ -15,6 +16,7 @@ import androidx.room.ForeignKey
|
|||
)
|
||||
]
|
||||
)
|
||||
@SuppressWarnings(RoomWarnings.MISSING_INDEX_ON_FOREIGN_KEY_CHILD)
|
||||
data class Utxo(
|
||||
@ColumnInfo(name = "id_utxo")
|
||||
val id: Long? = 0L,
|
||||
|
|
|
@ -10,8 +10,8 @@ class BatchMetrics(val range: ClosedRange<BlockHeight>, val batchSize: Int, priv
|
|||
private var batchStartTime = 0L
|
||||
private var batchEndTime = 0L
|
||||
private var rangeSize = range.endInclusive.value - range.start.value + 1
|
||||
private inline fun now() = System.currentTimeMillis()
|
||||
private inline fun ips(blocks: Long, time: Long) = 1000.0f * blocks / time
|
||||
private fun now() = System.currentTimeMillis()
|
||||
private fun ips(blocks: Long, time: Long) = 1000.0f * blocks / time
|
||||
|
||||
val isComplete get() = completedBatches * batchSize >= rangeSize
|
||||
val isBatchComplete get() = batchEndTime > batchStartTime
|
||||
|
@ -29,8 +29,6 @@ class BatchMetrics(val range: ClosedRange<BlockHeight>, val batchSize: Int, priv
|
|||
fun endBatch() {
|
||||
completedBatches++
|
||||
batchEndTime = now()
|
||||
onMetricComplete?.let {
|
||||
it.invoke(this, isComplete)
|
||||
}
|
||||
onMetricComplete?.invoke(this, isComplete)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ enum class ConsensusBranchId(val displayName: String, val id: Long, val hexId: S
|
|||
fun fromId(id: Long): ConsensusBranchId? = values().firstOrNull { it.id == id }
|
||||
|
||||
fun fromHex(hex: String): ConsensusBranchId? = values().firstOrNull { branch ->
|
||||
hex.toLowerCase(Locale.US).replace("_", "").replaceFirst("0x", "").let { sanitized ->
|
||||
hex.lowercase(Locale.US).replace("_", "").replaceFirst("0x", "").let { sanitized ->
|
||||
branch.hexId.equals(sanitized, true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.room.Dao
|
|||
import androidx.room.Database
|
||||
import androidx.room.Query
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.RoomWarnings
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
@ -302,6 +303,7 @@ interface TransactionDao {
|
|||
LIMIT :limit
|
||||
"""
|
||||
)
|
||||
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
|
||||
fun getSentTransactions(limit: Int = Int.MAX_VALUE): DataSource.Factory<Int, ConfirmedTransaction>
|
||||
|
||||
/**
|
||||
|
@ -328,6 +330,7 @@ interface TransactionDao {
|
|||
LIMIT :limit
|
||||
"""
|
||||
)
|
||||
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
|
||||
fun getReceivedTransactions(limit: Int = Int.MAX_VALUE): DataSource.Factory<Int, ConfirmedTransaction>
|
||||
|
||||
/**
|
||||
|
@ -430,7 +433,6 @@ interface TransactionDao {
|
|||
var success = false
|
||||
try {
|
||||
var hasInitialMatch = false
|
||||
var hasFinalMatch = true
|
||||
twig("[cleanup] cleanupCancelledTx starting...")
|
||||
findUnminedTransactionIds(rawTransactionId).also {
|
||||
twig("[cleanup] cleanupCancelledTx found ${it.size} matching transactions to cleanup")
|
||||
|
@ -438,7 +440,7 @@ interface TransactionDao {
|
|||
hasInitialMatch = true
|
||||
removeInvalidOutboundTransaction(transactionId)
|
||||
}
|
||||
hasFinalMatch = findMatchingTransactionId(rawTransactionId) != null
|
||||
val hasFinalMatch = findMatchingTransactionId(rawTransactionId) != null
|
||||
success = hasInitialMatch && !hasFinalMatch
|
||||
twig("[cleanup] cleanupCancelledTx Done. success? $success")
|
||||
} catch (t: Throwable) {
|
||||
|
|
|
@ -28,11 +28,11 @@ internal inline fun <R> tryWarn(
|
|||
} catch (t: Throwable) {
|
||||
val shouldThrowAnyway = (
|
||||
unlessContains != null &&
|
||||
(t.message?.toLowerCase()?.contains(unlessContains.toLowerCase()) == true)
|
||||
(t.message?.lowercase()?.contains(unlessContains.lowercase()) == true)
|
||||
) ||
|
||||
(
|
||||
ifContains != null &&
|
||||
(t.message?.toLowerCase()?.contains(ifContains.toLowerCase()) == false)
|
||||
(t.message?.lowercase()?.contains(ifContains.lowercase()) == false)
|
||||
)
|
||||
if (shouldThrowAnyway) {
|
||||
throw t
|
||||
|
|
|
@ -3,6 +3,7 @@ package cash.z.ecc.android.sdk.internal.ext.android
|
|||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
|
||||
|
@ -12,6 +13,9 @@ import kotlinx.coroutines.flow.onStart
|
|||
import kotlinx.coroutines.launch
|
||||
|
||||
/* Adapted from ComputableLiveData */
|
||||
// TODO [#658] https://github.com/zcash/zcash-android-wallet-sdk/issues/658
|
||||
@Suppress("DEPRECATION")
|
||||
@OptIn(ObsoleteCoroutinesApi::class)
|
||||
abstract class ComputableFlow<T>(dispatcher: CoroutineDispatcher = Dispatchers.IO) {
|
||||
private val computationScope: CoroutineScope = CoroutineScope(dispatcher + SupervisorJob())
|
||||
private val computationChannel: ConflatedBroadcastChannel<T> = ConflatedBroadcastChannel()
|
||||
|
|
|
@ -49,8 +49,7 @@ fun <Key, Value> DataSource.Factory<Key, Value>.toFlowPagedList(
|
|||
*
|
||||
* @see FlowPagedListBuilder
|
||||
*/
|
||||
@SuppressLint("RestrictedApi")
|
||||
inline fun <Key, Value> DataSource.Factory<Key, Value>.toFlowPagedList(
|
||||
fun <Key, Value> DataSource.Factory<Key, Value>.toFlowPagedList(
|
||||
pageSize: Int,
|
||||
initialLoadKey: Key? = null,
|
||||
boundaryCallback: PagedList.BoundaryCallback<Value>? = null,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package cash.z.ecc.android.sdk.internal.ext.android
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.paging.Config
|
||||
|
@ -29,7 +28,7 @@ class FlowPagedListBuilder<Key, Value>(
|
|||
* Creates a FlowPagedListBuilder with required parameters.
|
||||
*
|
||||
* @param dataSourceFactory DataSource factory providing DataSource generations.
|
||||
* @param config Paging configuration.
|
||||
* @param pageSize List page size.
|
||||
*/
|
||||
constructor(dataSourceFactory: DataSource.Factory<Key, Value>, pageSize: Int) : this(
|
||||
dataSourceFactory,
|
||||
|
@ -44,7 +43,6 @@ class FlowPagedListBuilder<Key, Value>(
|
|||
*
|
||||
* @return The Flow of PagedLists
|
||||
*/
|
||||
@SuppressLint("RestrictedApi")
|
||||
fun build(): Flow<List<Value>> {
|
||||
return object : ComputableFlow<List<Value>>(fetchContext) {
|
||||
private lateinit var dataSource: DataSource<Key, Value>
|
||||
|
@ -56,6 +54,7 @@ class FlowPagedListBuilder<Key, Value>(
|
|||
var initializeKey = initialLoadKey
|
||||
if (::list.isInitialized) {
|
||||
twig("list is initialized")
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
initializeKey = list.lastKey as Key
|
||||
}
|
||||
|
||||
|
|
|
@ -73,14 +73,14 @@ class PersistentTransactionManager(
|
|||
//
|
||||
|
||||
override suspend fun initSpend(
|
||||
value: Zatoshi,
|
||||
zatoshi: Zatoshi,
|
||||
toAddress: String,
|
||||
memo: String,
|
||||
fromAccountIndex: Int
|
||||
): PendingTransaction = withContext(Dispatchers.IO) {
|
||||
twig("constructing a placeholder transaction")
|
||||
var tx = PendingTransactionEntity(
|
||||
value = value.value,
|
||||
value = zatoshi.value,
|
||||
toAddress = toAddress,
|
||||
memo = memo.toByteArray(),
|
||||
accountIndex = fromAccountIndex
|
||||
|
@ -264,12 +264,14 @@ class PersistentTransactionManager(
|
|||
/**
|
||||
* Remove a transaction and pretend it never existed.
|
||||
*
|
||||
* @param transaction the transaction to be processed.
|
||||
*
|
||||
* @return the final number of transactions that were removed from the database.
|
||||
*/
|
||||
override suspend fun abort(existingTransaction: PendingTransaction): Int {
|
||||
override suspend fun abort(transaction: PendingTransaction): Int {
|
||||
return pendingTransactionDao {
|
||||
twig("[cleanup] Deleting pendingTxId: ${existingTransaction.id}")
|
||||
delete(existingTransaction as PendingTransactionEntity)
|
||||
twig("[cleanup] Deleting pendingTxId: ${transaction.id}")
|
||||
delete(transaction as PendingTransactionEntity)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -286,13 +288,14 @@ class PersistentTransactionManager(
|
|||
*/
|
||||
private suspend fun <R> safeUpdate(logMessage: String = "", priority: Int = 0, block: suspend PendingTransactionDao.() -> R): R? {
|
||||
return try {
|
||||
twig(logMessage)
|
||||
twig(logMessage, priority)
|
||||
pendingTransactionDao { block() }
|
||||
} catch (t: Throwable) {
|
||||
val stacktrace = StringWriter().also { t.printStackTrace(PrintWriter(it)) }.toString()
|
||||
twig(
|
||||
"Unknown error while attempting to '$logMessage':" +
|
||||
" ${t.message} caused by: ${t.cause} stacktrace: $stacktrace"
|
||||
" ${t.message} caused by: ${t.cause} stacktrace: $stacktrace",
|
||||
priority
|
||||
)
|
||||
null
|
||||
}
|
||||
|
|
|
@ -109,9 +109,11 @@ interface OutboundTransactionManager {
|
|||
/**
|
||||
* Delete the given transaction but return 0 if it did not exist.
|
||||
*
|
||||
* @param transaction the transaction to be processed.
|
||||
*
|
||||
* @return the total number of transactions successfully removed from storage.
|
||||
*/
|
||||
suspend fun abort(it: PendingTransaction): Int
|
||||
suspend fun abort(transaction: PendingTransaction): Int
|
||||
|
||||
/**
|
||||
* Get all pending transactions known to this wallet as a flow that is updated anytime the list
|
||||
|
|
|
@ -12,7 +12,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.withContext
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Tool for loading checkpoints for the wallet, based on the height at which the wallet was born.
|
||||
|
@ -65,11 +65,7 @@ internal object CheckpointTool {
|
|||
*/
|
||||
@VisibleForTesting
|
||||
internal fun checkpointDirectory(network: ZcashNetwork) =
|
||||
"co.electriccoin.zcash/checkpoint/${
|
||||
(network.networkName as java.lang.String).toLowerCase(
|
||||
Locale.ROOT
|
||||
)
|
||||
}"
|
||||
"co.electriccoin.zcash/checkpoint/${network.networkName.lowercase(Locale.ROOT)}"
|
||||
|
||||
internal fun checkpointHeightFromFilename(zcashNetwork: ZcashNetwork, fileName: String) =
|
||||
BlockHeight.new(zcashNetwork, fileName.split('.').first().toLong())
|
||||
|
|
|
@ -83,20 +83,21 @@ class DerivationTool {
|
|||
deriveTransparentAddressFromSeed(seed, account, index, networkId = network.id)
|
||||
}
|
||||
|
||||
override suspend fun deriveTransparentAddressFromPublicKey(transparentPublicKey: String, network: ZcashNetwork): String = withRustBackendLoaded {
|
||||
deriveTransparentAddressFromPubKey(transparentPublicKey, networkId = network.id)
|
||||
override suspend fun deriveTransparentAddressFromPublicKey(publicKey: String, network: ZcashNetwork): String = withRustBackendLoaded {
|
||||
deriveTransparentAddressFromPubKey(pk = publicKey, networkId = network.id)
|
||||
}
|
||||
|
||||
override suspend fun deriveTransparentAddressFromPrivateKey(transparentPrivateKey: String, network: ZcashNetwork): String = withRustBackendLoaded {
|
||||
deriveTransparentAddressFromPrivKey(transparentPrivateKey, networkId = network.id)
|
||||
override suspend fun deriveTransparentAddressFromPrivateKey(privateKey: String, network: ZcashNetwork): String = withRustBackendLoaded {
|
||||
deriveTransparentAddressFromPrivKey(sk = privateKey, networkId = network.id)
|
||||
}
|
||||
|
||||
override suspend fun deriveTransparentSecretKey(seed: ByteArray, network: ZcashNetwork, account: Int, index: Int): String = withRustBackendLoaded {
|
||||
deriveTransparentSecretKeyFromSeed(seed, account, index, networkId = network.id)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun validateUnifiedViewingKey(viewingKey: UnifiedViewingKey, networkId: Int = ZcashNetwork.Mainnet.id) {
|
||||
// TODO
|
||||
// TODO [#654] https://github.com/zcash/zcash-android-wallet-sdk/issues/654
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue