From e85071fc7fb629ae46ed7dcbfab757c65ce9efc5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 10 Feb 2020 17:47:05 +0300 Subject: [PATCH] Updated on 2026-08-14 --- blockchain/build.gradle | 3 + .../blockchain/bitcoin/BitcoinAddress.kt | 6 +- .../bitcoin/BitcoinTransactionBuilder.kt | 12 ++++ .../bitcoin/BitcoinWalletManager.kt | 22 ++----- .../blockchain/cardano/CardanoAddress.kt | 1 - .../cardano/CardanoTransactionBuilder.kt | 16 +++++ .../cardano/CardanoWalletManager.kt | 21 +------ .../tangem/blockchain/common/WalletManager.kt | 4 -- .../blockchain/common/WalletManagerFactory.kt | 33 ++++++----- .../common/WalletManagerFactoryTest.kt | 59 +++++++++++++++++++ 10 files changed, 119 insertions(+), 58 deletions(-) create mode 100644 blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt diff --git a/blockchain/build.gradle b/blockchain/build.gradle index 4f75526d84..829d18e0a0 100644 --- a/blockchain/build.gradle +++ b/blockchain/build.gradle @@ -47,6 +47,9 @@ dependencies { implementation 'org.bitcoinj:bitcoinj-core:0.15.2' implementation 'com.github.stellar:java-stellar-sdk:0.13.0' + implementation "com.madgag.spongycastle:core:1.58.0.0" + implementation "com.madgag.spongycastle:prov:1.58.0.0" + ext.kethereum_version = '0.79.5' implementation "com.github.walleth.kethereum:functions:$kethereum_version" implementation "com.github.walleth.kethereum:keccak_shortcut:$kethereum_version" diff --git a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinAddress.kt b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinAddress.kt index 1836223aee..01cdd30770 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinAddress.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinAddress.kt @@ -33,18 +33,18 @@ class BitcoinAddressValidator { if (testNet && firstLettersNonTestNet.contains(address.first())) return false if (address.length !in 26..35) return false val decoded = address.decodeBase58() ?: return false - val hash = sha256(decoded, 0, 21, 2) + val hash = recursiveSha256(decoded, 0, 21, 2) return hash.sliceArray(0..3).contentEquals(decoded.sliceArray(21..24)) } else { return validateSegwitAddress(address, testNet) } } - private fun sha256(data: ByteArray, start: Int, len: Int, recursion: Int): ByteArray { + private fun recursiveSha256(data: ByteArray, start: Int, len: Int, recursion: Int): ByteArray { if (recursion == 0) return data val md = MessageDigest.getInstance("SHA-256") md.update(data.sliceArray(start until start + len)) - return sha256(md.digest(), 0, 32, recursion - 1) + return recursiveSha256(md.digest(), 0, 32, recursion - 1) } private fun String.decodeBase58(): ByteArray? { diff --git a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinTransactionBuilder.kt b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinTransactionBuilder.kt index 31faf256a9..d066fa259e 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinTransactionBuilder.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinTransactionBuilder.kt @@ -58,6 +58,18 @@ class BitcoinTransactionBuilder(private val testNet: Boolean) { val signature = TransactionSignature(r, canonicalS) return ScriptBuilder.createInputScript(signature, ECKey.fromPublicOnly(publicKey)) } + + fun getEstimateSize(transactionData: TransactionData, walletPublicKey: ByteArray): Result { + val buildTransactionResult = buildToSign(transactionData) + when (buildTransactionResult) { + is Result.Failure -> return buildTransactionResult + is Result.Success -> { + val hashes = buildTransactionResult.data + val finalTransaction = buildToSend(ByteArray(64 * hashes.size) { 1 }, walletPublicKey) + return Result.Success(finalTransaction.size) + } + } + } } internal fun TransactionData.toBitcoinJTransaction(networkParameters: NetworkParameters?, diff --git a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinWalletManager.kt index 8820c732d9..a1779ce5ae 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinWalletManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinWalletManager.kt @@ -18,7 +18,6 @@ class BitcoinWalletManager( walletConfig: WalletConfig, isTestNet: Boolean = false ) : WalletManager, - TransactionEstimator, TransactionSender, FeeProvider { @@ -48,9 +47,9 @@ class BitcoinWalletManager( null, "unknown", currencyWallet.address)) - } else { - currencyWallet.pendingTransactions.clear() } + } else { + currencyWallet.pendingTransactions.clear() } } @@ -58,18 +57,6 @@ class BitcoinWalletManager( Log.e(this::class.java.simpleName, error?.message ?: "") } - override suspend fun getEstimateSize(transactionData: TransactionData): Result { - val buildTransactionResult = transactionBuilder.buildToSign(transactionData) - when (buildTransactionResult) { - is Result.Failure -> return buildTransactionResult - is Result.Success -> { - val hashes = buildTransactionResult.data - val finalTransaction = transactionBuilder.buildToSend(ByteArray(64 * hashes.size) {1}, walletPublicKey) - return Result.Success(finalTransaction.size) - } - } - } - override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult { val buildTransactionResult = transactionBuilder.buildToSign(transactionData) when (buildTransactionResult) { @@ -90,10 +77,11 @@ class BitcoinWalletManager( when (val result = networkManager.getFee()) { is Result.Failure -> return result is Result.Success -> { - val sizeResult = getEstimateSize( + val sizeResult = transactionBuilder.getEstimateSize( TransactionData(amount, Amount(1.toBigDecimal().divide(SATOSHI_IN_BTC), blockchain), - address, destination) + address, destination), + walletPublicKey ) when (sizeResult) { is Result.Failure -> return sizeResult diff --git a/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoAddress.kt b/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoAddress.kt index f8b6667362..3aecd18ff1 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoAddress.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoAddress.kt @@ -5,7 +5,6 @@ import co.nstant.`in`.cbor.CborDecoder import co.nstant.`in`.cbor.CborEncoder import co.nstant.`in`.cbor.model.Array import co.nstant.`in`.cbor.model.ByteString -import co.nstant.`in`.cbor.model.DataItem import co.nstant.`in`.cbor.model.UnsignedInteger import com.tangem.blockchain.cardano.crypto.Blake2b import com.tangem.blockchain.common.extensions.decodeBase58 diff --git a/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoTransactionBuilder.kt b/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoTransactionBuilder.kt index cbf54fc93d..a251e0068c 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoTransactionBuilder.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoTransactionBuilder.kt @@ -9,6 +9,7 @@ import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.extensions.decodeBase58 import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream +import java.math.BigDecimal class CardanoTransactionBuilder() { var unspentOutputs: List = listOf() @@ -143,6 +144,21 @@ class CardanoTransactionBuilder() { return fullAmount - (amount + fee) } + fun getEstimateSize(transactionData: TransactionData, walletPublicKey: ByteArray): Int { + val dummyFeeValue = BigDecimal.valueOf(0.1) + + val dummyFee = transactionData.amount.copy(value = dummyFeeValue) + val dummyAmount = + transactionData.amount.copy(value = transactionData.amount.value!! - dummyFeeValue) + + val dummyTransactionData = transactionData.copy( + amount = dummyAmount, + fee = dummyFee + ) + buildToSign(dummyTransactionData) + return buildToSend(ByteArray(64), walletPublicKey).size + } + companion object { private const val PROTOCOL_MAGIC: Long = 764824073 } diff --git a/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoWalletManager.kt index 421eb5d996..e5c7b71a70 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoWalletManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoWalletManager.kt @@ -17,7 +17,6 @@ class CardanoWalletManager( private val walletPublicKey: ByteArray, walletConfig: WalletConfig ) : WalletManager, - TransactionEstimator, TransactionSender, FeeProvider { @@ -47,21 +46,6 @@ class CardanoWalletManager( Log.e(this::class.java.simpleName, error?.message ?: "") } - override suspend fun getEstimateSize(transactionData: TransactionData): Int { - val dummyFeeValue = BigDecimal.valueOf(0.1) - - val dummyFee = transactionData.amount.copy(value = dummyFeeValue) - val dummyAmount = - transactionData.amount.copy(value = transactionData.amount.value!! - dummyFeeValue) - - val dummyTransactionData = transactionData.copy( - amount = dummyAmount, - fee = dummyFee - ) - transactionBuilder.buildToSign(dummyTransactionData) - return transactionBuilder.buildToSend(ByteArray(64), walletPublicKey).size - } - override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult { val transactionHash = transactionBuilder.buildToSign(transactionData) @@ -77,8 +61,9 @@ class CardanoWalletManager( override suspend fun getFee(amount: Amount, source: String, destination: String): Result> { val a = 0.155381 val b = 0.000043946 - val size = getEstimateSize(TransactionData(amount, null, source, destination)) - + val size = transactionBuilder.getEstimateSize( + TransactionData(amount, null, source, destination), walletPublicKey + ) val fee = (a + b * size).toBigDecimal() return Result.Success(listOf(Amount(blockchain.currency, fee, source, blockchain.decimals))) } diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManager.kt index 0b55b6fba3..967385e85b 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManager.kt @@ -13,10 +13,6 @@ interface WalletManager { suspend fun update() } -interface TransactionEstimator { - suspend fun getEstimateSize(transactionData: TransactionData): Result -} - interface TransactionSender { suspend fun send(transactionData: TransactionData, signer: TransactionSigner) : SimpleResult } diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt index 9705ab191d..166cb9f9b3 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt @@ -14,35 +14,40 @@ object WalletManagerFactory { val blockchainName: String = card.cardData?.blockchainName ?: return null when { - blockchainName.contains("btc") || blockchainName.contains("bitcoin") -> { + blockchainName == Blockchain.Bitcoin.id -> { + return BitcoinWalletManager( + cardId = card.cardId, + walletPublicKey = walletPublicKey, + walletConfig = WalletConfig(true, true) + ) + } + blockchainName == Blockchain.BitcoinTestnet.id -> { return BitcoinWalletManager( cardId = card.cardId, walletPublicKey = walletPublicKey, walletConfig = WalletConfig(true, true), - isTestNet = isTestNet(blockchainName)) + isTestNet = true + ) } - blockchainName.contains("eth") -> { - val chain = if (isTestNet(blockchainName)) { - Chain.EthereumClassicTestnet - } else { - Chain.Mainnet - } + blockchainName == Blockchain.Ethereum.id -> { + val chain = Chain.Mainnet return EthereumWalletManager( cardId = card.cardId, walletPublicKey = walletPublicKey, walletConfig = WalletConfig(true, true), - chain = chain) + chain = chain + ) } - blockchainName.contains("xlm") -> { + blockchainName == Blockchain.Stellar.id -> { val token = getToken(card) return StellarWalletManager( cardId = card.cardId, walletPublicKey = walletPublicKey, walletConfig = WalletConfig(true, token == null), - token = token, - isTestNet = isTestNet(blockchainName)) + token = token + ) } - blockchainName.contains("cardano") -> { + blockchainName == Blockchain.Cardano.id -> { return CardanoWalletManager( cardId = card.cardId, walletPublicKey = walletPublicKey, @@ -53,8 +58,6 @@ object WalletManagerFactory { } } - private fun isTestNet(blockchainName: String) = blockchainName.contains("test") - private fun getToken(card: Card): Token? { val symbol = card.cardData?.tokenSymbol ?: return null val contractAddress = card.cardData?.tokenContractAddress ?: return null diff --git a/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt b/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt new file mode 100644 index 0000000000..bf5109f133 --- /dev/null +++ b/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt @@ -0,0 +1,59 @@ +package com.tangem.blockchain.common + +import com.google.common.truth.Truth +import com.tangem.CardEnvironment +import com.tangem.blockchain.bitcoin.BitcoinWalletManager +import com.tangem.blockchain.cardano.CardanoWalletManager +import com.tangem.blockchain.ethereum.EthereumWalletManager +import com.tangem.blockchain.stellar.StellarWalletManager +import com.tangem.commands.ReadCommand +import com.tangem.common.apdu.ResponseApdu +import com.tangem.common.extensions.hexToBytes +import org.junit.Test + +internal class WalletManagerFactoryTest { + + @Test + fun createBitcoinWalletManager() { + val data = "0108bb00000000000304200754414e47454d00020102800a322e3432642053444b000341040876bdec26b89bd2159a668b9af3d9fe86370f318717c92b8d6c1186fb3648c32a5f9321998cc2d042901c91d40601e79a641e1cbcebe7a2358be6054e1b6e5d0a04041e76310c618102ffff8a0101820407e30b0d830b54414e47454d2053444b0084034254438640e17ceec48c5be36240c98019f95ad8b6e56acfebe60d11979c6279f715d607d76a860a137da8d109e805753f3f56b0130709f4bbf4cb9974b4c57b8469bf4b873041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd26050a736563703235366b310008040000006407010009020bb8604104752a727e14bba5bd73b6714d72500f61ffd11026ad1196d2e1c54577cbeeac3d11fc68a64700f8d533f4e311964ea8fb3aa26c588295f2133868d69c3e62869362040000005c6304000000090f01009000" + val responseApdu = ResponseApdu(data.hexToBytes()) + val card = ReadCommand().deserialize(CardEnvironment(), responseApdu) + val walletManager = WalletManagerFactory.makeWalletManager(card!!) + + Truth.assertThat(walletManager) + .isInstanceOf(BitcoinWalletManager::class.java) + } + + @Test + fun createEthereumWalletManager() { + val data = "0108bb00000000000536200754414e47454d00020102800a322e3432642053444b000341046c8aea0d5a850b0a608acf9a0c453c39ea86131e88bfa78800de3cfb5bf1007aeaa7b9ffc184212255758605c2461be343c0a661d73cabafa4c9c175b3f0e59a0a04041e76310c618102ffff8a0101820407e30b0d830b54414e47454d2053444b0084034554488640431b6244acfeac479becdff201a7f720a7d70a97edc4e019fb678596baf52dfe9d0e8faf08ceb4443b82d4e66815541f2dc8ec6dd3ff83eb42f06e5eab07f25f3041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd26050a736563703235366b3100080400000064070100090205dc60410464dddc3f356744aaecfa07427f9eb996ff537d65f20fb5be3abccf0354352a6b5f8a1942e0f8ddeea3a170eda78d060be8162ad60e94e4e91fbbdf0a7054785562040000005b6304000000090f01009000" + val responseApdu = ResponseApdu(data.hexToBytes()) + val card = ReadCommand().deserialize(CardEnvironment(), responseApdu) + val walletManager = WalletManagerFactory.makeWalletManager(card!!) + + Truth.assertThat(walletManager) + .isInstanceOf(EthereumWalletManager::class.java) + } + + @Test + fun createStellarWalletManager() { + val data = "0108bb00000000000379200754414e47454d00020102800a322e3432642053444b0003410487d7bb51b189213e3cedc3fcfa3fc047b3b71b7805b5b215e14639b3a8ebb1952c9dd5ea4354441b6ada4e8b8327674bb102ddae69df55be69643a2c916edf650a04041e76310c618102ffff8a0101820407e30b0d830b54414e47454d2053444b008403584c4d86409a4bc2baf0e5836887da21167cf33458d5249d1a610bced0e31dc053f23729ed24d715912bf89e6804669430dfe396ed83274e0031f6803e2bdb8c041fa993413041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd2605086564323535313900080400000064070100090205dc6020e078212d58b2b9d0edc9c936830d10081cd38b90c31778c56dfb1171027e294e62040000003863040000002c0f01009000" + val responseApdu = ResponseApdu(data.hexToBytes()) + val card = ReadCommand().deserialize(CardEnvironment(), responseApdu) + val walletManager = WalletManagerFactory.makeWalletManager(card!!) + + Truth.assertThat(walletManager) + .isInstanceOf(StellarWalletManager::class.java) + } + + @Test + fun createCardanoWalletManager() { + val data = "0108bb00000000000502200754414e47454d00020102800a322e3432642053444b0003410402c1e39257d60583489da2d67d35d1cc2a1c005cc05c1021f44838edcaf25d5615cad7c9d11c2e23f5efa93e50904d33c88808d0e169060508df840992e31f4d0a04041e76310c658102ffff8a0101820407e30b0d830b54414e47454d2053444b00840743415244414e4f8640f24ef5c8c6eba0ff97560d5b013edb4a452594270db9647bd0a3543df8104dec75731d4db3ebe0fc493f2afee00195e560b51e3c41189b7c61ba7895d6434b9d3041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd2605086564323535313900080400000064070100090205dc60208a71161cfdf1e0a85d8e7ff372aa4a01136046292aceb5f9ad7ebdb98d3f60a86204000000646304000000000f01009000" + val responseApdu = ResponseApdu(data.hexToBytes()) + val card = ReadCommand().deserialize(CardEnvironment(), responseApdu) + val walletManager = WalletManagerFactory.makeWalletManager(card!!) + + Truth.assertThat(walletManager) + .isInstanceOf(CardanoWalletManager::class.java) + } +} \ No newline at end of file