New: Functionality to refresh UTXOs and updated taddr support.

This commit is contained in:
Kevin Gorham 2021-04-05 18:37:13 -04:00
parent 591df083b5
commit 39972d8e43
No known key found for this signature in database
GPG Key ID: CCA55602DF49FC38
5 changed files with 54 additions and 147 deletions

View File

@ -1,116 +0,0 @@
import cash.z.ecc.android.Deps
/////////////////////////////////////////
// Publishing
/////////////////////////////////////////
group = Deps.group
version = Deps.versionName
// Create the pom configuration:
def pomConfig = {
licenses {
license {
name "MIT-style"
url "http://opensource.org/licenses/MIT"
distribution "repo"
}
}
developers {
developer {
id "gmale"
name "Kevin Gorham"
email "kevin.gorham@z.cash"
}
}
scm {
url Deps.githubUrl
}
}
// Jar containing Kotlin sources
task sourcesJar(type: Jar) {
archiveClassifier = 'sources'
from kotlin.sourceSets.main.kotlin.srcDirs
}
// Jar containing docs
task docsJar(type: Jar) {
archiveClassifier = "javadoc"
group = JavaBasePlugin.DOCUMENTATION_GROUP
dependsOn dokka
from dokka
}
def activePublication = Deps.publishingTarget
publishing {
publications {
android.libraryVariants.all { variant ->
if (variant.name != activePublication.variant) return
Production(MavenPublication) {
artifact variant.outputs[0].packageLibrary // the AAR
artifact sourcesJar
artifact docsJar
groupId Deps.group
artifactId activePublication.artifactId
version Deps.versionName
pom.withXml {
def root = asNode()
root.appendNode('description', Deps.description)
root.appendNode('name', activePublication.artifactId)
root.appendNode('url', )
root.children().last() + pomConfig
// AAR-specific: we must explicitly add dependencies for an AAR
def depsNode = root["dependencies"][0] ?: root.appendNode("dependencies")
def addDep = {
if (it.group == null) return // Avoid empty dependency nodes
def dependencyNode = depsNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
if (it.hasProperty('type')) {
dependencyNode.appendNode('type', it.type)
}
if (it.hasProperty('scope')) {
dependencyNode.appendNode('scope', it.scope)
}
if (it.hasProperty('optional') && it.optional) {
dependencyNode.appendNode('optional', 'true')
}
}
// run the 'addDep' closure over each dependency
configurations.implementation.allDependencies.each addDep
}
}
}
}
}
bintray {
user = project.findProperty('bintrayUser') ?: System.getenv('BINTRAY_USER')
key = project.findProperty('bintrayApiKey') ?: System.getenv('BINTRAY_API_KEY')
publications = ['Production']
override = true
pkg {
repo = 'android'
name = activePublication.artifactId
description = Deps.description
publish = true
publicDownloadNumbers = true
userOrg = 'ecc-mobile'
labels = ['aar', 'native', 'android', 'zcash', 'ecc', 'sdk', 'kotlin', 'mobile', 'electric coin company', 'open source', 'crypto', 'cryptocurrency', 'cryptography', 'privacy']
licenses = ['MIT']
vcsUrl = Deps.githubUrl
dryRun = Deps.publishingDryRun
version {
name = Deps.versionName
desc = Deps.description
released = new Date()
vcsTag = this.version
}
}
}

View File

@ -330,6 +330,11 @@ class SdkSynchronizer internal constructor(
// Private API
//
suspend fun refreshUtxos() {
twig("refreshing utxos")
refreshUtxos(getTransparentAddress())
}
/**
* Calculate the latest balance, based on the blocks that have been scanned and transmit this
* information into the flow of [balances].
@ -566,9 +571,7 @@ class SdkSynchronizer internal constructor(
override suspend fun getShieldedAddress(accountId: Int): String = processor.getShieldedAddress(accountId)
override suspend fun getTransparentAddress(seed: ByteArray, accountId: Int, index: Int): String {
return DerivationTool.deriveTransparentAddress(seed, accountId, index)
}
override suspend fun getTransparentAddress(accountId: Int): String = processor.getTransparentAddress(accountId)
override fun sendToAddress(
spendingKey: String,
@ -593,6 +596,8 @@ class SdkSynchronizer internal constructor(
}
}
}.flatMapLatest {
// switch this flow over to monitoring the database for transactions
// so we emit the placeholder TX above, then watch the database for all further updates
twig("Monitoring pending transaction (id: ${it.id}) for updates...")
txManager.monitorById(it.id)
}.distinctUntilChanged()
@ -626,9 +631,8 @@ class SdkSynchronizer internal constructor(
txManager.monitorById(it.id)
}.distinctUntilChanged()
override suspend fun refreshUtxos(address: String, sinceHeight: Int): Int {
// TODO: we need to think about how we restrict this to only our taddr
return processor.downloadUtxos(address, sinceHeight)
override suspend fun refreshUtxos(address: String, startHeight: Int): Int {
return processor.refreshUtxos(address, startHeight)
}
override suspend fun getTransparentBalance(tAddr: String): WalletBalance {

View File

@ -152,16 +152,14 @@ interface Synchronizer {
suspend fun getShieldedAddress(accountId: Int = 0): String
/**
* Gets the transparent address for the given account and index.
* Gets the transparent address for the given account.
*
* @param accountId the optional accountId whose address is of interest. By default, the first
* account is used.
* @param index the optional index whose address is of interest. By default, the first index is
* used.
*
* @return the address for the given account and index.
* @return the address for the given account.
*/
suspend fun getTransparentAddress(seed: ByteArray, accountId: Int = 0, index: Int = 0): String
suspend fun getTransparentAddress(accountId: Int = 0): String
/**
* Sends zatoshi.
@ -270,7 +268,7 @@ interface Synchronizer {
errorHandler: (Throwable) -> Unit = { throw it }
)
suspend fun refreshUtxos(tAddr: String, sinceHeight: Int): Int
suspend fun refreshUtxos(tAddr: String, sinceHeight: Int = ZcashSdk.SAPLING_ACTIVATION_HEIGHT): Int
/**
* Returns the balance that the wallet knows about. This should be called after [refreshUtxos].

View File

@ -386,7 +386,6 @@ class CompactBlockProcessor(
}
}
internal suspend fun downloadUtxos(tAddress: String, startHeight: Int): Int = withContext(IO) {
private suspend fun updateBirthdayHeight() {
try {
val betterBirthday = calculateBirthdayHeight()
@ -399,9 +398,13 @@ class CompactBlockProcessor(
}
}
internal suspend fun refreshUtxos(tAddress: String, startHeight: Int): Int = withContext(IO) {
var skipped = 0
twig("Downloading utxos starting at height $startHeight")
// todo test what happens when this call fails
downloader.lightWalletService.fetchUtxos(tAddress, startHeight).let { result ->
twig("Clearing utxos above height ${startHeight - 1}")
rustBackend.clearUtxos(tAddress, startHeight - 1)
twig("Downloading utxos starting at height $startHeight")
result.forEach { utxo: Service.GetAddressUtxosReply ->
twig("Found UTXO at height ${utxo.height.toInt()}")
try {
@ -570,7 +573,7 @@ class CompactBlockProcessor(
determineLowerBound(errorHeight).let { lowerBound ->
twig("handling chain error at $errorHeight by rewinding to block $lowerBound")
onChainErrorListener?.invoke(errorHeight, lowerBound)
rewindToHeight(lowerBound)
rewindToHeight(lowerBound, true)
}
}
@ -579,21 +582,24 @@ class CompactBlockProcessor(
* blocks. Otherwise, the cached blocks will be used in the rescan, which in most cases, is fine.
*/
suspend fun rewindToHeight(height: Int, alsoClearBlockCache: Boolean = false) = withContext(IO) {
val lastHeight = currentInfo.lastScannedHeight
twig("Rewinding from $lastHeight to height: $height")
val lastScannedHeight = currentInfo.lastScannedHeight
twig("Rewinding from $lastScannedHeight to height: $height")
// TODO: think about how we might pause all processing during a rewind
if (height < lastHeight) {
if (height < lastScannedHeight) {
rustBackend.rewindToHeight(height)
val range = (height + 1)..lastHeight
if (alsoClearBlockCache) {
twig("Rewound blocks will download in the next scheduled scan")
downloader.rewindToHeight(height)
// communicate that the wallet is no longer synced because it might remain this way for 20+ seconds because we only download on 20s time boundaries so we can't trigger any immediate action
setState(Downloading)
} else {
twig("Revalidating blocks that were rewound")
if (validateAndScanNewBlocks(range) == ERROR_CODE_NONE) enhanceTransactionDetails(range)
}
} else {
twig("not rewinding dataDb because the last scanned height is $lastScannedHeight which is less than the target height of $height")
}
if (alsoClearBlockCache) {
twig("Also clearing block cache back to $height. These rewound blocks will download in the next scheduled scan")
downloader.rewindToHeight(height)
// communicate that the wallet is no longer synced because it might remain this way for 20+ seconds because we only download on 20s time boundaries so we can't trigger any immediate action
setState(Downloading)
} else {
val range = (height + 1)..lastScannedHeight
twig("We kept the cache blocks in place so we don't need to wait for the next scheduled download to rescan. Instead we will rescan and validate blocks ${range.first}..${range.last}")
if (validateAndScanNewBlocks(range) == ERROR_CODE_NONE) enhanceTransactionDetails(range)
}
}
@ -700,8 +706,12 @@ class CompactBlockProcessor(
*
* @return the address of this wallet.
*/
suspend fun getShieldedAddress(accountId: Int) = withContext(IO) {
rustBackend.getShieldedAddress(accountId)
suspend fun getShieldedAddress(accountId: Int = 0) = withContext(IO) {
repository.getAccount(accountId)!!.rawShieldedAddress
}
suspend fun getTransparentAddress(accountId: Int = 0) = withContext(IO) {
repository.getAccount(accountId)!!.rawTransparentAddress
}
/**

View File

@ -41,4 +41,15 @@ data class WalletBirthday(
data class UnifiedViewingKey(
val extfvk: String = "",
val extpub: String = ""
)
)
data class UnifiedAddressAccount(
val accountId: Int = -1,
override val rawShieldedAddress: String = "",
override val rawTransparentAddress: String = ""
) : UnifiedAddress
interface UnifiedAddress {
val rawShieldedAddress: String
val rawTransparentAddress: String
}