v1.9.0-beta01 changes

This commit includes HUSH specific changes starting at v.1.9.0-beta01 release here: https://github.com/zcash/zcash-android-wallet-sdk/releases/tag/v1.9.0-beta01
This commit is contained in:
fekt
2022-11-29 20:19:12 -05:00
parent 1b4660b53b
commit 2225f65ee7
115 changed files with 2335 additions and 1766 deletions

View File

@@ -3,8 +3,8 @@ package cash.z.ecc.android.sdk
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.SmallTest
import cash.z.ecc.android.sdk.tool.WalletBirthdayTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.tool.CheckpointTool
import kotlinx.coroutines.runBlocking
import org.json.JSONObject
import org.junit.Assert.assertEquals
@@ -57,7 +57,7 @@ class AssetTest {
private fun assertFileContents(network: ZcashNetwork, files: Array<String>?) {
files?.map { filename ->
val filePath = "${WalletBirthdayTool.birthdayDirectory(network)}/$filename"
val filePath = "${CheckpointTool.checkpointDirectory(network)}/$filename"
ApplicationProvider.getApplicationContext<Context>().assets.open(filePath)
.use { inputSteam ->
inputSteam.bufferedReader().use { bufferedReader ->
@@ -77,12 +77,13 @@ class AssetTest {
val expectedNetworkName = when (network) {
ZcashNetwork.Mainnet -> "main"
ZcashNetwork.Testnet -> "test"
else -> IllegalArgumentException("Unsupported network $network")
}
assertEquals("File: ${it.filename}", expectedNetworkName, jsonObject.getString("network"))
assertEquals(
"File: ${it.filename}",
WalletBirthdayTool.birthdayHeight(it.filename),
CheckpointTool.checkpointHeightFromFilename(network, it.filename),
jsonObject.getInt("height")
)
@@ -94,9 +95,9 @@ class AssetTest {
companion object {
fun listAssets(network: ZcashNetwork) = runBlocking {
WalletBirthdayTool.listBirthdayDirectoryContents(
CheckpointTool.listCheckpointDirectoryContents(
ApplicationProvider.getApplicationContext<Context>(),
WalletBirthdayTool.birthdayDirectory(network)
CheckpointTool.checkpointDirectory(network)
)
}
}

View File

@@ -1,7 +1,7 @@
package cash.z.ecc.android.sdk.ext
import cash.z.ecc.android.sdk.Initializer
import cash.z.ecc.android.sdk.type.ZcashNetwork
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.util.SimpleMnemonics
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient
@@ -14,14 +14,14 @@ fun Initializer.Config.seedPhrase(seedPhrase: String, network: ZcashNetwork) {
}
object BlockExplorer {
suspend fun fetchLatestHeight(): Int {
suspend fun fetchLatestHeight(): Long {
val client = OkHttpClient()
val request = Request.Builder()
.url("https://api.blockchair.com/zcash/blocks?limit=1")
.build()
val result = client.newCall(request).await()
val body = result.body?.string()
return JSONObject(body).getJSONArray("data").getJSONObject(0).getInt("id")
return JSONObject(body).getJSONArray("data").getJSONObject(0).getLong("id")
}
}

View File

@@ -3,11 +3,11 @@ package cash.z.ecc.android.sdk.integration
import cash.z.ecc.android.sdk.annotation.MaintainedTest
import cash.z.ecc.android.sdk.annotation.TestPurpose
import cash.z.ecc.android.sdk.ext.BlockExplorer
import cash.z.ecc.android.sdk.type.ZcashNetwork
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.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
@@ -30,13 +30,6 @@ class SanityTest(
val networkName = wallet.networkName
val name = "$networkName wallet"
@Test
fun testNotPlaintext() {
val message =
"is using plaintext. This will cause problems for the test. Ensure that the `lightwalletd_allow_very_insecure_connections` resource value is false"
assertFalse("$name $message", wallet.service.connectionInfo.usePlaintext)
}
@Test
fun testFilePaths() {
assertEquals(
@@ -61,7 +54,7 @@ class SanityTest(
assertEquals(
"$name has invalid birthday height",
birthday,
wallet.initializer.birthday.height
wallet.initializer.checkpoint.height
)
}
@@ -79,25 +72,15 @@ class SanityTest(
)
}
@Test
fun testServerConnection() {
assertEquals(
"$name has an invalid server connection",
"$networkName.lite.hushpool.is:9067?usePlaintext=true",
wallet.connectionInfo
)
}
@Test
fun testLatestHeight() = runBlocking {
if (wallet.networkName == "mainnet") {
val expectedHeight = BlockExplorer.fetchLatestHeight()
// fetch height directly because the synchronizer hasn't started, yet
val downloaderHeight = wallet.service.getLatestBlockHeight()
val info = wallet.connectionInfo
assertTrue(
"$info\n ${wallet.networkName} Lightwalletd is too far behind. Downloader height $downloaderHeight is more than 10 blocks behind block explorer height $expectedHeight",
expectedHeight - 10 < downloaderHeight
"${wallet.endpoint} ${wallet.networkName} Lightwalletd is too far behind. Downloader height $downloaderHeight is more than 10 blocks behind block explorer height $expectedHeight",
expectedHeight - 10 < downloaderHeight.value
)
}
}
@@ -105,9 +88,9 @@ class SanityTest(
@Test
fun testSingleBlockDownload() = runBlocking {
// fetch block directly because the synchronizer hasn't started, yet
val height = 1_000_000
val block = wallet.service.getBlockRange(height..height)[0]
assertTrue("$networkName failed to return a proper block. Height was ${block.height} but we expected $height", block.height.toInt() == height)
val height = BlockHeight.new(wallet.network, 1_000_000)
val block = wallet.service.getBlockRange(height..height).first()
assertTrue("$networkName failed to return a proper block. Height was ${block.height} but we expected $height", block.height == height.value)
}
companion object {

View File

@@ -4,7 +4,6 @@ import androidx.test.filters.LargeTest
import androidx.test.filters.MediumTest
import cash.z.ecc.android.sdk.annotation.MaintainedTest
import cash.z.ecc.android.sdk.annotation.TestPurpose
import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService
import cash.z.ecc.android.sdk.util.TestWallet
import kotlinx.coroutines.runBlocking
import org.junit.Assert
@@ -19,16 +18,6 @@ import org.junit.Test
@MediumTest
class SmokeTest {
@Test
fun testNotPlaintext() {
val service =
wallet.synchronizer.processor.downloader.lightWalletService as LightWalletGrpcService
Assert.assertFalse(
"Wallet is using plaintext. This will cause problems for the test. Ensure that the `lightwalletd_allow_very_insecure_connections` resource value is false",
service.connectionInfo.usePlaintext
)
}
@Test
fun testFilePaths() {
Assert.assertEquals("Invalid DataDB file", "/data/user/0/cash.z.ecc.android.sdk.test/databases/TestWallet_testnet_Data.db", wallet.initializer.rustBackend.pathDataDb)
@@ -38,7 +27,7 @@ class SmokeTest {
@Test
fun testBirthday() {
Assert.assertEquals("Invalid birthday height", 1_320_000, wallet.initializer.birthday.height)
Assert.assertEquals("Invalid birthday height", 1_320_000, wallet.initializer.checkpoint.height)
}
@Test

View File

@@ -1,4 +1,4 @@
package cash.z.wallet.sdk.integration
package cash.z.ecc.android.sdk.integration
import androidx.test.filters.LargeTest
import androidx.test.platform.app.InstrumentationRegistry
@@ -12,11 +12,13 @@ import cash.z.ecc.android.sdk.internal.TroubleshootingTwig
import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService
import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.LightWalletEndpoint
import cash.z.ecc.android.sdk.model.Zatoshi
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.test.ScopedTest
import cash.z.ecc.android.sdk.tool.CheckpointTool
import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.tool.WalletBirthdayTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
@@ -38,9 +40,9 @@ class TestnetIntegrationTest : ScopedTest() {
@Test
@Ignore("This test is broken")
fun testLatestBlockTest() {
val service = LightWalletGrpcService(
val service = LightWalletGrpcService.new(
context,
host
lightWalletEndpoint
)
val height = service.getLatestBlockHeight()
assertTrue(height > saplingActivation)
@@ -49,7 +51,7 @@ class TestnetIntegrationTest : ScopedTest() {
@Test
fun testLoadBirthday() {
val (height, hash, time, tree) = runBlocking {
WalletBirthdayTool.loadNearest(
CheckpointTool.loadNearest(
context,
synchronizer.network,
saplingActivation + 1
@@ -117,8 +119,8 @@ class TestnetIntegrationTest : ScopedTest() {
companion object {
init { Twig.plant(TroubleshootingTwig()) }
const val host = "lightwalletd.testnet.z.cash"
private const val birthdayHeight = 963150
val lightWalletEndpoint = LightWalletEndpoint("lightwalletd.testnet.z.cash", 9087, true)
private const val birthdayHeight = 963150L
private const val targetHeight = 663250
private const 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"
val seed = "cash.z.ecc.android.sdk.integration.IntegrationTest.seed.value.64bytes".toByteArray()
@@ -128,8 +130,8 @@ class TestnetIntegrationTest : ScopedTest() {
private val context = InstrumentationRegistry.getInstrumentation().context
private val initializer = runBlocking {
Initializer.new(context) { config ->
config.setNetwork(ZcashNetwork.Testnet, host)
runBlocking { config.importWallet(seed, birthdayHeight, ZcashNetwork.Testnet) }
config.setNetwork(ZcashNetwork.Testnet, lightWalletEndpoint)
runBlocking { config.importWallet(seed, BlockHeight.new(ZcashNetwork.Testnet, birthdayHeight), ZcashNetwork.Testnet, lightWalletEndpoint) }
}
}
private lateinit var synchronizer: Synchronizer

View File

@@ -11,8 +11,12 @@ import cash.z.ecc.android.sdk.internal.block.CompactBlockStore
import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService
import cash.z.ecc.android.sdk.internal.service.LightWalletService
import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.LightWalletEndpoint
import cash.z.ecc.android.sdk.model.Mainnet
import cash.z.ecc.android.sdk.model.Testnet
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.test.ScopedTest
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@@ -34,13 +38,15 @@ import org.mockito.Spy
class ChangeServiceTest : ScopedTest() {
val network = ZcashNetwork.Mainnet
val lightWalletEndpoint = LightWalletEndpoint.Mainnet
private val eccEndpoint = LightWalletEndpoint("lightwalletd.electriccoin.co", 9087, true)
@Mock
lateinit var mockBlockStore: CompactBlockStore
var mockCloseable: AutoCloseable? = null
@Spy
val service = LightWalletGrpcService(context, network)
val service = LightWalletGrpcService.new(context, lightWalletEndpoint)
lateinit var downloader: CompactBlockDownloader
lateinit var otherService: LightWalletService
@@ -49,7 +55,7 @@ class ChangeServiceTest : ScopedTest() {
fun setup() {
initMocks()
downloader = CompactBlockDownloader(service, mockBlockStore)
otherService = LightWalletGrpcService(context, "lightwalletd.electriccoin.co")
otherService = LightWalletGrpcService.new(context, eccEndpoint)
}
@After
@@ -70,7 +76,7 @@ class ChangeServiceTest : ScopedTest() {
@Test
fun testCleanSwitch() = runBlocking {
downloader.changeService(otherService)
val result = downloader.downloadBlockRange(900_000..901_000)
val result = downloader.downloadBlockRange(BlockHeight.new(network, 900_000)..BlockHeight.new(network, 901_000))
assertEquals(1_001, result)
}
@@ -81,7 +87,7 @@ class ChangeServiceTest : ScopedTest() {
@Test
@Ignore("This test is broken")
fun testSwitchWhileActive() = runBlocking {
val start = 900_000
val start = BlockHeight.new(ZcashNetwork.Mainnet, 900_000)
val count = 5
val differentiators = mutableListOf<String>()
var initialValue = downloader.getServerInfo().buildUser
@@ -105,7 +111,7 @@ class ChangeServiceTest : ScopedTest() {
@Test
fun testSwitchToInvalidServer() = runBlocking {
var caughtException: Throwable? = null
downloader.changeService(LightWalletGrpcService(context, "invalid.lightwalletd")) {
downloader.changeService(LightWalletGrpcService.new(context, LightWalletEndpoint("invalid.lightwalletd", 9087, true))) {
caughtException = it
}
assertNotNull("Using an invalid host should generate an exception.", caughtException)
@@ -118,7 +124,7 @@ class ChangeServiceTest : ScopedTest() {
@Test
fun testSwitchToTestnetFails() = runBlocking {
var caughtException: Throwable? = null
downloader.changeService(LightWalletGrpcService(context, ZcashNetwork.Testnet)) {
downloader.changeService(LightWalletGrpcService.new(context, LightWalletEndpoint.Testnet)) {
caughtException = it
}
assertNotNull("Using an invalid host should generate an exception.", caughtException)

View File

@@ -0,0 +1,47 @@
package cash.z.ecc.android.sdk.internal
import androidx.test.filters.SmallTest
import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.fixture.CheckpointFixture
import cash.z.ecc.fixture.toJson
import org.json.JSONObject
import org.junit.Assert.assertEquals
import org.junit.Test
class CheckpointTest {
@Test
@SmallTest
fun deserialize() {
val fixtureCheckpoint = CheckpointFixture.new()
val deserialized = Checkpoint.from(CheckpointFixture.NETWORK, fixtureCheckpoint.toJson())
assertEquals(fixtureCheckpoint, deserialized)
}
@Test
@SmallTest
fun epoch_seconds_as_long_that_would_overflow_int() {
val jsonString = CheckpointFixture.new(time = Long.MAX_VALUE).toJson()
Checkpoint.from(CheckpointFixture.NETWORK, jsonString).also {
assertEquals(Long.MAX_VALUE, it.epochSeconds)
}
}
@Test
@SmallTest
fun parse_height_as_long_that_would_overflow_int() {
val jsonString = JSONObject().apply {
put(Checkpoint.KEY_VERSION, Checkpoint.VERSION_1)
put(Checkpoint.KEY_HEIGHT, UInt.MAX_VALUE.toLong())
put(Checkpoint.KEY_HASH, CheckpointFixture.HASH)
put(Checkpoint.KEY_EPOCH_SECONDS, CheckpointFixture.EPOCH_SECONDS)
put(Checkpoint.KEY_TREE, CheckpointFixture.TREE)
}.toString()
Checkpoint.from(CheckpointFixture.NETWORK, jsonString).also {
assertEquals(UInt.MAX_VALUE.toLong(), it.height.value)
}
}
}

View File

@@ -1,30 +0,0 @@
package cash.z.ecc.android.sdk.internal
import androidx.test.filters.SmallTest
import cash.z.ecc.android.sdk.type.WalletBirthday
import cash.z.ecc.fixture.WalletBirthdayFixture
import cash.z.ecc.fixture.toJson
import org.junit.Assert.assertEquals
import org.junit.Test
class WalletBirthdayTest {
@Test
@SmallTest
fun deserialize() {
val fixtureBirthday = WalletBirthdayFixture.new()
val deserialized = WalletBirthday.from(fixtureBirthday.toJson())
assertEquals(fixtureBirthday, deserialized)
}
@Test
@SmallTest
fun epoch_seconds_as_long_that_would_overflow_int() {
val jsonString = WalletBirthdayFixture.new(time = Long.MAX_VALUE).toJson()
WalletBirthday.from(jsonString).also {
assertEquals(Long.MAX_VALUE, it.time)
}
}
}

View File

@@ -2,7 +2,8 @@ package cash.z.ecc.android.sdk.jni
import cash.z.ecc.android.sdk.annotation.MaintainedTest
import cash.z.ecc.android.sdk.annotation.TestPurpose
import cash.z.ecc.android.sdk.type.ZcashNetwork
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.ZcashNetwork
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Test
@@ -14,9 +15,9 @@ import org.junit.runners.Parameterized
*/
@MaintainedTest(TestPurpose.REGRESSION)
@RunWith(Parameterized::class)
class BranchIdTest(
class BranchIdTest internal constructor(
private val networkName: String,
private val height: Int,
private val height: BlockHeight,
private val branchId: Long,
private val branchHex: String,
private val rustBackend: RustBackendWelding
@@ -44,14 +45,14 @@ class BranchIdTest(
// is an abnormal use of the SDK because this really should run at the rust level
// However, due to quirks on certain devices, we created this test at the Android level,
// as a sanity check
val testnetBackend = runBlocking { RustBackend.init("", "", "", ZcashNetwork.Testnet) }
val mainnetBackend = runBlocking { RustBackend.init("", "", "", ZcashNetwork.Mainnet) }
val testnetBackend = runBlocking { RustBackend.init("", "", "", ZcashNetwork.Testnet, ZcashNetwork.Testnet.saplingActivationHeight) }
val mainnetBackend = runBlocking { RustBackend.init("", "", "", ZcashNetwork.Mainnet, ZcashNetwork.Mainnet.saplingActivationHeight) }
return listOf(
// Mainnet Cases
arrayOf("Sapling", 419_200, 1991772603L, "76b809bb", mainnetBackend),
arrayOf("Sapling", 1, 1991772603L, "76b809bb", mainnetBackend),
// Testnet Cases
arrayOf("Sapling", 280_000, 1991772603L, "76b809bb", testnetBackend)
arrayOf("Sapling", 1, 1991772603L, "76b809bb", testnetBackend),
)
}
}

View File

@@ -7,8 +7,8 @@ import cash.z.ecc.android.sdk.annotation.MaintainedTest
import cash.z.ecc.android.sdk.annotation.TestPurpose
import cash.z.ecc.android.sdk.internal.TroubleshootingTwig
import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.BeforeClass

View File

@@ -2,7 +2,7 @@ package cash.z.ecc.android.sdk.sample
import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.model.Zatoshi
import cash.z.ecc.android.sdk.type.ZcashNetwork
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.util.TestWallet
import kotlinx.coroutines.runBlocking
import org.junit.Assert

View File

@@ -3,8 +3,9 @@ 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.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.Zatoshi
import cash.z.ecc.android.sdk.type.ZcashNetwork.Testnet
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.util.TestWallet
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
@@ -73,7 +74,7 @@ class TransparentRestoreSample {
// 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(
wallet.sync().rewindToHeight(BlockHeight.new(ZcashNetwork.Testnet, 1339178)).send(
"ztestsapling17zazsl8rryl8kjaqxnr2r29rw9d9a2mud37ugapm0s8gmyv0ue43h9lqwmhdsp3nu9dazeqfs6l",
"is send broken?"
).join(5)
@@ -85,7 +86,15 @@ class TransparentRestoreSample {
@LargeTest
@Ignore("This test is extremely slow")
fun kris() = runBlocking<Unit> {
val wallet0 = TestWallet(TestWallet.Backups.SAMPLE_WALLET.seedPhrase, "tmpabc", Testnet, startHeight = 1330190)
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()
@@ -107,7 +116,15 @@ class TransparentRestoreSample {
*/
// @Test
fun hasFunds() = runBlocking<Unit> {
val walletSandbox = TestWallet(TestWallet.Backups.SAMPLE_WALLET.seedPhrase, "WalletC", Testnet, startHeight = 1330190)
val walletSandbox = TestWallet(
TestWallet.Backups.SAMPLE_WALLET.seedPhrase,
"WalletC",
ZcashNetwork.Testnet,
startHeight = BlockHeight.new(
ZcashNetwork.Testnet,
1330190
)
)
// val job = walletA.walletScope.launch {
// twig("Syncing WalletA")
// walletA.sync()
@@ -125,7 +142,7 @@ class TransparentRestoreSample {
// send z->t
// walletA.send(TX_VALUE, walletA.transparentAddress, "${TransparentRestoreSample::class.java.simpleName} z->t")
walletSandbox.rewindToHeight(1339178)
walletSandbox.rewindToHeight(BlockHeight.new(ZcashNetwork.Testnet, 1339178))
twig("Done REWINDING!")
twig("T-ADDR (for the win!): ${walletSandbox.transparentAddress}")
delay(500)

View File

@@ -4,18 +4,19 @@ import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import cash.z.ecc.android.sdk.tool.WalletBirthdayTool.IS_FALLBACK_ON_FAILURE
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.tool.CheckpointTool.IS_FALLBACK_ON_FAILURE
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class WalletBirthdayToolTest {
class CheckpointToolTest {
@Test
@SmallTest
fun birthday_height_from_filename() {
assertEquals(123, WalletBirthdayTool.birthdayHeight("123.json"))
assertEquals(123, CheckpointTool.checkpointHeightFromFilename(ZcashNetwork.Mainnet, "123.json"))
}
@Test
@@ -27,13 +28,14 @@ class WalletBirthdayToolTest {
val context = ApplicationProvider.getApplicationContext<Context>()
val birthday = runBlocking {
WalletBirthdayTool.getFirstValidWalletBirthday(
CheckpointTool.getFirstValidWalletBirthday(
context,
ZcashNetwork.Mainnet,
directory,
listOf("1300000.json", "1290000.json")
)
}
assertEquals(1300000, birthday.height)
assertEquals(1300000, birthday.height.value)
}
@Test
@@ -46,12 +48,13 @@ class WalletBirthdayToolTest {
val directory = "co.electriccoin.zcash/checkpoint/badnet"
val context = ApplicationProvider.getApplicationContext<Context>()
val birthday = runBlocking {
WalletBirthdayTool.getFirstValidWalletBirthday(
CheckpointTool.getFirstValidWalletBirthday(
context,
ZcashNetwork.Mainnet,
directory,
listOf("1300000.json", "1290000.json")
)
}
assertEquals(1290000, birthday.height)
assertEquals(1290000, birthday.height.value)
}
}

View File

@@ -1,10 +1,9 @@
package cash.z.ecc.android.sdk.util
import androidx.test.platform.app.InstrumentationRegistry
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking

View File

@@ -6,10 +6,13 @@ import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.internal.TroubleshootingTwig
import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.internal.ext.deleteSuspend
import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.tool.WalletBirthdayTool
import cash.z.ecc.android.sdk.type.WalletBirthday
import cash.z.ecc.android.sdk.type.ZcashNetwork
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.LightWalletEndpoint
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.model.defaultForNetwork
import cash.z.ecc.android.sdk.tool.CheckpointTool
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
@@ -30,7 +33,7 @@ class BalancePrinterUtil {
private val network = ZcashNetwork.Mainnet
private val downloadBatchSize = 9_000
private val birthdayHeight = 523240
private val birthdayHeight = BlockHeight.new(network, 523240)
private val mnemonics = SimpleMnemonics()
private val context = InstrumentationRegistry.getInstrumentation().context
@@ -46,14 +49,14 @@ class BalancePrinterUtil {
// private val rustBackend = RustBackend.init(context, cacheDbName, dataDbName)
private lateinit var birthday: WalletBirthday
private lateinit var birthday: Checkpoint
private var synchronizer: Synchronizer? = null
@Before
fun setup() {
Twig.plant(TroubleshootingTwig())
cacheBlocks()
birthday = runBlocking { WalletBirthdayTool.loadNearest(context, network, birthdayHeight) }
birthday = runBlocking { CheckpointTool.loadNearest(context, network, birthdayHeight) }
}
private fun cacheBlocks() = runBlocking {
@@ -81,8 +84,8 @@ class BalancePrinterUtil {
}.collect { seed ->
// TODO: clear the dataDb but leave the cacheDb
val initializer = Initializer.new(context) { config ->
runBlocking { config.importWallet(seed, birthdayHeight, network) }
config.setNetwork(network)
val endpoint = LightWalletEndpoint.defaultForNetwork(network)
runBlocking { config.importWallet(seed, birthdayHeight, network, endpoint) }
config.alias = alias
}
/*

View File

@@ -6,6 +6,8 @@ import cash.z.ecc.android.sdk.SdkSynchronizer
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.internal.TroubleshootingTwig
import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.ZcashNetwork
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@@ -36,7 +38,7 @@ class DataDbScannerUtil {
// private val rustBackend = RustBackend.init(context, cacheDbName, dataDbName)
private val birthdayHeight = 600_000
private val birthdayHeight = 600_000L
private lateinit var synchronizer: Synchronizer
@Before
@@ -67,7 +69,11 @@ class DataDbScannerUtil {
val initializer = runBlocking {
Initializer.new(context) {
it.setBirthdayHeight(
birthdayHeight
BlockHeight.new(
ZcashNetwork.Mainnet,
birthdayHeight
),
false
)
}
}

View File

@@ -10,10 +10,13 @@ import cash.z.ecc.android.sdk.db.entity.isPending
import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService
import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.LightWalletEndpoint
import cash.z.ecc.android.sdk.model.Testnet
import cash.z.ecc.android.sdk.model.WalletBalance
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 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
@@ -35,9 +38,8 @@ class TestWallet(
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
val endpoint: LightWalletEndpoint = LightWalletEndpoint.Testnet,
startHeight: BlockHeight? = null
) {
constructor(
backup: Backups,
@@ -65,7 +67,7 @@ class TestWallet(
runBlocking { DerivationTool.deriveTransparentSecretKey(seed, network = network) }
val initializer = runBlocking {
Initializer.new(context) { config ->
runBlocking { config.importWallet(seed, startHeight, network, host, alias = alias) }
runBlocking { config.importWallet(seed, startHeight, network, endpoint, alias = alias) }
}
}
val synchronizer: SdkSynchronizer = Synchronizer.newBlocking(initializer) as SdkSynchronizer
@@ -78,14 +80,11 @@ class TestWallet(
runBlocking { DerivationTool.deriveTransparentAddress(seed, network = network) }
val birthdayHeight get() = synchronizer.latestBirthdayHeight
val networkName get() = synchronizer.network.networkName
val connectionInfo get() = service.connectionInfo.toString()
/* NOT SUPPORTED IN HUSH LIGHTWALLETD
suspend fun transparentBalance(): WalletBalance {
synchronizer.refreshUtxos(transparentAddress, synchronizer.latestBirthdayHeight)
return synchronizer.getTransparentBalance(transparentAddress)
}
*/
suspend fun sync(timeout: Long = -1): TestWallet {
val killSwitch = walletScope.launch {
@@ -111,7 +110,7 @@ class TestWallet(
suspend fun send(address: String = transparentAddress, memo: String = "", amount: Zatoshi = Zatoshi(500L), fromAccountIndex: Int = 0): TestWallet {
Twig.sprout("$alias sending")
synchronizer.sendToAddress(shieldedSpendingKey, amount, address, memo, fromAccountIndex)
.takeWhile { it.isPending() }
.takeWhile { it.isPending(null) }
.collect {
twig("Updated transaction: $it")
}
@@ -119,15 +118,14 @@ class TestWallet(
return this
}
suspend fun rewindToHeight(height: Int): TestWallet {
suspend fun rewindToHeight(height: BlockHeight): TestWallet {
synchronizer.rewindToNearestHeight(height, false)
return this
}
/* NOT SUPPORTED IN HUSH LIGHTWALLETD
suspend fun shieldFunds(): TestWallet {
twig("checking $transparentAddress for transactions!")
synchronizer.refreshUtxos(transparentAddress, 935000).let { count ->
synchronizer.refreshUtxos(transparentAddress, BlockHeight.new(ZcashNetwork.Mainnet, 935000)).let { count ->
twig("FOUND $count new UTXOs")
}
@@ -144,7 +142,6 @@ class TestWallet(
return this
}
*/
suspend fun join(timeout: Long? = null): TestWallet {
// block until stopped
@@ -167,13 +164,48 @@ class TestWallet(
}
}
enum class Backups(val seedPhrase: String, val testnetBirthday: Int, val mainnetBirthday: Int) {
enum class Backups(val seedPhrase: String, val testnetBirthday: BlockHeight, val mainnetBirthday: BlockHeight) {
// TODO: get the proper birthday values for these wallets
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, 1_000_000),
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, 1_000_000),
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", 1_000_000, 991645),
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, 1_000_000),
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, 1_000_000),
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",
BlockHeight.new(
ZcashNetwork.Testnet,
1_355_928
),
BlockHeight.new(ZcashNetwork.Mainnet, 1_000_000)
),
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",
BlockHeight.new(
ZcashNetwork.Testnet,
1_330_190
),
BlockHeight.new(ZcashNetwork.Mainnet, 1_000_000)
),
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",
BlockHeight.new(
ZcashNetwork.Testnet,
1_000_000
),
BlockHeight.new(ZcashNetwork.Mainnet, 991645)
),
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",
BlockHeight.new(
ZcashNetwork.Testnet,
1_330_190
),
BlockHeight.new(ZcashNetwork.Mainnet, 1_000_000)
),
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",
BlockHeight.new(
ZcashNetwork.Testnet,
1_330_190
),
BlockHeight.new(ZcashNetwork.Mainnet, 1_000_000)
),
;
}
}

View File

@@ -5,7 +5,10 @@ import cash.z.ecc.android.sdk.internal.TroubleshootingTwig
import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService
import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.type.ZcashNetwork
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.LightWalletEndpoint
import cash.z.ecc.android.sdk.model.Mainnet
import cash.z.ecc.android.sdk.model.ZcashNetwork
import org.junit.Ignore
import org.junit.Test
@@ -13,7 +16,7 @@ class TransactionCounterUtil {
private val network = ZcashNetwork.Mainnet
private val context = InstrumentationRegistry.getInstrumentation().context
private val service = LightWalletGrpcService(context, network)
private val service = LightWalletGrpcService.new(context, LightWalletEndpoint.Mainnet)
init {
Twig.plant(TroubleshootingTwig())
@@ -23,7 +26,12 @@ class TransactionCounterUtil {
@Ignore("This test is broken")
fun testBlockSize() {
val sizes = mutableMapOf<Int, Int>()
service.getBlockRange(900_000..910_000).forEach { b ->
service.getBlockRange(
BlockHeight.new(ZcashNetwork.Mainnet, 900_000)..BlockHeight.new(
ZcashNetwork.Mainnet,
910_000
)
).forEach { b ->
twig("h: ${b.header.size()}")
val s = b.serializedSize
sizes[s] = (sizes[s] ?: 0) + 1
@@ -38,7 +46,12 @@ class TransactionCounterUtil {
val outputCounts = mutableMapOf<Int, Int>()
var totalOutputs = 0
var totalTxs = 0
service.getBlockRange(900_000..950_000).forEach { b ->
service.getBlockRange(
BlockHeight.new(ZcashNetwork.Mainnet, 900_000)..BlockHeight.new(
ZcashNetwork.Mainnet,
950_000
)
).forEach { b ->
b.header.size()
b.vtxList.map { it.outputsCount }.forEach { oCount ->
outputCounts[oCount] = (outputCounts[oCount] ?: 0) + oCount.coerceAtLeast(1)

View File

@@ -6,31 +6,34 @@ import cash.z.ecc.android.sdk.internal.KEY_HEIGHT
import cash.z.ecc.android.sdk.internal.KEY_TREE
import cash.z.ecc.android.sdk.internal.KEY_VERSION
import cash.z.ecc.android.sdk.internal.VERSION_1
import cash.z.ecc.android.sdk.type.WalletBirthday
import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.ZcashNetwork
import org.json.JSONObject
object WalletBirthdayFixture {
object CheckpointFixture {
val NETWORK = ZcashNetwork.Mainnet
// These came from the mainnet 1500000.json file
const val HEIGHT = 1500000
val HEIGHT = BlockHeight.new(ZcashNetwork.Mainnet, 1500000L)
const val HASH = "00000000019e5b25a95c7607e7789eb326fddd69736970ebbe1c7d00247ef902"
const val EPOCH_SECONDS = 1639913234L
@Suppress("MaxLineLength")
const val TREE = "01ce183032b16ed87fcc5052a42d908376526126346567773f55bc58a63e4480160013000001bae5112769a07772345dd402039f2949c457478fe9327363ff631ea9d78fb80d0177c0b6c21aa9664dc255336ed450914088108c38a9171c85875b4e53d31b3e140171add6f9129e124651ca894aa842a3c71b1738f3ee2b7ba829106524ef51e62101f9cebe2141ee9d0a3f3a3e28bce07fa6b6e1c7b42c01cc4fe611269e9d52da540001d0adff06de48569129bd2a211e3253716362da97270d3504d9c1b694689ebe3c0122aaaea90a7fa2773b8166937310f79a4278b25d759128adf3138d052da3725b0137fb2cbc176075a45db2a3c32d3f78e669ff2258fd974e99ec9fb314d7fd90180165aaee3332ea432d13a9398c4863b38b8a7a491877a5c46b0802dcd88f7e324301a9a262f8b92efc2e0e3e4bd1207486a79d62e87b4ab9cc41814d62a23c4e28040001e3c4ee998682df5c5e230d6968e947f83d0c03682f0cfc85f1e6ec8e8552c95a000155989fed7a8cc7a0d479498d6881ca3bafbe05c7095110f85c64442d6a06c25c0185cd8c141e620eda0ca0516f42240aedfabdf9189c8c6ac834b7bdebc171331d01ecceb776c043662617d62646ee60985521b61c0b860f3a9731e66ef74ed8fb320118f64df255c9c43db708255e7bf6bffd481e5c2f38fe9ed8f3d189f7f9cf2644"
fun new(
height: Int = HEIGHT,
internal fun new(
height: BlockHeight = HEIGHT,
hash: String = HASH,
time: Long = EPOCH_SECONDS,
tree: String = TREE
) = WalletBirthday(height = height, hash = hash, time = time, tree = tree)
) = Checkpoint(height = height, hash = hash, epochSeconds = time, tree = tree)
}
fun WalletBirthday.toJson() = JSONObject().apply {
put(WalletBirthday.KEY_VERSION, WalletBirthday.VERSION_1)
put(WalletBirthday.KEY_HEIGHT, height)
put(WalletBirthday.KEY_HASH, hash)
put(WalletBirthday.KEY_EPOCH_SECONDS, time)
put(WalletBirthday.KEY_TREE, tree)
internal fun Checkpoint.toJson() = JSONObject().apply {
put(Checkpoint.KEY_VERSION, Checkpoint.VERSION_1)
put(Checkpoint.KEY_HEIGHT, height.value)
put(Checkpoint.KEY_HASH, hash)
put(Checkpoint.KEY_EPOCH_SECONDS, epochSeconds)
put(Checkpoint.KEY_TREE, tree)
}.toString()