Merge initializers.
The new API for initializing with a viewing key will support all existing uses of the other initializer. So they can be safely merged and the changes to support the new one are fairly straight forward. Also, iterated on the design of the Initializer to simplify construction and make the API easier to use.
This commit is contained in:
parent
306eac73b0
commit
58925b04b3
|
@ -110,4 +110,8 @@ class GetBlockRangeFragment : BaseDemoFragment<FragmentGetBlockRangeBinding>() {
|
|||
override fun inflateBinding(layoutInflater: LayoutInflater): FragmentGetBlockRangeBinding =
|
||||
FragmentGetBlockRangeBinding.inflate(layoutInflater)
|
||||
|
||||
override fun onActionButtonClicked() {
|
||||
super.onActionButtonClicked()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,9 +8,9 @@ import androidx.paging.PagedList
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import cash.z.ecc.android.bip39.Mnemonics
|
||||
import cash.z.ecc.android.bip39.toSeed
|
||||
import cash.z.ecc.android.sdk.Initializer
|
||||
import cash.z.ecc.android.sdk.SdkSynchronizer
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.VkInitializer
|
||||
import cash.z.ecc.android.sdk.block.CompactBlockProcessor
|
||||
import cash.z.ecc.android.sdk.db.entity.ConfirmedTransaction
|
||||
import cash.z.ecc.android.sdk.demoapp.App
|
||||
|
@ -49,9 +49,9 @@ class ListTransactionsFragment : BaseDemoFragment<FragmentListTransactionsBindin
|
|||
val seed = Mnemonics.MnemonicCode(seedPhrase).toSeed()
|
||||
|
||||
App.instance.defaultConfig.let { config ->
|
||||
initializer = VkInitializer(App.instance) {
|
||||
import(seed, config.birthdayHeight)
|
||||
server(config.host, config.port)
|
||||
initializer = Initializer(App.instance) {
|
||||
it.import(seed, config.birthdayHeight)
|
||||
it.server(config.host, config.port)
|
||||
}
|
||||
address = DerivationTool.deriveShieldedAddress(seed)
|
||||
}
|
||||
|
|
|
@ -46,9 +46,8 @@ import kotlinx.coroutines.withContext
|
|||
*/
|
||||
class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
|
||||
private val config = App.instance.defaultConfig
|
||||
private val initializer =
|
||||
Initializer(App.instance, host = config.host, port = config.port, alias = "Demo_Utxos")
|
||||
private val birthday = WalletBirthdayTool.loadNearest(App.instance, config.birthdayHeight)
|
||||
private lateinit var seed: ByteArray
|
||||
private lateinit var initializer: SdkSynchronizer.SdkInitializer
|
||||
private lateinit var synchronizer: Synchronizer
|
||||
private lateinit var adapter: UtxoAdapter<ConfirmedTransaction>
|
||||
private val address: String = "t1RwbKka1CnktvAJ1cSqdn7c6PXWG4tZqgd"
|
||||
|
@ -59,6 +58,26 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
|
|||
override fun inflateBinding(layoutInflater: LayoutInflater): FragmentListUtxosBinding =
|
||||
FragmentListUtxosBinding.inflate(layoutInflater)
|
||||
|
||||
/**
|
||||
* Initialize the required values that would normally live outside the demo but are repeated
|
||||
* here for completeness so that each demo file can serve as a standalone example.
|
||||
*/
|
||||
private fun setup() {
|
||||
// Use a BIP-39 library to convert a seed phrase into a byte array. Most wallets already
|
||||
// have the seed stored
|
||||
seed = Mnemonics.MnemonicCode(sharedViewModel.seedPhrase.value).toSeed()
|
||||
initializer = Initializer(App.instance) {
|
||||
it.import(seed, config.birthdayHeight)
|
||||
it.alias = "Demo_Utxos"
|
||||
}
|
||||
synchronizer = Synchronizer(initializer)
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
setup()
|
||||
}
|
||||
|
||||
fun initUi() {
|
||||
binding.inputAddress.setText(address)
|
||||
binding.inputRangeStart.setText(ZcashSdk.SAPLING_ACTIVATION_HEIGHT.toString())
|
||||
|
@ -85,7 +104,7 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
|
|||
val txids = lightwalletService?.getTAddressTransactions(addressToUse, startToUse..endToUse)
|
||||
var delta = now - allStart
|
||||
updateStatus("found ${txids?.size} transactions in ${delta}ms.", false)
|
||||
|
||||
|
||||
txids?.map {
|
||||
it.data.apply {
|
||||
try {
|
||||
|
@ -93,7 +112,7 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
|
|||
} catch (t: Throwable) {
|
||||
twig("failed to decrypt and store transaction due to: $t")
|
||||
}
|
||||
}
|
||||
}
|
||||
}?.let { txData ->
|
||||
val parseStart = now
|
||||
val tList = LocalRpcTypes.TransactionDataList.newBuilder().addAllData(txData).build()
|
||||
|
@ -147,18 +166,6 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
|
|||
var finalCount: Int = 0
|
||||
fun resetInBackground() {
|
||||
try {
|
||||
initializer.new(Mnemonics.MnemonicCode(sharedViewModel.seedPhrase.value).toSeed(), birthday)
|
||||
} catch (e: Throwable) {
|
||||
twig("warning to create a new initializer! Trying to open one instead due to: $e")
|
||||
try {
|
||||
initializer.open(birthday)
|
||||
} catch (t: Throwable) {
|
||||
twig("warning failed to open the initializer. Due to: $t")
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
synchronizer = Synchronizer(initializer)
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
initialCount = (synchronizer as SdkSynchronizer).getTransactionCount()
|
||||
|
@ -179,7 +186,6 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
|
|||
|
||||
fun onClear() {
|
||||
synchronizer.stop()
|
||||
initializer.clear()
|
||||
}
|
||||
|
||||
private fun initTransactionUi() {
|
||||
|
|
|
@ -7,14 +7,13 @@ import android.widget.TextView
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import cash.z.ecc.android.bip39.Mnemonics
|
||||
import cash.z.ecc.android.bip39.toSeed
|
||||
import cash.z.ecc.android.sdk.Initializer
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.VkInitializer
|
||||
import cash.z.ecc.android.sdk.block.CompactBlockProcessor
|
||||
import cash.z.ecc.android.sdk.db.entity.*
|
||||
import cash.z.ecc.android.sdk.demoapp.App
|
||||
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
|
||||
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentSendBinding
|
||||
import cash.z.ecc.android.sdk.demoapp.util.SampleStorageBridge
|
||||
import cash.z.ecc.android.sdk.demoapp.util.mainActivity
|
||||
import cash.z.ecc.android.sdk.ext.*
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
|
@ -51,9 +50,9 @@ class SendFragment : BaseDemoFragment<FragmentSendBinding>() {
|
|||
val seed = Mnemonics.MnemonicCode(seedPhrase).toSeed()
|
||||
|
||||
App.instance.defaultConfig.let { config ->
|
||||
VkInitializer(App.instance) {
|
||||
import(seed, config.birthdayHeight)
|
||||
server(config.host, config.port)
|
||||
Initializer(App.instance) {
|
||||
it.import(seed, config.birthdayHeight)
|
||||
it.server(config.host, config.port)
|
||||
}.let { initializer ->
|
||||
synchronizer = Synchronizer(initializer)
|
||||
}
|
||||
|
|
|
@ -6,17 +6,17 @@ import cash.z.ecc.android.sdk.ext.Twig
|
|||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class VkInitializerTest {
|
||||
class InitializerTest {
|
||||
|
||||
@Test
|
||||
fun testInit() {
|
||||
val height = 1_419_900
|
||||
|
||||
val initializer = VkInitializer(context) {
|
||||
importedWalletBirthday(height)
|
||||
setViewingKeys("zxviews1qvn6j50dqqqqpqxqkvqgx2sp63jccr4k5t8zefadpzsu0yy73vczfznwc794xz6lvy3yp5ucv43lww48zz95ey5vhrsq83dqh0ky9junq0cww2wjp9c3cd45n5l5x8l2g9atnx27e9jgyy8zasjy26gugjtefphan9al3tx208m8ekev5kkx3ug6pd0qk4gq4j4wfuxajn388pfpq54wklwktqkyjz9e6gam0n09xjc35ncd3yah5aa9ezj55lk4u7v7hn0v86vz7ygq4qj2v",
|
||||
val initializer = Initializer(context) { config ->
|
||||
config.importedWalletBirthday(height)
|
||||
config.setViewingKeys("zxviews1qvn6j50dqqqqpqxqkvqgx2sp63jccr4k5t8zefadpzsu0yy73vczfznwc794xz6lvy3yp5ucv43lww48zz95ey5vhrsq83dqh0ky9junq0cww2wjp9c3cd45n5l5x8l2g9atnx27e9jgyy8zasjy26gugjtefphan9al3tx208m8ekev5kkx3ug6pd0qk4gq4j4wfuxajn388pfpq54wklwktqkyjz9e6gam0n09xjc35ncd3yah5aa9ezj55lk4u7v7hn0v86vz7ygq4qj2v",
|
||||
"zxviews1qv886f6hqqqqpqy2ajg9sm22vs4gm4hhajthctfkfws34u45pjtut3qmz0eatpqzvllgsvlk3x0y35ktx5fnzqqzueyph20k3328kx46y3u5xs4750cwuwjuuccfp7la6rh8yt2vjz6tylsrwzy3khtjjzw7etkae6gw3vq608k7quka4nxkeqdxxsr9xxdagv2rhhwugs6w0cquu2ykgzgaln2vyv6ah3ram2h6lrpxuznyczt2xl3lyxcwlk4wfz5rh7wzfd7642c2ae5d7")
|
||||
alias = "VkInitTest2"
|
||||
config.alias = "VkInitTest2"
|
||||
}
|
||||
assertEquals(height, initializer.birthday.height)
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
package cash.z.ecc.android.sdk.ext
|
||||
|
||||
import cash.z.ecc.android.sdk.Initializer
|
||||
import cash.z.ecc.android.sdk.Initializer.DefaultBirthdayStore.Companion.ImportedWalletBirthdayStore
|
||||
import cash.z.ecc.android.sdk.Initializer.DefaultBirthdayStore.Companion.NewWalletBirthdayStore
|
||||
import cash.z.ecc.android.sdk.util.SimpleMnemonics
|
||||
import kotlinx.coroutines.*
|
||||
import org.junit.After
|
||||
|
@ -11,34 +9,11 @@ import org.junit.Before
|
|||
import org.junit.BeforeClass
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
fun Initializer.importPhrase(
|
||||
seedPhrase: String,
|
||||
birthdayHeight: Int,
|
||||
alias: String = ZcashSdk.DEFAULT_ALIAS,
|
||||
clearCacheDb: Boolean = true,
|
||||
clearDataDb: Boolean = true
|
||||
) {
|
||||
SimpleMnemonics().toSeed(seedPhrase.toCharArray()).let { seed ->
|
||||
ImportedWalletBirthdayStore(context, birthdayHeight, alias).getBirthday().let {
|
||||
import(seed, it, clearCacheDb, clearDataDb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Initializer.new(alias: String = ZcashSdk.DEFAULT_ALIAS) {
|
||||
SimpleMnemonics().let { mnemonics ->
|
||||
mnemonics.nextMnemonic().let { phrase ->
|
||||
twig("DELETE THIS LOG! ${String(phrase)}")
|
||||
NewWalletBirthdayStore(context, alias).getBirthday().let {
|
||||
new(mnemonics.toSeed(phrase), it, 1, true, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
fun Initializer.Builder.seedPhrase(seedPhrase: String) {
|
||||
setSeed(SimpleMnemonics().toSeed(seedPhrase.toCharArray()))
|
||||
}
|
||||
|
||||
fun Initializer.deriveSpendingKey(seedPhrase: String) =
|
||||
deriveSpendingKeys(SimpleMnemonics().toSeed(seedPhrase.toCharArray()))[0]
|
||||
|
||||
|
||||
open class ScopedTest(val defaultTimeout: Long = 2000L) {
|
||||
protected lateinit var testScope: CoroutineScope
|
||||
|
|
|
@ -2,7 +2,8 @@ package cash.z.ecc.android.sdk.util
|
|||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import cash.z.ecc.android.sdk.Initializer
|
||||
import cash.z.ecc.android.sdk.Initializer.WalletBirthday
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import cash.z.ecc.android.sdk.tool.WalletBirthdayTool
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
@ -18,7 +19,7 @@ import java.io.IOException
|
|||
class AddressGeneratorUtil {
|
||||
|
||||
private val context = InstrumentationRegistry.getInstrumentation().context
|
||||
private val initializer = Initializer(context).open(WalletBirthday())
|
||||
|
||||
private val mnemonics = SimpleMnemonics()
|
||||
|
||||
@Test
|
||||
|
@ -36,7 +37,7 @@ class AddressGeneratorUtil {
|
|||
.map { seedPhrase ->
|
||||
mnemonics.toSeed(seedPhrase.toCharArray())
|
||||
}.map { seed ->
|
||||
initializer.rustBackend.deriveAddress(seed)
|
||||
DerivationTool.deriveShieldedAddress(seed)
|
||||
}.collect { address ->
|
||||
println("xrxrx2\t$address")
|
||||
assertTrue(address.startsWith("zs1"))
|
||||
|
@ -45,7 +46,7 @@ class AddressGeneratorUtil {
|
|||
|
||||
@Throws(IOException::class)
|
||||
fun readLines() = flow<String> {
|
||||
val seedFile = javaClass.getResourceAsStream("/utils/seeds.txt")
|
||||
val seedFile = javaClass.getResourceAsStream("/utils/seeds.txt")!!
|
||||
Okio.buffer(Okio.source(seedFile)).use { source ->
|
||||
var line: String? = source.readUtf8Line()
|
||||
while (line != null) {
|
||||
|
|
|
@ -10,6 +10,7 @@ import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
|
|||
import cash.z.ecc.android.sdk.ext.Twig
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.sdk.service.LightWalletGrpcService
|
||||
import cash.z.ecc.android.sdk.tool.WalletBirthdayTool
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
@ -37,27 +38,26 @@ class BalancePrinterUtil {
|
|||
private val mnemonics = SimpleMnemonics()
|
||||
private val context = InstrumentationRegistry.getInstrumentation().context
|
||||
private val alias = "BalanceUtil"
|
||||
private val caceDbPath = Initializer.cacheDbPath(context, alias)
|
||||
|
||||
private val downloader = CompactBlockDownloader(
|
||||
LightWalletGrpcService(context, host, port),
|
||||
CompactBlockDbStore(context, caceDbPath)
|
||||
)
|
||||
// private val caceDbPath = Initializer.cacheDbPath(context, alias)
|
||||
//
|
||||
// private val downloader = CompactBlockDownloader(
|
||||
// LightWalletGrpcService(context, host, port),
|
||||
// CompactBlockDbStore(context, caceDbPath)
|
||||
// )
|
||||
|
||||
// private val processor = CompactBlockProcessor(downloader)
|
||||
|
||||
// private val rustBackend = RustBackend.init(context, cacheDbName, dataDbName)
|
||||
|
||||
private val initializer = Initializer(context, host, port, alias)
|
||||
|
||||
private lateinit var birthday: Initializer.WalletBirthday
|
||||
private lateinit var birthday: WalletBirthdayTool.WalletBirthday
|
||||
private var synchronizer: Synchronizer? = null
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
Twig.plant(TroubleshootingTwig())
|
||||
cacheBlocks()
|
||||
birthday = Initializer.DefaultBirthdayStore(context, birthdayHeight, alias).getBirthday()
|
||||
birthday = WalletBirthdayTool.loadNearest(context, birthdayHeight)
|
||||
}
|
||||
|
||||
private fun cacheBlocks() = runBlocking {
|
||||
|
@ -82,7 +82,12 @@ class BalancePrinterUtil {
|
|||
twig("checking balance for: $seedPhrase")
|
||||
mnemonics.toSeed(seedPhrase.toCharArray())
|
||||
}.collect { seed ->
|
||||
initializer.import(seed, birthday, clearDataDb = true, clearCacheDb = false)
|
||||
// TODO: clear the dataDb but leave the cacheDb
|
||||
val initializer = Initializer(context) { config ->
|
||||
config.import(seed, birthdayHeight)
|
||||
config.server(host, port)
|
||||
config.alias = alias
|
||||
}
|
||||
/*
|
||||
what I need to do right now
|
||||
- for each seed
|
||||
|
|
|
@ -6,6 +6,7 @@ import cash.z.ecc.android.sdk.SdkSynchronizer
|
|||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.db.entity.isSubmitSuccess
|
||||
import cash.z.ecc.android.sdk.ext.*
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import io.grpc.StatusRuntimeException
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
@ -27,10 +28,10 @@ class DarksideTestCoordinator(val host: String = "127.0.0.1", val testName: Stri
|
|||
// dependencies: public
|
||||
val validator = DarksideTestValidator()
|
||||
val chainMaker = DarksideChainMaker()
|
||||
var initializer = Initializer(context, host, port, testName)
|
||||
// var initializer = Initializer(context, Initializer.Builder(host, port, testName))
|
||||
lateinit var synchronizer: SdkSynchronizer
|
||||
|
||||
val spendingKey: String get() = initializer.deriveSpendingKey(seedPhrase)
|
||||
val spendingKey: String get() = DerivationTool.deriveSpendingKeys(SimpleMnemonics().toSeed(seedPhrase.toCharArray()))[0]
|
||||
|
||||
//
|
||||
// High-level APIs
|
||||
|
@ -66,11 +67,11 @@ class DarksideTestCoordinator(val host: String = "127.0.0.1", val testName: Stri
|
|||
*/
|
||||
fun initiate() {
|
||||
twig("*************** INITIALIZING TEST COORDINATOR (ONLY ONCE) ***********************")
|
||||
initializer.importPhrase(
|
||||
seedPhrase,
|
||||
birthdayHeight,
|
||||
testName
|
||||
)
|
||||
val initializer = Initializer(context) { config ->
|
||||
config.seedPhrase(seedPhrase)
|
||||
config.birthdayHeight = birthdayHeight
|
||||
config.alias = testName
|
||||
}
|
||||
synchronizer = Synchronizer(initializer) as SdkSynchronizer
|
||||
val channel = (synchronizer as SdkSynchronizer).channel
|
||||
darkside = DarksideApi(channel)
|
||||
|
@ -307,4 +308,4 @@ class DarksideTestCoordinator(val host: String = "127.0.0.1", val testName: Stri
|
|||
"https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/after-large-reorg.txt"
|
||||
private const val DEFAULT_START_HEIGHT = 663150
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import cash.z.ecc.android.sdk.SdkSynchronizer
|
|||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
|
||||
import cash.z.ecc.android.sdk.ext.Twig
|
||||
import cash.z.ecc.android.sdk.tool.WalletBirthdayTool
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -37,9 +38,7 @@ class DataDbScannerUtil {
|
|||
|
||||
// private val rustBackend = RustBackend.init(context, cacheDbName, dataDbName)
|
||||
|
||||
private val initializer = Initializer(context, host, port, alias)
|
||||
|
||||
private lateinit var birthday: Initializer.WalletBirthday
|
||||
private val birthdayHeight = 600_000
|
||||
private lateinit var synchronizer: Synchronizer
|
||||
|
||||
|
@ -47,7 +46,6 @@ class DataDbScannerUtil {
|
|||
fun setup() {
|
||||
Twig.plant(TroubleshootingTwig())
|
||||
// cacheBlocks()
|
||||
birthday = Initializer.DefaultBirthdayStore(context, birthdayHeight, alias).getBirthday()
|
||||
}
|
||||
|
||||
private fun cacheBlocks() = runBlocking {
|
||||
|
@ -67,8 +65,7 @@ class DataDbScannerUtil {
|
|||
|
||||
@Test
|
||||
fun scanExistingDb() {
|
||||
initializer.open(birthday)
|
||||
synchronizer = Synchronizer(initializer)
|
||||
synchronizer = Synchronizer(Initializer(context) { it.birthdayHeight = birthdayHeight})
|
||||
|
||||
println("sync!")
|
||||
synchronizer.start()
|
||||
|
|
|
@ -6,9 +6,10 @@ import cash.z.ecc.android.sdk.Synchronizer
|
|||
import cash.z.ecc.android.sdk.Synchronizer.Status.SYNCED
|
||||
import cash.z.ecc.android.sdk.db.entity.isSubmitSuccess
|
||||
import cash.z.ecc.android.sdk.ext.*
|
||||
import cash.z.ecc.android.sdk.import
|
||||
import cash.z.ecc.android.sdk.jni.RustBackend
|
||||
import cash.z.ecc.android.sdk.service.LightWalletGrpcService
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import cash.z.ecc.android.sdk.tool.WalletBirthdayTool
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
@ -38,7 +39,7 @@ class IntegrationTest {
|
|||
|
||||
@Test
|
||||
fun testLoadBirthday() {
|
||||
val (height, hash, time, tree) = Initializer.DefaultBirthdayStore.loadBirthdayFromAssets(context, ZcashSdk.SAPLING_ACTIVATION_HEIGHT + 1)
|
||||
val (height, hash, time, tree) = WalletBirthdayTool.loadNearest(context, ZcashSdk.SAPLING_ACTIVATION_HEIGHT + 1)
|
||||
assertEquals(ZcashSdk.SAPLING_ACTIVATION_HEIGHT, height)
|
||||
}
|
||||
|
||||
|
@ -74,7 +75,7 @@ class IntegrationTest {
|
|||
}
|
||||
|
||||
private suspend fun sendFunds(): Boolean {
|
||||
val spendingKey = RustBackend().deriveSpendingKeys(seed)[0]
|
||||
val spendingKey = DerivationTool.deriveSpendingKeys(seed)[0]
|
||||
log("sending to address")
|
||||
synchronizer.sendToAddress(
|
||||
spendingKey,
|
||||
|
@ -104,8 +105,10 @@ class IntegrationTest {
|
|||
val toAddress = "zs1vp7kvlqr4n9gpehztr76lcn6skkss9p8keqs3nv8avkdtjrcctrvmk9a7u494kluv756jeee5k0"
|
||||
|
||||
private val context = InstrumentationRegistry.getInstrumentation().context
|
||||
private val initializer = Initializer(context, host, port).apply {
|
||||
import(seed, birthdayHeight, overwrite = true)
|
||||
private val initializer = Initializer(context) { config ->
|
||||
config.setSeed(seed)
|
||||
config.server(host, port)
|
||||
config.birthdayHeight = birthdayHeight
|
||||
}
|
||||
private val synchronizer: Synchronizer = Synchronizer(initializer)
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import java.io.File
|
|||
/**
|
||||
* Simplified Initializer focused on starting from a ViewingKey.
|
||||
*/
|
||||
class VkInitializer(appContext: Context, builder: Builder): SdkSynchronizer.SdkInitializer {
|
||||
class Initializer private constructor(appContext: Context, builder: Builder): SdkSynchronizer.SdkInitializer {
|
||||
override val context = appContext.applicationContext
|
||||
override val rustBackend: RustBackend
|
||||
override val alias: String
|
||||
|
@ -23,7 +23,9 @@ class VkInitializer(appContext: Context, builder: Builder): SdkSynchronizer.Sdk
|
|||
val birthday: WalletBirthdayTool.WalletBirthday
|
||||
|
||||
init {
|
||||
birthday = builder.birthday
|
||||
val loadedBirthday =
|
||||
builder.birthday ?: WalletBirthdayTool.loadNearest(context, builder.birthdayHeight)
|
||||
birthday = loadedBirthday
|
||||
viewingKeys = builder.viewingKeys
|
||||
alias = builder.alias
|
||||
host = builder.host
|
||||
|
@ -32,11 +34,11 @@ class VkInitializer(appContext: Context, builder: Builder): SdkSynchronizer.Sdk
|
|||
initMissingDatabases(birthday, *viewingKeys.toTypedArray())
|
||||
}
|
||||
|
||||
constructor(appContext: Context, block: Builder.() -> Unit) : this(appContext, Builder(appContext, block))
|
||||
constructor(appContext: Context, block: (Builder) -> Unit) : this(appContext, Builder(block))
|
||||
|
||||
|
||||
private fun initRustBackend(birthday: WalletBirthdayTool.WalletBirthday): RustBackend {
|
||||
return RustBackend().init(
|
||||
return RustBackend.init(
|
||||
cacheDbPath(context, alias),
|
||||
dataDbPath(context, alias),
|
||||
"${context.cacheDir.absolutePath}/params",
|
||||
|
@ -151,34 +153,47 @@ class VkInitializer(appContext: Context, builder: Builder): SdkSynchronizer.Sdk
|
|||
}
|
||||
|
||||
|
||||
class Builder(appContext: Context, block: Builder.() -> Unit) {
|
||||
private val context = appContext.applicationContext
|
||||
/* lateinit fields that can be set in multiple ways on this builder */
|
||||
lateinit var birthday: WalletBirthdayTool.WalletBirthday
|
||||
private set
|
||||
|
||||
val viewingKeys = mutableListOf<String>()
|
||||
|
||||
/* optional fields with default values */
|
||||
var alias: String = ZcashSdk.DEFAULT_ALIAS
|
||||
var host: String = ZcashSdk.DEFAULT_LIGHTWALLETD_HOST
|
||||
var port: Int = ZcashSdk.DEFAULT_LIGHTWALLETD_PORT
|
||||
|
||||
|
||||
var birthdayHeight: Int? = null
|
||||
set(value) {
|
||||
field = value
|
||||
birthday = WalletBirthdayTool(context).loadNearest(value)
|
||||
}
|
||||
|
||||
|
||||
init {
|
||||
block()
|
||||
validateAlias(alias)
|
||||
validateViewingKeys()
|
||||
validateBirthday()
|
||||
class Builder private constructor(
|
||||
val viewingKeys: MutableList<String> = mutableListOf(),
|
||||
var birthday: WalletBirthdayTool.WalletBirthday? = null,
|
||||
var birthdayHeight: Int? = null,
|
||||
var alias: String = ZcashSdk.DEFAULT_ALIAS,
|
||||
var host: String = ZcashSdk.DEFAULT_LIGHTWALLETD_HOST,
|
||||
var port: Int = ZcashSdk.DEFAULT_LIGHTWALLETD_PORT
|
||||
) {
|
||||
constructor(block: (Builder) -> Unit) : this(mutableListOf(), null, null) {
|
||||
block(this)
|
||||
validate()
|
||||
}
|
||||
constructor(
|
||||
viewingKeys: MutableList<String> = mutableListOf(),
|
||||
birthday: WalletBirthdayTool.WalletBirthday? = null,
|
||||
/* optional fields with default values */
|
||||
alias: String = ZcashSdk.DEFAULT_ALIAS,
|
||||
host: String = ZcashSdk.DEFAULT_LIGHTWALLETD_HOST,
|
||||
port: Int = ZcashSdk.DEFAULT_LIGHTWALLETD_PORT
|
||||
) : this(viewingKeys, birthday, -1, alias, host, port) {
|
||||
validate()
|
||||
}
|
||||
|
||||
constructor(
|
||||
viewingKeys: MutableList<String> = mutableListOf(),
|
||||
birthdayHeight: Int = -1,
|
||||
/* optional fields with default values */
|
||||
alias: String = ZcashSdk.DEFAULT_ALIAS,
|
||||
host: String = ZcashSdk.DEFAULT_LIGHTWALLETD_HOST,
|
||||
port: Int = ZcashSdk.DEFAULT_LIGHTWALLETD_PORT
|
||||
) : this(viewingKeys, null, birthdayHeight, alias, host, port) {
|
||||
validate()
|
||||
}
|
||||
|
||||
|
||||
fun build(context: Context): Initializer {
|
||||
if (birthday == null) {
|
||||
birthday = WalletBirthdayTool.loadNearest(context, birthdayHeight)
|
||||
}
|
||||
return Initializer(context, this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add viewing keys to the set of accounts to monitor. Note: Using more than one viewing key
|
||||
|
@ -258,16 +273,35 @@ class VkInitializer(appContext: Context, builder: Builder): SdkSynchronizer.Sdk
|
|||
// Validation helpers
|
||||
//
|
||||
|
||||
fun validate() {
|
||||
validateAlias(alias)
|
||||
validateViewingKeys()
|
||||
validateBirthday()
|
||||
}
|
||||
|
||||
private fun validateBirthday() {
|
||||
require(::birthday.isInitialized) {
|
||||
// one of the fields must be properly set
|
||||
require((birthdayHeight ?: -1) >= ZcashSdk.SAPLING_ACTIVATION_HEIGHT
|
||||
|| birthdayHeight != null) {
|
||||
"Birthday is required but was not set on this initializer. Verify that a valid" +
|
||||
" birthday was provided when creating the Initializer such as" +
|
||||
" WalletBirthdayTool.loadNearest()"
|
||||
}
|
||||
require(birthday.height >= ZcashSdk.SAPLING_ACTIVATION_HEIGHT) {
|
||||
"Invalid birthday height of ${birthday.height}. The birthday height must be at" +
|
||||
" least the height of Sapling activation on ${ZcashSdk.NETWORK}" +
|
||||
" (${ZcashSdk.SAPLING_ACTIVATION_HEIGHT})."
|
||||
|
||||
// but not both
|
||||
require((birthdayHeight ?: -1) < ZcashSdk.SAPLING_ACTIVATION_HEIGHT
|
||||
|| birthday == null) {
|
||||
"Ambiguous birthday. Either the birthday Object or the birthdayHeight Int should" +
|
||||
" be set but not both."
|
||||
}
|
||||
|
||||
// the field that is set should contain a proper value
|
||||
require(
|
||||
(birthdayHeight ?: birthday?.height ?: -1) >= ZcashSdk.SAPLING_ACTIVATION_HEIGHT
|
||||
) {
|
||||
"Invalid birthday height of ${birthdayHeight ?: birthday?.height}. The birthday" +
|
||||
" height must be at least the height of Sapling activation on" +
|
||||
" ${ZcashSdk.NETWORK} (${ZcashSdk.SAPLING_ACTIVATION_HEIGHT})."
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,5 +315,23 @@ class VkInitializer(appContext: Context, builder: Builder): SdkSynchronizer.Sdk
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Validate that the alias doesn't contain malicious characters by enforcing simple rules which
|
||||
* permit the alias to be used as part of a file name for the preferences and databases. This
|
||||
* enables multiple wallets to exist on one device, which is also helpful for sweeping funds.
|
||||
*
|
||||
* @param alias the alias to validate.
|
||||
*
|
||||
* @throws IllegalArgumentException whenever the alias is not less than 100 characters or
|
||||
* contains something other than alphanumeric characters. Underscores are allowed but aliases
|
||||
* must start with a letter.
|
||||
*/
|
||||
internal fun validateAlias(alias: String) {
|
||||
require(alias.length in 1..99 && alias[0].isLetter()
|
||||
&& alias.all{ it.isLetterOrDigit() || it == '_' }) {
|
||||
"ERROR: Invalid alias ($alias). For security, the alias must be shorter than 100 " +
|
||||
"characters and only contain letters, digits or underscores and start with a letter"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import java.io.File
|
|||
* not be called directly by code outside of the SDK. Instead, one of the higher-level components
|
||||
* should be used such as Wallet.kt or CompactBlockProcessor.kt.
|
||||
*/
|
||||
class RustBackend : RustBackendWelding {
|
||||
class RustBackend private constructor() : RustBackendWelding {
|
||||
|
||||
init {
|
||||
load()
|
||||
|
@ -27,27 +27,6 @@ class RustBackend : RustBackendWelding {
|
|||
get() = if (field != -1) field else throw BirthdayException.UninitializedBirthdayException
|
||||
private set
|
||||
|
||||
/**
|
||||
* Loads the library and initializes path variables. Although it is best to only call this
|
||||
* function once, it is idempotent.
|
||||
*/
|
||||
fun init(
|
||||
cacheDbPath: String,
|
||||
dataDbPath: String,
|
||||
paramsPath: String,
|
||||
birthdayHeight: Int? = null
|
||||
): RustBackend {
|
||||
twig("Creating RustBackend") {
|
||||
pathCacheDb = cacheDbPath
|
||||
pathDataDb = dataDbPath
|
||||
pathParamsDir = paramsPath
|
||||
if (birthdayHeight != null) {
|
||||
this.birthdayHeight = birthdayHeight
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun clear(clearCacheDb: Boolean = true, clearDataDb: Boolean = true) {
|
||||
if (clearCacheDb) {
|
||||
twig("Deleting the cache database!")
|
||||
|
@ -158,6 +137,26 @@ class RustBackend : RustBackendWelding {
|
|||
companion object {
|
||||
private var loaded = false
|
||||
|
||||
/**
|
||||
* Loads the library and initializes path variables. Although it is best to only call this
|
||||
* function once, it is idempotent.
|
||||
*/
|
||||
fun init(
|
||||
cacheDbPath: String,
|
||||
dataDbPath: String,
|
||||
paramsPath: String,
|
||||
birthdayHeight: Int? = null
|
||||
): RustBackend {
|
||||
return RustBackend().apply {
|
||||
pathCacheDb = cacheDbPath
|
||||
pathDataDb = dataDbPath
|
||||
pathParamsDir = paramsPath
|
||||
if (birthdayHeight != null) {
|
||||
this.birthdayHeight = birthdayHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun load() {
|
||||
// It is safe to call these things twice but not efficient. So we add a loose check and
|
||||
// ignore the fact that it's not thread-safe.
|
||||
|
|
Loading…
Reference in New Issue