Skip to content

Commit

Permalink
Fix race condition in mini wallet (#582)
Browse files Browse the repository at this point in the history
When subscribing to a new address, we register a new handler for `ScriptHashSubscriptionResponse` *before* updating the `scriptHashes` map. If Electrum notifies us too quickly, `processSubscriptionResponse` will be unable to identify the `scriptHash`.
  • Loading branch information
pm47 authored Jan 18, 2024
1 parent 8483fdd commit 61b1431
Showing 1 changed file with 16 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -165,20 +165,22 @@ class ElectrumMiniWallet(
* Depending on the status of the electrum connection, the subscription may or may not be sent to a server.
* It is the responsibility of the caller to resubscribe on reconnection.
*/
suspend fun subscribe(bitcoinAddress: String): Pair<ByteVector32, String>? {
suspend fun subscribe(scriptHash: ByteVector32, bitcoinAddress: String) {
kotlin.runCatching { client.startScriptHashSubscription(scriptHash) }.map { response ->
logger.info { "subscribed to address=$bitcoinAddress scriptHash=$scriptHash" }
_walletStateFlow.value = _walletStateFlow.value.processSubscriptionResponse(response)
}
}

fun computeScriptHash(bitcoinAddress: String): ByteVector32? {
return when (val result = Bitcoin.addressToPublicKeyScript(chainHash, bitcoinAddress)) {
is AddressToPublicKeyScriptResult.Failure -> {
logger.error { "cannot subscribe to $bitcoinAddress ($result)" }
null
}
is AddressToPublicKeyScriptResult.Success -> {
val pubkeyScript = ByteVector(Script.write(result.script))
val scriptHash = ElectrumClient.computeScriptHash(pubkeyScript)
kotlin.runCatching { client.startScriptHashSubscription(scriptHash) }.map { response ->
logger.info { "subscribed to address=$bitcoinAddress pubkeyScript=$pubkeyScript scriptHash=$scriptHash" }
_walletStateFlow.value = _walletStateFlow.value.processSubscriptionResponse(response)
}
scriptHash to bitcoinAddress
return ElectrumClient.computeScriptHash(pubkeyScript)
}
}
}
Expand All @@ -197,17 +199,20 @@ class ElectrumMiniWallet(
when (it) {
is WalletCommand.Companion.ElectrumConnected -> {
logger.mdcinfo { "electrum connected" }
scriptHashes.values.forEach { scriptHash -> subscribe(scriptHash) }
scriptHashes.forEach { (scriptHash, address) -> subscribe(scriptHash, address) }
}
is WalletCommand.Companion.ElectrumNotification -> {
if (it.msg is ScriptHashSubscriptionResponse) {
_walletStateFlow.value = _walletStateFlow.value.processSubscriptionResponse(it.msg)
}
}
is WalletCommand.Companion.AddAddress -> {
logger.mdcinfo { "adding new address=${it.bitcoinAddress}" }
subscribe(it.bitcoinAddress)?.let {
scriptHashes = scriptHashes + it
computeScriptHash(it.bitcoinAddress)?.let { scriptHash ->
if (!scriptHashes.containsKey(scriptHash)) {
logger.mdcinfo { "adding new address=${it.bitcoinAddress}" }
scriptHashes = scriptHashes + (scriptHash to it.bitcoinAddress)
subscribe(scriptHash, it.bitcoinAddress)
}
}
}
}
Expand Down

0 comments on commit 61b1431

Please sign in to comment.