New: Begin working on test harness.

For now, just pushing all uncommitted changes to tests. Next sprint we will begin focusing on generating a useful test harness.
This commit is contained in:
Kevin Gorham 2021-04-22 18:52:32 -04:00
parent 05b8d0ddc4
commit 30f7bf0b82
No known key found for this signature in database
GPG Key ID: CCA55602DF49FC38
4 changed files with 299 additions and 52 deletions

View File

@ -0,0 +1,29 @@
package cash.z.ecc.android.sdk.integration.darkside.reproduce
import cash.z.ecc.android.sdk.ext.DarksideTest
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test
class ReproduceZ2TFailureTest : DarksideTest() {
@Before
fun setup() {
println("dBUG RUNNING")
}
@Test
fun once() {
}
@Test
fun twice() {
}
companion object {
@JvmStatic
@BeforeClass
fun beforeAll() {
println("dBUG BEFOERE IOT ALL")
}
}
}

View File

@ -0,0 +1,229 @@
package cash.z.ecc.android.sdk.sample
import cash.z.ecc.android.sdk.ext.ZcashSdk
import cash.z.ecc.android.sdk.ext.twig
import cash.z.ecc.android.sdk.type.ZcashNetwork.Testnet
import cash.z.ecc.android.sdk.util.TestWallet
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.junit.Assert
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 = ZcashSdk.MINERS_FEE_ZATOSHI / 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 {
twig("Syncing WalletExt")
val extWallet = TestWallet(TestWallet.Backups.ALICE, alias = "WalletE")
extWallet.sync()
// extWallet.send(542, walletSandbox.transparentAddress, "External funds memo is lost, though")
delay(1000)
twig("Done sending funds to external address (Z->T COMPLETE!)")
}
// @Test
fun sendZ2T() = runBlocking {
// walletSandbox.sync()
// walletZ2T.send(543, externalTransparentAddress, "External funds memo is lost, though")
delay(1000)
twig("Done sending funds to external address (Z->T COMPLETE!)")
}
// @Test
fun autoShield() = runBlocking<Unit> {
val wallet = TestWallet(TestWallet.Backups.SAMPLE_WALLET, alias = "WalletC")
wallet.sync()
twig("Done syncing wallet!")
val tbalance = wallet.transparentBalance()
val address = wallet.transparentAddress
twig("t-avail: ${tbalance.availableZatoshi} t-total: ${tbalance.totalZatoshi}")
Assert.assertTrue("Not enough funds to run sample. Expected some Zatoshi but found ${tbalance.availableZatoshi}. Try adding funds to $address", tbalance.availableZatoshi > 0)
twig("Shielding available transparent funds!")
// wallet.shieldFunds()
}
// @Test
fun cli() = runBlocking<Unit> {
// 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(1339178).send(
"ztestsapling17zazsl8rryl8kjaqxnr2r29rw9d9a2mud37ugapm0s8gmyv0ue43h9lqwmhdsp3nu9dazeqfs6l",
"is send broken?"
).join(5)
}
@Test
fun kris() = runBlocking<Unit> {
val wallet0 = TestWallet(TestWallet.Backups.SAMPLE_WALLET.seedPhrase, "tmpabc", Testnet, startHeight = 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<Unit> {
val walletSandbox = TestWallet(TestWallet.Backups.SAMPLE_WALLET.seedPhrase, "WalletC", Testnet, startHeight = 1330190)
// val job = walletA.walletScope.launch {
// twig("Syncing WalletA")
// walletA.sync()
// }
twig("Syncing WalletSandbox")
walletSandbox.sync()
// job.join()
delay(500)
twig("Done syncing both wallets!")
// 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(1339178)
twig("Done REWINDING!")
twig("T-ADDR (for the win!): ${walletSandbox.transparentAddress}")
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 transparentSecretKey = DerivationTool.deriveTransparentSecretKey(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, transparentSecretKey)
// .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())
// }
// }
// }
}

View File

@ -1,24 +1,17 @@
package cash.z.ecc.android.sdk.util
import androidx.test.platform.app.InstrumentationRegistry
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.isSubmitSuccess
import cash.z.ecc.android.sdk.ext.ScopedTest
import cash.z.ecc.android.sdk.ext.Twig
import cash.z.ecc.android.sdk.ext.seedPhrase
import cash.z.ecc.android.sdk.ext.twig
import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import io.grpc.StatusRuntimeException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
@ -26,11 +19,17 @@ import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
class DarksideTestCoordinator(val host: String = "127.0.0.1", val testName: String = "DarksideTestCoordinator", val network: ZcashNetwork = ZcashNetwork.Mainnet, val port: Int = 9067) {
private val birthdayHeight = 663150
class DarksideTestCoordinator(val wallet: TestWallet) {
constructor(
alias: String = "DarksideTestCoordinator",
seedPhrase: String = DEFAULT_SEED_PHRASE,
startHeight: Int = DEFAULT_START_HEIGHT,
host: String = "127.0.0.1",
network: ZcashNetwork = ZcashNetwork.Mainnet,
port: Int = network.defaultPort,
) : this(TestWallet(seedPhrase, alias, network, host, port, startHeight))
private val targetHeight = 663250
private val seedPhrase =
"still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread"
private val context = InstrumentationRegistry.getInstrumentation().context
// dependencies: private
@ -39,10 +38,10 @@ class DarksideTestCoordinator(val host: String = "127.0.0.1", val testName: Stri
// dependencies: public
val validator = DarksideTestValidator()
val chainMaker = DarksideChainMaker()
lateinit var synchronizer: Synchronizer
val spendingKey: String get() = DerivationTool.deriveSpendingKeys(SimpleMnemonics().toSeed(seedPhrase.toCharArray()), network)[0]
// wallet delegates
val synchronizer get() = wallet.synchronizer
val send get() = wallet::send
//
// High-level APIs
//
@ -77,14 +76,7 @@ class DarksideTestCoordinator(val host: String = "127.0.0.1", val testName: Stri
*/
fun initiate() {
twig("*************** INITIALIZING TEST COORDINATOR (ONLY ONCE) ***********************")
val initializer = Initializer(context) { config ->
config.setNetwork(network, host, port)
config.seedPhrase(seedPhrase, network)
config.setBirthdayHeight(birthdayHeight)
config.alias = testName
}
synchronizer = Synchronizer(initializer)
val channel = (synchronizer as SdkSynchronizer).channel
val channel = synchronizer.channel
darkside = DarksideApi(channel)
darkside.reset()
}
@ -101,6 +93,7 @@ class DarksideTestCoordinator(val host: String = "127.0.0.1", val testName: Stri
synchronizer.start(scope)
}
// redo this as a call to wallet but add delay time to wallet join() function
/**
* Waits for, at most, the given amount of time for the synchronizer to download and scan blocks
* and reach a 'SYNCED' status.
@ -129,27 +122,19 @@ class DarksideTestCoordinator(val host: String = "127.0.0.1", val testName: Stri
}
}
/**
* Send a transaction and wait until it has been fully created and successfully submitted, which
* takes about 10 seconds.
*/
suspend fun createAndSubmitTx(
zatoshi: Long,
toAddress: String,
memo: String = "",
fromAccountIndex: Int = 0
) = coroutineScope {
Twig.sprout("sending")
var job: Job? = null
job = synchronizer
.sendToAddress(spendingKey, zatoshi, toAddress, memo, fromAccountIndex)
.onEach {
twig("got an update submitted yet? ${it.isSubmitSuccess()}")
if (it.isSubmitSuccess()) job?.cancel()
}.launchIn(this)
job.join()
Twig.clip("sending")
}
// /**
// * Send a transaction and wait until it has been fully created and successfully submitted, which
// * takes about 10 seconds.
// */
// suspend fun createAndSubmitTx(
// zatoshi: Long,
// toAddress: String,
// memo: String = "",
// fromAccountIndex: Int = 0
// ) = coroutineScope {
//
// wallet.send(toAddress, memo, zatoshi, fromAccountIndex)
// }
fun stall(delay: Long = 5000L) = runBlocking {
twig("*** Stalling for ${delay}ms ***")
@ -319,5 +304,7 @@ class DarksideTestCoordinator(val host: String = "127.0.0.1", val testName: Stri
private const val largeReorg =
"https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/after-large-reorg.txt"
private const val DEFAULT_START_HEIGHT = 663150
private const val DEFAULT_SEED_PHRASE =
"still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread"
}
}

View File

@ -30,11 +30,12 @@ import java.util.concurrent.TimeoutException
* easy to drive and nice to use.
*/
class TestWallet(
seedPhrase: String,
alias: String = "TestWallet",
network: ZcashNetwork = ZcashNetwork.Testnet,
host: String? = null,
startHeight: Int? = null
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,
) {
constructor(
backup: Backups,
@ -47,7 +48,6 @@ class TestWallet(
alias = alias
)
val hostToUse = host ?: network.defaultHost
val walletScope = CoroutineScope(
SupervisorJob() + newFixedThreadPoolContext(3, this.javaClass.simpleName)
)
@ -56,7 +56,7 @@ class TestWallet(
private val shieldedSpendingKey = DerivationTool.deriveSpendingKeys(seed, network = network)[0]
private val transparentSecretKey = DerivationTool.deriveTransparentSecretKey(seed, network = network)
val initializer = Initializer(context) { config ->
config.importWallet(seed, startHeight, network, hostToUse, alias = alias)
config.importWallet(seed, startHeight, network, host, alias = alias)
}
val synchronizer: SdkSynchronizer = Synchronizer(initializer) as SdkSynchronizer
val service = (synchronizer.processor.downloader.lightWalletService as LightWalletGrpcService)
@ -94,12 +94,14 @@ class TestWallet(
return this
}
suspend fun send(address: String = transparentAddress, memo: String = "", amount: Long = 500L): TestWallet {
synchronizer.sendToAddress(shieldedSpendingKey, amount, address, memo)
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)
.takeWhile { it.isPending() }
.collect {
twig("Updated transaction: $it")
}
Twig.clip("$alias sending")
return this
}