[#610] Fix block download regression

This includes a few different fixes that prevented the demo app and other SDK consumers from working.

First, initialization of the demo app was fixed to use the sapling activation height rather than the current latest checkpoint.

Second, CompactBlockProcessor wasn’t downloading blocks.

Third, a few potential thread safety issues were resolved by making fields that are accessed by multiple threads volatile.
This commit is contained in:
Carter Jernigan 2022-07-25 14:47:58 -04:00 committed by Carter Jernigan
parent b53b09d40d
commit 8e3c4a636f
6 changed files with 50 additions and 23 deletions

View File

@ -48,12 +48,18 @@ class ListTransactionsFragment : BaseDemoFragment<FragmentListTransactionsBindin
// have the seed stored
val seed = Mnemonics.MnemonicCode(seedPhrase).toSeed()
initializer = runBlocking {
Initializer.new(requireApplicationContext()) {
runBlocking { it.newWallet(seed, network = ZcashNetwork.fromResources(requireApplicationContext())) }
it.setNetwork(ZcashNetwork.fromResources(requireApplicationContext()))
initializer = Initializer.newBlocking(
requireApplicationContext(),
Initializer.Config {
runBlocking {
it.importWallet(
seed,
birthday = null,
network = ZcashNetwork.fromResources(requireApplicationContext())
)
}
}
}
)
address = runBlocking {
DerivationTool.deriveShieldedAddress(
seed,

View File

@ -72,7 +72,8 @@ class DataDbScannerUtil {
BlockHeight.new(
ZcashNetwork.Mainnet,
birthdayHeight
)
),
false
)
}
}

View File

@ -87,7 +87,7 @@ class Initializer private constructor(
* transactions. Again, this value is only considered when [height] is null.
*
*/
fun setBirthdayHeight(height: BlockHeight?, defaultToOldestHeight: Boolean = false): Config =
fun setBirthdayHeight(height: BlockHeight?, defaultToOldestHeight: Boolean): Config =
apply {
this.birthdayHeight = height
this.defaultToOldestHeight = defaultToOldestHeight
@ -329,12 +329,23 @@ class Initializer private constructor(
config: Config
): Initializer {
config.validate()
val heightToUse = config.birthdayHeight
?: (if (config.defaultToOldestHeight == true) config.network.saplingActivationHeight else null)
val loadedBirthday =
CheckpointTool.loadNearest(context, config.network, heightToUse)
val rustBackend = initRustBackend(context, config.network, config.alias, loadedBirthday.height)
val loadedCheckpoint = run {
val height = config.birthdayHeight
?: if (config.defaultToOldestHeight == true) {
config.network.saplingActivationHeight
} else {
null
}
CheckpointTool.loadNearest(
context,
config.network,
height
)
}
val rustBackend = initRustBackend(context, config.network, config.alias, loadedCheckpoint.height)
return Initializer(
context.applicationContext,
@ -345,7 +356,7 @@ class Initializer private constructor(
config.port,
config.viewingKeys,
config.overwriteVks,
loadedBirthday
loadedCheckpoint
)
}

View File

@ -147,6 +147,9 @@ class CompactBlockProcessor internal constructor(
* sequentially, due to the way sqlite works so it is okay for this not to be threadsafe or
* coroutine safe because processing cannot be concurrent.
*/
// This accessed by the Dispatchers.IO thread, which means multiple threads are reading/writing
// concurrently.
@Volatile
internal var currentInfo = ProcessorInfo(null, null, null, null, null)
/**
@ -325,13 +328,14 @@ class CompactBlockProcessor internal constructor(
} else {
null
},
lastDownloadRange = if (initialInfo.lastDownloadedHeight != null && initialInfo.lastScannedHeight != null && initialInfo.networkBlockHeight != null) {
lastDownloadRange = if (initialInfo.networkBlockHeight != null) {
BlockHeight.new(
network,
max(
initialInfo.lastDownloadedHeight.value,
initialInfo.lastScannedHeight.value
) + 1
buildList {
add(network.saplingActivationHeight.value)
initialInfo.lastDownloadedHeight?.let { add(it.value + 1) }
initialInfo.lastScannedHeight?.let { add(it.value + 1) }
}.max()
)..initialInfo.networkBlockHeight
} else {
null
@ -633,7 +637,8 @@ class CompactBlockProcessor internal constructor(
metrics.beginBatch()
result = rustBackend.scanBlocks(SCAN_BATCH_SIZE)
metrics.endBatch()
val lastScannedHeight = BlockHeight.new(network, range.start.value + metrics.cumulativeItems - 1)
val lastScannedHeight =
BlockHeight.new(network, range.start.value + metrics.cumulativeItems - 1)
val percentValue =
(lastScannedHeight.value - range.start.value) / (range.endInclusive.value - range.start.value + 1).toFloat() * 100.0f
val percent = "%.0f".format(percentValue.coerceAtMost(100f).coerceAtLeast(0f))
@ -674,7 +679,7 @@ class CompactBlockProcessor internal constructor(
lastDownloadedHeight: BlockHeight? = currentInfo.lastDownloadedHeight,
lastScanRange: ClosedRange<BlockHeight>? = currentInfo.lastScanRange,
lastDownloadRange: ClosedRange<BlockHeight>? = currentInfo.lastDownloadRange
): Unit = withContext(IO) {
) {
currentInfo = currentInfo.copy(
networkBlockHeight = networkBlockHeight,
lastScannedHeight = lastScannedHeight,
@ -682,8 +687,11 @@ class CompactBlockProcessor internal constructor(
lastScanRange = lastScanRange,
lastDownloadRange = lastDownloadRange
)
_networkHeight.value = networkBlockHeight
_processorInfo.send(currentInfo)
withContext(IO) {
_networkHeight.value = networkBlockHeight
_processorInfo.send(currentInfo)
}
}
private suspend fun handleChainError(errorHeight: BlockHeight) {

View File

@ -75,6 +75,7 @@ interface Twig {
* @see [Twig.clip]
*/
object Bush {
@Volatile
var trunk: Twig = SilentTwig()
val leaves: MutableSet<Leaf> = CopyOnWriteArraySet<Leaf>()
}

View File

@ -105,7 +105,7 @@ internal object CheckpointTool {
context: Context,
network: ZcashNetwork,
directory: String,
birthday: BlockHeight? = null
birthday: BlockHeight?
): List<String> {
val unfilteredTreeFiles = listCheckpointDirectoryContents(context, directory)
if (unfilteredTreeFiles.isNullOrEmpty()) {