2021-04-09 19:29:29 -07:00
package cash.z.ecc.android.sdk.util
import androidx.test.platform.app.InstrumentationRegistry
import cash.z.ecc.android.bip39.Mnemonics
import cash.z.ecc.android.bip39.toSeed
import cash.z.ecc.android.sdk.Initializer
import cash.z.ecc.android.sdk.SdkSynchronizer
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.db.entity.isPending
import cash.z.ecc.android.sdk.ext.Twig
import cash.z.ecc.android.sdk.ext.twig
import cash.z.ecc.android.sdk.service.LightWalletGrpcService
import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.WalletBalance
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.launch
import kotlinx.coroutines.newFixedThreadPoolContext
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 .
* /
class TestWallet (
2021-04-22 15:52:32 -07:00
val seedPhrase : String ,
val alias : String = " TestWallet " ,
val network : ZcashNetwork = ZcashNetwork . Testnet ,
val host : String = network . defaultHost ,
startHeight : Int ? = null ,
val port : Int = network . defaultPort ,
2021-04-09 19:29:29 -07:00
) {
constructor (
backup : Backups ,
network : ZcashNetwork = ZcashNetwork . Testnet ,
alias : String = " TestWallet "
) : this (
backup . seedPhrase ,
network = network ,
startHeight = backup . testnetBirthday ,
alias = alias
)
val walletScope = CoroutineScope (
SupervisorJob ( ) + newFixedThreadPoolContext ( 3 , this . javaClass . simpleName )
)
private val context = InstrumentationRegistry . getInstrumentation ( ) . context
private val seed : ByteArray = Mnemonics . MnemonicCode ( seedPhrase ) . toSeed ( )
private val shieldedSpendingKey = DerivationTool . deriveSpendingKeys ( seed , network = network ) [ 0 ]
private val transparentSecretKey = DerivationTool . deriveTransparentSecretKey ( seed , network = network )
val initializer = Initializer ( context ) { config ->
2021-04-22 15:52:32 -07:00
config . importWallet ( seed , startHeight , network , host , alias = alias )
2021-04-09 19:29:29 -07:00
}
val synchronizer : SdkSynchronizer = Synchronizer ( initializer ) as SdkSynchronizer
val service = ( synchronizer . processor . downloader . lightWalletService as LightWalletGrpcService )
val available get ( ) = synchronizer . latestBalance . availableZatoshi
val shieldedAddress = DerivationTool . deriveShieldedAddress ( seed , network = network )
val transparentAddress = DerivationTool . deriveTransparentAddress ( seed , network = network )
val birthdayHeight get ( ) = synchronizer . latestBirthdayHeight
val networkName get ( ) = synchronizer . network . networkName
val connectionInfo get ( ) = service . connectionInfo . toString ( )
suspend fun transparentBalance ( ) : WalletBalance {
synchronizer . refreshUtxos ( transparentAddress , synchronizer . latestBirthdayHeight )
return synchronizer . getTransparentBalance ( transparentAddress )
}
suspend fun sync ( timeout : Long = - 1 ) : TestWallet {
val killSwitch = walletScope . launch {
if ( timeout > 0 ) {
delay ( timeout )
throw TimeoutException ( " Failed to sync wallet within ${timeout} ms " )
}
}
if ( ! synchronizer . isStarted ) {
twig ( " Starting sync " )
synchronizer . start ( walletScope )
} else {
twig ( " Awaiting next SYNCED status " )
}
// block until synced
synchronizer . status . first { it == Synchronizer . Status . SYNCED }
killSwitch . cancel ( )
twig ( " Synced! " )
return this
}
2021-04-22 15:52:32 -07:00
suspend fun send ( address : String = transparentAddress , memo : String = " " , amount : Long = 500L , fromAccountIndex : Int = 0 ) : TestWallet {
Twig . sprout ( " $alias sending " )
synchronizer . sendToAddress ( shieldedSpendingKey , amount , address , memo , fromAccountIndex )
2021-04-09 19:29:29 -07:00
. takeWhile { it . isPending ( ) }
. collect {
twig ( " Updated transaction: $it " )
}
2021-04-22 15:52:32 -07:00
Twig . clip ( " $alias sending " )
2021-04-09 19:29:29 -07:00
return this
}
suspend fun rewindToHeight ( height : Int ) : TestWallet {
2021-04-26 14:37:30 -07:00
synchronizer . rewindToNearestHeight ( height , false )
2021-04-09 19:29:29 -07:00
return this
}
suspend fun shieldFunds ( ) : TestWallet {
twig ( " checking $transparentAddress for transactions! " )
synchronizer . refreshUtxos ( transparentAddress , 935000 ) . let { count ->
twig ( " FOUND $count new UTXOs " )
}
synchronizer . getTransparentBalance ( transparentAddress ) . let { walletBalance ->
twig ( " FOUND utxo balance of total: ${walletBalance.totalZatoshi} available: ${walletBalance.availableZatoshi} " )
if ( walletBalance . availableZatoshi > 0L ) {
synchronizer . shieldFunds ( shieldedSpendingKey , transparentSecretKey )
. onCompletion { twig ( " done shielding funds " ) }
. catch { twig ( " Failed with $it " ) }
. collect ( )
}
}
return this
}
suspend fun join ( timeout : Long ? = null ) : TestWallet {
// block until stopped
twig ( " Staying alive until synchronizer is stopped! " )
if ( timeout != null ) {
twig ( " Scheduling a stop in ${timeout} ms " )
walletScope . launch {
delay ( timeout )
synchronizer . stop ( )
}
}
synchronizer . status . first { it == Synchronizer . Status . STOPPED }
twig ( " Stopped! " )
return this
}
companion object {
init {
Twig . enabled ( true )
}
}
enum class Backups ( val seedPhrase : String , val testnetBirthday : Int ) {
DEFAULT ( " column rhythm acoustic gym cost fit keen maze fence seed mail medal shrimp tell relief clip cannon foster soldier shallow refuse lunar parrot banana " , 1 _355 _928 ) ,
SAMPLE _WALLET ( " input frown warm senior anxiety abuse yard prefer churn reject people glimpse govern glory crumble swallow verb laptop switch trophy inform friend permit purpose " , 1 _330 _190 ) ,
2021-04-26 14:37:30 -07:00
DEV _WALLET ( " still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread " , 991645 ) ,
2021-04-09 19:29:29 -07:00
ALICE ( " quantum whisper lion route fury lunar pelican image job client hundred sauce chimney barely life cliff spirit admit weekend message recipe trumpet impact kitten " , 1 _330 _190 ) ,
BOB ( " canvas wine sugar acquire garment spy tongue odor hole cage year habit bullet make label human unit option top calm neutral try vocal arena " , 1 _330 _190 ) ,
;
}
}