[#593] Refactor ZcashNetwork

Splits the ZcashNetwork and Lightwalletd server information, making it easier to configure different servers
This commit is contained in:
Carter Jernigan 2022-08-02 09:29:09 -04:00 committed by GitHub
parent 2362c60dd6
commit e01a906407
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 429 additions and 307 deletions

View File

@ -1,6 +1,10 @@
Change Log Change Log
========== ==========
Upcoming
------------------------------------
- Split `ZcashNetwork` into `ZcashNetwork` and `LightWalletEndpoint` to decouple network and server configuration
Version 1.8.0-beta01 Version 1.8.0-beta01
------------------------------------ ------------------------------------
- Added `BlockHeight` typesafe object to represent block heights - Added `BlockHeight` typesafe object to represent block heights

View File

@ -1,7 +1,16 @@
Troubleshooting Migrations Troubleshooting Migrations
========== ==========
Upcoming
--------------------------------------
`ZcashNetwork` is no longer an enum. The prior enum values are now declared as object properties `ZcashNetwork.Mainnet` and `ZcashNetwork.Testnet`. For the most part, this change should have minimal impact. ZcashNetwork was also moved from the package `cash.z.ecc.android.sdk.type` to `cash.z.ecc.android.sdk.model`, which will require a change to your import statements. The server fields have been removed from `ZcashNetwork`, allowing server and network configuration to be done independently.
`LightWalletEndpoint` is a new object to represent server information. Default values can be obtained from `LightWalletEndpoint.defaultForNetwork(ZcashNetwork)`
`Synchronizer` no longer allows changing the endpoint after construction. Instead, construct a new `Synchronizer` with the desired endpoint.
Migration to Version 1.8 from 1.7 Migration to Version 1.8 from 1.7
--------------------------------------
Various APIs used `Int` to represent network block heights. Those APIs now use a typesafe `BlockHeight` type. BlockHeight is constructed with a factory method `BlockHeight.new(ZcashNetwork, Long)` which uses the network to validate the height is above the network's sapling activation height. Various APIs used `Int` to represent network block heights. Those APIs now use a typesafe `BlockHeight` type. BlockHeight is constructed with a factory method `BlockHeight.new(ZcashNetwork, Long)` which uses the network to validate the height is above the network's sapling activation height.
`WalletBirthday` has been renamed to `Checkpoint` and removed from the public API. Where clients previously passed in a `WalletBirthday` object, now a `BlockHeight` can be passed in instead. `WalletBirthday` has been renamed to `Checkpoint` and removed from the public API. Where clients previously passed in a `WalletBirthday` object, now a `BlockHeight` can be passed in instead.

View File

@ -5,7 +5,7 @@ import cash.z.ecc.android.sdk.darkside.test.DarksideTestCoordinator
import cash.z.ecc.android.sdk.darkside.test.ScopedTest import cash.z.ecc.android.sdk.darkside.test.ScopedTest
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
import org.junit.BeforeClass import org.junit.BeforeClass
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith

View File

@ -4,7 +4,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import cash.z.ecc.android.sdk.darkside.test.DarksideTestCoordinator import cash.z.ecc.android.sdk.darkside.test.DarksideTestCoordinator
import cash.z.ecc.android.sdk.darkside.test.ScopedTest import cash.z.ecc.android.sdk.darkside.test.ScopedTest
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
import org.junit.Before import org.junit.Before
import org.junit.BeforeClass import org.junit.BeforeClass
import org.junit.Test import org.junit.Test

View File

@ -5,7 +5,7 @@ import cash.z.ecc.android.sdk.darkside.test.DarksideTestCoordinator
import cash.z.ecc.android.sdk.darkside.test.ScopedTest import cash.z.ecc.android.sdk.darkside.test.ScopedTest
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Before import org.junit.Before
import org.junit.BeforeClass import org.junit.BeforeClass

View File

@ -1,11 +1,12 @@
package cash.z.ecc.android.sdk.darkside.test package cash.z.ecc.android.sdk.darkside.test
import android.content.Context import android.content.Context
import cash.z.ecc.android.sdk.R
import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.Darkside
import cash.z.ecc.android.sdk.model.LightWalletEndpoint
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.wallet.sdk.rpc.Darkside import cash.z.wallet.sdk.rpc.Darkside
import cash.z.wallet.sdk.rpc.Darkside.DarksideTransactionsURL import cash.z.wallet.sdk.rpc.Darkside.DarksideTransactionsURL
import cash.z.wallet.sdk.rpc.DarksideStreamerGrpc import cash.z.wallet.sdk.rpc.DarksideStreamerGrpc
@ -23,17 +24,11 @@ class DarksideApi(
constructor( constructor(
appContext: Context, appContext: Context,
host: String, lightWalletEndpoint: LightWalletEndpoint
port: Int = ZcashNetwork.Mainnet.defaultPort,
usePlainText: Boolean = appContext.resources.getBoolean(
R.bool.lightwalletd_allow_very_insecure_connections
)
) : this( ) : this(
LightWalletGrpcService.createDefaultChannel( LightWalletGrpcService.createDefaultChannel(
appContext, appContext,
host, lightWalletEndpoint
port,
usePlainText
) )
) )

View File

@ -5,7 +5,9 @@ import cash.z.ecc.android.sdk.SdkSynchronizer
import cash.z.ecc.android.sdk.Synchronizer import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.Darkside
import cash.z.ecc.android.sdk.model.LightWalletEndpoint
import cash.z.ecc.android.sdk.model.ZcashNetwork
import io.grpc.StatusRuntimeException import io.grpc.StatusRuntimeException
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
@ -23,10 +25,9 @@ class DarksideTestCoordinator(val wallet: TestWallet) {
alias: String = "DarksideTestCoordinator", alias: String = "DarksideTestCoordinator",
seedPhrase: String = DEFAULT_SEED_PHRASE, seedPhrase: String = DEFAULT_SEED_PHRASE,
startHeight: BlockHeight = DEFAULT_START_HEIGHT, startHeight: BlockHeight = DEFAULT_START_HEIGHT,
host: String = COMPUTER_LOCALHOST,
network: ZcashNetwork = ZcashNetwork.Mainnet, network: ZcashNetwork = ZcashNetwork.Mainnet,
port: Int = network.defaultPort endpoint: LightWalletEndpoint = LightWalletEndpoint.Darkside
) : this(TestWallet(seedPhrase, alias, network, host, startHeight = startHeight, port = port)) ) : this(TestWallet(seedPhrase, alias, network, endpoint, startHeight = startHeight))
private val targetHeight = BlockHeight.new(wallet.network, 663250) private val targetHeight = BlockHeight.new(wallet.network, 663250)
private val context = InstrumentationRegistry.getInstrumentation().context private val context = InstrumentationRegistry.getInstrumentation().context

View File

@ -11,10 +11,12 @@ import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.Darkside
import cash.z.ecc.android.sdk.model.LightWalletEndpoint
import cash.z.ecc.android.sdk.model.WalletBalance import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.Zatoshi 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.tool.DerivationTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -36,9 +38,8 @@ class TestWallet(
val seedPhrase: String, val seedPhrase: String,
val alias: String = "TestWallet", val alias: String = "TestWallet",
val network: ZcashNetwork = ZcashNetwork.Testnet, val network: ZcashNetwork = ZcashNetwork.Testnet,
val host: String = network.defaultHost, val endpoint: LightWalletEndpoint = LightWalletEndpoint.Darkside,
startHeight: BlockHeight? = null, startHeight: BlockHeight? = null
val port: Int = network.defaultPort
) { ) {
constructor( constructor(
backup: Backups, backup: Backups,
@ -66,7 +67,7 @@ class TestWallet(
runBlocking { DerivationTool.deriveTransparentSecretKey(seed, network = network) } runBlocking { DerivationTool.deriveTransparentSecretKey(seed, network = network) }
val initializer = runBlocking { val initializer = runBlocking {
Initializer.new(context) { config -> 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 = runBlocking { Synchronizer.new(initializer) } as SdkSynchronizer val synchronizer: SdkSynchronizer = runBlocking { Synchronizer.new(initializer) } as SdkSynchronizer
@ -79,7 +80,6 @@ class TestWallet(
runBlocking { DerivationTool.deriveTransparentAddress(seed, network = network) } runBlocking { DerivationTool.deriveTransparentAddress(seed, network = network) }
val birthdayHeight get() = synchronizer.latestBirthdayHeight val birthdayHeight get() = synchronizer.latestBirthdayHeight
val networkName get() = synchronizer.network.networkName val networkName get() = synchronizer.network.networkName
val connectionInfo get() = service.connectionInfo.toString()
suspend fun transparentBalance(): WalletBalance { suspend fun transparentBalance(): WalletBalance {
synchronizer.refreshUtxos(transparentAddress, synchronizer.latestBirthdayHeight) synchronizer.refreshUtxos(transparentAddress, synchronizer.latestBirthdayHeight)
@ -166,11 +166,46 @@ class TestWallet(
enum class Backups(val seedPhrase: String, val testnetBirthday: BlockHeight, val mainnetBirthday: BlockHeight) { enum class Backups(val seedPhrase: String, val testnetBirthday: BlockHeight, val mainnetBirthday: BlockHeight) {
// TODO: get the proper birthday values for these wallets // 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", BlockHeight.new(ZcashNetwork.Testnet, 1_355_928), BlockHeight.new(ZcashNetwork.Mainnet, 1_000_000)), DEFAULT(
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)), "column rhythm acoustic gym cost fit keen maze fence seed mail medal shrimp tell relief clip cannon foster soldier shallow refuse lunar parrot banana",
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)), BlockHeight.new(
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)), ZcashNetwork.Testnet,
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)), 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

@ -11,8 +11,10 @@ import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight 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 cash.z.ecc.android.sdk.tool.DerivationTool import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
@ -87,15 +89,18 @@ class SampleCodeTest {
// /////////////////////////////////////////////////// // ///////////////////////////////////////////////////
// Query latest block height // Query latest block height
@Test fun getLatestBlockHeightTest() { @Test fun getLatestBlockHeightTest() {
val lightwalletService = LightWalletGrpcService(context, lightwalletdHost) val lightwalletService = LightWalletGrpcService.new(context, lightwalletdHost)
log("Latest Block: ${lightwalletService.getLatestBlockHeight()}") log("Latest Block: ${lightwalletService.getLatestBlockHeight()}")
} }
// /////////////////////////////////////////////////// // ///////////////////////////////////////////////////
// Download compact block range // Download compact block range
@Test fun getBlockRange() { @Test fun getBlockRange() {
val blockRange = BlockHeight.new(ZcashNetwork.Mainnet, 500_000)..BlockHeight.new(ZcashNetwork.Mainnet, 500_009) val blockRange = BlockHeight.new(ZcashNetwork.Mainnet, 500_000)..BlockHeight.new(
val lightwalletService = LightWalletGrpcService(context, lightwalletdHost) ZcashNetwork.Mainnet,
500_009
)
val lightwalletService = LightWalletGrpcService.new(context, lightwalletdHost)
val blocks = lightwalletService.getBlockRange(blockRange) val blocks = lightwalletService.getBlockRange(blockRange)
assertEquals(blockRange.endInclusive.value - blockRange.start.value, blocks.count()) assertEquals(blockRange.endInclusive.value - blockRange.start.value, blocks.count())
@ -151,7 +156,7 @@ class SampleCodeTest {
companion object { companion object {
private val seed = "Insert seed for testing".toByteArray() private val seed = "Insert seed for testing".toByteArray()
private val lightwalletdHost: String = ZcashNetwork.Mainnet.defaultHost private val lightwalletdHost = LightWalletEndpoint.Mainnet
private val context = InstrumentationRegistry.getInstrumentation().targetContext private val context = InstrumentationRegistry.getInstrumentation().targetContext
private val synchronizer: Synchronizer = run { private val synchronizer: Synchronizer = run {

View File

@ -20,7 +20,9 @@ import androidx.viewbinding.ViewBinding
import cash.z.ecc.android.sdk.demoapp.util.fromResources import cash.z.ecc.android.sdk.demoapp.util.fromResources
import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService 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.service.LightWalletService
import cash.z.ecc.android.sdk.type.ZcashNetwork 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 com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
@ -108,7 +110,11 @@ class MainActivity :
if (lightwalletService != null) { if (lightwalletService != null) {
lightwalletService?.shutdown() lightwalletService?.shutdown()
} }
lightwalletService = LightWalletGrpcService(applicationContext, ZcashNetwork.fromResources(applicationContext)) val network = ZcashNetwork.fromResources(applicationContext)
lightwalletService = LightWalletGrpcService.new(
applicationContext,
LightWalletEndpoint.defaultForNetwork(network)
)
} }
private fun onFabClicked(view: View) { private fun onFabClicked(view: View) {

View File

@ -9,9 +9,9 @@ import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetAddressBinding import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetAddressBinding
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
import cash.z.ecc.android.sdk.demoapp.util.fromResources import cash.z.ecc.android.sdk.demoapp.util.fromResources
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.tool.DerivationTool import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.UnifiedViewingKey import cash.z.ecc.android.sdk.type.UnifiedViewingKey
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking

View File

@ -14,9 +14,11 @@ import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
import cash.z.ecc.android.sdk.demoapp.util.fromResources import cash.z.ecc.android.sdk.demoapp.util.fromResources
import cash.z.ecc.android.sdk.ext.collectWith import cash.z.ecc.android.sdk.ext.collectWith
import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString
import cash.z.ecc.android.sdk.model.LightWalletEndpoint
import cash.z.ecc.android.sdk.model.WalletBalance import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.model.defaultForNetwork
import cash.z.ecc.android.sdk.tool.DerivationTool import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@ -50,8 +52,12 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
// using the ViewingKey to initialize // using the ViewingKey to initialize
runBlocking { runBlocking {
Initializer.new(requireApplicationContext(), null) { Initializer.new(requireApplicationContext(), null) {
it.setNetwork(ZcashNetwork.fromResources(requireApplicationContext())) val network = ZcashNetwork.fromResources(requireApplicationContext())
it.newWallet(viewingKey, network = ZcashNetwork.fromResources(requireApplicationContext())) it.newWallet(
viewingKey,
network = network,
lightWalletEndpoint = LightWalletEndpoint.defaultForNetwork(network)
)
} }
}.let { initializer -> }.let { initializer ->
synchronizer = Synchronizer.newBlocking(initializer) synchronizer = Synchronizer.newBlocking(initializer)

View File

@ -14,7 +14,7 @@ import cash.z.ecc.android.sdk.demoapp.util.toRelativeTime
import cash.z.ecc.android.sdk.demoapp.util.withCommas import cash.z.ecc.android.sdk.demoapp.util.withCommas
import cash.z.ecc.android.sdk.ext.toHex import cash.z.ecc.android.sdk.ext.toHex
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
import kotlin.math.min import kotlin.math.min
/** /**

View File

@ -13,7 +13,7 @@ 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.toRelativeTime
import cash.z.ecc.android.sdk.demoapp.util.withCommas import cash.z.ecc.android.sdk.demoapp.util.withCommas
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
import kotlin.math.max import kotlin.math.max
/** /**

View File

@ -9,8 +9,8 @@ import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetPrivateKeyBinding import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetPrivateKeyBinding
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
import cash.z.ecc.android.sdk.demoapp.util.fromResources import cash.z.ecc.android.sdk.demoapp.util.fromResources
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.tool.DerivationTool import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
/** /**

View File

@ -17,8 +17,10 @@ import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
import cash.z.ecc.android.sdk.demoapp.util.fromResources import cash.z.ecc.android.sdk.demoapp.util.fromResources
import cash.z.ecc.android.sdk.ext.collectWith import cash.z.ecc.android.sdk.ext.collectWith
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
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.DerivationTool import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
/** /**
@ -51,11 +53,13 @@ class ListTransactionsFragment : BaseDemoFragment<FragmentListTransactionsBindin
initializer = Initializer.newBlocking( initializer = Initializer.newBlocking(
requireApplicationContext(), requireApplicationContext(),
Initializer.Config { Initializer.Config {
val network = ZcashNetwork.fromResources(requireApplicationContext())
runBlocking { runBlocking {
it.importWallet( it.importWallet(
seed, seed,
birthday = null, birthday = null,
network = ZcashNetwork.fromResources(requireApplicationContext()) network = network,
lightWalletEndpoint = LightWalletEndpoint.defaultForNetwork(network)
) )
} }
} }

View File

@ -21,8 +21,10 @@ import cash.z.ecc.android.sdk.demoapp.util.mainActivity
import cash.z.ecc.android.sdk.ext.collectWith import cash.z.ecc.android.sdk.ext.collectWith
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight 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.DerivationTool import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -64,8 +66,15 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
// have the seed stored // have the seed stored
seed = Mnemonics.MnemonicCode(sharedViewModel.seedPhrase.value).toSeed() seed = Mnemonics.MnemonicCode(sharedViewModel.seedPhrase.value).toSeed()
initializer = runBlocking { initializer = runBlocking {
val network = ZcashNetwork.fromResources(requireApplicationContext())
Initializer.new(requireApplicationContext()) { Initializer.new(requireApplicationContext()) {
runBlocking { it.newWallet(seed, network = ZcashNetwork.fromResources(requireApplicationContext())) } runBlocking {
it.newWallet(
seed,
network = network,
lightWalletEndpoint = LightWalletEndpoint.defaultForNetwork(network)
)
}
it.alias = "Demo_Utxos" it.alias = "Demo_Utxos"
} }
} }
@ -96,12 +105,19 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
val network = ZcashNetwork.fromResources(requireApplicationContext()) val network = ZcashNetwork.fromResources(requireApplicationContext())
binding.textStatus.requestFocus() binding.textStatus.requestFocus()
val addressToUse = binding.inputAddress.text.toString() val addressToUse = binding.inputAddress.text.toString()
val startToUse = max(binding.inputRangeStart.text.toString().toLongOrNull() ?: network.saplingActivationHeight.value, network.saplingActivationHeight.value) val startToUse = max(
binding.inputRangeStart.text.toString().toLongOrNull()
?: network.saplingActivationHeight.value,
network.saplingActivationHeight.value
)
val endToUse = binding.inputRangeEnd.text.toString().toLongOrNull() val endToUse = binding.inputRangeEnd.text.toString().toLongOrNull()
?: getUxtoEndHeight(requireApplicationContext()).value ?: getUxtoEndHeight(requireApplicationContext()).value
var allStart = now var allStart = now
twig("loading transactions in range $startToUse..$endToUse") twig("loading transactions in range $startToUse..$endToUse")
val txids = lightwalletService?.getTAddressTransactions(addressToUse, BlockHeight.new(network, startToUse)..BlockHeight.new(network, endToUse)) val txids = lightwalletService?.getTAddressTransactions(
addressToUse,
BlockHeight.new(network, startToUse)..BlockHeight.new(network, endToUse)
)
var delta = now - allStart var delta = now - allStart
updateStatus("found ${txids?.size} transactions in ${delta}ms.", false) updateStatus("found ${txids?.size} transactions in ${delta}ms.", false)
@ -163,7 +179,12 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
resetInBackground() resetInBackground()
val seed = Mnemonics.MnemonicCode(sharedViewModel.seedPhrase.value).toSeed() val seed = Mnemonics.MnemonicCode(sharedViewModel.seedPhrase.value).toSeed()
viewLifecycleOwner.lifecycleScope.launchWhenStarted { viewLifecycleOwner.lifecycleScope.launchWhenStarted {
binding.inputAddress.setText(DerivationTool.deriveTransparentAddress(seed, ZcashNetwork.fromResources(requireApplicationContext()))) binding.inputAddress.setText(
DerivationTool.deriveTransparentAddress(
seed,
ZcashNetwork.fromResources(requireApplicationContext())
)
)
} }
} }

View File

@ -29,9 +29,11 @@ import cash.z.ecc.android.sdk.ext.convertZecToZatoshi
import cash.z.ecc.android.sdk.ext.toZecString import cash.z.ecc.android.sdk.ext.toZecString
import cash.z.ecc.android.sdk.internal.Twig import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.LightWalletEndpoint
import cash.z.ecc.android.sdk.model.WalletBalance import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.model.defaultForNetwork
import cash.z.ecc.android.sdk.tool.DerivationTool import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
/** /**
@ -66,8 +68,14 @@ class SendFragment : BaseDemoFragment<FragmentSendBinding>() {
runBlocking { runBlocking {
Initializer.new(requireApplicationContext()) { Initializer.new(requireApplicationContext()) {
runBlocking { it.newWallet(seed, network = ZcashNetwork.fromResources(requireApplicationContext())) } val network = ZcashNetwork.fromResources(requireApplicationContext())
it.setNetwork(ZcashNetwork.fromResources(requireApplicationContext())) runBlocking {
it.newWallet(
seed,
network = network,
lightWalletEndpoint = LightWalletEndpoint.defaultForNetwork(network)
)
}
} }
}.let { initializer -> }.let { initializer ->
synchronizer = Synchronizer.newBlocking(initializer) synchronizer = Synchronizer.newBlocking(initializer)

View File

@ -4,10 +4,16 @@ package cash.z.ecc.android.sdk.demoapp.util
import android.content.Context import android.content.Context
import cash.z.ecc.android.sdk.demoapp.R import cash.z.ecc.android.sdk.demoapp.R
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
import java.util.*
fun ZcashNetwork.Companion.fromResources(context: Context) = ZcashNetwork.valueOf( fun ZcashNetwork.Companion.fromResources(context: Context): ZcashNetwork {
context.getString( val networkNameFromResources = context.getString(R.string.network_name).lowercase(Locale.ROOT)
R.string.network_name return if (networkNameFromResources == Testnet.networkName) {
) ZcashNetwork.Testnet
) } else if (networkNameFromResources.lowercase(Locale.ROOT) == Mainnet.networkName) {
ZcashNetwork.Mainnet
} else {
throw IllegalArgumentException("Unknown network name: $networkNameFromResources")
}
}

View File

@ -3,8 +3,8 @@ package cash.z.ecc.android.sdk
import android.content.Context import android.content.Context
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.tool.CheckpointTool import cash.z.ecc.android.sdk.tool.CheckpointTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.json.JSONObject import org.json.JSONObject
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
@ -77,6 +77,7 @@ class AssetTest {
val expectedNetworkName = when (network) { val expectedNetworkName = when (network) {
ZcashNetwork.Mainnet -> "main" ZcashNetwork.Mainnet -> "main"
ZcashNetwork.Testnet -> "test" ZcashNetwork.Testnet -> "test"
else -> IllegalArgumentException("Unsupported network $network")
} }
assertEquals("File: ${it.filename}", expectedNetworkName, jsonObject.getString("network")) assertEquals("File: ${it.filename}", expectedNetworkName, jsonObject.getString("network"))

View File

@ -1,7 +1,7 @@
package cash.z.ecc.android.sdk.ext package cash.z.ecc.android.sdk.ext
import cash.z.ecc.android.sdk.Initializer 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 cash.z.ecc.android.sdk.util.SimpleMnemonics
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient import okhttp3.OkHttpClient

View File

@ -4,11 +4,10 @@ import cash.z.ecc.android.sdk.annotation.MaintainedTest
import cash.z.ecc.android.sdk.annotation.TestPurpose import cash.z.ecc.android.sdk.annotation.TestPurpose
import cash.z.ecc.android.sdk.ext.BlockExplorer import cash.z.ecc.android.sdk.ext.BlockExplorer
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
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 cash.z.ecc.android.sdk.util.TestWallet
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -31,13 +30,6 @@ class SanityTest(
val networkName = wallet.networkName val networkName = wallet.networkName
val name = "$networkName wallet" 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 @Test
fun testFilePaths() { fun testFilePaths() {
assertEquals( assertEquals(
@ -80,24 +72,14 @@ class SanityTest(
) )
} }
@Test
fun testServerConnection() {
assertEquals(
"$name has an invalid server connection",
"$networkName.lightwalletd.com:9067?usePlaintext=false",
wallet.connectionInfo
)
}
@Test @Test
fun testLatestHeight() = runBlocking { fun testLatestHeight() = runBlocking {
if (wallet.networkName == "mainnet") { if (wallet.networkName == "mainnet") {
val expectedHeight = BlockExplorer.fetchLatestHeight() val expectedHeight = BlockExplorer.fetchLatestHeight()
// fetch height directly because the synchronizer hasn't started, yet // fetch height directly because the synchronizer hasn't started, yet
val downloaderHeight = wallet.service.getLatestBlockHeight() val downloaderHeight = wallet.service.getLatestBlockHeight()
val info = wallet.connectionInfo
assertTrue( assertTrue(
"$info\n ${wallet.networkName} Lightwalletd is too far behind. Downloader height $downloaderHeight is more than 10 blocks behind block explorer height $expectedHeight", "${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 expectedHeight - 10 < downloaderHeight.value
) )
} }

View File

@ -4,7 +4,6 @@ import androidx.test.filters.LargeTest
import androidx.test.filters.MediumTest import androidx.test.filters.MediumTest
import cash.z.ecc.android.sdk.annotation.MaintainedTest import cash.z.ecc.android.sdk.annotation.MaintainedTest
import cash.z.ecc.android.sdk.annotation.TestPurpose 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 cash.z.ecc.android.sdk.util.TestWallet
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Assert import org.junit.Assert
@ -19,16 +18,6 @@ import org.junit.Test
@MediumTest @MediumTest
class SmokeTest { 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 @Test
fun testFilePaths() { 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) Assert.assertEquals("Invalid DataDB file", "/data/user/0/cash.z.ecc.android.sdk.test/databases/TestWallet_testnet_Data.db", wallet.initializer.rustBackend.pathDataDb)

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

View File

@ -12,8 +12,11 @@ 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.service.LightWalletService
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight 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.test.ScopedTest
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@ -35,13 +38,15 @@ import org.mockito.Spy
class ChangeServiceTest : ScopedTest() { class ChangeServiceTest : ScopedTest() {
val network = ZcashNetwork.Mainnet val network = ZcashNetwork.Mainnet
val lightWalletEndpoint = LightWalletEndpoint.Mainnet
private val eccEndpoint = LightWalletEndpoint("lightwalletd.electriccoin.co", 9087, true)
@Mock @Mock
lateinit var mockBlockStore: CompactBlockStore lateinit var mockBlockStore: CompactBlockStore
var mockCloseable: AutoCloseable? = null var mockCloseable: AutoCloseable? = null
@Spy @Spy
val service = LightWalletGrpcService(context, network) val service = LightWalletGrpcService.new(context, lightWalletEndpoint)
lateinit var downloader: CompactBlockDownloader lateinit var downloader: CompactBlockDownloader
lateinit var otherService: LightWalletService lateinit var otherService: LightWalletService
@ -50,7 +55,7 @@ class ChangeServiceTest : ScopedTest() {
fun setup() { fun setup() {
initMocks() initMocks()
downloader = CompactBlockDownloader(service, mockBlockStore) downloader = CompactBlockDownloader(service, mockBlockStore)
otherService = LightWalletGrpcService(context, "lightwalletd.electriccoin.co") otherService = LightWalletGrpcService.new(context, eccEndpoint)
} }
@After @After
@ -106,7 +111,7 @@ class ChangeServiceTest : ScopedTest() {
@Test @Test
fun testSwitchToInvalidServer() = runBlocking { fun testSwitchToInvalidServer() = runBlocking {
var caughtException: Throwable? = null var caughtException: Throwable? = null
downloader.changeService(LightWalletGrpcService(context, "invalid.lightwalletd")) { downloader.changeService(LightWalletGrpcService.new(context, LightWalletEndpoint("invalid.lightwalletd", 9087, true))) {
caughtException = it caughtException = it
} }
assertNotNull("Using an invalid host should generate an exception.", caughtException) assertNotNull("Using an invalid host should generate an exception.", caughtException)
@ -119,7 +124,7 @@ class ChangeServiceTest : ScopedTest() {
@Test @Test
fun testSwitchToTestnetFails() = runBlocking { fun testSwitchToTestnetFails() = runBlocking {
var caughtException: Throwable? = null var caughtException: Throwable? = null
downloader.changeService(LightWalletGrpcService(context, ZcashNetwork.Testnet)) { downloader.changeService(LightWalletGrpcService.new(context, LightWalletEndpoint.Testnet)) {
caughtException = it caughtException = it
} }
assertNotNull("Using an invalid host should generate an exception.", caughtException) assertNotNull("Using an invalid host should generate an exception.", caughtException)

View File

@ -3,7 +3,7 @@ package cash.z.ecc.android.sdk.jni
import cash.z.ecc.android.sdk.annotation.MaintainedTest import cash.z.ecc.android.sdk.annotation.MaintainedTest
import cash.z.ecc.android.sdk.annotation.TestPurpose import cash.z.ecc.android.sdk.annotation.TestPurpose
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test

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.annotation.TestPurpose
import cash.z.ecc.android.sdk.internal.TroubleshootingTwig import cash.z.ecc.android.sdk.internal.TroubleshootingTwig
import cash.z.ecc.android.sdk.internal.Twig 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.tool.DerivationTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.BeforeClass 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.internal.Twig
import cash.z.ecc.android.sdk.model.Zatoshi 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 cash.z.ecc.android.sdk.util.TestWallet
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Assert import org.junit.Assert

View File

@ -5,8 +5,7 @@ import cash.z.ecc.android.sdk.ext.ZcashSdk
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.Zatoshi 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.type.ZcashNetwork.Testnet
import cash.z.ecc.android.sdk.util.TestWallet import cash.z.ecc.android.sdk.util.TestWallet
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@ -90,7 +89,7 @@ class TransparentRestoreSample {
val wallet0 = TestWallet( val wallet0 = TestWallet(
TestWallet.Backups.SAMPLE_WALLET.seedPhrase, TestWallet.Backups.SAMPLE_WALLET.seedPhrase,
"tmpabc", "tmpabc",
Testnet, ZcashNetwork.Testnet,
startHeight = BlockHeight.new( startHeight = BlockHeight.new(
ZcashNetwork.Testnet, ZcashNetwork.Testnet,
1330190 1330190
@ -117,7 +116,15 @@ class TransparentRestoreSample {
*/ */
// @Test // @Test
fun hasFunds() = runBlocking<Unit> { fun hasFunds() = runBlocking<Unit> {
val walletSandbox = TestWallet(TestWallet.Backups.SAMPLE_WALLET.seedPhrase, "WalletC", Testnet, startHeight = BlockHeight.new(ZcashNetwork.Testnet, 1330190)) val walletSandbox = TestWallet(
TestWallet.Backups.SAMPLE_WALLET.seedPhrase,
"WalletC",
ZcashNetwork.Testnet,
startHeight = BlockHeight.new(
ZcashNetwork.Testnet,
1330190
)
)
// val job = walletA.walletScope.launch { // val job = walletA.walletScope.launch {
// twig("Syncing WalletA") // twig("Syncing WalletA")
// walletA.sync() // walletA.sync()

View File

@ -4,8 +4,8 @@ import android.content.Context
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.tool.CheckpointTool.IS_FALLBACK_ON_FAILURE import cash.z.ecc.android.sdk.tool.CheckpointTool.IS_FALLBACK_ON_FAILURE
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test

View File

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

View File

@ -9,8 +9,10 @@ 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.model.Checkpoint
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight 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 cash.z.ecc.android.sdk.tool.CheckpointTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@ -82,8 +84,8 @@ class BalancePrinterUtil {
}.collect { seed -> }.collect { seed ->
// TODO: clear the dataDb but leave the cacheDb // TODO: clear the dataDb but leave the cacheDb
val initializer = Initializer.new(context) { config -> val initializer = Initializer.new(context) { config ->
runBlocking { config.importWallet(seed, birthdayHeight, network) } val endpoint = LightWalletEndpoint.defaultForNetwork(network)
config.setNetwork(network) runBlocking { config.importWallet(seed, birthdayHeight, network, endpoint) }
config.alias = alias config.alias = alias
} }
/* /*

View File

@ -7,7 +7,7 @@ import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.internal.TroubleshootingTwig import cash.z.ecc.android.sdk.internal.TroubleshootingTwig
import cash.z.ecc.android.sdk.internal.Twig import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking

View File

@ -11,10 +11,12 @@ import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight 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.WalletBalance
import cash.z.ecc.android.sdk.model.Zatoshi 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.tool.DerivationTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -36,9 +38,8 @@ class TestWallet(
val seedPhrase: String, val seedPhrase: String,
val alias: String = "TestWallet", val alias: String = "TestWallet",
val network: ZcashNetwork = ZcashNetwork.Testnet, val network: ZcashNetwork = ZcashNetwork.Testnet,
val host: String = network.defaultHost, val endpoint: LightWalletEndpoint = LightWalletEndpoint.Testnet,
startHeight: BlockHeight? = null, startHeight: BlockHeight? = null
val port: Int = network.defaultPort
) { ) {
constructor( constructor(
backup: Backups, backup: Backups,
@ -66,7 +67,7 @@ class TestWallet(
runBlocking { DerivationTool.deriveTransparentSecretKey(seed, network = network) } runBlocking { DerivationTool.deriveTransparentSecretKey(seed, network = network) }
val initializer = runBlocking { val initializer = runBlocking {
Initializer.new(context) { config -> 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 val synchronizer: SdkSynchronizer = Synchronizer.newBlocking(initializer) as SdkSynchronizer
@ -79,7 +80,6 @@ class TestWallet(
runBlocking { DerivationTool.deriveTransparentAddress(seed, network = network) } runBlocking { DerivationTool.deriveTransparentAddress(seed, network = network) }
val birthdayHeight get() = synchronizer.latestBirthdayHeight val birthdayHeight get() = synchronizer.latestBirthdayHeight
val networkName get() = synchronizer.network.networkName val networkName get() = synchronizer.network.networkName
val connectionInfo get() = service.connectionInfo.toString()
suspend fun transparentBalance(): WalletBalance { suspend fun transparentBalance(): WalletBalance {
synchronizer.refreshUtxos(transparentAddress, synchronizer.latestBirthdayHeight) synchronizer.refreshUtxos(transparentAddress, synchronizer.latestBirthdayHeight)
@ -166,11 +166,46 @@ class TestWallet(
enum class Backups(val seedPhrase: String, val testnetBirthday: BlockHeight, val mainnetBirthday: BlockHeight) { enum class Backups(val seedPhrase: String, val testnetBirthday: BlockHeight, val mainnetBirthday: BlockHeight) {
// TODO: get the proper birthday values for these wallets // 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", BlockHeight.new(ZcashNetwork.Testnet, 1_355_928), BlockHeight.new(ZcashNetwork.Mainnet, 1_000_000)), DEFAULT(
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)), "column rhythm acoustic gym cost fit keen maze fence seed mail medal shrimp tell relief clip cannon foster soldier shallow refuse lunar parrot banana",
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)), BlockHeight.new(
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)), ZcashNetwork.Testnet,
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)), 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

@ -6,7 +6,9 @@ import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService import cash.z.ecc.android.sdk.internal.service.LightWalletGrpcService
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork 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.Ignore
import org.junit.Test import org.junit.Test
@ -14,7 +16,7 @@ class TransactionCounterUtil {
private val network = ZcashNetwork.Mainnet private val network = ZcashNetwork.Mainnet
private val context = InstrumentationRegistry.getInstrumentation().context private val context = InstrumentationRegistry.getInstrumentation().context
private val service = LightWalletGrpcService(context, network) private val service = LightWalletGrpcService.new(context, LightWalletEndpoint.Mainnet)
init { init {
Twig.plant(TroubleshootingTwig()) Twig.plant(TroubleshootingTwig())

View File

@ -8,7 +8,7 @@ import cash.z.ecc.android.sdk.internal.KEY_VERSION
import cash.z.ecc.android.sdk.internal.VERSION_1 import cash.z.ecc.android.sdk.internal.VERSION_1
import cash.z.ecc.android.sdk.internal.model.Checkpoint import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
import org.json.JSONObject import org.json.JSONObject
object CheckpointFixture { object CheckpointFixture {

View File

@ -10,10 +10,11 @@ import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.jni.RustBackend import cash.z.ecc.android.sdk.jni.RustBackend
import cash.z.ecc.android.sdk.model.BlockHeight 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.tool.CheckpointTool import cash.z.ecc.android.sdk.tool.CheckpointTool
import cash.z.ecc.android.sdk.tool.DerivationTool import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.UnifiedViewingKey import cash.z.ecc.android.sdk.type.UnifiedViewingKey
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
@ -21,13 +22,13 @@ import java.io.File
/** /**
* Simplified Initializer focused on starting from a ViewingKey. * Simplified Initializer focused on starting from a ViewingKey.
*/ */
@Suppress("LongParameterList")
class Initializer private constructor( class Initializer private constructor(
val context: Context, val context: Context,
internal val rustBackend: RustBackend, internal val rustBackend: RustBackend,
val network: ZcashNetwork, val network: ZcashNetwork,
val alias: String, val alias: String,
val host: String, val lightWalletEndpoint: LightWalletEndpoint,
val port: Int,
val viewingKeys: List<UnifiedViewingKey>, val viewingKeys: List<UnifiedViewingKey>,
val overwriteVks: Boolean, val overwriteVks: Boolean,
internal val checkpoint: Checkpoint internal val checkpoint: Checkpoint
@ -45,10 +46,7 @@ class Initializer private constructor(
lateinit var network: ZcashNetwork lateinit var network: ZcashNetwork
private set private set
lateinit var host: String lateinit var lightWalletEndpoint: LightWalletEndpoint
private set
var port: Int = ZcashNetwork.Mainnet.defaultPort
private set private set
/** /**
@ -160,12 +158,10 @@ class Initializer private constructor(
*/ */
fun setNetwork( fun setNetwork(
network: ZcashNetwork, network: ZcashNetwork,
host: String = network.defaultHost, lightWalletEndpoint: LightWalletEndpoint
port: Int = network.defaultPort
): Config = apply { ): Config = apply {
this.network = network this.network = network
this.host = host this.lightWalletEndpoint = lightWalletEndpoint
this.port = port
} }
/** /**
@ -175,16 +171,14 @@ class Initializer private constructor(
seed: ByteArray, seed: ByteArray,
birthday: BlockHeight?, birthday: BlockHeight?,
network: ZcashNetwork, network: ZcashNetwork,
host: String = network.defaultHost, lightWalletEndpoint: LightWalletEndpoint,
port: Int = network.defaultPort,
alias: String = ZcashSdk.DEFAULT_ALIAS alias: String = ZcashSdk.DEFAULT_ALIAS
): Config = ): Config =
importWallet( importWallet(
DerivationTool.deriveUnifiedViewingKeys(seed, network = network)[0], DerivationTool.deriveUnifiedViewingKeys(seed, network = network)[0],
birthday, birthday,
network, network,
host, lightWalletEndpoint,
port,
alias alias
) )
@ -195,12 +189,11 @@ class Initializer private constructor(
viewingKey: UnifiedViewingKey, viewingKey: UnifiedViewingKey,
birthday: BlockHeight?, birthday: BlockHeight?,
network: ZcashNetwork, network: ZcashNetwork,
host: String = network.defaultHost, lightWalletEndpoint: LightWalletEndpoint,
port: Int = network.defaultPort,
alias: String = ZcashSdk.DEFAULT_ALIAS alias: String = ZcashSdk.DEFAULT_ALIAS
): Config = apply { ): Config = apply {
setViewingKeys(viewingKey) setViewingKeys(viewingKey)
setNetwork(network, host, port) setNetwork(network, lightWalletEndpoint)
importedWalletBirthday(birthday) importedWalletBirthday(birthday)
this.alias = alias this.alias = alias
} }
@ -211,14 +204,12 @@ class Initializer private constructor(
suspend fun newWallet( suspend fun newWallet(
seed: ByteArray, seed: ByteArray,
network: ZcashNetwork, network: ZcashNetwork,
host: String = network.defaultHost, lightWalletEndpoint: LightWalletEndpoint,
port: Int = network.defaultPort,
alias: String = ZcashSdk.DEFAULT_ALIAS alias: String = ZcashSdk.DEFAULT_ALIAS
): Config = newWallet( ): Config = newWallet(
DerivationTool.deriveUnifiedViewingKeys(seed, network)[0], DerivationTool.deriveUnifiedViewingKeys(seed, network)[0],
network, network,
host, lightWalletEndpoint,
port,
alias alias
) )
@ -228,12 +219,11 @@ class Initializer private constructor(
fun newWallet( fun newWallet(
viewingKey: UnifiedViewingKey, viewingKey: UnifiedViewingKey,
network: ZcashNetwork, network: ZcashNetwork,
host: String = network.defaultHost, lightWalletEndpoint: LightWalletEndpoint,
port: Int = network.defaultPort,
alias: String = ZcashSdk.DEFAULT_ALIAS alias: String = ZcashSdk.DEFAULT_ALIAS
): Config = apply { ): Config = apply {
setViewingKeys(viewingKey) setViewingKeys(viewingKey)
setNetwork(network, host, port) setNetwork(network, lightWalletEndpoint)
newWalletBirthday() newWalletBirthday()
this.alias = alias this.alias = alias
} }
@ -352,8 +342,7 @@ class Initializer private constructor(
rustBackend, rustBackend,
config.network, config.network,
config.alias, config.alias,
config.host, config.lightWalletEndpoint,
config.port,
config.viewingKeys, config.viewingKeys,
config.overwriteVks, config.overwriteVks,
loadedCheckpoint loadedCheckpoint

View File

@ -50,12 +50,12 @@ import cash.z.ecc.android.sdk.internal.twigTask
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.WalletBalance import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.Zatoshi 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.tool.DerivationTool
import cash.z.ecc.android.sdk.type.AddressType import cash.z.ecc.android.sdk.type.AddressType
import cash.z.ecc.android.sdk.type.AddressType.Shielded import cash.z.ecc.android.sdk.type.AddressType.Shielded
import cash.z.ecc.android.sdk.type.AddressType.Transparent import cash.z.ecc.android.sdk.type.AddressType.Transparent
import cash.z.ecc.android.sdk.type.ConsensusMatchType import cash.z.ecc.android.sdk.type.ConsensusMatchType
import cash.z.ecc.android.sdk.type.ZcashNetwork
import cash.z.wallet.sdk.rpc.Service import cash.z.wallet.sdk.rpc.Service
import io.grpc.ManagedChannel import io.grpc.ManagedChannel
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
@ -308,20 +308,6 @@ class SdkSynchronizer internal constructor(
*/ */
override suspend fun getServerInfo(): Service.LightdInfo = processor.downloader.getServerInfo() override suspend fun getServerInfo(): Service.LightdInfo = processor.downloader.getServerInfo()
/**
* Changes the server that is being used to download compact blocks. This will throw an
* exception if it detects that the server change is invalid e.g. switching to testnet from
* mainnet.
*/
override suspend fun changeServer(host: String, port: Int, errorHandler: (Throwable) -> Unit) {
val info =
(processor.downloader.lightWalletService as LightWalletGrpcService).connectionInfo
processor.downloader.changeService(
LightWalletGrpcService(info.appContext, host, port),
errorHandler
)
}
override suspend fun getNearestRewindHeight(height: BlockHeight): BlockHeight = override suspend fun getNearestRewindHeight(height: BlockHeight): BlockHeight =
processor.getNearestRewindHeight(height) processor.getNearestRewindHeight(height)
@ -800,7 +786,7 @@ object DefaultSynchronizerFactory {
CompactBlockDbStore.new(initializer.context, initializer.network, initializer.rustBackend.pathCacheDb) CompactBlockDbStore.new(initializer.context, initializer.network, initializer.rustBackend.pathCacheDb)
fun defaultService(initializer: Initializer): LightWalletService = fun defaultService(initializer: Initializer): LightWalletService =
LightWalletGrpcService(initializer.context, initializer.host, initializer.port) LightWalletGrpcService.new(initializer.context, initializer.lightWalletEndpoint)
fun defaultEncoder( fun defaultEncoder(
initializer: Initializer, initializer: Initializer,

View File

@ -7,9 +7,9 @@ import cash.z.ecc.android.sdk.ext.ZcashSdk
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.WalletBalance import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.Zatoshi import cash.z.ecc.android.sdk.model.Zatoshi
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.type.AddressType import cash.z.ecc.android.sdk.type.AddressType
import cash.z.ecc.android.sdk.type.ConsensusMatchType import cash.z.ecc.android.sdk.type.ConsensusMatchType
import cash.z.ecc.android.sdk.type.ZcashNetwork
import cash.z.wallet.sdk.rpc.Service import cash.z.wallet.sdk.rpc.Service
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -284,18 +284,6 @@ interface Synchronizer {
*/ */
suspend fun getServerInfo(): Service.LightdInfo suspend fun getServerInfo(): Service.LightdInfo
/**
* Gracefully change the server that the Synchronizer is currently using. In some cases, this
* will require waiting until current network activity is complete. Ideally, this would protect
* against accidentally switching between testnet and mainnet, by comparing the service info of
* the existing server with that of the new one.
*/
suspend fun changeServer(
host: String,
port: Int = network.defaultPort,
errorHandler: (Throwable) -> Unit = { throw it }
)
/** /**
* Download all UTXOs for the given address and store any new ones in the database. * Download all UTXOs for the given address and store any new ones in the database.
* *

View File

@ -2,7 +2,7 @@ package cash.z.ecc.android.sdk.exception
import cash.z.ecc.android.sdk.internal.model.Checkpoint import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.wallet.sdk.rpc.Service import cash.z.wallet.sdk.rpc.Service
import io.grpc.Status import io.grpc.Status
import io.grpc.Status.Code.UNAVAILABLE import io.grpc.Status.Code.UNAVAILABLE

View File

@ -2,7 +2,7 @@ package cash.z.ecc.android.sdk.internal
import cash.z.ecc.android.sdk.internal.model.Checkpoint import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
import org.json.JSONObject import org.json.JSONObject
// Version is not returned from the server, so version 1 is implied. A version is declared here // Version is not returned from the server, so version 1 is implied. A version is declared here

View File

@ -8,7 +8,7 @@ import cash.z.ecc.android.sdk.internal.SdkDispatchers
import cash.z.ecc.android.sdk.internal.SdkExecutors import cash.z.ecc.android.sdk.internal.SdkExecutors
import cash.z.ecc.android.sdk.internal.db.CompactBlockDb import cash.z.ecc.android.sdk.internal.db.CompactBlockDb
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.wallet.sdk.rpc.CompactFormats import cash.z.wallet.sdk.rpc.CompactFormats
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext

View File

@ -1,12 +1,10 @@
package cash.z.ecc.android.sdk.internal.service package cash.z.ecc.android.sdk.internal.service
import android.content.Context import android.content.Context
import cash.z.ecc.android.sdk.R
import cash.z.ecc.android.sdk.annotation.OpenForTesting import cash.z.ecc.android.sdk.annotation.OpenForTesting
import cash.z.ecc.android.sdk.exception.LightWalletException
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.LightWalletEndpoint
import cash.z.wallet.sdk.rpc.CompactFormats import cash.z.wallet.sdk.rpc.CompactFormats
import cash.z.wallet.sdk.rpc.CompactTxStreamerGrpc import cash.z.wallet.sdk.rpc.CompactTxStreamerGrpc
import cash.z.wallet.sdk.rpc.Service import cash.z.wallet.sdk.rpc.Service
@ -31,39 +29,14 @@ import kotlin.time.Duration.Companion.seconds
*/ */
@OpenForTesting @OpenForTesting
class LightWalletGrpcService private constructor( class LightWalletGrpcService private constructor(
context: Context,
private val lightWalletEndpoint: LightWalletEndpoint,
var channel: ManagedChannel, var channel: ManagedChannel,
private val singleRequestTimeout: Duration = 10.seconds, private val singleRequestTimeout: Duration = 10.seconds,
private val streamingRequestTimeout: Duration = 90.seconds private val streamingRequestTimeout: Duration = 90.seconds
) : LightWalletService { ) : LightWalletService {
lateinit var connectionInfo: ConnectionInfo private val applicationContext = context.applicationContext
constructor(
appContext: Context,
network: ZcashNetwork,
usePlaintext: Boolean =
appContext.resources.getBoolean(R.bool.lightwalletd_allow_very_insecure_connections)
) : this(appContext, network.defaultHost, network.defaultPort, usePlaintext)
/**
* Construct an instance that corresponds to the given host and port.
*
* @param appContext the application context used to check whether TLS is required by this build
* flavor.
* @param host the host of the server to use.
* @param port the port of the server to use.
* @param usePlaintext whether to use TLS or plaintext for requests. Plaintext is dangerous so
* it requires jumping through a few more hoops.
*/
constructor(
appContext: Context,
host: String,
port: Int = ZcashNetwork.Mainnet.defaultPort,
usePlaintext: Boolean =
appContext.resources.getBoolean(R.bool.lightwalletd_allow_very_insecure_connections)
) : this(createDefaultChannel(appContext, host, port, usePlaintext)) {
connectionInfo = ConnectionInfo(appContext.applicationContext, host, port, usePlaintext)
}
/* LightWalletService implementation */ /* LightWalletService implementation */
@ -141,22 +114,14 @@ class LightWalletGrpcService private constructor(
} }
override fun reconnect() { override fun reconnect() {
twig( twig("closing existing channel and then reconnecting")
"closing existing channel and then reconnecting to ${connectionInfo.host}:" +
"${connectionInfo.port}?usePlaintext=${connectionInfo.usePlaintext}"
)
channel.shutdown() channel.shutdown()
channel = createDefaultChannel( channel = createDefaultChannel(applicationContext, lightWalletEndpoint)
connectionInfo.appContext,
connectionInfo.host,
connectionInfo.port,
connectionInfo.usePlaintext
)
} }
// test code // test code
var stateCount = 0 internal var stateCount = 0
var state: ConnectivityState? = null internal var state: ConnectivityState? = null
private fun requireChannel(): ManagedChannel { private fun requireChannel(): ManagedChannel {
state = channel.getState(false).let { new -> state = channel.getState(false).let { new ->
if (state == new) stateCount++ else stateCount = 0 if (state == new) stateCount++ else stateCount = 0
@ -172,37 +137,13 @@ class LightWalletGrpcService private constructor(
return channel return channel
} }
//
// Utilities
//
private fun Channel.createStub(timeoutSec: Duration = 60.seconds) = CompactTxStreamerGrpc
.newBlockingStub(this)
.withDeadlineAfter(timeoutSec.inWholeSeconds, TimeUnit.SECONDS)
/**
* This function effectively parses streaming responses. Each call to next(), on the iterators
* returned from grpc, triggers a network call.
*/
private fun <T> Iterator<T>.toList(): List<T> =
mutableListOf<T>().apply {
while (hasNext()) {
this@apply += next()
}
}
inner class ConnectionInfo(
val appContext: Context,
val host: String,
val port: Int,
val usePlaintext: Boolean
) {
override fun toString(): String {
return "$host:$port?usePlaintext=$usePlaintext"
}
}
companion object { companion object {
fun new(context: Context, lightWalletEndpoint: LightWalletEndpoint): LightWalletGrpcService {
val channel = createDefaultChannel(context, lightWalletEndpoint)
return LightWalletGrpcService(context, lightWalletEndpoint, channel)
}
/** /**
* Convenience function for creating the default channel to be used for all connections. It * Convenience function for creating the default channel to be used for all connections. It
* is important that this channel can handle transitioning from WiFi to Cellular connections * is important that this channel can handle transitioning from WiFi to Cellular connections
@ -210,24 +151,23 @@ class LightWalletGrpcService private constructor(
*/ */
fun createDefaultChannel( fun createDefaultChannel(
appContext: Context, appContext: Context,
host: String, lightWalletEndpoint: LightWalletEndpoint
port: Int,
usePlaintext: Boolean
): ManagedChannel { ): ManagedChannel {
twig("Creating channel that will connect to $host:$port?usePlaintext=$usePlaintext") twig(
"Creating channel that will connect to " +
"${lightWalletEndpoint.host}:${lightWalletEndpoint.port}" +
"/?usePlaintext=${!lightWalletEndpoint.isSecure}"
)
return AndroidChannelBuilder return AndroidChannelBuilder
.forAddress(host, port) .forAddress(lightWalletEndpoint.host, lightWalletEndpoint.port)
.context(appContext) .context(appContext)
.enableFullStreamDecompression() .enableFullStreamDecompression()
.apply { .apply {
if (usePlaintext) { if (lightWalletEndpoint.isSecure) {
if (!appContext.resources.getBoolean(
R.bool.lightwalletd_allow_very_insecure_connections
)
) throw LightWalletException.InsecureConnection
usePlaintext()
} else {
useTransportSecurity() useTransportSecurity()
} else {
twig("WARNING: Using insecure channel")
usePlaintext()
} }
} }
.build() .build()
@ -235,6 +175,10 @@ class LightWalletGrpcService private constructor(
} }
} }
private fun Channel.createStub(timeoutSec: Duration = 60.seconds) = CompactTxStreamerGrpc
.newBlockingStub(this)
.withDeadlineAfter(timeoutSec.inWholeSeconds, TimeUnit.SECONDS)
private fun BlockHeight.toBlockHeight(): Service.BlockID = private fun BlockHeight.toBlockHeight(): Service.BlockID =
Service.BlockID.newBuilder().setHeight(value).build() Service.BlockID.newBuilder().setHeight(value).build()
@ -243,3 +187,14 @@ private fun ClosedRange<BlockHeight>.toBlockRange(): Service.BlockRange =
.setStart(start.toBlockHeight()) .setStart(start.toBlockHeight())
.setEnd(endInclusive.toBlockHeight()) .setEnd(endInclusive.toBlockHeight())
.build() .build()
/**
* This function effectively parses streaming responses. Each call to next(), on the iterators
* returned from grpc, triggers a network call.
*/
private fun <T> Iterator<T>.toList(): List<T> =
mutableListOf<T>().apply {
while (hasNext()) {
this@apply += next()
}
}

View File

@ -16,8 +16,8 @@ import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.jni.RustBackend import cash.z.ecc.android.sdk.jni.RustBackend
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.type.UnifiedViewingKey import cash.z.ecc.android.sdk.type.UnifiedViewingKey
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext

View File

@ -9,9 +9,9 @@ import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.WalletBalance import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.Zatoshi 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.tool.DerivationTool
import cash.z.ecc.android.sdk.type.UnifiedViewingKey import cash.z.ecc.android.sdk.type.UnifiedViewingKey
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File

View File

@ -4,8 +4,8 @@ import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.WalletBalance import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.Zatoshi import cash.z.ecc.android.sdk.model.Zatoshi
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.type.UnifiedViewingKey import cash.z.ecc.android.sdk.type.UnifiedViewingKey
import cash.z.ecc.android.sdk.type.ZcashNetwork
/** /**
* Contract defining the exposed capabilities of the Rust backend. * Contract defining the exposed capabilities of the Rust backend.

View File

@ -2,7 +2,6 @@ package cash.z.ecc.android.sdk.model
import android.content.Context import android.content.Context
import cash.z.ecc.android.sdk.tool.CheckpointTool import cash.z.ecc.android.sdk.tool.CheckpointTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
/** /**
* Represents a block height, which is a UInt32. SDK clients use this class to represent the "birthday" of a wallet. * Represents a block height, which is a UInt32. SDK clients use this class to represent the "birthday" of a wallet.

View File

@ -0,0 +1,5 @@
package cash.z.ecc.android.sdk.model
data class LightWalletEndpoint(val host: String, val port: Int, val isSecure: Boolean) {
companion object
}

View File

@ -0,0 +1,44 @@
@file:Suppress("ktlint:filename")
package cash.z.ecc.android.sdk.model
/*
* This is a set of extension functions currently, because we expect them to change in the future.
*/
fun LightWalletEndpoint.Companion.defaultForNetwork(zcashNetwork: ZcashNetwork): LightWalletEndpoint {
return when (zcashNetwork.id) {
ZcashNetwork.Mainnet.id -> LightWalletEndpoint.Mainnet
ZcashNetwork.Testnet.id -> LightWalletEndpoint.Testnet
else -> error("Unknown network id: ${zcashNetwork.id}")
}
}
/**
* This is a special localhost value on the Android emulator, which allows it to contact
* the localhost of the computer running the emulator.
*/
private const val COMPUTER_LOCALHOST = "10.0.2.2"
private const val DEFAULT_PORT = 9067
val LightWalletEndpoint.Companion.Mainnet
get() = LightWalletEndpoint(
"mainnet.lightwalletd.com",
DEFAULT_PORT,
isSecure = true
)
val LightWalletEndpoint.Companion.Testnet
get() = LightWalletEndpoint(
"testnet.lightwalletd.com",
DEFAULT_PORT,
isSecure = true
)
val LightWalletEndpoint.Companion.Darkside
get() = LightWalletEndpoint(
COMPUTER_LOCALHOST,
DEFAULT_PORT,
isSecure = false
)

View File

@ -0,0 +1,43 @@
package cash.z.ecc.android.sdk.model
/**
* The Zcash network. Should be one of [ZcashNetwork.Testnet] or [ZcashNetwork.Mainnet].
*
* The constructor for the network is public to allow for certain test cases to use a custom "darkside" network.
*/
data class ZcashNetwork(
val id: Int,
val networkName: String,
val saplingActivationHeight: BlockHeight,
val orchardActivationHeight: BlockHeight
) {
@Suppress("MagicNumber")
companion object {
const val ID_TESTNET = 0
const val ID_MAINNET = 1
// You may notice there are extra checkpoints bundled in the SDK that match the
// sapling/orchard activation heights.
val Testnet = ZcashNetwork(
ID_TESTNET,
"testnet",
saplingActivationHeight = BlockHeight(280_000),
orchardActivationHeight = BlockHeight(1_842_420)
)
val Mainnet = ZcashNetwork(
ID_MAINNET,
"mainnet",
saplingActivationHeight = BlockHeight(419_200),
orchardActivationHeight = BlockHeight(1_687_104)
)
fun from(id: Int) = when (id) {
0 -> Testnet
1 -> Mainnet
else -> throw IllegalArgumentException("Unknown network id: $id")
}
}
}

View File

@ -7,7 +7,7 @@ import cash.z.ecc.android.sdk.internal.from
import cash.z.ecc.android.sdk.internal.model.Checkpoint import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.android.sdk.internal.twig import cash.z.ecc.android.sdk.internal.twig
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.type.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.BufferedReader import java.io.BufferedReader

View File

@ -2,8 +2,8 @@ package cash.z.ecc.android.sdk.tool
import cash.z.ecc.android.sdk.jni.RustBackend import cash.z.ecc.android.sdk.jni.RustBackend
import cash.z.ecc.android.sdk.jni.RustBackendWelding import cash.z.ecc.android.sdk.jni.RustBackendWelding
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.type.UnifiedViewingKey import cash.z.ecc.android.sdk.type.UnifiedViewingKey
import cash.z.ecc.android.sdk.type.ZcashNetwork
class DerivationTool { class DerivationTool {

View File

@ -1,18 +0,0 @@
package cash.z.ecc.android.sdk.type
import cash.z.ecc.android.sdk.model.BlockHeight
enum class ZcashNetwork(
val id: Int,
val networkName: String,
val saplingActivationHeight: BlockHeight,
val defaultHost: String,
val defaultPort: Int
) {
Testnet(0, "testnet", BlockHeight(280_000), "testnet.lightwalletd.com", 9067),
Mainnet(1, "mainnet", BlockHeight(419_200), "mainnet.lightwalletd.com", 9067);
companion object {
fun from(id: Int) = values().first { it.id == id }
}
}

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="lightwalletd_allow_very_insecure_connections">false</bool>
</resources>

View File

@ -1,3 +0,0 @@
<resources>
<string name="sdk_test_message">Library linking is working!</string>
</resources>

View File

@ -1,6 +1,5 @@
package cash.z.ecc.android.sdk.model package cash.z.ecc.android.sdk.model
import cash.z.ecc.android.sdk.type.ZcashNetwork
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith

View File

@ -0,0 +1,16 @@
package cash.z.ecc.android.sdk.model
import org.junit.Test
import kotlin.test.assertTrue
class LightWalletEndpointTest {
@Test
fun requireSecureMainnet() {
assertTrue(LightWalletEndpoint.Mainnet.isSecure)
}
@Test
fun requireSecureTestnet() {
assertTrue(LightWalletEndpoint.Testnet.isSecure)
}
}