Skip to content

Commit 7ccf9ae

Browse files
authored
Add more details to refund signing result (#675)
We seem to sometimes obtain invalid sigs from Phoenix users. Having more details should help troubleshoot the issue.
1 parent 8bc0950 commit 7ccf9ae

File tree

4 files changed

+44
-4
lines changed

4 files changed

+44
-4
lines changed

phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/settings/channels/SpendFromChannelAddress.kt

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,9 @@ fun SpendFromChannelAddress(
154154
ErrorMessage(
155155
header = stringResource(id = R.string.spendchanneladdress_error_generic),
156156
details = when (state) {
157-
is SpendFromChannelAddressViewState.Error.Generic -> state.cause.localizedMessage
157+
is SpendFromChannelAddressViewState.Error.Generic -> {
158+
state.cause.localizedMessage
159+
}
158160
is SpendFromChannelAddressViewState.Error.AmountMissing -> {
159161
stringResource(id = R.string.spendchanneladdress_error_amount)
160162
}
@@ -179,9 +181,30 @@ fun SpendFromChannelAddress(
179181
is SpendFromChannelAddressViewState.Error.TransactionMalformed -> {
180182
stringResource(id = R.string.spendchanneladdress_error_tx, state.details)
181183
}
184+
is SpendFromChannelAddressViewState.Error.InvalidSig -> {
185+
stringResource(R.string.spendchanneladdress_error_invalid_sig)
186+
}
182187
},
183188
alignment = Alignment.CenterHorizontally
184189
)
190+
if (state is SpendFromChannelAddressViewState.Error.InvalidSig) {
191+
val context = LocalContext.current
192+
FilledButton(
193+
text = "Copy error data",
194+
onClick = {
195+
copyToClipboard(
196+
context = context,
197+
data = """
198+
tx_id=${state.txId}
199+
funding_script=${state.fundingScript.toHex()}
200+
public_key=${state.publicKey.toHex()}
201+
signature=${state.signature.toHex()}
202+
""".trimIndent(),
203+
dataLabel = "signature error data"
204+
)
205+
}
206+
)
207+
}
185208
}
186209

187210
Card {

phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/settings/channels/SpendFromChannelAddressViewModel.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ import androidx.compose.runtime.mutableStateOf
2020
import androidx.lifecycle.ViewModel
2121
import androidx.lifecycle.ViewModelProvider
2222
import androidx.lifecycle.viewModelScope
23+
import fr.acinq.bitcoin.ByteVector
2324
import fr.acinq.bitcoin.ByteVector64
2425
import fr.acinq.bitcoin.PublicKey
2526
import fr.acinq.bitcoin.Satoshi
27+
import fr.acinq.bitcoin.TxId
2628
import fr.acinq.phoenix.PhoenixBusiness
2729
import fr.acinq.phoenix.utils.channels.SpendChannelAddressHelper
2830
import fr.acinq.phoenix.utils.channels.SpendChannelAddressResult
@@ -48,6 +50,7 @@ sealed class SpendFromChannelAddressViewState {
4850
data class ChannelDataUnhandledVersion(val version: Int) : Error()
4951
data class PublicKeyMalformed(val details: String) : Error()
5052
data class TransactionMalformed(val details: String) : Error()
53+
data class InvalidSig(val txId: TxId, val publicKey: PublicKey, val fundingScript: ByteVector, val signature: ByteVector64): Error()
5154
}
5255

5356
val canProcess: Boolean = this !is Processing && this !is SignedTransaction
@@ -120,6 +123,9 @@ class SpendFromChannelAddressViewModel(
120123
is SpendChannelAddressResult.Failure.TransactionMalformed -> {
121124
state.value = SpendFromChannelAddressViewState.Error.TransactionMalformed(result.details)
122125
}
126+
is SpendChannelAddressResult.Failure.InvalidSig -> {
127+
state.value = SpendFromChannelAddressViewState.Error.InvalidSig(result.txId, result.publicKey, result.fundingScript, result.signature)
128+
}
123129
}
124130
}
125131
}

phoenix-android/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@
331331
<string name="spendchanneladdress_error_unsigned_tx">Malformed unsigned tx [%1$s]</string>
332332
<string name="spendchanneladdress_error_remote_funding_pubkey">Malformed remote funding pubkey [%1$s]</string>
333333
<string name="spendchanneladdress_error_tx">Malformed transaction [%1$s]</string>
334+
<string name="spendchanneladdress_error_invalid_sig">Invalid signature</string>
334335

335336
<!-- home: balance section & misc -->
336337

phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/utils/channels/SpendChannelAddressHelper.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,15 @@ package fr.acinq.phoenix.utils.channels
1818

1919
import fr.acinq.bitcoin.ByteVector
2020
import fr.acinq.bitcoin.ByteVector64
21+
import fr.acinq.bitcoin.Crypto
2122
import fr.acinq.bitcoin.PublicKey
2223
import fr.acinq.bitcoin.Satoshi
24+
import fr.acinq.bitcoin.SigHash
25+
import fr.acinq.bitcoin.SigVersion
2326
import fr.acinq.bitcoin.Script
2427
import fr.acinq.bitcoin.Transaction
28+
import fr.acinq.bitcoin.TxId
29+
import fr.acinq.bitcoin.byteVector
2530
import fr.acinq.lightning.channel.states.ChannelStateWithCommitments
2631
import fr.acinq.lightning.channel.states.PersistedChannelState
2732
import fr.acinq.lightning.logging.error
@@ -112,8 +117,12 @@ object SpendChannelAddressHelper {
112117
val fundingScript = Scripts.multiSig2of2(localFundingKey.publicKey(), pubkey)
113118

114119
val sig = Transactions.sign(tx = tx, inputIndex = 0, Script.write(fundingScript), amount, localFundingKey)
115-
return SpendChannelAddressResult.Success(localFundingKey.publicKey(), sig)
116-
120+
val signedData = tx.hashForSigning(0, Script.write(fundingScript), SigHash.SIGHASH_ALL, amount, SigVersion.SIGVERSION_WITNESS_V0)
121+
return if (!Crypto.verifySignature(signedData, sig, localFundingKey.publicKey())) {
122+
SpendChannelAddressResult.Failure.InvalidSig(tx.txid, localFundingKey.publicKey(), Script.write(fundingScript).byteVector(), sig)
123+
} else {
124+
SpendChannelAddressResult.Success(tx.txid, localFundingKey.publicKey(), Script.write(fundingScript).byteVector(), sig)
125+
}
117126
} catch (e: Exception) {
118127
log.error { "error when spending from channel address: ${e.message}" }
119128
return SpendChannelAddressResult.Failure.Generic(e)
@@ -122,7 +131,7 @@ object SpendChannelAddressHelper {
122131
}
123132

124133
sealed class SpendChannelAddressResult {
125-
data class Success(val publicKey: PublicKey, val signature: ByteVector64) : SpendChannelAddressResult()
134+
data class Success(val txId: TxId, val publicKey: PublicKey, val fundingScript: ByteVector, val signature: ByteVector64) : SpendChannelAddressResult()
126135
sealed class Failure : SpendChannelAddressResult() {
127136
data class Generic(val error: Throwable) : Failure()
128137
data object ChannelDataMalformed : Failure()
@@ -131,5 +140,6 @@ sealed class SpendChannelAddressResult {
131140
data class ChannelDataUnhandledVersion(val version: Int) : Failure()
132141
data class TransactionMalformed(val details: String) : Failure()
133142
data class RemoteFundingPubkeyMalformed(val details: String) : Failure()
143+
data class InvalidSig(val txId: TxId, val publicKey: PublicKey, val fundingScript: ByteVector, val signature: ByteVector64) : Failure()
134144
}
135145
}

0 commit comments

Comments
 (0)