diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 790823bd..624c76ed 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -556,7 +556,8 @@ "Bash(adb logcat:*)", "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(android\\): add 5-minute polling timeout mechanism for keygen/sign\n\nImplements Electron''s checkAndTriggerKeygen\\(\\) polling fallback:\n- Adds polling every 2 seconds with 5-minute timeout\n- Triggers keygen/sign via synthetic session_started event on in_progress status\n- Handles gRPC stream disconnection when app goes to background\n- Shows timeout error in UI via existing error mechanism\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")", "Bash(go list:*)", - "Bash(adb install:*)" + "Bash(adb install:*)", + "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(tss\\): add real-time round progress from msg.Type\\(\\) parsing\n\nExtract current round number from tss-lib message type string using\nregex pattern `Round\\(\\\\d+\\)`. This enables real-time progress updates\n\\(1/4, 2/4... for keygen, 1/9, 2/9... for signing\\) instead of only\nshowing completion status.\n\nChanges across all three platforms:\n- tss-wasm/main.go: Add extractRoundFromMessageType\\(\\) and call\n OnProgress with parsed round on each outgoing message\n- service-party-android/tsslib/tsslib.go: Same implementation for\n Android gomobile binding\n- service-party-app/tss-party/main.go: Same implementation for\n Electron subprocess, with isKeygen parameter to distinguish\n keygen \\(4 rounds\\) vs signing \\(9 rounds\\)\n\nSafe fallback: Returns 0 if parsing fails, which doesn''t affect\nprotocol execution - only UI display.\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")" ], "deny": [], "ask": [] diff --git a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt index 8115b911..bd42591e 100644 --- a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt +++ b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt @@ -2259,20 +2259,43 @@ class TssRepository @Inject constructor( ): Result { return withContext(Dispatchers.IO) { try { - // Parse signature (format: 0x + r(64) + s(64) + v(2)) - val sigHex = signature.removePrefix("0x") - if (sigHex.length != 130) { - return@withContext Result.failure(Exception("Invalid signature length")) + android.util.Log.d("TssRepository", "[BROADCAST] Input signature: ${signature.take(40)}...") + android.util.Log.d("TssRepository", "[BROADCAST] Signature length: ${signature.length}") + + // Signature from TSS is Base64 encoded, need to decode and convert to hex + // Format after decode: r (32 bytes) + s (32 bytes) + v (1 byte) = 65 bytes + val sigBytes = try { + android.util.Base64.decode(signature, android.util.Base64.NO_WRAP) + } catch (e: Exception) { + // If not Base64, try to parse as hex directly + android.util.Log.d("TssRepository", "[BROADCAST] Not Base64, trying hex...") + signature.removePrefix("0x").hexToByteArray() } - val rHex = sigHex.substring(0, 64) - val sHex = sigHex.substring(64, 128) - val vHex = sigHex.substring(128, 130) + android.util.Log.d("TssRepository", "[BROADCAST] Decoded signature bytes: ${sigBytes.size}") - val r = rHex.hexToByteArray() - val s = sHex.hexToByteArray() - val v = vHex.toInt(16) - val recoveryId = if (v >= 27) v - 27 else v + if (sigBytes.size != 65) { + return@withContext Result.failure(Exception("Invalid signature length: ${sigBytes.size} bytes, expected 65")) + } + + // Extract r, s, v from decoded bytes + val rBytes = sigBytes.copyOfRange(0, 32) + val sBytes = sigBytes.copyOfRange(32, 64) + val vByte = sigBytes[64].toInt() and 0xFF + + // Convert to hex for logging + val rHex = rBytes.joinToString("") { "%02x".format(it) } + val sHex = sBytes.joinToString("") { "%02x".format(it) } + + android.util.Log.d("TssRepository", "[BROADCAST] r: ${rHex.take(16)}...") + android.util.Log.d("TssRepository", "[BROADCAST] s: ${sHex.take(16)}...") + android.util.Log.d("TssRepository", "[BROADCAST] v: $vByte") + + val recoveryId = if (vByte >= 27) vByte - 27 else vByte + + // Use byte arrays directly for finalizeTransaction + val r = rBytes + val s = sBytes // Finalize transaction with signature val signedTx = TransactionUtils.finalizeTransaction(