From 7168e62dfe617a187c8c6c7bf793ad29814fa9fb Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 10 Dec 2019 10:50:33 +0300 Subject: [PATCH 01/50] Updated on 2026-08-14 --- blockchain/.gitignore | 1 + blockchain/build.gradle | 47 ++++++++++++++ blockchain/proguard-rules.pro | 21 ++++++ blockchain/src/main/AndroidManifest.xml | 1 + .../tangem/blockchain/common/Blockchain.kt | 64 +++++++++++++++++++ .../com/tangem/blockchain/common/Wallet.kt | 39 +++++++++++ .../tangem/blockchain/common/WalletManager.kt | 22 +++++++ .../blockchain/wallets/CurrencyWallet.kt | 20 ++++++ blockchain/src/main/res/values/strings.xml | 3 + 9 files changed, 218 insertions(+) create mode 100644 blockchain/.gitignore create mode 100644 blockchain/build.gradle create mode 100644 blockchain/proguard-rules.pro create mode 100644 blockchain/src/main/AndroidManifest.xml create mode 100644 blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt create mode 100644 blockchain/src/main/java/com/tangem/blockchain/common/Wallet.kt create mode 100644 blockchain/src/main/java/com/tangem/blockchain/common/WalletManager.kt create mode 100644 blockchain/src/main/java/com/tangem/blockchain/wallets/CurrencyWallet.kt create mode 100644 blockchain/src/main/res/values/strings.xml diff --git a/blockchain/.gitignore b/blockchain/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/blockchain/.gitignore @@ -0,0 +1 @@ +/build diff --git a/blockchain/build.gradle b/blockchain/build.gradle new file mode 100644 index 0000000000..5864305e8f --- /dev/null +++ b/blockchain/build.gradle @@ -0,0 +1,47 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +android { + compileSdkVersion 29 + buildToolsVersion "29.0.0" + + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation project(':tangem-core') + implementation project(':tangem-sdk') + + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.core:core-ktx:1.1.0' + + implementation 'com.squareup.retrofit2:retrofit:2.6.0' + implementation 'com.squareup.retrofit2:converter-moshi:2.6.0' + implementation 'com.squareup.moshi:moshi:1.9.2' + + implementation 'org.bitcoinj:bitcoinj-core:0.15.2' + + + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' +} diff --git a/blockchain/proguard-rules.pro b/blockchain/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/blockchain/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/blockchain/src/main/AndroidManifest.xml b/blockchain/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..83f5f31450 --- /dev/null +++ b/blockchain/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt b/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt new file mode 100644 index 0000000000..6d9033458c --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt @@ -0,0 +1,64 @@ +package com.tangem.blockchain.common + +import com.tangem.blockchain.bitcoin.BitcoinAddressFactory +import com.tangem.blockchain.bitcoin.BitcoinAddressValidator +import java.math.BigDecimal + +enum class Blockchain( + val id: String, + val currency: String, + val decimals: Byte, + val fullName: String, + val pendingTransactionTimeout: Int) { + Unknown("", "", 0, "", 0), + Bitcoin("", "", 8, "", 0), + BitcoinTestnet("", "", 8, "", 0), + Ethereum("", "", 18, "", 0), + Rootstock("", "", 18, "", 0), + Cardano("", "", 6, "", 0), + Ripple("", "", 6, "", 0), + Binance("", "", 8, "", 0), + Stellar("", "", 7, "", 0); + + fun roundingMode(): Int = when (this) { + Bitcoin, Ethereum, Rootstock, Binance -> BigDecimal.ROUND_DOWN + Cardano -> BigDecimal.ROUND_UP + else -> BigDecimal.ROUND_HALF_UP + } + + fun makeAddress(cardPublicKey: ByteArray): String { + return when (this) { + Unknown -> throw Exception("unsupported blockchain") + Bitcoin -> BitcoinAddressFactory.makeAddress(cardPublicKey) + BitcoinTestnet -> BitcoinAddressFactory.makeAddress(cardPublicKey, testNet = true) +// Ethereum -> EthereumAddressFactory.makeAddress(cardPublicKey) +// Rootstock -> RootstockAddressFactory.makeAddress(cardPublicKey) +// Cardano -> CardanoAddressFactory.makeAddress(cardPublicKey) +// Ripple -> RippleAddressFactory.makeAddress(cardPublicKey) +// Binance -> BinanceAddressFactory.makeAddress(cardPublicKey) +// Stellar -> StellarAddressFactory.makeAddress(cardPublicKey) + } + } + + fun validateAddress(address: String): Boolean { + return when (this) { + Unknown -> throw Exception("unsupported blockchain") + Bitcoin -> BitcoinAddressValidator.validate(address) + BitcoinTestnet -> BitcoinAddressValidator.validate(address, testNet = true) +// Ethereum -> EthereumAddressValidator.validate(address) +// Rootstock -> RootstockAddressValidator.validate(address) +// Cardano -> CardanoAddressValidator.validate(address) +// Ripple -> RippleAddressValidator.validate(address) +// Binance -> BinanceAddressValidator.validate(address) +// Stellar -> StellarAddressValidator.validate(address) + } + } + + companion object { + private val values = values() + fun fromId(id: String): Blockchain = values.find { it.id == id } ?: Unknown + fun fromName(name: String): Blockchain = values.find { it.name == name } ?: Unknown + fun fromCurrency(currency: String): Blockchain = values.find { it.currency == currency } + ?: Unknown + } +} diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/Wallet.kt b/blockchain/src/main/java/com/tangem/blockchain/common/Wallet.kt new file mode 100644 index 0000000000..73457822cc --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/common/Wallet.kt @@ -0,0 +1,39 @@ +package com.tangem.blockchain.common + +import java.math.BigDecimal +import java.util.* + +interface Wallet { + val config: WalletConfig + val address: String + val exploreUrl: String? + val shareUrl: String? +} + +class WalletConfig( + val allowFeeSelection: Boolean, + val allowFeeInclusion: Boolean, + var allowExtract: Boolean = false, + var allowLoad: Boolean = false +) + +data class Amount( + val type: AmountType = AmountType.Coin, + val currencySymbol: String, + val value: BigDecimal +) + +data class Transaction( + val amount: Amount, + val fee: Amount?, + val sourceAddress: String, + val destinationAddress: String +) + +enum class AmountType { Coin, Token, Reserve } + +enum class ValidationError { WrongAmount, WrongFee, WrongTotal } + +interface TransactionValidator{ + fun validateTransaction(amount: Amount, fee: Amount?): EnumSet +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManager.kt new file mode 100644 index 0000000000..818a863eb3 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManager.kt @@ -0,0 +1,22 @@ +package com.tangem.blockchain.common + +interface WalletManager { + var wallet: Wallet + val blockchain: Blockchain + + fun update() +} + +interface TransactionBuilder { + fun getEstimateSize(transaction: Transaction): Int +} + +interface TransactionSender { + fun send(transaction: Transaction, signer: TransactionSigner) +} + +interface TransactionSigner + +interface FeeProvider { + fun getFee(amount: Amount, source: String, destination: String): List +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/wallets/CurrencyWallet.kt b/blockchain/src/main/java/com/tangem/blockchain/wallets/CurrencyWallet.kt new file mode 100644 index 0000000000..a8b56cc042 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/wallets/CurrencyWallet.kt @@ -0,0 +1,20 @@ +package com.tangem.blockchain.wallets + +import com.tangem.blockchain.common.* +import java.util.* + +class CurrencyWallet( + override val config: WalletConfig, + override val address: String, + override val exploreUrl: String?, + override val shareUrl: String?, + val pendingTransactions: List = listOf(), + val balances: List = listOf(), + val isTestnet: Boolean = false +) : Wallet, TransactionValidator { + + + override fun validateTransaction(amount: Amount, fee: Amount?): EnumSet { + TODO("not implemented") + } +} \ No newline at end of file diff --git a/blockchain/src/main/res/values/strings.xml b/blockchain/src/main/res/values/strings.xml new file mode 100644 index 0000000000..4b289d3aaa --- /dev/null +++ b/blockchain/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Blockchain + From 792ca79ebeb8f02f6b1a73f5f96d3cf6724e3973 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 16 Dec 2019 14:40:19 +0300 Subject: [PATCH 02/50] Updated on 2026-08-14 --- blockchain/build.gradle | 7 ++- .../tangem/blockchain/common/Blockchain.kt | 28 ++++++---- .../com/tangem/blockchain/common/Wallet.kt | 12 ++-- .../tangem/blockchain/common/WalletManager.kt | 14 +++-- .../blockchain/common/WalletManagerFactory.kt | 54 ++++++++++++++++++ .../tangem/blockchain/eth/EthereumAddress.kt | 15 +++++ .../blockchain/eth/EthereumWalletManager.kt | 55 +++++++++++++++++++ .../tangem/blockchain/extensions/ByteArray.kt | 11 ++++ .../blockchain/wallets/CurrencyWallet.kt | 8 +-- 9 files changed, 176 insertions(+), 28 deletions(-) create mode 100644 blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt create mode 100644 blockchain/src/main/java/com/tangem/blockchain/eth/EthereumAddress.kt create mode 100644 blockchain/src/main/java/com/tangem/blockchain/eth/EthereumWalletManager.kt create mode 100644 blockchain/src/main/java/com/tangem/blockchain/extensions/ByteArray.kt diff --git a/blockchain/build.gradle b/blockchain/build.gradle index 5864305e8f..2bd12af9e7 100644 --- a/blockchain/build.gradle +++ b/blockchain/build.gradle @@ -26,7 +26,7 @@ android { } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) +// implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':tangem-core') implementation project(':tangem-sdk') @@ -34,12 +34,13 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.core:core-ktx:1.1.0' - implementation 'com.squareup.retrofit2:retrofit:2.6.0' + implementation 'com.squareup.retrofit2:retrofit:2.6.2' implementation 'com.squareup.retrofit2:converter-moshi:2.6.0' implementation 'com.squareup.moshi:moshi:1.9.2' implementation 'org.bitcoinj:bitcoinj-core:0.15.2' - + implementation 'com.github.komputing:kethereum:0.79.5' + implementation 'com.github.stellar:java-stellar-sdk:0.11.0' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2' androidTestImplementation 'androidx.test.ext:junit:1.1.1' diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt b/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt index 6d9033458c..065f19f1d8 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt @@ -2,6 +2,10 @@ package com.tangem.blockchain.common import com.tangem.blockchain.bitcoin.BitcoinAddressFactory import com.tangem.blockchain.bitcoin.BitcoinAddressValidator +import com.tangem.blockchain.eth.EthereumAddressFactory +import com.tangem.blockchain.eth.EthereumAddressValidator +import com.tangem.blockchain.stellar.StellarAddressFactory +import com.tangem.blockchain.stellar.StellarAddressValidator import java.math.BigDecimal enum class Blockchain( @@ -31,12 +35,12 @@ enum class Blockchain( Unknown -> throw Exception("unsupported blockchain") Bitcoin -> BitcoinAddressFactory.makeAddress(cardPublicKey) BitcoinTestnet -> BitcoinAddressFactory.makeAddress(cardPublicKey, testNet = true) -// Ethereum -> EthereumAddressFactory.makeAddress(cardPublicKey) -// Rootstock -> RootstockAddressFactory.makeAddress(cardPublicKey) -// Cardano -> CardanoAddressFactory.makeAddress(cardPublicKey) -// Ripple -> RippleAddressFactory.makeAddress(cardPublicKey) -// Binance -> BinanceAddressFactory.makeAddress(cardPublicKey) -// Stellar -> StellarAddressFactory.makeAddress(cardPublicKey) + Ethereum -> EthereumAddressFactory.makeAddress(cardPublicKey) + Rootstock -> RootstockAddressFactory.makeAddress(cardPublicKey) + Cardano -> CardanoAddressFactory.makeAddress(cardPublicKey) + Ripple -> RippleAddressFactory.makeAddress(cardPublicKey) + Binance -> BinanceAddressFactory.makeAddress(cardPublicKey) + Stellar -> StellarAddressFactory.makeAddress(cardPublicKey) } } @@ -45,12 +49,12 @@ enum class Blockchain( Unknown -> throw Exception("unsupported blockchain") Bitcoin -> BitcoinAddressValidator.validate(address) BitcoinTestnet -> BitcoinAddressValidator.validate(address, testNet = true) -// Ethereum -> EthereumAddressValidator.validate(address) -// Rootstock -> RootstockAddressValidator.validate(address) -// Cardano -> CardanoAddressValidator.validate(address) -// Ripple -> RippleAddressValidator.validate(address) -// Binance -> BinanceAddressValidator.validate(address) -// Stellar -> StellarAddressValidator.validate(address) + Ethereum -> EthereumAddressValidator.validate(address) + Rootstock -> RootstockAddressValidator.validate(address) + Cardano -> CardanoAddressValidator.validate(address) + Ripple -> RippleAddressValidator.validate(address) + Binance -> BinanceAddressValidator.validate(address) + Stellar -> StellarAddressValidator.validate(address) } } diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/Wallet.kt b/blockchain/src/main/java/com/tangem/blockchain/common/Wallet.kt index 73457822cc..ef9ab03b94 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/Wallet.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/Wallet.kt @@ -18,12 +18,14 @@ class WalletConfig( ) data class Amount( - val type: AmountType = AmountType.Coin, val currencySymbol: String, - val value: BigDecimal -) + val value: BigDecimal?, + val address: String, + val decimals: Int, + val type: AmountType = AmountType.Coin + ) -data class Transaction( +data class TransactionData( val amount: Amount, val fee: Amount?, val sourceAddress: String, @@ -34,6 +36,6 @@ enum class AmountType { Coin, Token, Reserve } enum class ValidationError { WrongAmount, WrongFee, WrongTotal } -interface TransactionValidator{ +interface TransactionValidator { fun validateTransaction(amount: Amount, fee: Amount?): EnumSet } \ No newline at end of file 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 818a863eb3..610319380f 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManager.kt @@ -1,5 +1,8 @@ package com.tangem.blockchain.common +import com.tangem.commands.SignResponse +import com.tangem.tasks.TaskEvent + interface WalletManager { var wallet: Wallet val blockchain: Blockchain @@ -7,15 +10,18 @@ interface WalletManager { fun update() } -interface TransactionBuilder { - fun getEstimateSize(transaction: Transaction): Int +interface TransactionEstimator { + fun getEstimateSize(transactionData: TransactionData): Int } interface TransactionSender { - fun send(transaction: Transaction, signer: TransactionSigner) + fun send(transactionData: TransactionData, signer: TransactionSigner) } -interface TransactionSigner +interface TransactionSigner { + fun sign(hashes: Array, cardId: String, + callback: (result: TaskEvent) -> Unit) +} interface FeeProvider { fun getFee(amount: Amount, source: String, destination: String): List diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt new file mode 100644 index 0000000000..33e0a0edb0 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt @@ -0,0 +1,54 @@ +package com.tangem.blockchain.common + +import com.tangem.blockchain.bitcoin.BitcoinWalletManager +import com.tangem.blockchain.eth.EthereumWalletManager +import com.tangem.blockchain.stellar.StellarWalletManager +import com.tangem.commands.Card + +object WalletManagerFactory { + + fun makeWalletManager(card: Card): WalletManager? { + val walletPublicKey = card.walletPublicKey ?: return null + val blockchainName = card.cardData?.blockchainName ?: return null + + when { + blockchainName.contains("btc") || blockchainName.contains("bitcoin") -> { + return BitcoinWalletManager( + cardId = card.cardId, + walletPublicKey = walletPublicKey, + walletConfig = WalletConfig(true, true), + isTestNet = blockchainName.contains("test")) + } + blockchainName.contains("eth") -> { + return EthereumWalletManager( + cardId = card.cardId, + walletPublicKey = walletPublicKey, + walletConfig = WalletConfig(true, true)) + } + blockchainName.contains("xlm") -> { + val token = getToken(card) + return StellarWalletManager( + cardId = card.cardId, + walletPublicKey = walletPublicKey, + walletConfig = WalletConfig(true, token == null), + token = token, + isTestNet = blockchainName.contains("test")) + } + else -> return null + } + } + + private fun getToken(card: Card): Token? { + val symbol = card.cardData?.tokenSymbol ?: return null + val contractAddress = card.cardData?.tokenContractAddress ?: return null + val decimals = card.cardData?.tokenDecimal ?: return null + return Token(symbol, contractAddress, decimals) + } +} + + +data class Token( + val symbol: String, + val contractAddress: String, + val decimals: Int +) \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/eth/EthereumAddress.kt b/blockchain/src/main/java/com/tangem/blockchain/eth/EthereumAddress.kt new file mode 100644 index 0000000000..f1d93046a1 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/eth/EthereumAddress.kt @@ -0,0 +1,15 @@ +package com.tangem.blockchain.eth + +import org.kethereum.crypto.toAddress +import org.kethereum.functions.isValid +import org.kethereum.model.Address +import org.kethereum.model.PublicKey + +object EthereumAddressFactory { + fun makeAddress(cardPublicKey: ByteArray, testNet: Boolean = false): String = + PublicKey(cardPublicKey).toAddress().hex +} + +object EthereumAddressValidator { + fun validate(address: String, testNet: Boolean = false): Boolean = Address(address).isValid() +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/eth/EthereumWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/eth/EthereumWalletManager.kt new file mode 100644 index 0000000000..a84dc8e57b --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/eth/EthereumWalletManager.kt @@ -0,0 +1,55 @@ +package com.tangem.blockchain.eth + +import com.tangem.blockchain.common.* +import com.tangem.blockchain.wallets.CurrencyWallet +import com.tangem.tasks.TaskEvent + +class EthereumWalletManager( + private val cardId: String, + private val walletPublicKey: ByteArray, + walletConfig: WalletConfig +) : WalletManager, + TransactionEstimator, + TransactionSender, + FeeProvider { + + override val blockchain: Blockchain = Blockchain.Ethereum + private val address = blockchain.makeAddress(walletPublicKey) + override var wallet: Wallet = CurrencyWallet(walletConfig, address) + + override fun update() { + + } + + override fun getEstimateSize(transactionData: TransactionData): Int { + + } + + override fun send(transactionData: TransactionData, signer: TransactionSigner) { + val builder = EthereumTransactionBuilder() + val hashes = builder.buildToSign(transactionData, walletPublicKey) + signer.sign(hashes.toTypedArray(), cardId) { + when (it) { + is TaskEvent.Event -> builder.buildToSend(transactionData, it.data.signature) + } + } + } + + override fun getFee(amount: Amount, source: String, destination: String): List { + + } + +} + + +class EthereumTransactionBuilder { + + + fun buildToSign(transactionData: TransactionData, publicKey: ByteArray): List { + + } + + fun buildToSend(transactionData: TransactionData, signature: ByteArray) { + + } +} diff --git a/blockchain/src/main/java/com/tangem/blockchain/extensions/ByteArray.kt b/blockchain/src/main/java/com/tangem/blockchain/extensions/ByteArray.kt new file mode 100644 index 0000000000..2619ca0184 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/extensions/ByteArray.kt @@ -0,0 +1,11 @@ +package com.tangem.blockchain.extensions + +import org.spongycastle.crypto.digests.RIPEMD160Digest + +fun ByteArray.calculateRipemd160(): ByteArray { + val digest = RIPEMD160Digest() + digest.update(this, 0, this.size) + val out = ByteArray(20) + digest.doFinal(out, 0) + return out +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/wallets/CurrencyWallet.kt b/blockchain/src/main/java/com/tangem/blockchain/wallets/CurrencyWallet.kt index a8b56cc042..a1e2bb544a 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/wallets/CurrencyWallet.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/wallets/CurrencyWallet.kt @@ -6,10 +6,10 @@ import java.util.* class CurrencyWallet( override val config: WalletConfig, override val address: String, - override val exploreUrl: String?, - override val shareUrl: String?, - val pendingTransactions: List = listOf(), - val balances: List = listOf(), + override val exploreUrl: String? = null, + override val shareUrl: String? = null, + val pendingTransactions: List = listOf(), + val balances: MutableList = mutableListOf(), val isTestnet: Boolean = false ) : Wallet, TransactionValidator { From 392547c7b6b53e72beeec419c6fbe12639c863bf Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 16 Dec 2019 15:06:13 +0300 Subject: [PATCH 03/50] Updated on 2026-08-14 --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index e060f73444..6f8af10c4a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':tangem-sdk-old', ':server-android', ':tangem-card-old', ':tangem-core', ':tangem-sdk', ':tangem-demo' +include ':app', ':tangem-sdk-old', ':server-android', ':tangem-card-old', ':tangem-core', ':tangem-sdk', ':tangem-demo', ':blockchain' From 7326783102c593199144c4b095f93af6e7664790 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 23 Dec 2019 10:33:43 +0300 Subject: [PATCH 04/50] Updated on 2026-08-14 --- .../blockchain/bitcoin/BitcoinAddress.kt | 71 ++++++++++++++++ .../bitcoin/BitcoinTransactionBuilder.kt | 85 +++++++++++++++++++ .../bitcoin/BitcoinWalletManager.kt | 53 ++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinAddress.kt create mode 100644 blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinTransactionBuilder.kt create mode 100644 blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinWalletManager.kt diff --git a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinAddress.kt b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinAddress.kt new file mode 100644 index 0000000000..54fabba6b5 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinAddress.kt @@ -0,0 +1,71 @@ +package com.tangem.blockchain.bitcoin + +import com.tangem.blockchain.extensions.calculateRipemd160 +import com.tangem.common.extensions.calculateSha256 +import org.bitcoinj.core.AddressFormatException +import org.bitcoinj.core.Base58 +import org.bitcoinj.core.SegwitAddress +import org.bitcoinj.params.MainNetParams +import org.bitcoinj.params.TestNet3Params +import java.security.MessageDigest + +class BitcoinAddressFactory { + companion object { + fun makeAddress(cardPublicKey: ByteArray, testNet: Boolean = false): String { + val netSelectionByte = if (testNet) 0x6f.toByte() else 0x00.toByte() + val hash1 = cardPublicKey.calculateSha256().calculateRipemd160() + val hash2 = byteArrayOf(netSelectionByte).plus(hash1).calculateSha256().calculateSha256() + val result = byteArrayOf(netSelectionByte) + hash1 + hash2[0] + hash2[1] + hash2[2] + hash2[3] + return Base58.encode(result) + } + } +} + +class BitcoinAddressValidator { + companion object { + + private const val firstLetters = "123nm" + private const val firstLettersNonTestNet = "13" + + fun validate(address: String, testNet: Boolean = false): Boolean { + if (firstLetters.contains(address.first())) { + 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) + 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 { + 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) + } + + private fun String.decodeBase58(): ByteArray? { + return try { + Base58.decode(this) + } catch (exception: AddressFormatException) { + null + } + } + + private fun validateSegwitAddress(address: String, testNet: Boolean): Boolean { + return try { + if (testNet) { + SegwitAddress.fromBech32(TestNet3Params(), address) + true + } else { + SegwitAddress.fromBech32(MainNetParams(), address) + true + } + } catch (e: Exception) { + false + } + } + } +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinTransactionBuilder.kt b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinTransactionBuilder.kt new file mode 100644 index 0000000000..1d99ce8db0 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinTransactionBuilder.kt @@ -0,0 +1,85 @@ +package com.tangem.blockchain.bitcoin + +import com.tangem.blockchain.common.TransactionData +import com.tangem.blockchain.extensions.toCanonicalised +import org.bitcoinj.core.* +import org.bitcoinj.crypto.TransactionSignature +import org.bitcoinj.script.Script +import org.bitcoinj.script.ScriptBuilder +import java.io.ByteArrayOutputStream +import java.math.BigInteger + +class BitcoinTransactionBuilder(private val testNet: Boolean) { + + private lateinit var transaction: Transaction + private var networkParameters: NetworkParameters? = null + var unspentOutputs: List = listOf() + + fun calculateChange(transactionData: TransactionData) : Long { + val fullAmount = unspentOutputs.map { it.amount }.sum() + return fullAmount - (transactionData.amount.value!!.toLong() + (transactionData.fee?.value!!.toLong())) + } + + fun buildToSign( + transactionData: TransactionData): List { + + val change: Long = calculateChange(transactionData) + + networkParameters = if (testNet) { + NetworkParameters.fromID(NetworkParameters.ID_TESTNET) + } else { + NetworkParameters.fromID(NetworkParameters.ID_MAINNET) + } + transaction = transactionData.toBitcoinJTransaction(networkParameters, unspentOutputs, change) + + val hashesForSign: MutableList = mutableListOf() + for (input in transaction.inputs) { + val index = input.index + hashesForSign[index] = transaction.hashForSignature(index, input.scriptBytes, Transaction.SigHash.ALL, false).bytes + } + return hashesForSign + } + + fun buildToSend(signedTransaction: ByteArray, publicKey: ByteArray): ByteArray { + for (index in transaction.inputs.indices) { + transaction.inputs[index].scriptSig = createScript(index, signedTransaction, publicKey) + } + val serializer = BitcoinSerializer(networkParameters, false) + val outputStream = ByteArrayOutputStream() + serializer.serialize(transaction, outputStream) + return outputStream.toByteArray() + } + + private fun createScript(index: Int, signedTransaction: ByteArray, publicKey: ByteArray): Script { + val r = BigInteger(1, signedTransaction.copyOfRange(index * 64, 32 + index * 64)); + val s = BigInteger(1, signedTransaction.copyOfRange(32 + index * 64, 64 + index * 64)); + val signature = TransactionSignature(r, s.toCanonicalised()) + return ScriptBuilder.createInputScript(signature, ECKey.fromPublicOnly(publicKey)) + } +} + +internal fun TransactionData.toBitcoinJTransaction(networkParameters: NetworkParameters?, + unspentOutputs: List, + change: Long): Transaction { + val transaction = Transaction(networkParameters) + for (utxo in unspentOutputs) { + transaction.addInput(Sha256Hash.wrap(utxo.hash), utxo.outputIndex, Script(utxo.outputScript)) + } + transaction.addOutput( + Coin.parseCoin(this.amount.value!!.toPlainString()), + Address.fromString(networkParameters, this.destinationAddress)) + if (change != 0L) { + transaction.addOutput( + Coin.parseCoin(change.toString()), + Address.fromString(networkParameters, + this.sourceAddress)) + } + return transaction +} + +class UnspentTransaction( + val amount: Long, + val outputIndex: Long, + val hash: ByteArray, + val outputScript: ByteArray +) \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinWalletManager.kt new file mode 100644 index 0000000000..d7da71a6fc --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinWalletManager.kt @@ -0,0 +1,53 @@ +package com.tangem.blockchain.bitcoin + +import com.tangem.blockchain.common.* +import com.tangem.blockchain.wallets.CurrencyWallet +import com.tangem.tasks.TaskEvent +import org.bitcoinj.core.NetworkParameters +import org.bitcoinj.core.Transaction + +class BitcoinWalletManager( + private val cardId: String, + private val walletPublicKey: ByteArray, + walletConfig: WalletConfig, + isTestNet: Boolean = false +) : WalletManager, + TransactionEstimator, + TransactionSender, + FeeProvider { + + override val blockchain = if (isTestNet) Blockchain.BitcoinTestnet else Blockchain.Bitcoin + private val address = blockchain.makeAddress(walletPublicKey) + override var wallet: Wallet = CurrencyWallet(walletConfig, address) + private val transactionBuilder = BitcoinTransactionBuilder(isTestNet) + + + override fun update() { + transactionBuilder.unspentOutputs = listOf() + } + + + override fun getEstimateSize(transactionData: TransactionData): Int { + val transaction: Transaction = transactionData.toBitcoinJTransaction( + NetworkParameters.fromID(NetworkParameters.ID_MAINNET), + transactionBuilder.unspentOutputs, + transactionBuilder.calculateChange(transactionData) + ) + var size: Int = transaction.unsafeBitcoinSerialize().size + size += transaction.inputs.sumBy { 130 } + return size + } + + override fun send(transactionData: TransactionData, signer: TransactionSigner) { + val hashes = transactionBuilder.buildToSign(transactionData) + signer.sign(hashes.toTypedArray(), cardId) { + when (it) { + is TaskEvent.Event -> transactionBuilder.buildToSend(it.data.signature, walletPublicKey) + } + } + } + + override fun getFee(amount: Amount, source: String, destination: String): List { + return BitcoinServer.getFee() + } +} \ No newline at end of file From 22a43ca524e26c239a160a8b2b8afe34d7e5a96e Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 23 Dec 2019 11:31:45 +0300 Subject: [PATCH 05/50] Updated on 2026-08-14 --- .idea/gradle.xml | 1 + .../com/tangem/blockchain/common/Wallet.kt | 2 +- .../tangem/blockchain/eth/EthereumAddress.kt | 1 + .../blockchain/eth/EthereumWalletManager.kt | 92 +++++++++++++++++-- 4 files changed, 86 insertions(+), 10 deletions(-) diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 1dbc25d9a4..280cc6ed9d 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -11,6 +11,7 @@