Note that this change modifies the on-disk representation of the wallet. Anyone who has an earlier build install will see a runtime crash. This is expected, as we are not supporting data migrations at this stage of development. The solution is to uninstall/reinstall the app.
This commit is contained in:
parent
072d73e99c
commit
f9afd2e5f9
|
@ -132,7 +132,7 @@ PLAY_CORE_KTX_VERSION=1.8.1
|
|||
ZCASH_ANDROID_WALLET_PLUGINS_VERSION=1.0.0
|
||||
ZCASH_BIP39_VERSION=1.0.2
|
||||
# TODO [#279]: Revert to stable SDK before app release
|
||||
ZCASH_SDK_VERSION=1.7.0-beta01
|
||||
ZCASH_SDK_VERSION=1.8.0-beta01-SNAPSHOT
|
||||
ZXING_VERSION=3.5.0
|
||||
|
||||
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
package cash.z.ecc.sdk.model
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
import cash.z.ecc.android.sdk.type.WalletBirthday
|
||||
import cash.z.ecc.sdk.fixture.WalletBirthdayFixture
|
||||
import cash.z.ecc.sdk.test.count
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class WalletBirthdayTest {
|
||||
@Test
|
||||
@SmallTest
|
||||
fun serialize() {
|
||||
val walletBirthday = WalletBirthdayFixture.new()
|
||||
|
||||
val jsonObject = walletBirthday.toJson()
|
||||
assertEquals(5, jsonObject.keys().count())
|
||||
assertTrue(jsonObject.has(WalletBirthday.KEY_VERSION))
|
||||
assertTrue(jsonObject.has(WalletBirthday.KEY_HEIGHT))
|
||||
assertTrue(jsonObject.has(WalletBirthday.KEY_HASH))
|
||||
assertTrue(jsonObject.has(WalletBirthday.KEY_EPOCH_SECONDS))
|
||||
assertTrue(jsonObject.has(WalletBirthday.KEY_TREE))
|
||||
|
||||
assertEquals(1, jsonObject.getInt(WalletBirthday.KEY_VERSION))
|
||||
assertEquals(WalletBirthdayFixture.HEIGHT, jsonObject.getInt(WalletBirthday.KEY_HEIGHT))
|
||||
assertEquals(WalletBirthdayFixture.HASH, jsonObject.getString(WalletBirthday.KEY_HASH))
|
||||
assertEquals(WalletBirthdayFixture.EPOCH_SECONDS, jsonObject.getLong(WalletBirthday.KEY_EPOCH_SECONDS))
|
||||
assertEquals(WalletBirthdayFixture.TREE, jsonObject.getString(WalletBirthday.KEY_TREE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun epoch_seconds_as_long_that_would_overflow_int() {
|
||||
val walletBirthday = WalletBirthdayFixture.new(time = Long.MAX_VALUE)
|
||||
|
||||
val jsonObject = walletBirthday.toJson()
|
||||
|
||||
assertEquals(Long.MAX_VALUE, jsonObject.getLong(WalletBirthday.KEY_EPOCH_SECONDS))
|
||||
|
||||
WalletBirthday.from(jsonObject).also {
|
||||
assertEquals(Long.MAX_VALUE, it.time)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun round_trip() {
|
||||
val walletBirthday = WalletBirthdayFixture.new()
|
||||
|
||||
val deserialized = WalletBirthday.from(walletBirthday.toJson())
|
||||
|
||||
assertEquals(walletBirthday, deserialized)
|
||||
assertFalse(walletBirthday === deserialized)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package cash.z.ecc.sdk.fixture
|
||||
|
||||
import cash.z.ecc.android.sdk.type.WalletBirthday
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||
import cash.z.ecc.sdk.model.PersistableWallet
|
||||
import cash.z.ecc.sdk.model.SeedPhrase
|
||||
|
@ -9,13 +9,15 @@ object PersistableWalletFixture {
|
|||
|
||||
val NETWORK = ZcashNetwork.Testnet
|
||||
|
||||
val BIRTHDAY = WalletBirthdayFixture.new()
|
||||
// These came from the mainnet 1500000.json file
|
||||
@Suppress("MagicNumber")
|
||||
val BIRTHDAY = BlockHeight.new(ZcashNetwork.Mainnet, 1500000L)
|
||||
|
||||
val SEED_PHRASE = SeedPhraseFixture.new()
|
||||
|
||||
fun new(
|
||||
network: ZcashNetwork = NETWORK,
|
||||
birthday: WalletBirthday = BIRTHDAY,
|
||||
birthday: BlockHeight = BIRTHDAY,
|
||||
seedPhrase: SeedPhrase = SEED_PHRASE
|
||||
) = PersistableWallet(network, birthday, seedPhrase)
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
package cash.z.ecc.sdk.fixture
|
||||
|
||||
import cash.z.ecc.android.sdk.type.WalletBirthday
|
||||
|
||||
object WalletBirthdayFixture {
|
||||
|
||||
const val HEIGHT = 1500000
|
||||
const val HASH = "00047a34c61409682f44640af9352023ad92f69b827d0f2b288f152ebea50f46"
|
||||
const val EPOCH_SECONDS = 1627076501L
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
const val TREE = "01172b95f271c6af8f68388f08c8ef970db8ec8d8d61204ecb7b2bb2c38262b92d0010016284585a6c85dadfef27ff33f1403926b4bb391de92e8be797e4280cc4ca2971000001a1ff388639379c0120782b3929bd8871af797be4b651f694aa961bad65a9c12400000001d806c98bda9653d5ae22757eed750871e16e0fb657f52c3d771a4411668e84330001260f6e9fac0922f98d58afbcc3f391ac19d5d944081466929a33b99df19c0e6a0000013d2fd009bf8a22d68f720eac19c411c99014ed9c5f85d5942e15d1fc039e28680001f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
|
||||
|
||||
fun new(
|
||||
height: Int = HEIGHT,
|
||||
hash: String = HASH,
|
||||
time: Long = EPOCH_SECONDS,
|
||||
tree: String = TREE
|
||||
) = WalletBirthday(height = height, hash = hash, time = time, tree = tree)
|
||||
}
|
|
@ -2,28 +2,42 @@ package cash.z.ecc.sdk.model
|
|||
|
||||
import cash.z.ecc.android.sdk.block.CompactBlockProcessor
|
||||
|
||||
fun CompactBlockProcessor.ProcessorInfo.downloadProgress() = if (lastDownloadRange.isEmpty()) {
|
||||
PercentDecimal.ONE_HUNDRED_PERCENT
|
||||
} else {
|
||||
val numerator = (lastDownloadedHeight - lastDownloadRange.first + 1)
|
||||
.toFloat()
|
||||
.coerceAtLeast(PercentDecimal.MIN)
|
||||
val denominator = (lastDownloadRange.last - lastDownloadRange.first + 1).toFloat()
|
||||
fun CompactBlockProcessor.ProcessorInfo.downloadProgress(): PercentDecimal {
|
||||
val lastDownloadRangeSnapshot = lastDownloadRange
|
||||
val lastDownloadedHeightSnapshot = lastDownloadedHeight
|
||||
|
||||
val progress = (numerator / denominator).coerceAtMost(PercentDecimal.MAX)
|
||||
return if (lastDownloadRangeSnapshot?.isEmpty() != false || lastDownloadedHeightSnapshot == null) {
|
||||
PercentDecimal.ONE_HUNDRED_PERCENT
|
||||
} else {
|
||||
val numerator = (lastDownloadedHeightSnapshot.value - lastDownloadRangeSnapshot.start.value + 1)
|
||||
.toFloat()
|
||||
.coerceAtLeast(PercentDecimal.MIN)
|
||||
val denominator = (lastDownloadRangeSnapshot.endInclusive.value - lastDownloadRangeSnapshot.start.value + 1)
|
||||
.toFloat()
|
||||
|
||||
PercentDecimal(progress)
|
||||
val progress = (numerator / denominator).coerceAtMost(PercentDecimal.MAX)
|
||||
|
||||
PercentDecimal(progress)
|
||||
}
|
||||
}
|
||||
|
||||
fun CompactBlockProcessor.ProcessorInfo.scanProgress() = if (lastScanRange.isEmpty()) {
|
||||
PercentDecimal.ONE_HUNDRED_PERCENT
|
||||
} else {
|
||||
val numerator = (lastScannedHeight - lastScanRange.first + 1).toFloat().coerceAtLeast(PercentDecimal.MIN)
|
||||
val demonimator = (lastScanRange.last - lastScanRange.first + 1).toFloat()
|
||||
fun CompactBlockProcessor.ProcessorInfo.scanProgress(): PercentDecimal {
|
||||
val lastScanRangeSnapshot = lastScanRange
|
||||
val lastScannedHeightSnapshot = lastScannedHeight
|
||||
|
||||
val progress = (numerator / demonimator).coerceAtMost(PercentDecimal.MAX)
|
||||
return if (lastScanRangeSnapshot?.isEmpty() != false || lastScannedHeightSnapshot == null) {
|
||||
PercentDecimal.ONE_HUNDRED_PERCENT
|
||||
} else {
|
||||
val numerator = (lastScannedHeightSnapshot.value - lastScanRangeSnapshot.start.value + 1)
|
||||
.toFloat()
|
||||
.coerceAtLeast(PercentDecimal.MIN)
|
||||
val demonimator = (lastScanRangeSnapshot.endInclusive.value - lastScanRangeSnapshot.start.value + 1)
|
||||
.toFloat()
|
||||
|
||||
PercentDecimal(progress)
|
||||
val progress = (numerator / demonimator).coerceAtMost(PercentDecimal.MAX)
|
||||
|
||||
PercentDecimal(progress)
|
||||
}
|
||||
}
|
||||
|
||||
// These are estimates
|
||||
|
|
|
@ -3,8 +3,7 @@ package cash.z.ecc.sdk.model
|
|||
import android.app.Application
|
||||
import cash.z.ecc.android.bip39.Mnemonics
|
||||
import cash.z.ecc.android.bip39.toEntropy
|
||||
import cash.z.ecc.android.sdk.tool.WalletBirthdayTool
|
||||
import cash.z.ecc.android.sdk.type.WalletBirthday
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||
import cash.z.ecc.sdk.type.fromResources
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -16,7 +15,7 @@ import org.json.JSONObject
|
|||
*/
|
||||
data class PersistableWallet(
|
||||
val network: ZcashNetwork,
|
||||
val birthday: WalletBirthday?,
|
||||
val birthday: BlockHeight?,
|
||||
val seedPhrase: SeedPhrase
|
||||
) {
|
||||
|
||||
|
@ -29,7 +28,7 @@ data class PersistableWallet(
|
|||
put(KEY_VERSION, VERSION_1)
|
||||
put(KEY_NETWORK_ID, network.id)
|
||||
birthday?.let {
|
||||
put(KEY_BIRTHDAY, it.toJson())
|
||||
put(KEY_BIRTHDAY, it.value)
|
||||
}
|
||||
put(KEY_SEED_PHRASE, seedPhrase.joinToString())
|
||||
}
|
||||
|
@ -48,15 +47,19 @@ data class PersistableWallet(
|
|||
fun from(jsonObject: JSONObject): PersistableWallet {
|
||||
when (val version = jsonObject.getInt(KEY_VERSION)) {
|
||||
VERSION_1 -> {
|
||||
val networkId = jsonObject.getInt(KEY_NETWORK_ID)
|
||||
val network = run {
|
||||
val networkId = jsonObject.getInt(KEY_NETWORK_ID)
|
||||
ZcashNetwork.from(networkId)
|
||||
}
|
||||
val birthday = if (jsonObject.has(KEY_BIRTHDAY)) {
|
||||
WalletBirthday.from(jsonObject.getJSONObject(KEY_BIRTHDAY))
|
||||
val birthdayBlockHeightLong = jsonObject.getLong(KEY_BIRTHDAY)
|
||||
BlockHeight.new(network, birthdayBlockHeightLong)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val seedPhrase = jsonObject.getString(KEY_SEED_PHRASE)
|
||||
|
||||
return PersistableWallet(ZcashNetwork.from(networkId), birthday, SeedPhrase.new(seedPhrase))
|
||||
return PersistableWallet(network, birthday, SeedPhrase.new(seedPhrase))
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("Unsupported version $version")
|
||||
|
@ -69,11 +72,11 @@ data class PersistableWallet(
|
|||
*/
|
||||
suspend fun new(application: Application): PersistableWallet {
|
||||
val zcashNetwork = ZcashNetwork.fromResources(application)
|
||||
val walletBirthday = WalletBirthdayTool.loadNearest(application, zcashNetwork)
|
||||
val birthday = BlockHeight.ofLatestCheckpoint(application, zcashNetwork)
|
||||
|
||||
val seedPhrase = newSeedPhrase()
|
||||
|
||||
return PersistableWallet(zcashNetwork, walletBirthday, seedPhrase)
|
||||
return PersistableWallet(zcashNetwork, birthday, seedPhrase)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
package cash.z.ecc.sdk.model
|
||||
|
||||
import cash.z.ecc.android.sdk.type.WalletBirthday
|
||||
import org.json.JSONObject
|
||||
|
||||
internal val WalletBirthday.Companion.VERSION_1
|
||||
get() = 1
|
||||
internal val WalletBirthday.Companion.KEY_VERSION
|
||||
get() = "version"
|
||||
internal val WalletBirthday.Companion.KEY_HEIGHT
|
||||
get() = "height"
|
||||
internal val WalletBirthday.Companion.KEY_HASH
|
||||
get() = "hash"
|
||||
internal val WalletBirthday.Companion.KEY_EPOCH_SECONDS
|
||||
get() = "epoch_seconds"
|
||||
internal val WalletBirthday.Companion.KEY_TREE
|
||||
get() = "tree"
|
||||
|
||||
fun WalletBirthday.Companion.from(jsonString: String) = from(JSONObject(jsonString))
|
||||
|
||||
fun WalletBirthday.Companion.from(jsonObject: JSONObject): WalletBirthday {
|
||||
when (val version = jsonObject.getInt(WalletBirthday.KEY_VERSION)) {
|
||||
WalletBirthday.VERSION_1 -> {
|
||||
val height = jsonObject.getInt(WalletBirthday.KEY_HEIGHT)
|
||||
val hash = jsonObject.getString(WalletBirthday.KEY_HASH)
|
||||
val epochSeconds = jsonObject.getLong(WalletBirthday.KEY_EPOCH_SECONDS)
|
||||
val tree = jsonObject.getString(WalletBirthday.KEY_TREE)
|
||||
|
||||
return WalletBirthday(height, hash, epochSeconds, tree)
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("Unsupported version $version")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun WalletBirthday.toJson() = JSONObject().apply {
|
||||
put(WalletBirthday.KEY_VERSION, WalletBirthday.VERSION_1)
|
||||
put(WalletBirthday.KEY_HEIGHT, height)
|
||||
put(WalletBirthday.KEY_HASH, hash)
|
||||
put(WalletBirthday.KEY_EPOCH_SECONDS, time)
|
||||
put(WalletBirthday.KEY_TREE, tree)
|
||||
}
|
|
@ -136,13 +136,15 @@ class WalletCoordinator(context: Context) {
|
|||
* In order for a rescan to occur, the synchronizer must be loaded already
|
||||
* which would happen if the UI is collecting it.
|
||||
*
|
||||
* @return True if the rewind was performed and false if the wipe was not performed.
|
||||
* @return True if the rescan was performed and false if the rescan was not performed.
|
||||
*/
|
||||
suspend fun rescanBlockchain(): Boolean {
|
||||
synchronizerMutex.withLock {
|
||||
synchronizer.value?.let {
|
||||
it.rewindToNearestHeight(it.latestBirthdayHeight, true)
|
||||
return true
|
||||
it.latestBirthdayHeight?.let { height ->
|
||||
it.rewindToNearestHeight(height, true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,6 +237,6 @@ private suspend fun PersistableWallet.toConfig(): Initializer.Config {
|
|||
val vk = deriveViewingKey()
|
||||
|
||||
return Initializer.Config {
|
||||
it.importWallet(vk, birthday?.height, network, network.defaultHost, network.defaultPort)
|
||||
it.importWallet(vk, birthday, network, network.defaultHost, network.defaultPort)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,13 @@ object WalletSnapshotFixture {
|
|||
@Suppress("LongParameterList")
|
||||
fun new(
|
||||
status: Synchronizer.Status = STATUS,
|
||||
processorInfo: CompactBlockProcessor.ProcessorInfo = CompactBlockProcessor.ProcessorInfo(),
|
||||
processorInfo: CompactBlockProcessor.ProcessorInfo = CompactBlockProcessor.ProcessorInfo(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
),
|
||||
orchardBalance: WalletBalance = ORCHARD_BALANCE,
|
||||
saplingBalance: WalletBalance = SAPLING_BALANCE,
|
||||
transparentBalance: WalletBalance = TRANSPARENT_BALANCE,
|
||||
|
|
|
@ -11,6 +11,7 @@ import cash.z.ecc.android.sdk.db.entity.PendingTransaction
|
|||
import cash.z.ecc.android.sdk.db.entity.Transaction
|
||||
import cash.z.ecc.android.sdk.db.entity.isMined
|
||||
import cash.z.ecc.android.sdk.db.entity.isSubmitSuccess
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.model.WalletBalance
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
|
@ -282,7 +283,7 @@ sealed class SynchronizerError {
|
|||
override fun getCauseMessage(): String? = error?.localizedMessage
|
||||
}
|
||||
|
||||
class Chain(val x: Int, val y: Int) : SynchronizerError() {
|
||||
class Chain(val x: BlockHeight, val y: BlockHeight) : SynchronizerError() {
|
||||
override fun getCauseMessage(): String = "$x, $y"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue