General cleanup before merging first pass at demo app.
This commit is contained in:
parent
652e862d5c
commit
5380c0d365
|
@ -1,14 +1,18 @@
|
|||
package cash.z.wallet.sdk.sample.demoapp
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import cash.z.wallet.sdk.SdkSynchronizer
|
||||
import cash.z.wallet.sdk.Initializer
|
||||
import cash.z.wallet.sdk.Synchronizer
|
||||
import cash.z.wallet.sdk.entity.isFailure
|
||||
import cash.z.wallet.sdk.transaction.*
|
||||
import cash.z.wallet.sdk.demoapp.util.SampleStorageBridge
|
||||
import cash.z.wallet.sdk.ext.*
|
||||
import cash.z.wallet.sdk.jni.RustBackend
|
||||
import cash.z.wallet.sdk.service.LightWalletGrpcService
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.*
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
|
@ -19,9 +23,6 @@ import org.junit.Test
|
|||
*/
|
||||
class SampleCodeTest {
|
||||
|
||||
private val seed = "Insert seed for testing".toByteArray()
|
||||
private val lightwalletdHost: String = "34.68.177.238"
|
||||
|
||||
// ///////////////////////////////////////////////////
|
||||
// Seed derivation
|
||||
@Ignore @Test fun createBip39Seed_fromSeedPhrase() {
|
||||
|
@ -48,21 +49,17 @@ class SampleCodeTest {
|
|||
/////////////////////////////////////////////////////
|
||||
// Derive Extended Spending Key
|
||||
@Test fun deriveSpendingKey() {
|
||||
// val wallet = Wallet()
|
||||
// val privateKeys = wallet.initialize(context, seed)
|
||||
// assertNotNull("Wallet already existed.", privateKeys)
|
||||
//
|
||||
// log("Spending Key: ${privateKeys?.get(0)}")
|
||||
// log("Address: ${wallet.getAddress()}")
|
||||
val spendingKeys = RustBackend().deriveSpendingKeys(seed)
|
||||
assertEquals(1, spendingKeys.size)
|
||||
log("Spending Key: ${spendingKeys?.get(0)}")
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// Derive address from Extended Spending Key
|
||||
@Test fun getAddressFromSpendingKey() {
|
||||
// TODO: turn spending key into viewing key via:
|
||||
// let extfvks: Vec<_> = extsks.iter().map(ExtendedFullViewingKey::from).collect();
|
||||
// val viewingKey = spendingKey //.asViewingKey()
|
||||
// return getAddressFromViewingKey(viewingKey)
|
||||
// Get Address
|
||||
@Test fun getAddress() = runBlocking {
|
||||
val address = synchronizer.getAddress()
|
||||
assertFalse(address.isNullOrBlank())
|
||||
log("Address: $address")
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
@ -106,51 +103,54 @@ class SampleCodeTest {
|
|||
// ///////////////////////////////////////////////////
|
||||
// Create a signed transaction (with memo)
|
||||
@Test fun createTransaction() = runBlocking {
|
||||
// val wallet = Wallet()
|
||||
// val repository = PagedTransactionRepository(context)
|
||||
// val keyManager = SampleStorageBridge().securelyStoreSeed(seed)
|
||||
// val encoder = WalletTransactionEncoder(wallet, repository, keyManager)
|
||||
// val amount = 0.123.toZec().convertZecToZatoshi()
|
||||
// val address = "ztestsapling1tklsjr0wyw0d58f3p7wufvrj2cyfv6q6caumyueadq8qvqt8lda6v6tpx474rfru9y6u75u7qnw"
|
||||
// val memo = "Test Transaction"
|
||||
// val encodedTx = encoder.create(amount, address, memo ?: "")
|
||||
val rustBackend = RustBackend().init(context)
|
||||
val repository = PagedTransactionRepository(context)
|
||||
val encoder = WalletTransactionEncoder(rustBackend, repository)
|
||||
val spendingKey = rustBackend.deriveSpendingKeys(seed)[0]
|
||||
|
||||
val amount = 0.123.convertZecToZatoshi()
|
||||
val address = "ztestsapling1tklsjr0wyw0d58f3p7wufvrj2cyfv6q6caumyueadq8qvqt8lda6v6tpx474rfru9y6u75u7qnw"
|
||||
val memo = "Test Transaction".toByteArray()
|
||||
|
||||
val encodedTx = encoder.createTransaction(spendingKey, amount, address, memo)
|
||||
assertTrue(encodedTx.raw.isNotEmpty())
|
||||
log("Transaction ID: ${encodedTx.txId.toHex()}")
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////////
|
||||
// Create a signed transaction (with memo) and broadcast
|
||||
@Test fun submitTransaction() = runBlocking {
|
||||
// val amount = 0.123.toZec().convertZecToZatoshi()
|
||||
// val address = "ztestsapling1tklsjr0wyw0d58f3p7wufvrj2cyfv6q6caumyueadq8qvqt8lda6v6tpx474rfru9y6u75u7qnw"
|
||||
// val memo = "Test Transaction"
|
||||
// val transaction = synchronizer.sendToAddress(amount, address, memo)
|
||||
// log("transaction: $transaction")
|
||||
val amount = 0.123.convertZecToZatoshi()
|
||||
val address = "ztestsapling1tklsjr0wyw0d58f3p7wufvrj2cyfv6q6caumyueadq8qvqt8lda6v6tpx474rfru9y6u75u7qnw"
|
||||
val memo = "Test Transaction"
|
||||
val spendingKey = RustBackend().deriveSpendingKeys(seed)[0]
|
||||
val transactionFlow = synchronizer.sendToAddress(spendingKey, amount, address, memo)
|
||||
transactionFlow.collect {
|
||||
log("pending transaction updated $it")
|
||||
assertTrue("Failed to send funds. See log for details.", it?.isFailure() == false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// Utility Functions
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
companion object {
|
||||
private val seed = "Insert seed for testing".toByteArray()
|
||||
private val lightwalletdHost: String = ZcashSdk.DEFAULT_LIGHTWALLETD_HOST
|
||||
|
||||
private val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
private lateinit var synchronizer: SdkSynchronizer
|
||||
private val synchronizer = Synchronizer(context, lightwalletdHost, seed)
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun init() {
|
||||
Twig.plant(TroubleshootingTwig())
|
||||
reset()
|
||||
}
|
||||
|
||||
fun log(message: String?) = twig(message ?: "null")
|
||||
|
||||
private fun reset() {
|
||||
context.getDatabasePath(ZcashSdk.DB_DATA_NAME).absoluteFile.delete()
|
||||
context.getDatabasePath(ZcashSdk.DB_CACHE_NAME).absoluteFile.delete()
|
||||
}
|
||||
|
||||
|
||||
private fun ByteArray.toHex(): String {
|
||||
val sb = StringBuilder(size * 2)
|
||||
for (b in this)
|
||||
|
|
|
@ -57,14 +57,3 @@ class MainActivity : AppCompatActivity() {
|
|||
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class GetAddressFragment : BaseDemoFragment<FragmentGetAddressBinding>() {
|
|||
* Create and initialize the wallet. Initialization will return the private keys but for the
|
||||
* purposes of this demo we don't need them.
|
||||
*/
|
||||
initializer.initializeAccounts(seed)
|
||||
initializer.new(seed)
|
||||
}
|
||||
|
||||
override fun onResetComplete() {
|
||||
|
|
|
@ -43,8 +43,4 @@ class GetBlockFragment : BaseDemoFragment<FragmentGetBlockBinding>() {
|
|||
override fun onClear() {
|
||||
lightwalletService.shutdown()
|
||||
}
|
||||
}
|
||||
//
|
||||
//for block range, put them in a table and show a list of block heights and vtx count sorted by time
|
||||
//
|
||||
// and for this, allow input
|
||||
}
|
|
@ -40,6 +40,7 @@ class GetBlockRangeFragment : BaseDemoFragment<FragmentGetBlockRangeBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: iterate on this demo to show all the blocks in a recyclerview showing block heights and vtx count
|
||||
private fun setBlockRange(blockRange: IntRange) {
|
||||
val blocks =
|
||||
lightwalletService.getBlockRange(blockRange)
|
||||
|
@ -54,8 +55,4 @@ class GetBlockRangeFragment : BaseDemoFragment<FragmentGetBlockRangeBinding>() {
|
|||
private fun setError(message: String) {
|
||||
binding.textInfo.text = "Error: $message"
|
||||
}
|
||||
}
|
||||
//
|
||||
//for block range, put them in a table and show a list of block heights and vtx count sorted by time
|
||||
//
|
||||
// and for this, allow input
|
||||
}
|
|
@ -34,8 +34,8 @@ class HomeFragment : Fragment() {
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
twig("Visiting the home screen clears the default databases, for sanity sake, because " +
|
||||
"each demo is intended to be self-contained.")
|
||||
twig("CLEARING DATA: Visiting the home screen clears the default databases, for sanity" +
|
||||
" sake, because each demo is intended to be self-contained.")
|
||||
App.instance.getDatabasePath("unusued.db").parentFile.listFiles().forEach { it.delete() }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,12 +66,6 @@ class ListTransactionsFragment : BaseDemoFragment<FragmentListTransactionsBindin
|
|||
synchronizer.progress.collectWith(lifecycleScope, ::onProgress)
|
||||
}
|
||||
|
||||
// private fun CoroutineScope.launchProgressMonitor(channel: ReceiveChannel<Int>) = launch {
|
||||
// for (i in channel) {
|
||||
// onProgress(i)
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun onProgress(i: Int) {
|
||||
val message = when (i) {
|
||||
100 -> "Scanning blocks..."
|
||||
|
|
|
@ -6,7 +6,8 @@ data class DemoConfig(
|
|||
val birthdayHeight: Int = 620_000,//523_240,
|
||||
val network: ZcashNetwork = ZcashNetwork.TEST_NET,
|
||||
val seed: ByteArray = "testreferencealice".toByteArray(),
|
||||
val toAddress: String = "ztestsapling1fg82ar8y8whjfd52l0xcq0w3n7nn7cask2scp9rp27njeurr72ychvud57s9tu90fdqgwdt07lg"
|
||||
val toAddress: String = "ztestsapling1fg82ar8y8whjfd52l0xcq0w3n7nn7cask2scp9rp27njeurr72ychvud57s9tu90fdqgwdt07lg",
|
||||
val sendAmount: Double = 0.0024
|
||||
)
|
||||
|
||||
enum class ZcashNetwork { MAIN_NET, TEST_NET }
|
|
@ -1,17 +0,0 @@
|
|||
package cash.z.wallet.sdk.sample.demoapp
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
|
@ -88,10 +88,11 @@ class Initializer(
|
|||
fun new(
|
||||
seed: ByteArray,
|
||||
birthday: WalletBirthday = newWalletBirthday,
|
||||
numberOfAccounts: Int = 1
|
||||
numberOfAccounts: Int = 1,
|
||||
overwrite: Boolean = false
|
||||
): Array<String> {
|
||||
initRustLibrary()
|
||||
return initializeAccounts(seed, birthday, numberOfAccounts)
|
||||
return initializeAccounts(seed, birthday, numberOfAccounts, overwrite)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,10 +104,11 @@ class Initializer(
|
|||
*/
|
||||
fun import(
|
||||
seed: ByteArray,
|
||||
birthday: WalletBirthday = saplingBirthday
|
||||
birthday: WalletBirthday = saplingBirthday,
|
||||
overwrite: Boolean = false
|
||||
): Array<String> {
|
||||
initRustLibrary()
|
||||
return initializeAccounts(seed, birthday)
|
||||
return initializeAccounts(seed, birthday, overwrite = overwrite)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,14 +132,16 @@ class Initializer(
|
|||
* @return the spending keys for each account, ordered by index. These keys are only needed for
|
||||
* spending funds.
|
||||
*/
|
||||
fun initializeAccounts(
|
||||
private fun initializeAccounts(
|
||||
seed: ByteArray,
|
||||
birthday: WalletBirthday = newWalletBirthday,
|
||||
numberOfAccounts: Int = 1
|
||||
numberOfAccounts: Int = 1,
|
||||
overwrite: Boolean = false
|
||||
): Array<String> {
|
||||
this.birthday = birthday
|
||||
|
||||
try {
|
||||
if (overwrite) rustBackend.clear()
|
||||
// only creates tables, if they don't exist
|
||||
rustBackend.initDataDb()
|
||||
twig("Initialized wallet for first run")
|
||||
|
@ -155,7 +159,7 @@ class Initializer(
|
|||
twig("seeded the database with sapling tree at height ${birthday.height}")
|
||||
} catch (t: Throwable) {
|
||||
if (t.message?.contains("is not empty") == true) {
|
||||
throw InitializerException.AlreadyInitializedException(t)
|
||||
throw InitializerException.AlreadyInitializedException(t, rustBackend.dbDataPath)
|
||||
} else {
|
||||
throw InitializerException.FalseStart(t)
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import cash.z.wallet.sdk.block.CompactBlockProcessor.WalletBalance
|
|||
import cash.z.wallet.sdk.block.CompactBlockStore
|
||||
import cash.z.wallet.sdk.entity.*
|
||||
import cash.z.wallet.sdk.exception.SynchronizerException
|
||||
import cash.z.wallet.sdk.ext.ZcashSdk
|
||||
import cash.z.wallet.sdk.ext.twig
|
||||
import cash.z.wallet.sdk.ext.twigTask
|
||||
import cash.z.wallet.sdk.jni.RustBackend
|
||||
|
@ -300,6 +301,31 @@ class SdkSynchronizer internal constructor(
|
|||
}.distinctUntilChanged()
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplest constructor possible. Useful for demos, sample apps or PoC's. Anything more complex
|
||||
* will probably want to handle initialization, directly.
|
||||
*/
|
||||
fun Synchronizer(
|
||||
appContext: Context,
|
||||
lightwalletdHost: String = ZcashSdk.DEFAULT_LIGHTWALLETD_HOST,
|
||||
seed: ByteArray? = null,
|
||||
birthday: Initializer.WalletBirthday? = null
|
||||
): Synchronizer {
|
||||
val initializer = Initializer(appContext)
|
||||
if (initializer.hasData()) {
|
||||
initializer.open()
|
||||
} else {
|
||||
seed ?: throw IllegalArgumentException(
|
||||
"Failed to initialize. A seed is required when no wallet exists on the device."
|
||||
)
|
||||
if (birthday == null) {
|
||||
initializer.new(seed, overwrite = true)
|
||||
} else {
|
||||
initializer.import(seed, birthday, overwrite = true)
|
||||
}
|
||||
}
|
||||
return Synchronizer(appContext, lightwalletdHost, initializer.rustBackend)
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor function for building a Synchronizer in the most flexible way possible. This allows
|
||||
|
|
|
@ -73,8 +73,8 @@ sealed class BirthdayException(message: String, cause: Throwable? = null) : SdkE
|
|||
|
||||
sealed class InitializerException(message: String, cause: Throwable? = null) : SdkException(message, cause){
|
||||
class FalseStart(cause: Throwable?) : InitializerException("Failed to initialize accounts due to: $cause", cause)
|
||||
class AlreadyInitializedException(cause: Throwable) : InitializerException("Failed to initialize the blocks table" +
|
||||
" because it already exists.", cause)
|
||||
class AlreadyInitializedException(cause: Throwable, dbPath: String) : InitializerException("Failed to initialize the blocks table" +
|
||||
" because it already exists in $dbPath", cause)
|
||||
object DatabasePathException :
|
||||
InitializerException("Critical failure to locate path for storing databases. Perhaps this" +
|
||||
" device prevents apps from storing data? We cannot manage initialize the wallet" +
|
||||
|
|
|
@ -68,6 +68,11 @@ object ZcashSdk {
|
|||
*/
|
||||
const val LIGHTWALLETD_PORT = 9067
|
||||
|
||||
/**
|
||||
* The default host to use for lightwalletd.
|
||||
*/
|
||||
const val DEFAULT_LIGHTWALLETD_HOST = "listwallted.z.cash"
|
||||
|
||||
const val DB_DATA_NAME = "Data.db"
|
||||
const val DB_CACHE_NAME = "Cache.db"
|
||||
const val DEFAULT_DB_NAME_PREFIX = "ZcashSdk_"
|
||||
|
|
|
@ -15,6 +15,10 @@ import java.io.File
|
|||
*/
|
||||
class RustBackend : RustBackendWelding {
|
||||
|
||||
init {
|
||||
load()
|
||||
}
|
||||
|
||||
internal lateinit var dbDataPath: String
|
||||
internal lateinit var dbCachePath: String
|
||||
internal lateinit var dbNamePrefix: String
|
||||
|
@ -40,7 +44,6 @@ class RustBackend : RustBackendWelding {
|
|||
): RustBackend {
|
||||
this.dbNamePrefix = dbNamePrefix
|
||||
twig("Creating RustBackend") {
|
||||
load()
|
||||
dbCachePath = File(dbPath, "${dbNamePrefix}${ZcashSdk.DB_CACHE_NAME}").absolutePath
|
||||
dbDataPath = File(dbPath, "${dbNamePrefix}${ZcashSdk.DB_DATA_NAME}").absolutePath
|
||||
paramDestinationDir = paramsPath
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package cash.z.wallet.sdk.transaction
|
||||
|
||||
import cash.z.wallet.sdk.entity.ConfirmedTransaction
|
||||
import cash.z.wallet.sdk.entity.PendingTransaction
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class WalletTransactionEncoder(
|
|||
* @return the row id in the transactions table that contains the spend transaction
|
||||
* or -1 if it failed
|
||||
*/
|
||||
suspend fun createSpend(
|
||||
private suspend fun createSpend(
|
||||
spendingKey: String,
|
||||
value: Long,
|
||||
toAddress: String,
|
||||
|
|
Loading…
Reference in New Issue