2020-06-10 00:08:19 -07:00
package cash.z.ecc.android.sdk.demoapp.demos.getblockrange
2019-10-21 03:17:07 -07:00
2020-09-11 00:16:46 -07:00
import android.os.Bundle
2019-10-21 03:17:07 -07:00
import android.view.LayoutInflater
import android.view.View
2022-08-17 06:48:02 -07:00
import androidx.core.text.HtmlCompat
2020-06-10 00:08:19 -07:00
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
2020-09-11 00:16:46 -07:00
import cash.z.ecc.android.sdk.demoapp.R
2020-06-10 00:08:19 -07:00
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetBlockRangeBinding
2021-09-04 04:05:41 -07:00
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
2022-12-23 02:00:37 -08:00
import cash.z.ecc.android.sdk.demoapp.type.fromResources
2020-09-11 00:16:46 -07:00
import cash.z.ecc.android.sdk.demoapp.util.mainActivity
import cash.z.ecc.android.sdk.demoapp.util.toRelativeTime
import cash.z.ecc.android.sdk.demoapp.util.withCommas
2022-07-12 05:40:09 -07:00
import cash.z.ecc.android.sdk.model.BlockHeight
2022-08-02 06:29:09 -07:00
import cash.z.ecc.android.sdk.model.ZcashNetwork
2022-07-12 05:40:09 -07:00
import kotlin.math.max
2019-10-21 03:17:07 -07:00
2020-02-27 00:25:07 -08:00
/ * *
* Retrieves a range of compact block from the lightwalletd service and displays basic information
2020-09-11 00:16:46 -07:00
* about them . This demonstrates the basic ability to connect to the server , request a range of
2020-02-27 00:25:07 -08:00
* compact block and parse the response . This could be augmented to display metadata about certain
* block ranges for instance , to find the block with the most shielded transactions in a range .
* /
2019-10-21 03:17:07 -07:00
class GetBlockRangeFragment : BaseDemoFragment < FragmentGetBlockRangeBinding > ( ) {
2022-07-12 05:40:09 -07:00
private fun setBlockRange ( blockRange : ClosedRange < BlockHeight > ) {
2020-09-11 00:16:46 -07:00
val start = System . currentTimeMillis ( )
val blocks =
2022-08-17 06:48:02 -07:00
lightWalletService ?. getBlockRange ( blockRange )
2020-09-11 00:16:46 -07:00
val fetchDelta = System . currentTimeMillis ( ) - start
2019-10-21 03:17:07 -07:00
2020-09-11 00:16:46 -07:00
// Note: This is a demo so we won't worry about iterating efficiently over these blocks
2022-07-28 05:39:48 -07:00
// Note: Converting the blocks sequence to a list can consume a lot of memory and may
// cause OOM.
2022-08-17 06:48:02 -07:00
binding . textInfo . text = HtmlCompat . fromHtml (
2022-07-28 05:39:48 -07:00
blocks ?. toList ( ) ?. run {
2021-09-25 08:11:33 -07:00
val count = size
val emptyCount = count { it . vtxCount == 0 }
val maxTxs = maxByOrNull { it . vtxCount }
val maxIns = maxByOrNull { block ->
block . vtxList . maxOfOrNull { it . spendsCount } ?: - 1
}
val maxInTx = maxIns ?. vtxList ?. maxByOrNull { it . spendsCount }
val maxOuts = maxByOrNull { block ->
block . vtxList . maxOfOrNull { it . outputsCount } ?: - 1
}
val maxOutTx = maxOuts ?. vtxList ?. maxByOrNull { it . outputsCount }
2022-07-28 05:39:48 -07:00
val txCount = sumOf { it . vtxCount }
val outCount = sumOf { block -> block . vtxList . sumOf { it . outputsCount } }
val inCount = sumOf { block -> block . vtxList . sumOf { it . spendsCount } }
2019-10-21 03:17:07 -07:00
2021-09-25 08:11:33 -07:00
val processTime = System . currentTimeMillis ( ) - start - fetchDelta
2022-08-23 06:49:00 -07:00
@Suppress ( " MaxLineLength " , " MagicNumber " )
2021-09-25 08:11:33 -07:00
"""
2020-09-11 00:16:46 -07:00
< b > total blocks : < / b > $ { count . withCommas ( ) }
2021-09-25 08:11:33 -07:00
< br / > < b > fetch time : < / b > $ { if ( fetchDelta > 1000 ) " %.2f sec " . format ( fetchDelta / 1000.0 ) else " %d ms " . format ( fetchDelta ) }
< br / > < b > process time : < / b > $ { if ( processTime > 1000 ) " %.2f sec " . format ( processTime / 1000.0 ) else " %d ms " . format ( processTime ) }
2021-09-04 04:05:41 -07:00
< br / > < b > block time range : < / b > $ { first ( ) . time . toRelativeTime ( requireApplicationContext ( ) ) } < br / > & nbsp ; & nbsp to $ { last ( ) . time . toRelativeTime ( requireApplicationContext ( ) ) }
2020-09-11 00:16:46 -07:00
< br / > < b > total empty blocks : < / b > $ { emptyCount . withCommas ( ) }
< br / > < b > total TXs : < / b > $ { txCount . withCommas ( ) }
< br / > < b > total outputs : < / b > $ { outCount . withCommas ( ) }
< br / > < b > total inputs : < / b > $ { inCount . withCommas ( ) }
2021-09-25 08:11:33 -07:00
< br / > < b > avg TXs / block : < / b > $ { " %.1f " . format ( txCount / count . toDouble ( ) ) }
< br / > < b > avg TXs ( excluding empty blocks ) : < / b > $ { " %.1f " . format ( txCount . toDouble ( ) / ( count - emptyCount ) ) }
< 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()} " }
2022-08-17 06:48:02 -07:00
< 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} " }
2021-09-25 08:11:33 -07:00
""" .trimIndent()
2022-08-17 06:48:02 -07:00
} ?: " No blocks found in that range. " ,
HtmlCompat . FROM _HTML _MODE _LEGACY
2021-09-25 08:11:33 -07:00
)
2019-10-21 03:17:07 -07:00
}
2022-08-17 06:48:02 -07:00
@Suppress ( " UNUSED_PARAMETER " )
2022-08-23 06:49:00 -07:00
private fun onApply ( unused : View ) {
2022-07-12 05:40:09 -07:00
val network = ZcashNetwork . fromResources ( requireApplicationContext ( ) )
2022-08-23 06:49:00 -07:00
val start = max (
binding . textStartHeight . text . toString ( ) . toLongOrNull ( )
?: network . saplingActivationHeight . value ,
network . saplingActivationHeight . value
)
val end = max (
binding . textEndHeight . text . toString ( ) . toLongOrNull ( )
?: network . saplingActivationHeight . value ,
network . saplingActivationHeight . value
)
2019-10-21 03:17:07 -07:00
if ( start <= end ) {
2022-08-23 06:49:00 -07:00
@Suppress ( " TooGenericExceptionCaught " )
2020-09-11 00:16:46 -07:00
try {
with ( binding . buttonApply ) {
isEnabled = false
setText ( R . string . loading )
binding . textInfo . setText ( R . string . loading )
post {
2022-07-12 05:40:09 -07:00
setBlockRange ( BlockHeight . new ( network , start ) .. BlockHeight . new ( network , end ) )
2020-09-11 00:16:46 -07:00
isEnabled = true
setText ( R . string . apply )
}
}
} catch ( t : Throwable ) {
setError ( t . toString ( ) )
}
2019-10-21 03:17:07 -07:00
} else {
setError ( " Invalid range " )
}
2020-09-11 00:16:46 -07:00
mainActivity ( ) ?. hideKeyboard ( )
2019-10-21 03:17:07 -07:00
}
private fun setError ( message : String ) {
binding . textInfo . text = " Error: $message "
}
2020-09-11 00:16:46 -07:00
//
// Android Lifecycle overrides
//
override fun onViewCreated ( view : View , savedInstanceState : Bundle ? ) {
super . onViewCreated ( view , savedInstanceState )
binding . buttonApply . setOnClickListener ( :: onApply )
}
//
// Base Fragment overrides
//
override fun inflateBinding ( layoutInflater : LayoutInflater ) : FragmentGetBlockRangeBinding =
FragmentGetBlockRangeBinding . inflate ( layoutInflater )
2020-09-21 19:43:50 -07:00
override fun onActionButtonClicked ( ) {
super . onActionButtonClicked ( )
}
2020-06-09 20:28:21 -07:00
}