Skip to content

Commit 2532043

Browse files
committed
fix: match cjit against fetched list to avoid state race
1 parent 85d27e6 commit 2532043

1 file changed

Lines changed: 10 additions & 9 deletions

File tree

app/src/main/java/to/bitkit/repositories/BlocktankRepo.kt

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,40 +131,41 @@ class BlocktankRepo @Inject constructor(
131131
suspend fun getCjitEntry(channel: ChannelDetails): IcJitEntry? = withContext(bgDispatcher) {
132132
val fundingTxId = channel.fundingTxo?.txid ?: return@withContext null
133133

134-
fun cachedMatch(): IcJitEntry? = _blocktankState.value.cjitEntries
135-
.firstOrNull { it.channel?.fundingTx?.id == fundingTxId }
134+
fun List<IcJitEntry>.matching(): IcJitEntry? =
135+
firstOrNull { it.channel?.fundingTx?.id == fundingTxId }
136136

137-
cachedMatch()?.let { return@withContext it }
137+
val cached = _blocktankState.value.cjitEntries
138+
cached.matching()?.let { return@withContext it }
138139

139140
// A ChannelReady can only be a CJIT if a live cached entry is still awaiting its channel; otherwise skip
140141
// the server round-trip so a non-CJIT transfer confirmation isn't delayed by a slow Blocktank API.
141-
val hasPendingCjit = _blocktankState.value.cjitEntries.any {
142+
val hasPendingCjit = cached.any {
142143
it.channel == null && it.state != CJitStateEnum.EXPIRED && it.state != CJitStateEnum.FAILED
143144
}
144145
if (!hasPendingCjit) return@withContext null
145146

146-
refreshCjitEntries()
147-
return@withContext cachedMatch()
147+
// Match against the freshly fetched list so a concurrent refreshOrders() can't clobber state before we read.
148+
return@withContext refreshCjitEntries().matching()
148149
}
149150

150-
private suspend fun refreshCjitEntries() {
151+
private suspend fun refreshCjitEntries(): List<IcJitEntry> {
151152
repeat(CJIT_REFRESH_ATTEMPTS) { attempt ->
152153
runCatching {
153154
withTimeout(CJIT_REFRESH_TIMEOUT) {
154155
coreService.blocktank.cjitEntries(refresh = true)
155156
}
156157
}.onSuccess { entries ->
157158
_blocktankState.update { it.copy(cjitEntries = entries.toImmutableList()) }
158-
return
159+
return entries
159160
}.onFailure {
160161
if (it is CancellationException && it !is TimeoutCancellationException) throw it
161162
if (attempt == CJIT_REFRESH_ATTEMPTS - 1) {
162163
Logger.warn("Failed to refresh CJIT entries; using cached state", it, context = TAG)
163-
return
164164
}
165165
}
166166
delay(CJIT_REFRESH_RETRY_DELAY)
167167
}
168+
return _blocktankState.value.cjitEntries
168169
}
169170

170171
suspend fun refreshInfo() = withContext(bgDispatcher) {

0 commit comments

Comments
 (0)