@file:Suppress("MaxLineLength") package cash.z.ecc.android.sdk.sample import androidx.test.filters.LargeTest import cash.z.ecc.android.sdk.ext.ZcashSdk import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.Zatoshi import cash.z.ecc.android.sdk.model.ZcashNetwork import cash.z.ecc.android.sdk.util.TestWallet import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import org.junit.Assert import org.junit.Ignore import org.junit.Test /** * Sample tests are used to demonstrate functionality. This one attempts to setup a scenario where * one wallet shields funds and the other restores from the blockchain. Ultimately, they should have * the same data. */ class TransparentRestoreSample { val TX_VALUE = Zatoshi(ZcashSdk.MINERS_FEE.value / 2) // val walletA = SimpleWallet(SEED_PHRASE, "WalletA") // the wallet that only restores what everyone else did // val walletB = SimpleWallet(SEED_PHRASE, "WalletB") // // the wallet that sends Z2T transactions // // // sandbox wallet // val walletSandbox = SimpleWallet(SEED_PHRASE, "WalletC") // val walletZ2T = SimpleWallet(SEED_PHRASE, "WalletZ2T") // val externalTransparentAddress = // DerivationTool.deriveTransparentAddress(Mnemonics.MnemonicCode(RANDOM_PHRASE).toSeed(), Testnet) // @Test fun sendZ2Texternal() = runBlocking { val extWallet = TestWallet(TestWallet.Backups.ALICE, alias = "WalletE") extWallet.sync() // extWallet.send(542, walletSandbox.transparentAddress, "External funds memo is lost, though") delay(1000) } // @Test fun sendZ2T() = runBlocking { // walletSandbox.sync() // walletZ2T.send(543, externalTransparentAddress, "External funds memo is lost, though") delay(1000) } // @Test fun autoShield() = runBlocking { val wallet = TestWallet(TestWallet.Backups.SAMPLE_WALLET, alias = "WalletC") wallet.sync() val tbalance = wallet.transparentBalance() val address = wallet.transparentAddress Assert.assertTrue( "Not enough funds to run sample. Expected some Zatoshi but found ${tbalance.available}. " + "Try adding funds to $address", tbalance.available.value > 0 ) // wallet.shieldFunds() } // @Test fun cli() = runBlocking { // val wallet = SimpleWallet(SEED_PHRASE, "WalletCli") // wallet.sync() // wallet.rewindToHeight(1343500).join(45_000) val wallet = TestWallet(TestWallet.Backups.SAMPLE_WALLET, alias = "WalletC") // wallet.sync().rewindToHeight(1339178).join(10000) wallet.sync().rewindToHeight(BlockHeight.new(ZcashNetwork.Testnet, 1339178)).send( "ztestsapling17zazsl8rryl8kjaqxnr2r29rw9d9a2mud37ugapm0s8gmyv0ue43h9lqwmhdsp3nu9dazeqfs6l", "is send broken?" ).join(5) } // This test is extremely slow and doesn't assert anything, so the benefit of this test is unclear // It is disabled to allow moving forward with configuring CI. @Test @LargeTest @Ignore("This test is extremely slow") fun kris() = runBlocking { val wallet0 = TestWallet( TestWallet.Backups.SAMPLE_WALLET.seedPhrase, "tmpabc", ZcashNetwork.Testnet, startHeight = BlockHeight.new( ZcashNetwork.Testnet, 1330190 ) ) // val wallet1 = SimpleWallet(WALLET0_PHRASE, "Wallet1") wallet0.sync() // .shieldFunds() // .send(amount = 1543L, memo = "") .join() // wallet1.sync().join(5_000L) } /* */ /** * Sanity check that the wallet has enough funds for the test */ // @Test fun hasFunds() = runBlocking { val walletSandbox = TestWallet( TestWallet.Backups.SAMPLE_WALLET.seedPhrase, "WalletC", ZcashNetwork.Testnet, startHeight = BlockHeight.new( ZcashNetwork.Testnet, 1330190 ) ) // val job = walletA.walletScope.launch { // walletA.sync() // } walletSandbox.sync() // job.join() delay(500) // val value = walletA.available // val address = walletA.shieldedAddress // Assert.assertTrue("Not enough funds to run sample. Expected at least $TX_VALUE Zatoshi but found $value. Try adding funds to $address", value >= TX_VALUE) // send z->t // walletA.send(TX_VALUE, walletA.transparentAddress, "${TransparentRestoreSample::class.java.simpleName} z->t") walletSandbox.rewindToHeight(BlockHeight.new(ZcashNetwork.Testnet, 1339178)) delay(500) // walletB.sync() // rewind database B to height then rescan } // // when startHeight is null, it will use the latest checkpoint // class SimpleWallet( // seedPhrase: String, // alias: String = ZcashSdk.DEFAULT_ALIAS, // startHeight: Int? = null // ) { // 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, Testnet)[0] // private val transparentAccountPrivateKey = DerivationTool.deriveTransparentAccountPrivateKey(seed, Testnet) // private val host = "lightwalletd.testnet.electriccoin.co" // private val initializer = Initializer(context) { config -> // config.importWallet(seed, startHeight) // config.setNetwork(Testnet, host) // config.alias = alias // } // // val synchronizer = Synchronizer(initializer) // val available get() = synchronizer.latestBalance.availableZatoshi // val shieldedAddress = DerivationTool.deriveShieldedAddress(seed, Testnet) // val transparentAddress = DerivationTool.deriveTransparentAddress(seed, Testnet) // val birthdayHeight get() = synchronizer.latestBirthdayHeight // // suspend fun transparentBalance(): WalletBalance { // synchronizer.refreshUtxos(transparentAddress, synchronizer.latestBirthdayHeight) // return synchronizer.getTransparentBalance(transparentAddress) // } // // suspend fun sync(): SimpleWallet { // if (!synchronizer.isStarted) { // twig("Starting sync") // synchronizer.start(walletScope) // } else { // twig("Awaiting next SYNCED status") // } // // // block until synced // synchronizer.status.first { it == SYNCED } // twig("Synced!") // return this // } // // suspend fun send(address: String = transparentAddress, memo: String = "", amount: Long = 500L): SimpleWallet { // synchronizer.sendToAddress(shieldedSpendingKey, amount, address, memo) // .takeWhile { it.isPending() } // .collect { // twig("Updated transaction: $it") // } // return this // } // // suspend fun rewindToHeight(height: Int): SimpleWallet { // synchronizer.rewindToHeight(height, false) // return this // } // // suspend fun shieldFunds(): SimpleWallet { // 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, transparentAccountPrivateKey) // .onCompletion { twig("done shielding funds") } // .catch { twig("Failed with $it") } // .collect() // } // } // // return this // } // // suspend fun join(timeout: Long? = null): SimpleWallet { // // 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.plant(TroubleshootingTwig()) // } // } // } }