[#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:
Honza Rychnovsky 2022-08-17 15:48:02 +02:00 committed by GitHub
parent 10d0652a8d
commit 150778f008
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 256 additions and 192 deletions

View File

@ -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" />

View File

@ -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)
}

View File

@ -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

View File

@ -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)
}

View File

@ -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)
)
}

View File

@ -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
}

View File

@ -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",

View File

@ -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")
}

View File

@ -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)
}

View File

@ -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()
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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()
}
//

View File

@ -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() {

View File

@ -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)

View File

@ -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?) {

View File

@ -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))
}

View File

@ -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"
}
}

View File

@ -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() {

View File

@ -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))
}

View File

@ -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"
}
}

View File

@ -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?) {

View File

@ -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)

View File

@ -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)

View File

@ -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
}

View File

@ -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

View File

@ -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)

View File

@ -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")
}
}

View File

@ -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)

View File

@ -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")

View File

@ -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 {

View File

@ -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)
)
}

View File

@ -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
}

View File

@ -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",

View File

@ -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)?,

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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?,

View File

@ -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,

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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()

View File

@ -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,

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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())

View File

@ -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
}
/**