From 9629e25e6977dcbb7fa41226c1691433773f59ca Mon Sep 17 00:00:00 2001 From: Satoshi Qazi Muhammed Date: Mon, 13 Jul 2026 06:00:31 -0700 Subject: [PATCH] fix: reuse TronAddress.kt's existing Base58Check instead of duplicating it CI caught a redeclaration - Base58Check is already a chain-agnostic object in this same package (TronAddress.kt), used for Tron's own Base58Check addresses. Bitcoin's legacy addresses are the same shape (1 version byte + 20-byte hash), just a different version byte - reuse it directly. --- .../nova/common/utils/Base58Check.kt | 53 ------------------- .../common/utils/BitcoinDestinationAddress.kt | 14 +++-- 2 files changed, 10 insertions(+), 57 deletions(-) delete mode 100644 common/src/main/java/io/novafoundation/nova/common/utils/Base58Check.kt diff --git a/common/src/main/java/io/novafoundation/nova/common/utils/Base58Check.kt b/common/src/main/java/io/novafoundation/nova/common/utils/Base58Check.kt deleted file mode 100644 index 15ff4da0..00000000 --- a/common/src/main/java/io/novafoundation/nova/common/utils/Base58Check.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.novafoundation.nova.common.utils - -import java.math.BigInteger - -private const val BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" - -/** - * Base58Check - the legacy encoding used by Bitcoin's P2PKH (`1...`) and P2SH (`3...`) addresses, as opposed to - * native SegWit's bech32. Needed only to recognize/decode EXTERNAL destination addresses for sending (many - * exchanges, including at least one confirmed live, only offer a P2SH withdrawal address with no way to pick a - * native SegWit one) - this app's OWN address (derivation, receiving, `bitcoinAddressToAccountId()`) remains - * native SegWit only, unaffected by this. - */ -object Base58Check { - - class Decoded(val version: Int, val payload: ByteArray) - - fun decode(address: String): Decoded { - val full = base58Decode(address) - require(full.size == 25) { "Base58Check payload must be 25 bytes, got ${full.size}" } - - val versionAndPayload = full.copyOfRange(0, 21) - val checksum = full.copyOfRange(21, 25) - val expectedChecksum = versionAndPayload.sha256d().copyOfRange(0, 4) - - require(checksum.contentEquals(expectedChecksum)) { "Base58Check checksum mismatch" } - - return Decoded(version = versionAndPayload[0].toInt() and 0xff, payload = versionAndPayload.copyOfRange(1, 21)) - } - - private fun base58Decode(input: String): ByteArray { - require(input.isNotEmpty()) { "Base58 input must not be empty" } - - var value = BigInteger.ZERO - val base = BigInteger.valueOf(58) - - for (c in input) { - val digit = BASE58_ALPHABET.indexOf(c) - require(digit >= 0) { "Invalid base58 character: $c" } - value = value.multiply(base).add(BigInteger.valueOf(digit.toLong())) - } - - val bytes = value.toByteArray().let { - // BigInteger.toByteArray() may prepend a 0x00 sign-disambiguation byte - strip it, it is not part of the data. - if (it.size > 1 && it[0] == 0.toByte()) it.copyOfRange(1, it.size) else it - } - - // Each leading '1' character encodes a leading zero byte that BigInteger's conversion would otherwise drop. - val leadingZeros = input.takeWhile { it == '1' }.length - - return ByteArray(leadingZeros) + bytes - } -} diff --git a/common/src/main/java/io/novafoundation/nova/common/utils/BitcoinDestinationAddress.kt b/common/src/main/java/io/novafoundation/nova/common/utils/BitcoinDestinationAddress.kt index 7487205d..7263268b 100644 --- a/common/src/main/java/io/novafoundation/nova/common/utils/BitcoinDestinationAddress.kt +++ b/common/src/main/java/io/novafoundation/nova/common/utils/BitcoinDestinationAddress.kt @@ -33,12 +33,18 @@ fun String.decodeBitcoinDestination(): BitcoinDestination { } } + // Base58Check itself is chain-agnostic (see TronAddress.kt) - a Bitcoin legacy address is 1 version byte + + // 20-byte hash, same shape as Tron's own address, just a different version byte and no fixed prefix meaning. val decoded = Base58Check.decode(this) + require(decoded.size == 21) { "Not a valid Bitcoin legacy address: $this" } - return when (decoded.version) { - P2PKH_VERSION -> BitcoinDestination.P2pkh(decoded.payload) - P2SH_VERSION -> BitcoinDestination.P2sh(decoded.payload) - else -> error("Unsupported Bitcoin address version byte: ${decoded.version}") + val version = decoded[0].toInt() and 0xff + val hash = decoded.copyOfRange(1, decoded.size) + + return when (version) { + P2PKH_VERSION -> BitcoinDestination.P2pkh(hash) + P2SH_VERSION -> BitcoinDestination.P2sh(hash) + else -> error("Unsupported Bitcoin address version byte: $version") } }