From bf1c374582a4dc9fed6321ca045d8a82c8fbdcaf Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Apr 2020 14:53:21 +0300 Subject: [PATCH 01/10] Updated on 2026-08-14 --- app/build.gradle | 3 + .../main/java/com/tangem/data/Blockchain.java | 3 +- .../tangem/data/network/ServerApiTokenEmv.kt | 43 +++ .../com/tangem/data/network/TokenEmvApi.kt | 11 + .../tangem/data/network/model/TokenEmvBody.kt | 15 + .../com/tangem/wallet/CoinEngineFactory.kt | 4 + .../com/tangem/wallet/EthTransaction.java | 2 +- .../com/tangem/wallet/token/TokenData.java | 28 +- .../tangem/wallet/tokenEmv/TokenEmvEngine.kt | 310 ++++++++++++++++++ 9 files changed, 412 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/tangem/data/network/ServerApiTokenEmv.kt create mode 100644 app/src/main/java/com/tangem/data/network/TokenEmvApi.kt create mode 100644 app/src/main/java/com/tangem/data/network/model/TokenEmvBody.kt create mode 100644 app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt diff --git a/app/build.gradle b/app/build.gradle index c30dedd2c2..10bac3b34e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -158,4 +158,7 @@ dependencies { //dependencies for flow demo implementation 'io.grpc:grpc-okhttp:1.28.0' implementation 'io.grpc:grpc-stub:1.28.0' + + //dependencies for TokenEmvEngine + implementation 'com.github.walleth.kethereum:extensions_kotlin:0.81.4' } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/data/Blockchain.java b/app/src/main/java/com/tangem/data/Blockchain.java index c9747e62f8..dee0c71b2c 100644 --- a/app/src/main/java/com/tangem/data/Blockchain.java +++ b/app/src/main/java/com/tangem/data/Blockchain.java @@ -32,7 +32,8 @@ public enum Blockchain { Eos("EOS", "EOS", 10000.0, R.drawable.tangem2, "EOS"), Ducatus("DUC", "DUC", 100000000.0, R.drawable.tangem2, "Ducatus"), Tezos("TEZOS", "XTZ", 10000000.0, R.drawable.ic_logo_tezos, "Tezos"), - FlowDemo("FLOW/demo", "", 1.0, R.drawable.tangem2, "Flow demo"); + FlowDemo("FLOW/demo", "", 1.0, R.drawable.tangem2, "Flow demo"), + TokenEmv("TTW", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"); Blockchain(String ID, String currency, double multiplier, int imageResource, String officialName) { mID = ID; diff --git a/app/src/main/java/com/tangem/data/network/ServerApiTokenEmv.kt b/app/src/main/java/com/tangem/data/network/ServerApiTokenEmv.kt new file mode 100644 index 0000000000..25e3eb03d9 --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/ServerApiTokenEmv.kt @@ -0,0 +1,43 @@ +package com.tangem.data.network + +import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory +import com.tangem.data.network.model.TezosAccountResponse +import com.tangem.data.network.model.TokenEmvTransferBody +import com.tangem.tangem_card.util.Log +import io.reactivex.CompletableObserver +import io.reactivex.Single +import io.reactivex.SingleObserver +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.functions.BiConsumer +import io.reactivex.schedulers.Schedulers +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory + +class ServerApiTokenEmv { + private val TAG = ServerApiTokenEmv::class.java.simpleName + + private val tangemServer = "" + + private val tokenEmvApi = Retrofit.Builder() + .baseUrl(tangemServer) + .addConverterFactory(GsonConverterFactory.create()) + .addConverterFactory(ScalarsConverterFactory.create()) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //logging for testing + .client(OkHttpClient.Builder().addInterceptor( + HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY) + ).build()) + .build() + .create(TokenEmvApi::class.java) + + fun transfer(tokenEmvTransferBody: TokenEmvTransferBody, transferObserver: CompletableObserver) { + Log.i(TAG, "new transfer request") + + tokenEmvApi.transfer(tokenEmvTransferBody) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(transferObserver) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/data/network/TokenEmvApi.kt b/app/src/main/java/com/tangem/data/network/TokenEmvApi.kt new file mode 100644 index 0000000000..f885cad2c4 --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/TokenEmvApi.kt @@ -0,0 +1,11 @@ +package com.tangem.data.network + +import com.tangem.data.network.model.TokenEmvTransferBody +import io.reactivex.Completable +import retrofit2.http.Body +import retrofit2.http.POST + +interface TokenEmvApi { + @POST("./") + fun transfer(@Body tokenEmvTransferBody: TokenEmvTransferBody): Completable +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/data/network/model/TokenEmvBody.kt b/app/src/main/java/com/tangem/data/network/model/TokenEmvBody.kt new file mode 100644 index 0000000000..cbfdcf038e --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/model/TokenEmvBody.kt @@ -0,0 +1,15 @@ +package com.tangem.data.network.model + +import com.google.gson.annotations.SerializedName + +data class TokenEmvTransferBody( + val contract: String, + val amount: String, + val recipient: String, + @SerializedName("fee_limit") + val feeLimit: String, + val sequence: Int, + val r: String, + val s: String, + val v: Int +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/CoinEngineFactory.kt b/app/src/main/java/com/tangem/wallet/CoinEngineFactory.kt index 310f567aea..8bfbb7f361 100644 --- a/app/src/main/java/com/tangem/wallet/CoinEngineFactory.kt +++ b/app/src/main/java/com/tangem/wallet/CoinEngineFactory.kt @@ -20,6 +20,7 @@ import com.tangem.wallet.rsk.RskEngine import com.tangem.wallet.rsk.RskTokenEngine import com.tangem.wallet.token.TokenEngine import com.tangem.wallet.tezos.TezosEngine +import com.tangem.wallet.tokenEmv.TokenEmvEngine import com.tangem.wallet.xlm.XlmAssetEngine import com.tangem.wallet.xlm.XlmEngine import com.tangem.wallet.xlmTag.XlmTagEngine @@ -60,6 +61,7 @@ object CoinEngineFactory { Blockchain.Tezos -> TezosEngine() Blockchain.BitcoinDual -> BtcMultisigEngine() Blockchain.FlowDemo -> FlowDemoEngine() + Blockchain.TokenEmv -> TokenEmvEngine() else -> null } } @@ -109,6 +111,8 @@ object CoinEngineFactory { BtcMultisigEngine(context) else if (Blockchain.FlowDemo == context.blockchain) FlowDemoEngine(context) + else if (Blockchain.TokenEmv == context.blockchain) + TokenEmvEngine(context) else return null } catch (e: Exception) { diff --git a/app/src/main/java/com/tangem/wallet/EthTransaction.java b/app/src/main/java/com/tangem/wallet/EthTransaction.java index c7ba6e98b3..48e86ecf6a 100644 --- a/app/src/main/java/com/tangem/wallet/EthTransaction.java +++ b/app/src/main/java/com/tangem/wallet/EthTransaction.java @@ -110,7 +110,7 @@ public class EthTransaction { return kec.digest(plainMsg); } - public int BruteRecoveryID2(ECDSASignatureETH sig, byte[] messageHash, byte[] thisKey) { + public static int BruteRecoveryID2(ECDSASignatureETH sig, byte[] messageHash, byte[] thisKey) { Log.e("ETH_KZ", BTCUtils.toHex(thisKey)); int recId = -1; for (int i = 0; i < 4; i++) { diff --git a/app/src/main/java/com/tangem/wallet/token/TokenData.java b/app/src/main/java/com/tangem/wallet/token/TokenData.java index ef67680617..7cf2c24255 100644 --- a/app/src/main/java/com/tangem/wallet/token/TokenData.java +++ b/app/src/main/java/com/tangem/wallet/token/TokenData.java @@ -8,30 +8,45 @@ import com.tangem.wallet.eth.EthData; public class TokenData extends EthData { private CoinEngine.InternalAmount balanceAlter = null; - + private Integer sequence = null; @Override public void clearInfo() { super.clearInfo(); balanceAlter = null; + sequence = null; } public CoinEngine.InternalAmount getBalanceAlterInInternalUnits() { return balanceAlter; } + public void setBalanceAlterInInternalUnits(CoinEngine.InternalAmount value) { balanceAlter = value; } + public Integer getSequence() { + return sequence; + } + + public void setSequence(Integer sequence) { + this.sequence = sequence; + } + @Override public void loadFromBundle(Bundle B) { super.loadFromBundle(B); - if( B.containsKey("BalanceDecimalAlter" )) { + if (B.containsKey("BalanceDecimalAlter")) { balanceAlter = new CoinEngine.InternalAmount(B.getString("BalanceDecimalAlter"), "wei"); - }else{ - balanceAlter=null; + } else { + balanceAlter = null; + } + if (B.containsKey("Sequence")) { + sequence = B.getInt("Sequence"); + } else { + sequence = null; } } @@ -40,9 +55,12 @@ public class TokenData extends EthData { super.saveToBundle(B); try { - if( balanceAlter!=null ) { + if (balanceAlter != null) { B.putString("BalanceDecimalAlter", balanceAlter.toString()); } + if (sequence != null) { + B.putInt("Sequence", sequence); + } } catch (Exception e) { Log.e("Can't save to bundle ", e.getMessage()); } diff --git a/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt b/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt new file mode 100644 index 0000000000..9813ee6d80 --- /dev/null +++ b/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt @@ -0,0 +1,310 @@ +package com.tangem.wallet.tokenEmv + +import android.text.InputFilter +import android.util.Log +import com.google.gson.Gson +import com.tangem.data.Blockchain +import com.tangem.data.network.ServerApiTokenEmv +import com.tangem.data.network.model.TokenEmvTransferBody +import com.tangem.tangem_card.data.TangemCard +import com.tangem.tangem_card.reader.CardProtocol.TangemException +import com.tangem.tangem_card.tasks.SignTask +import com.tangem.tangem_card.util.Util +import com.tangem.util.CryptoUtil +import com.tangem.util.DecimalDigitsInputFilter +import com.tangem.wallet.* +import com.tangem.wallet.EthTransaction.BruteRecoveryID2 +import com.tangem.wallet.token.TokenEngine +import io.reactivex.observers.DisposableCompletableObserver +import org.apache.commons.lang3.SerializationUtils +import org.bitcoinj.core.ECKey +import org.kethereum.extensions.toBytesPadded +import org.kethereum.extensions.toFixedLengthByteArray +import java.math.BigInteger +import java.util.* + +class TokenEmvEngine : TokenEngine { + constructor() : super() + constructor(context: TangemContext) : super(context) + + private val TAG = TokenEmvEngine::class.java.simpleName + + private fun hasLinkedContract(): Boolean { + return ctx.card.issuerData != null && ctx.card.issuerData.size == 20 + } + + override fun getBlockchain(): Blockchain { + return Blockchain.TokenEmv + } + + override fun getBalance(): Amount? { + return if (!hasBalanceInfo()) { + null + } else { + convertToAmount(coinData.balanceInInternalUnits) + } + } + + override fun getBalanceHTML(): String? { + return if (hasLinkedContract()) { + if (balance != null) { + balance!!.toDescriptionString(tokenDecimals) + } else { + "" + } + } else { + "NO LINKED CONTRACT" + } + } + + override fun getBalanceCurrency(): String? { + return ctx.card.getTokenSymbol() + } + + override fun getAmountInputFilters(): Array? { + return arrayOf(DecimalDigitsInputFilter(tokenDecimals)) + } + + override fun getFeeCurrency(): String? { + return balanceCurrency + } + + override fun isBalanceNotZero(): Boolean { + if (coinData == null) return false + return if (coinData.balanceInInternalUnits == null) { + false + } else { + coinData.balanceInInternalUnits.notZero() + } + } + + override fun getBalanceEquivalent(): String? { + return "" + } + + override fun evaluateFeeEquivalent(fee: String?): String? { + return "" + } + + override fun defineWallet() { + try { + if (hasLinkedContract()) { + ctx.coinData.wallet = String.format("0x%s", BTCUtils.toHex(ctx.card.issuerData)) + } else { + ctx.coinData.wallet = calculateAddress(ctx.card.walletPublicKey) + } + } catch (e: Exception) { + ctx.coinData.wallet = "ERROR" + throw TangemException("Can't define wallet address") + } + } + + override fun hasBalanceInfo(): Boolean { + return coinData.balanceInInternalUnits != null + } + + override fun isExtractPossible(): Boolean { + if (!hasBalanceInfo()) { + ctx.setMessage(R.string.loaded_wallet_error_obtaining_blockchain_data) + } else if (!isBalanceNotZero) { + ctx.setMessage(R.string.general_wallet_empty) + } else if (awaitingConfirmation()) { + ctx.setMessage(R.string.loaded_wallet_message_wait) + } else { + return true + } + return false + } + + override fun checkNewTransactionAmountAndFee(amount: Amount, fee: Amount?, isFeeIncluded: Boolean): Boolean { + try { + if (isFeeIncluded && (amount > balance || amount < fee)) return false + if (!isFeeIncluded && amount.add(fee) > balance) return false + } catch (e: NumberFormatException) { + e.printStackTrace() + } + return true + } + + override fun constructTransaction(amountValue: Amount, feeValue: Amount?, IncFee: Boolean, targetAddress: String?): SignTask.TransactionToSign? { + val functionBytes = "transfer".toByteArray() + + val contractHex = coinData.wallet.substring(2) + val contractBytes = Util.hexToBytes(contractHex) + val recipientHex = targetAddress!!.substring(2) + val recipientBytes = Util.hexToBytes(recipientHex) + + val amountBytes = convertToInternalAmount(amountValue).toBigInteger().toBytesPadded(32) + val feeLimitBytes = convertToInternalAmount(feeValue).toBigInteger().toBytesPadded(32) + + val sequenceBytes = coinData.sequence.toBigInteger().toBytesPadded(4) + + val hashToSign = Keccak256().digest(contractBytes + functionBytes + + amountBytes + recipientBytes + feeLimitBytes + sequenceBytes) + + return object : SignTask.TransactionToSign { + override fun isSigningMethodSupported(signingMethod: TangemCard.SigningMethod): Boolean { + return signingMethod == TangemCard.SigningMethod.Sign_Hash + } + + override fun getHashesToSign(): Array { + return arrayOf(hashToSign) + } + + @Throws(java.lang.Exception::class) + override fun getRawDataToSign(): ByteArray { + throw java.lang.Exception("Signing of raw transaction not supported for " + this.javaClass.simpleName) + } + + @Throws(java.lang.Exception::class) + override fun getHashAlgToSign(): String { + throw java.lang.Exception("Signing of raw transaction not supported for " + this.javaClass.simpleName) + } + + @Throws(java.lang.Exception::class) + override fun getIssuerTransactionSignature(dataToSignByIssuer: ByteArray): ByteArray { + throw java.lang.Exception("Transaction validation by issuer not supported in this version") + } + + @Throws(java.lang.Exception::class) + override fun onSignCompleted(signFromCard: ByteArray): ByteArray { + val r = BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)) + var s: BigInteger? = BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)) + s = CryptoUtil.toCanonicalised(s) + + val publicKey = ctx.getCard().getWalletPublicKey() + + val verified = ECKey.verify(hashToSign, ECKey.ECDSASignature(r, s), publicKey) + if (!verified) { + Log.e(this.javaClass.simpleName + "-CHECK", "sign Failed.") + } + + val v = BruteRecoveryID2(ECDSASignatureETH(r, s), hashToSign, publicKey) + if (v != 27 && v != 28) { + Log.e(TAG, "invalid v") + throw java.lang.Exception("Error in " + this.javaClass.simpleName + " - invalid v") + } + Log.e(TAG, this.javaClass.simpleName + " V: " + v.toString()) + + var rBytes = r.toByteArray() + if (rBytes.size == 33) { + rBytes = rBytes.copyOfRange(1,33) + } + val sBytes = s.toByteArray() + + val tokenEmvTransferBody = TokenEmvTransferBody( + contract = contractHex, + amount = Util.byteArrayToHexString(amountBytes), + recipient = recipientHex, + feeLimit = Util.byteArrayToHexString(feeLimitBytes), + sequence = coinData.sequence, + r = Util.byteArrayToHexString(rBytes), + s = Util.byteArrayToHexString(sBytes), + v = v + ) + + val jsonBody = Gson().toJson(tokenEmvTransferBody) + val serializedBody = SerializationUtils.serialize(jsonBody) + + notifyOnNeedSendTransaction(serializedBody) + return serializedBody + } + } + } + + override fun requestSendTransaction(blockchainRequestsCallbacks: BlockchainRequestsCallbacks, txForSend: ByteArray?) { + val jsonBody = SerializationUtils.deserialize(txForSend) + val tokenEmvTransferBody = Gson().fromJson(jsonBody, TokenEmvTransferBody::class.java) + + val transferObserver = object : DisposableCompletableObserver() { + override fun onComplete() { + blockchainRequestsCallbacks.onComplete(true) + } + + override fun onError(e: Throwable) { + blockchainRequestsCallbacks.onComplete(false) + } + } + + ServerApiTokenEmv().transfer(tokenEmvTransferBody, transferObserver) + } + + // TODO: move all below to the server + fun constructTransfer(tokenEmvTransferBody: TokenEmvTransferBody, feeValue: Amount?): SignTask.TransactionToSign { + val contractAddress = tokenEmvTransferBody.contract + + val nonceValue = coinData.confirmedTXCount + val weiFee: BigInteger = convertToInternalAmount(feeValue).toBigIntegerExact() //TODO: get fee as usual but set multiplier m = BigInteger.valueOf(100000), use normal? + var gasLimitInt = 100000 + + val gasPrice = weiFee.divide(BigInteger.valueOf(gasLimitInt.toLong())) + val gasLimit = BigInteger.valueOf(gasLimitInt.toLong()) + val chainId = this.chainIdNum + val amountZero = BigInteger.ZERO + + val transferSignature = "transfer(uint256,address,uint8,bytes32,bytes32,uint256,uint256,uint32)".toByteArray() + val selector = Keccak256().digest(transferSignature).copyOf(4) + + val tokenAmount = Util.hexToBytes(tokenEmvTransferBody.amount).toFixedLengthByteArray(32) + val recipient = Util.hexToBytes(tokenEmvTransferBody.recipient).toFixedLengthByteArray(32) + val sigV = BigInteger.valueOf(tokenEmvTransferBody.v.toLong()).toBytesPadded(32) + val sigR = Util.hexToBytes(tokenEmvTransferBody.r).toFixedLengthByteArray(32) + val sigS = Util.hexToBytes(tokenEmvTransferBody.s).toFixedLengthByteArray(32) + val tokenFee = BigInteger.valueOf(1).toBytesPadded(32) //TODO: calculate using equivalent data + val tokenFeeLimit = Util.hexToBytes(tokenEmvTransferBody.feeLimit).toFixedLengthByteArray(32) + val sequence = BigInteger.valueOf(tokenEmvTransferBody.sequence.toLong()).toBytesPadded(32) + + val data = selector + tokenAmount + recipient + sigV + sigR + sigS + tokenFee + tokenFeeLimit + sequence + + val tx = EthTransaction.create(contractAddress, amountZero, nonceValue, gasPrice, gasLimit, chainId, data) + + return object : SignTask.TransactionToSign { + override fun isSigningMethodSupported(signingMethod: TangemCard.SigningMethod): Boolean { + return signingMethod == TangemCard.SigningMethod.Sign_Hash + } + + override fun getHashesToSign(): Array { + return arrayOf(tx.rawHash) + } + + @Throws(java.lang.Exception::class) + override fun getRawDataToSign(): ByteArray { + throw java.lang.Exception("Signing of raw transaction not supported for " + this.javaClass.simpleName) + } + + @Throws(java.lang.Exception::class) + override fun getHashAlgToSign(): String { + throw java.lang.Exception("Signing of raw transaction not supported for " + this.javaClass.simpleName) + } + + @Throws(java.lang.Exception::class) + override fun getIssuerTransactionSignature(dataToSignByIssuer: ByteArray): ByteArray { + throw java.lang.Exception("Transaction validation by issuer not supported in this version") + } + + @Throws(java.lang.Exception::class) + override fun onSignCompleted(signFromCard: ByteArray): ByteArray { + val publicKey = ctx.getCard().getWalletPublicKey() + val for_hash = tx.rawHash + val r = BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)) + var s: BigInteger? = BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)) + s = CryptoUtil.toCanonicalised(s) + val f = ECKey.verify(for_hash, ECKey.ECDSASignature(r, s), publicKey) + if (!f) { + Log.e(this.javaClass.simpleName + "-CHECK", "sign Failed.") + } + tx.signature = ECDSASignatureETH(r, s) + val v = BruteRecoveryID2(tx.signature, for_hash, publicKey) + if (v != 27 && v != 28) { + Log.e(TAG, "invalid v") + throw java.lang.Exception("Error in " + this.javaClass.simpleName + " - invalid v") + } + tx.signature.v = v.toByte() + Log.e(TAG, this.javaClass.simpleName + " V: " + v.toString()) + val txForSend = tx.encoded + notifyOnNeedSendTransaction(txForSend) + return txForSend + } + } + } +} \ No newline at end of file From 44c69929b22bb4936fab2c5cda336c2c43a9a251 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Apr 2020 21:16:17 +0300 Subject: [PATCH 02/10] Updated on 2026-08-14 --- .../java/com/tangem/data/network/Server.java | 8 ++ .../tangem/data/network/ServerApiInfura.java | 2 + .../com/tangem/data/network/ServerURL.java | 1 + .../java/com/tangem/di/NetworkComponent.kt | 3 + .../main/java/com/tangem/di/NetworkModule.kt | 12 +++ .../tangem/wallet/tokenEmv/TokenEmvEngine.kt | 89 ++++++++++++++++++- 6 files changed, 113 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/data/network/Server.java b/app/src/main/java/com/tangem/data/network/Server.java index c31e4cf46b..8ef83ebd52 100644 --- a/app/src/main/java/com/tangem/data/network/Server.java +++ b/app/src/main/java/com/tangem/data/network/Server.java @@ -40,6 +40,14 @@ public class Server { } } + public static class ApiInfuraRopsten { + public static final String URL_INFURA_ROPSTEN = ServerURL.API_INFURA_ROPSTEN; + + public static class Method { + public static final String MAIN = "v3/613a0b14833145968b1f656240c7d245"; + } + } + public static class ApiSoChain { public static final String URL = ServerURL.API_SOCHAIN_V2; diff --git a/app/src/main/java/com/tangem/data/network/ServerApiInfura.java b/app/src/main/java/com/tangem/data/network/ServerApiInfura.java index e168293fa4..29ded17af1 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiInfura.java +++ b/app/src/main/java/com/tangem/data/network/ServerApiInfura.java @@ -42,6 +42,8 @@ public class ServerApiInfura { public ServerApiInfura(Blockchain blockchain) { if (blockchain == Blockchain.EthereumTestNet) { infuraApi = App.Companion.getNetworkComponent().getRetrofitInfuraTestnet().create(InfuraApi.class); + } else if (blockchain == Blockchain.TokenEmv) { + infuraApi = App.Companion.getNetworkComponent().getRetrofitInfuraRopsten().create(InfuraApi.class); } } diff --git a/app/src/main/java/com/tangem/data/network/ServerURL.java b/app/src/main/java/com/tangem/data/network/ServerURL.java index b30fc802ce..dedda6858d 100644 --- a/app/src/main/java/com/tangem/data/network/ServerURL.java +++ b/app/src/main/java/com/tangem/data/network/ServerURL.java @@ -5,6 +5,7 @@ class ServerURL { static final String API_COINMARKETCAP = "https://pro-api.coinmarketcap.com/"; static final String API_INFURA = "https://mainnet.infura.io/"; static final String API_INFURA_TESTNET = "https://rinkeby.infura.io/"; + static final String API_INFURA_ROPSTEN = "https://ropsten.infura.io/"; static final String API_SOCHAIN_V2 = "https://chain.so/"; static final String API_ESTIMATEFEE = "https://estimatefee.com/"; static final String API_UPDATE_VERSION = "https://raw.githubusercontent.com/"; diff --git a/app/src/main/java/com/tangem/di/NetworkComponent.kt b/app/src/main/java/com/tangem/di/NetworkComponent.kt index b09c235aba..3d100a99e6 100644 --- a/app/src/main/java/com/tangem/di/NetworkComponent.kt +++ b/app/src/main/java/com/tangem/di/NetworkComponent.kt @@ -17,6 +17,9 @@ interface NetworkComponent { @get:Named(Server.ApiInfuraTestnet.URL_INFURA_TESTNET) val retrofitInfuraTestnet: Retrofit + @get:Named(Server.ApiInfuraRopsten.URL_INFURA_ROPSTEN) + val retrofitInfuraRopsten: Retrofit + @get:Named(Server.ApiMaticTesnet.URL_MATIC_TESTNET) val retrofitMaticTesnet: Retrofit diff --git a/app/src/main/java/com/tangem/di/NetworkModule.kt b/app/src/main/java/com/tangem/di/NetworkModule.kt index 0358c3bd9b..339a0ca5d8 100644 --- a/app/src/main/java/com/tangem/di/NetworkModule.kt +++ b/app/src/main/java/com/tangem/di/NetworkModule.kt @@ -44,6 +44,18 @@ internal class NetworkModule { return builder.build() } + @Singleton + @Provides + @Named(Server.ApiInfuraRopsten.URL_INFURA_ROPSTEN) + fun provideRetrofitInfuraRopsten(): Retrofit { + val builder = Retrofit.Builder() + .baseUrl(Server.ApiInfuraRopsten.URL_INFURA_ROPSTEN) + .addConverterFactory(GsonConverterFactory.create()) + if (BuildConfig.DEBUG) + builder.client(createOkHttpClient()) + return builder.build() + } + @Singleton @Provides @Named(Server.ApiRootstock.URL_ROOTSTOCK) diff --git a/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt b/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt index 9813ee6d80..225094e7a8 100644 --- a/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt +++ b/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt @@ -4,7 +4,9 @@ import android.text.InputFilter import android.util.Log import com.google.gson.Gson import com.tangem.data.Blockchain +import com.tangem.data.network.ServerApiInfura import com.tangem.data.network.ServerApiTokenEmv +import com.tangem.data.network.model.InfuraResponse import com.tangem.data.network.model.TokenEmvTransferBody import com.tangem.tangem_card.data.TangemCard import com.tangem.tangem_card.reader.CardProtocol.TangemException @@ -20,6 +22,7 @@ import org.apache.commons.lang3.SerializationUtils import org.bitcoinj.core.ECKey import org.kethereum.extensions.toBytesPadded import org.kethereum.extensions.toFixedLengthByteArray +import java.math.BigDecimal import java.math.BigInteger import java.util.* @@ -30,13 +33,17 @@ class TokenEmvEngine : TokenEngine { private val TAG = TokenEmvEngine::class.java.simpleName private fun hasLinkedContract(): Boolean { - return ctx.card.issuerData != null && ctx.card.issuerData.size == 20 + return ctx.card.issuerData != null && ctx.card.issuerData.size == 44 } override fun getBlockchain(): Blockchain { return Blockchain.TokenEmv } + override fun getChainIdNum(): Int { + return EthTransaction.ChainEnum.Mainnet.value + } + override fun getBalance(): Amount? { return if (!hasBalanceInfo()) { null @@ -89,7 +96,8 @@ class TokenEmvEngine : TokenEngine { override fun defineWallet() { try { if (hasLinkedContract()) { - ctx.coinData.wallet = String.format("0x%s", BTCUtils.toHex(ctx.card.issuerData)) + val issuerData = ctx.card.issuerData + ctx.coinData.wallet = String(issuerData.copyOfRange(2, issuerData.size)) } else { ctx.coinData.wallet = calculateAddress(ctx.card.walletPublicKey) } @@ -212,6 +220,79 @@ class TokenEmvEngine : TokenEngine { } } + override fun requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks: BlockchainRequestsCallbacks) { + val serverApiInfura = ServerApiInfura(ctx.blockchain) + + val responseListener: ServerApiInfura.ResponseListener = object : ServerApiInfura.ResponseListener { + override fun onSuccess(method: String, infuraResponse: InfuraResponse) { + when (method) { + ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT -> { + var nonce = infuraResponse.result + nonce = nonce!!.substring(2) + val count = BigInteger(nonce, 16) + coinData.confirmedTXCount = count + if (serverApiInfura.isRequestsSequenceCompleted) { //getting balances after checking for pending to avoid showing old balance as verified + serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.wallet, getContractAddress(ctx.card), "") + } + } + ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT -> { + var pending = infuraResponse.result + pending = pending!!.substring(2) + val count = BigInteger(pending, 16) + coinData.unconfirmedTXCount = count + if (serverApiInfura.isRequestsSequenceCompleted) { //getting balances after checking for pending to avoid showing old balance as verified + serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.wallet, getContractAddress(ctx.card), "") + } + } + ServerApiInfura.INFURA_ETH_CALL -> { + try { + var balanceCap = infuraResponse.result + balanceCap = balanceCap!!.substring(2) + val l = BigInteger(balanceCap, 16) + coinData.balanceInInternalUnits = InternalAmount(l, ctx.card.tokenSymbol) + coinData.isBalanceReceived = true + // Log.i("$TAG eth_call", balanceCap) + } catch (e: java.lang.Exception) { + e.printStackTrace() + } + } + } + if (serverApiInfura.isRequestsSequenceCompleted) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()) + } else { + blockchainRequestsCallbacks.onProgress() + } + } + + override fun onFail(method: String, message: String) { + Log.e(TAG, "onFail: $method $message") + ctx.error = message + if (serverApiInfura.isRequestsSequenceCompleted) { + blockchainRequestsCallbacks.onComplete(false) + } else { + blockchainRequestsCallbacks.onProgress() + } + } + } + + serverApiInfura.setResponseListener(responseListener) + + if (validateAddress(getContractAddress(ctx.card))) { + serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, 67, coinData.wallet, "", "") + serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.wallet, "", "") + } else { + ctx.error = "Smart contract address not defined" + blockchainRequestsCallbacks.onComplete(false) + } + } + + override fun requestFee(blockchainRequestsCallbacks: BlockchainRequestsCallbacks, targetAddress: String?, amount: Amount) { + val fee = Amount(BigDecimal.ONE, balanceCurrency) + coinData.minFee = fee + coinData.normalFee = fee + coinData.maxFee = fee + } + override fun requestSendTransaction(blockchainRequestsCallbacks: BlockchainRequestsCallbacks, txForSend: ByteArray?) { val jsonBody = SerializationUtils.deserialize(txForSend) val tokenEmvTransferBody = Gson().fromJson(jsonBody, TokenEmvTransferBody::class.java) @@ -229,6 +310,10 @@ class TokenEmvEngine : TokenEngine { ServerApiTokenEmv().transfer(tokenEmvTransferBody, transferObserver) } + override fun allowSelectFeeLevel(): Boolean { + return false + } + // TODO: move all below to the server fun constructTransfer(tokenEmvTransferBody: TokenEmvTransferBody, feeValue: Amount?): SignTask.TransactionToSign { val contractAddress = tokenEmvTransferBody.contract From f3835792c5ce027c8666f6d52c594533dd5ffe93 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 13 Apr 2020 17:31:03 +0300 Subject: [PATCH 03/10] Updated on 2026-08-14 --- .../main/java/com/tangem/ui/activity/MainActivity.kt | 11 +++++++++-- dependencies.gradle | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/tangem/ui/activity/MainActivity.kt b/app/src/main/java/com/tangem/ui/activity/MainActivity.kt index c5bdb69201..8c6f685715 100644 --- a/app/src/main/java/com/tangem/ui/activity/MainActivity.kt +++ b/app/src/main/java/com/tangem/ui/activity/MainActivity.kt @@ -8,6 +8,7 @@ import android.nfc.Tag import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProviders import androidx.navigation.findNavController import com.scottyab.rootbeer.RootBeer @@ -16,6 +17,7 @@ import com.tangem.di.ToastHelper import com.tangem.tangem_sdk.android.nfc.NfcLifecycleObserver import com.tangem.tangem_sdk.android.reader.NfcManager import com.tangem.ui.dialog.RootFoundDialog +import com.tangem.ui.fragment.MainFragment import com.tangem.wallet.BuildConfig import com.tangem.wallet.R import javax.inject.Inject @@ -44,6 +46,8 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { } private fun navigateSafelyToMainFragment() { + if (getActiveFragment() is MainFragment) return + try { findNavController(R.id.nav_host_fragment).popBackStack() } catch (e: IllegalArgumentException) { @@ -73,8 +77,7 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { } override fun onTagDiscovered(tag: Tag) { - val activeFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) - ?.childFragmentManager?.primaryNavigationFragment + val activeFragment = getActiveFragment() if (activeFragment is NfcAdapter.ReaderCallback) { activeFragment.onTagDiscovered(tag) } else { @@ -82,4 +85,8 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { } } + private fun getActiveFragment(): Fragment? { + return supportFragmentManager.findFragmentById(R.id.nav_host_fragment) + ?.childFragmentManager?.primaryNavigationFragment + } } \ No newline at end of file diff --git a/dependencies.gradle b/dependencies.gradle index f1121c113d..f4514092a4 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,4 +1,4 @@ ext.versions = [ kotlin : '1.3.71', - build_gradle: '3.6.1', + build_gradle: '3.6.2', ] From 9f2a73e7f49605f054a35f699d5b093b705accf3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 13 Apr 2020 19:28:25 +0300 Subject: [PATCH 04/10] Updated on 2026-08-14 --- .../tangem/data/network/ServerApiTokenEmv.kt | 22 +- .../com/tangem/data/network/TokenEmvApi.kt | 10 +- .../tangem/data/network/model/TokenEmvBody.kt | 29 ++- .../com/tangem/wallet/token/TokenData.java | 34 +-- .../tangem/wallet/tokenEmv/TokenEmvEngine.kt | 245 ++++++++---------- 5 files changed, 177 insertions(+), 163 deletions(-) diff --git a/app/src/main/java/com/tangem/data/network/ServerApiTokenEmv.kt b/app/src/main/java/com/tangem/data/network/ServerApiTokenEmv.kt index 25e3eb03d9..d3fdba5f87 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiTokenEmv.kt +++ b/app/src/main/java/com/tangem/data/network/ServerApiTokenEmv.kt @@ -1,25 +1,25 @@ package com.tangem.data.network import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory -import com.tangem.data.network.model.TezosAccountResponse +import com.tangem.data.network.model.TokenEmvGetTransferFeeAnswer +import com.tangem.data.network.model.TokenEmvGetTransferFeeBody +import com.tangem.data.network.model.TokenEmvTransferAnswer import com.tangem.data.network.model.TokenEmvTransferBody import com.tangem.tangem_card.util.Log -import io.reactivex.CompletableObserver -import io.reactivex.Single import io.reactivex.SingleObserver import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.functions.BiConsumer import io.reactivex.schedulers.Schedulers import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.scalars.ScalarsConverterFactory +import java.util.concurrent.TimeUnit class ServerApiTokenEmv { private val TAG = ServerApiTokenEmv::class.java.simpleName - private val tangemServer = "" + private val tangemServer = "https://emvsupport.appspot.com/" private val tokenEmvApi = Retrofit.Builder() .baseUrl(tangemServer) @@ -32,10 +32,20 @@ class ServerApiTokenEmv { .build() .create(TokenEmvApi::class.java) - fun transfer(tokenEmvTransferBody: TokenEmvTransferBody, transferObserver: CompletableObserver) { + fun transfer(tokenEmvTransferBody: TokenEmvTransferBody, transferObserver: SingleObserver) { Log.i(TAG, "new transfer request") tokenEmvApi.transfer(tokenEmvTransferBody) + .timeout(30, TimeUnit.SECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(transferObserver) + } + + fun getTransferFee(tokenEmvGetTransferFeeBody: TokenEmvGetTransferFeeBody, transferObserver: SingleObserver) { + Log.i(TAG, "new get transfer fee request") + + tokenEmvApi.getTransferFee(tokenEmvGetTransferFeeBody) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(transferObserver) diff --git a/app/src/main/java/com/tangem/data/network/TokenEmvApi.kt b/app/src/main/java/com/tangem/data/network/TokenEmvApi.kt index f885cad2c4..43e09897c0 100644 --- a/app/src/main/java/com/tangem/data/network/TokenEmvApi.kt +++ b/app/src/main/java/com/tangem/data/network/TokenEmvApi.kt @@ -1,11 +1,17 @@ package com.tangem.data.network +import com.tangem.data.network.model.TokenEmvGetTransferFeeAnswer +import com.tangem.data.network.model.TokenEmvGetTransferFeeBody +import com.tangem.data.network.model.TokenEmvTransferAnswer import com.tangem.data.network.model.TokenEmvTransferBody import io.reactivex.Completable +import io.reactivex.Single import retrofit2.http.Body import retrofit2.http.POST interface TokenEmvApi { - @POST("./") - fun transfer(@Body tokenEmvTransferBody: TokenEmvTransferBody): Completable + @POST("./card/transfer") + fun transfer(@Body tokenEmvTransferBody: TokenEmvTransferBody): Single + @POST("./card/transfer/fee") + fun getTransferFee(@Body tokenEmvGetTransferFeeBody: TokenEmvGetTransferFeeBody): Single } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/data/network/model/TokenEmvBody.kt b/app/src/main/java/com/tangem/data/network/model/TokenEmvBody.kt index cbfdcf038e..ea89dd88d9 100644 --- a/app/src/main/java/com/tangem/data/network/model/TokenEmvBody.kt +++ b/app/src/main/java/com/tangem/data/network/model/TokenEmvBody.kt @@ -3,13 +3,34 @@ package com.tangem.data.network.model import com.google.gson.annotations.SerializedName data class TokenEmvTransferBody( - val contract: String, + val CID: String, + val publicKey: String, val amount: String, + val currency: String, val recipient: String, @SerializedName("fee_limit") val feeLimit: String, val sequence: Int, - val r: String, - val s: String, - val v: Int + val signature: String +) + +data class TokenEmvTransferAnswer( + val error: String?, + val errorCode: Int?, + val success: Boolean?, + val tx_id: String?, + val blockchain_tx_id: String? +) + +data class TokenEmvGetTransferFeeBody( + val CID: String, + val publicKey: String +) + +data class TokenEmvGetTransferFeeAnswer( + val error: String?, + val errorCode: Int?, + val success: Boolean?, + val fee: String?, + val currency: String? ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/token/TokenData.java b/app/src/main/java/com/tangem/wallet/token/TokenData.java index 7cf2c24255..77e0c7ac42 100644 --- a/app/src/main/java/com/tangem/wallet/token/TokenData.java +++ b/app/src/main/java/com/tangem/wallet/token/TokenData.java @@ -8,13 +8,13 @@ import com.tangem.wallet.eth.EthData; public class TokenData extends EthData { private CoinEngine.InternalAmount balanceAlter = null; - private Integer sequence = null; +// private Integer sequence = null; @Override public void clearInfo() { super.clearInfo(); balanceAlter = null; - sequence = null; +// sequence = null; } public CoinEngine.InternalAmount getBalanceAlterInInternalUnits() { @@ -26,13 +26,13 @@ public class TokenData extends EthData { balanceAlter = value; } - public Integer getSequence() { - return sequence; - } - - public void setSequence(Integer sequence) { - this.sequence = sequence; - } +// public Integer getSequence() { +// return sequence; +// } +// +// public void setSequence(Integer sequence) { +// this.sequence = sequence; +// } @Override public void loadFromBundle(Bundle B) { @@ -43,11 +43,11 @@ public class TokenData extends EthData { } else { balanceAlter = null; } - if (B.containsKey("Sequence")) { - sequence = B.getInt("Sequence"); - } else { - sequence = null; - } +// if (B.containsKey("Sequence")) { +// sequence = B.getInt("Sequence"); +// } else { +// sequence = null; +// } } @Override @@ -58,9 +58,9 @@ public class TokenData extends EthData { if (balanceAlter != null) { B.putString("BalanceDecimalAlter", balanceAlter.toString()); } - if (sequence != null) { - B.putInt("Sequence", sequence); - } +// if (sequence != null) { +// B.putInt("Sequence", sequence); +// } } catch (Exception e) { Log.e("Can't save to bundle ", e.getMessage()); } diff --git a/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt b/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt index 225094e7a8..0997a06cbf 100644 --- a/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt +++ b/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt @@ -6,25 +6,23 @@ import com.google.gson.Gson import com.tangem.data.Blockchain import com.tangem.data.network.ServerApiInfura import com.tangem.data.network.ServerApiTokenEmv -import com.tangem.data.network.model.InfuraResponse -import com.tangem.data.network.model.TokenEmvTransferBody +import com.tangem.data.network.model.* import com.tangem.tangem_card.data.TangemCard import com.tangem.tangem_card.reader.CardProtocol.TangemException +import com.tangem.tangem_card.reader.TLV +import com.tangem.tangem_card.reader.TLVList import com.tangem.tangem_card.tasks.SignTask import com.tangem.tangem_card.util.Util -import com.tangem.util.CryptoUtil import com.tangem.util.DecimalDigitsInputFilter -import com.tangem.wallet.* -import com.tangem.wallet.EthTransaction.BruteRecoveryID2 +import com.tangem.wallet.EthTransaction +import com.tangem.wallet.Keccak256 +import com.tangem.wallet.R +import com.tangem.wallet.TangemContext import com.tangem.wallet.token.TokenEngine -import io.reactivex.observers.DisposableCompletableObserver +import io.reactivex.observers.DisposableSingleObserver import org.apache.commons.lang3.SerializationUtils -import org.bitcoinj.core.ECKey import org.kethereum.extensions.toBytesPadded -import org.kethereum.extensions.toFixedLengthByteArray -import java.math.BigDecimal import java.math.BigInteger -import java.util.* class TokenEmvEngine : TokenEngine { constructor() : super() @@ -41,7 +39,7 @@ class TokenEmvEngine : TokenEngine { } override fun getChainIdNum(): Int { - return EthTransaction.ChainEnum.Mainnet.value + return EthTransaction.ChainEnum.Ropsten.value } override fun getBalance(): Amount? { @@ -96,8 +94,7 @@ class TokenEmvEngine : TokenEngine { override fun defineWallet() { try { if (hasLinkedContract()) { - val issuerData = ctx.card.issuerData - ctx.coinData.wallet = String(issuerData.copyOfRange(2, issuerData.size)) + ctx.coinData.wallet = TLVList.fromBytes(ctx.card.issuerData).getTLV(TLV.Tag.TAG_Token_Contract_Address).asString } else { ctx.coinData.wallet = calculateAddress(ctx.card.walletPublicKey) } @@ -145,10 +142,10 @@ class TokenEmvEngine : TokenEngine { val amountBytes = convertToInternalAmount(amountValue).toBigInteger().toBytesPadded(32) val feeLimitBytes = convertToInternalAmount(feeValue).toBigInteger().toBytesPadded(32) - val sequenceBytes = coinData.sequence.toBigInteger().toBytesPadded(4) + val sequence = ctx.card.SignedHashes + val sequenceBytes = sequence.toBigInteger().toBytesPadded(4) - val hashToSign = Keccak256().digest(contractBytes + functionBytes - + amountBytes + recipientBytes + feeLimitBytes + sequenceBytes) + val hashToSign = Keccak256().digest(contractBytes + functionBytes + amountBytes + recipientBytes + feeLimitBytes + sequenceBytes) return object : SignTask.TransactionToSign { override fun isSigningMethodSupported(signingMethod: TangemCard.SigningMethod): Boolean { @@ -176,39 +173,52 @@ class TokenEmvEngine : TokenEngine { @Throws(java.lang.Exception::class) override fun onSignCompleted(signFromCard: ByteArray): ByteArray { - val r = BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)) - var s: BigInteger? = BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)) - s = CryptoUtil.toCanonicalised(s) - - val publicKey = ctx.getCard().getWalletPublicKey() - - val verified = ECKey.verify(hashToSign, ECKey.ECDSASignature(r, s), publicKey) - if (!verified) { - Log.e(this.javaClass.simpleName + "-CHECK", "sign Failed.") - } - - val v = BruteRecoveryID2(ECDSASignatureETH(r, s), hashToSign, publicKey) - if (v != 27 && v != 28) { - Log.e(TAG, "invalid v") - throw java.lang.Exception("Error in " + this.javaClass.simpleName + " - invalid v") - } - Log.e(TAG, this.javaClass.simpleName + " V: " + v.toString()) - - var rBytes = r.toByteArray() - if (rBytes.size == 33) { - rBytes = rBytes.copyOfRange(1,33) - } - val sBytes = s.toByteArray() +// val r = BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)) +// var s: BigInteger? = BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)) +// s = CryptoUtil.toCanonicalised(s) +// +// val publicKey = ctx.getCard().getWalletPublicKey() +// +// val verified = ECKey.verify(hashToSign, ECKey.ECDSASignature(r, s), publicKey) +// if (!verified) { +// Log.e(this.javaClass.simpleName + "-CHECK", "sign Failed.") +// } +// +// val v = BruteRecoveryID2(ECDSASignatureETH(r, s), hashToSign, publicKey) +// if (v != 27 && v != 28) { +// Log.e(TAG, "invalid v") +// throw java.lang.Exception("Error in " + this.javaClass.simpleName + " - invalid v") +// } +// Log.e(TAG, this.javaClass.simpleName + " V: " + v.toString()) +// +// var rBytes = r.toByteArray() +// if (rBytes.size == 33) { +// rBytes = rBytes.copyOfRange(1,33) +// } +// val sBytes = s.toByteArray() +// +// val tokenEmvTransferBody = TokenEmvTransferBody( +// CID = Util.bytesToHex(ctx.card.cid), +// publicKey = Util.bytesToHex(ctx.card.walletPublicKey), +// contract = contractHex, +// amount = Util.byteArrayToHexString(amountBytes), +// recipient = recipientHex, +// feeLimit = Util.byteArrayToHexString(feeLimitBytes), +// sequence = sequence, +// r = Util.byteArrayToHexString(rBytes), +// s = Util.byteArrayToHexString(sBytes), +// v = v +// ) val tokenEmvTransferBody = TokenEmvTransferBody( - contract = contractHex, - amount = Util.byteArrayToHexString(amountBytes), - recipient = recipientHex, - feeLimit = Util.byteArrayToHexString(feeLimitBytes), - sequence = coinData.sequence, - r = Util.byteArrayToHexString(rBytes), - s = Util.byteArrayToHexString(sBytes), - v = v + CID = Util.bytesToHex(ctx.card.cid), + publicKey = Util.bytesToHex(ctx.card.walletPublicKey), + amount = amountValue.toValueString(), + currency = amountValue.currency, + recipient = targetAddress, + feeLimit = feeValue!!.toValueString(), + sequence = sequence, + signature = Util.bytesToHex(signFromCard) ) val jsonBody = Gson().toJson(tokenEmvTransferBody) @@ -287,109 +297,76 @@ class TokenEmvEngine : TokenEngine { } override fun requestFee(blockchainRequestsCallbacks: BlockchainRequestsCallbacks, targetAddress: String?, amount: Amount) { - val fee = Amount(BigDecimal.ONE, balanceCurrency) - coinData.minFee = fee - coinData.normalFee = fee - coinData.maxFee = fee + + val tokenEmvTransferBody = TokenEmvGetTransferFeeBody( + CID = Util.bytesToHex(ctx.card.cid), + publicKey = Util.bytesToHex(ctx.card.walletPublicKey) + ) + + val observer = object : DisposableSingleObserver() { + override fun onError(e: Throwable) { + ctx.error = "Can't get transfer fee, ${e.message}" + blockchainRequestsCallbacks.onComplete(false) + } + + override fun onSuccess(t: TokenEmvGetTransferFeeAnswer) { + if (t.success != null && t.success) { + ctx.error = null + coinData.minFee = Amount(t.fee, t.currency) + coinData.normalFee = Amount(t.fee, t.currency) + coinData.maxFee = Amount(t.fee, t.currency) + blockchainRequestsCallbacks.onComplete(true) + } else { + if (t.error != null) { + ctx.error = t.error + } else { + ctx.error = "Can't get transfer fee, code ${t.errorCode}" + } + blockchainRequestsCallbacks.onComplete(false) + } + } + + } + + ServerApiTokenEmv().getTransferFee(tokenEmvTransferBody, observer) + blockchainRequestsCallbacks.onComplete(true) } override fun requestSendTransaction(blockchainRequestsCallbacks: BlockchainRequestsCallbacks, txForSend: ByteArray?) { val jsonBody = SerializationUtils.deserialize(txForSend) + + Log.e(TAG, jsonBody) + val tokenEmvTransferBody = Gson().fromJson(jsonBody, TokenEmvTransferBody::class.java) - val transferObserver = object : DisposableCompletableObserver() { - override fun onComplete() { - blockchainRequestsCallbacks.onComplete(true) + val observer = object : DisposableSingleObserver() { + override fun onError(e: Throwable) { + ctx.error = "Can't send transfer, ${e.message}" + blockchainRequestsCallbacks.onComplete(false) } - override fun onError(e: Throwable) { - blockchainRequestsCallbacks.onComplete(false) + override fun onSuccess(t: TokenEmvTransferAnswer) { + if (t.success != null && t.success) { + ctx.error = null + blockchainRequestsCallbacks.onComplete(true) + } else { + if (t.error != null) { + ctx.error = t.error + } else { + ctx.error = "Can't send transfer, code ${t.errorCode}" + } + blockchainRequestsCallbacks.onComplete(false) + } + } } - ServerApiTokenEmv().transfer(tokenEmvTransferBody, transferObserver) + ServerApiTokenEmv().transfer(tokenEmvTransferBody, observer) } override fun allowSelectFeeLevel(): Boolean { return false } - // TODO: move all below to the server - fun constructTransfer(tokenEmvTransferBody: TokenEmvTransferBody, feeValue: Amount?): SignTask.TransactionToSign { - val contractAddress = tokenEmvTransferBody.contract - val nonceValue = coinData.confirmedTXCount - val weiFee: BigInteger = convertToInternalAmount(feeValue).toBigIntegerExact() //TODO: get fee as usual but set multiplier m = BigInteger.valueOf(100000), use normal? - var gasLimitInt = 100000 - - val gasPrice = weiFee.divide(BigInteger.valueOf(gasLimitInt.toLong())) - val gasLimit = BigInteger.valueOf(gasLimitInt.toLong()) - val chainId = this.chainIdNum - val amountZero = BigInteger.ZERO - - val transferSignature = "transfer(uint256,address,uint8,bytes32,bytes32,uint256,uint256,uint32)".toByteArray() - val selector = Keccak256().digest(transferSignature).copyOf(4) - - val tokenAmount = Util.hexToBytes(tokenEmvTransferBody.amount).toFixedLengthByteArray(32) - val recipient = Util.hexToBytes(tokenEmvTransferBody.recipient).toFixedLengthByteArray(32) - val sigV = BigInteger.valueOf(tokenEmvTransferBody.v.toLong()).toBytesPadded(32) - val sigR = Util.hexToBytes(tokenEmvTransferBody.r).toFixedLengthByteArray(32) - val sigS = Util.hexToBytes(tokenEmvTransferBody.s).toFixedLengthByteArray(32) - val tokenFee = BigInteger.valueOf(1).toBytesPadded(32) //TODO: calculate using equivalent data - val tokenFeeLimit = Util.hexToBytes(tokenEmvTransferBody.feeLimit).toFixedLengthByteArray(32) - val sequence = BigInteger.valueOf(tokenEmvTransferBody.sequence.toLong()).toBytesPadded(32) - - val data = selector + tokenAmount + recipient + sigV + sigR + sigS + tokenFee + tokenFeeLimit + sequence - - val tx = EthTransaction.create(contractAddress, amountZero, nonceValue, gasPrice, gasLimit, chainId, data) - - return object : SignTask.TransactionToSign { - override fun isSigningMethodSupported(signingMethod: TangemCard.SigningMethod): Boolean { - return signingMethod == TangemCard.SigningMethod.Sign_Hash - } - - override fun getHashesToSign(): Array { - return arrayOf(tx.rawHash) - } - - @Throws(java.lang.Exception::class) - override fun getRawDataToSign(): ByteArray { - throw java.lang.Exception("Signing of raw transaction not supported for " + this.javaClass.simpleName) - } - - @Throws(java.lang.Exception::class) - override fun getHashAlgToSign(): String { - throw java.lang.Exception("Signing of raw transaction not supported for " + this.javaClass.simpleName) - } - - @Throws(java.lang.Exception::class) - override fun getIssuerTransactionSignature(dataToSignByIssuer: ByteArray): ByteArray { - throw java.lang.Exception("Transaction validation by issuer not supported in this version") - } - - @Throws(java.lang.Exception::class) - override fun onSignCompleted(signFromCard: ByteArray): ByteArray { - val publicKey = ctx.getCard().getWalletPublicKey() - val for_hash = tx.rawHash - val r = BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)) - var s: BigInteger? = BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)) - s = CryptoUtil.toCanonicalised(s) - val f = ECKey.verify(for_hash, ECKey.ECDSASignature(r, s), publicKey) - if (!f) { - Log.e(this.javaClass.simpleName + "-CHECK", "sign Failed.") - } - tx.signature = ECDSASignatureETH(r, s) - val v = BruteRecoveryID2(tx.signature, for_hash, publicKey) - if (v != 27 && v != 28) { - Log.e(TAG, "invalid v") - throw java.lang.Exception("Error in " + this.javaClass.simpleName + " - invalid v") - } - tx.signature.v = v.toByte() - Log.e(TAG, this.javaClass.simpleName + " V: " + v.toString()) - val txForSend = tx.encoded - notifyOnNeedSendTransaction(txForSend) - return txForSend - } - } - } } \ No newline at end of file From f96987e0cdef7b73dc540f5b6afed38e704644f6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 14 Apr 2020 14:32:32 +0300 Subject: [PATCH 05/10] Updated on 2026-08-14 --- app/src/main/java/com/tangem/data/Blockchain.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/data/Blockchain.java b/app/src/main/java/com/tangem/data/Blockchain.java index ed1ab10387..0e0df81a93 100644 --- a/app/src/main/java/com/tangem/data/Blockchain.java +++ b/app/src/main/java/com/tangem/data/Blockchain.java @@ -6,7 +6,7 @@ import com.tangem.tangem_sdk.R; * Created by dvol on 06.08.2017. */ public enum Blockchain { - Unknown("", "", 1.0, R.drawable.ic_logo_unknown, ""), + Unknown("", "", 1.0, R.drawable.ic_logo_unknown, "Unknown"), Bitcoin("BTC", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Bitcoin"), BitcoinTestNet("BTC/test", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin_testnet, "Bitcoin Testnet"), BitcoinDual("BTC/dual", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Bitcoin"), @@ -67,7 +67,7 @@ public enum Blockchain { for (Blockchain blockchain : values()) { if (blockchain.getID().equals(id)) return blockchain; } - return null; + return Blockchain.Unknown; } public static Blockchain fromCurrency(String currency) { From e4d39da779ec75a031ec622367849472089a1b1f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 14 Apr 2020 16:41:50 +0300 Subject: [PATCH 06/10] Updated on 2026-08-14 --- .../tangem/wallet/tokenEmv/TokenEmvEngine.kt | 58 +++++-------------- 1 file changed, 15 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt b/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt index 0997a06cbf..ef083c5d2d 100644 --- a/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt +++ b/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt @@ -235,61 +235,30 @@ class TokenEmvEngine : TokenEngine { val responseListener: ServerApiInfura.ResponseListener = object : ServerApiInfura.ResponseListener { override fun onSuccess(method: String, infuraResponse: InfuraResponse) { - when (method) { - ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT -> { - var nonce = infuraResponse.result - nonce = nonce!!.substring(2) - val count = BigInteger(nonce, 16) - coinData.confirmedTXCount = count - if (serverApiInfura.isRequestsSequenceCompleted) { //getting balances after checking for pending to avoid showing old balance as verified - serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.wallet, getContractAddress(ctx.card), "") - } - } - ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT -> { - var pending = infuraResponse.result - pending = pending!!.substring(2) - val count = BigInteger(pending, 16) - coinData.unconfirmedTXCount = count - if (serverApiInfura.isRequestsSequenceCompleted) { //getting balances after checking for pending to avoid showing old balance as verified - serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.wallet, getContractAddress(ctx.card), "") - } - } - ServerApiInfura.INFURA_ETH_CALL -> { - try { - var balanceCap = infuraResponse.result - balanceCap = balanceCap!!.substring(2) - val l = BigInteger(balanceCap, 16) - coinData.balanceInInternalUnits = InternalAmount(l, ctx.card.tokenSymbol) - coinData.isBalanceReceived = true - // Log.i("$TAG eth_call", balanceCap) - } catch (e: java.lang.Exception) { - e.printStackTrace() - } - } - } - if (serverApiInfura.isRequestsSequenceCompleted) { - blockchainRequestsCallbacks.onComplete(!ctx.hasError()) - } else { - blockchainRequestsCallbacks.onProgress() + try { + var balanceCap = infuraResponse.result + balanceCap = balanceCap!!.substring(2) + val l = BigInteger(balanceCap, 16) + coinData.balanceInInternalUnits = InternalAmount(l, ctx.card.tokenSymbol) + coinData.isBalanceReceived = true + // Log.i("$TAG eth_call", balanceCap) + } catch (e: java.lang.Exception) { + onFail(method, e.message ?: "invalid response") } + blockchainRequestsCallbacks.onComplete(true) } override fun onFail(method: String, message: String) { Log.e(TAG, "onFail: $method $message") ctx.error = message - if (serverApiInfura.isRequestsSequenceCompleted) { - blockchainRequestsCallbacks.onComplete(false) - } else { - blockchainRequestsCallbacks.onProgress() - } + blockchainRequestsCallbacks.onComplete(false) } } serverApiInfura.setResponseListener(responseListener) if (validateAddress(getContractAddress(ctx.card))) { - serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, 67, coinData.wallet, "", "") - serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.wallet, "", "") + serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.wallet, getContractAddress(ctx.card), "") } else { ctx.error = "Smart contract address not defined" blockchainRequestsCallbacks.onComplete(false) @@ -368,5 +337,8 @@ class TokenEmvEngine : TokenEngine { return false } + override fun pendingTransactionTimeoutInSeconds(): Int { + return 60 + } } \ No newline at end of file From acc9d0d92f914218a41068e0c85b28affbb1ba9f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 14 Apr 2020 16:55:28 +0300 Subject: [PATCH 07/10] Updated on 2026-08-14 --- .../main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt b/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt index ef083c5d2d..c63851d57c 100644 --- a/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt +++ b/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt @@ -1,5 +1,6 @@ package com.tangem.wallet.tokenEmv +import android.net.Uri import android.text.InputFilter import android.util.Log import com.google.gson.Gson @@ -108,6 +109,10 @@ class TokenEmvEngine : TokenEngine { return coinData.balanceInInternalUnits != null } + override fun getWalletExplorerUri(): Uri { + return Uri.parse("https://ropsten.etherscan.io/token/" + getContractAddress(ctx.card) + "?a=" + ctx.coinData.wallet) + } + override fun isExtractPossible(): Boolean { if (!hasBalanceInfo()) { ctx.setMessage(R.string.loaded_wallet_error_obtaining_blockchain_data) From 230ef64f372c673daa2c6627b65b51b6f9fe01d3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 15 Apr 2020 11:56:23 +0300 Subject: [PATCH 08/10] Updated on 2026-08-14 --- tangem-core/src/main/java/com/tangem/CardSession.kt | 6 ++++-- tangem-core/src/main/java/com/tangem/commands/Command.kt | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tangem-core/src/main/java/com/tangem/CardSession.kt b/tangem-core/src/main/java/com/tangem/CardSession.kt index 9c338ca586..450c056997 100644 --- a/tangem-core/src/main/java/com/tangem/CardSession.kt +++ b/tangem-core/src/main/java/com/tangem/CardSession.kt @@ -74,7 +74,10 @@ class CardSession( } runnable.run(this) { result -> - stop() + when (result) { + is CompletionResult.Success -> stop() + is CompletionResult.Failure -> stopWithError(result.error) + } callback(result) } } @@ -99,7 +102,6 @@ class CardSession( } is CompletionResult.Success -> { callback(this, null) - } } } diff --git a/tangem-core/src/main/java/com/tangem/commands/Command.kt b/tangem-core/src/main/java/com/tangem/commands/Command.kt index f78e5663cb..225fa97430 100644 --- a/tangem-core/src/main/java/com/tangem/commands/Command.kt +++ b/tangem-core/src/main/java/com/tangem/commands/Command.kt @@ -91,7 +91,7 @@ abstract class Command : CardSessionRunnable { } } is CompletionResult.Failure -> - if (result.error == SessionError.TagLost()) { + if (result.error is SessionError.TagLost) { session.viewDelegate.onTagLost() } else { callback(CompletionResult.Failure(result.error)) From 9580b977479ab8eedeabe9d219251d7533d7dc9c Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 15 Apr 2020 14:38:55 +0300 Subject: [PATCH 09/10] Updated on 2026-08-14 --- .../java/com/tangem/commands/ReadCommand.kt | 110 ++++++++---------- .../personalization/PersonalizeCommand.kt | 4 +- .../personalization/entities/CardConfig.kt | 4 +- .../java/com/tangem/common/tlv/TlvDecoder.kt | 4 +- .../java/com/tangem/common/tlv/TlvEncoder.kt | 4 +- .../main/java/com/tangem/tasks/ScanTask.kt | 2 +- .../com/tangem/common/tlv/TlvDecoderTest.kt | 24 ++-- .../tangemtest/extensions/CardConfig.kt | 40 ++++--- .../domain/responses/ResponseJsonConverter.kt | 6 +- .../PersonalizationConfigToCardConfig.kt | 44 ++++--- .../responses/converter/CardConverter.kt | 2 +- 11 files changed, 127 insertions(+), 117 deletions(-) diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt index 09c188072d..37caa1f0a5 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt @@ -14,67 +14,50 @@ import java.util.* /** * Determines which type of data is required for signing. */ -data class SigningMethod(val rawValue: Int) { +data class SigningMethodMask(val rawValue: Int) { - fun contains(value: Int): Boolean { + fun contains(signingMethod: SigningMethod): Boolean { return if (rawValue and 0x80 == 0) { - value == rawValue + signingMethod.code == rawValue } else { - rawValue and (0x01 shl value) != 0 + rawValue and (0x01 shl signingMethod.code) != 0 } } +} - companion object { - const val signHash = 0 - const val signRaw = 1 - const val signHashValidatedByIssuer = 2 - const val signRawValidatedByIssuer = 3 - const val signHashValidatedByIssuerAndWriteIssuerData = 4 - const val signRawValidatedByIssuerAndWriteIssuerData = 5 - const val signPos = 6 +enum class SigningMethod(val code: Int) { + SIGN_HASH(0), + SIGN_RAW(1), + SIGN_HASH_VALIDATE_BY_ISSUER(2), + SIGN_RAW_VALIDATE_BY_ISSUER(3), + SIGN_HASH_VALIDATE_BY_ISSUER_WRITE_ISSUER_DATA(4), + SIGN_RAW_VALIDATE_BY_ISSUER_WRITE_ISSUER_DATA(5), + SIGN_POS(6) +} - fun build( - signHash: Boolean = false, - signRaw: Boolean = false, - signHashValidatedByIssuer: Boolean = false, - signRawValidatedByIssuer: Boolean = false, - signHashValidatedByIssuerAndWriteIssuerData: Boolean = false, - signRawValidatedByIssuerAndWriteIssuerData: Boolean = false, - signPos: Boolean = false +class SigningMethodMaskBuilder() { - ): SigningMethod { - fun Boolean.toInt() = if (this) 1 else 0 + private val signingMethods = mutableSetOf() - val signingMethodsCount = 0 + - signHash.toInt() + - signRaw.toInt() + - signHashValidatedByIssuer.toInt() + - signRawValidatedByIssuer.toInt() + - signHashValidatedByIssuerAndWriteIssuerData.toInt() + - signRawValidatedByIssuerAndWriteIssuerData.toInt() + - signPos.toInt() + fun add(signingMethod: SigningMethod) { + signingMethods.add(signingMethod) + } - var signingMethod: Int = 0 - if (signingMethodsCount == 1) { - if (signHash) signingMethod += SigningMethod.signHash - if (signRaw) signingMethod += SigningMethod.signRaw - if (signHashValidatedByIssuer) signingMethod += SigningMethod.signHashValidatedByIssuer - if (signRawValidatedByIssuer) signingMethod += SigningMethod.signRawValidatedByIssuer - if (signHashValidatedByIssuerAndWriteIssuerData) signingMethod += SigningMethod.signHashValidatedByIssuerAndWriteIssuerData - if (signRawValidatedByIssuerAndWriteIssuerData) signingMethod += SigningMethod.signRawValidatedByIssuerAndWriteIssuerData - if (signPos) signingMethod += SigningMethod.signPos - } else if (signingMethodsCount > 1) { - signingMethod = 0x80 - if (signHash) signingMethod += 0x01 - if (signRaw) signingMethod += 0x01 shl SigningMethod.signRaw - if (signHashValidatedByIssuer) signingMethod += 0x01 shl SigningMethod.signHashValidatedByIssuer - if (signRawValidatedByIssuer) signingMethod += 0x01 shl SigningMethod.signRawValidatedByIssuer - if (signHashValidatedByIssuerAndWriteIssuerData) signingMethod += 0x01 shl SigningMethod.signHashValidatedByIssuerAndWriteIssuerData - if (signRawValidatedByIssuerAndWriteIssuerData) signingMethod += 0x01 shl SigningMethod.signRawValidatedByIssuerAndWriteIssuerData - if (signPos) signingMethod += 0x01 shl SigningMethod.signPos + fun build(): SigningMethodMask { + val rawValue: Int = when { + signingMethods.count() == 0 -> { + 0 + } + signingMethods.count() == 1 -> { + signingMethods.iterator().next().code + } + else -> { + signingMethods.fold( + 0x80, { acc, singingMethod -> acc + (0x01 shl singingMethod.code) } + ) } - return SigningMethod(signingMethod) } + return SigningMethodMask(rawValue) } } @@ -113,22 +96,23 @@ enum class CardStatus(val code: Int) { */ data class ProductMask(val rawValue: Int) { - fun contains(value: Int): Boolean = (rawValue and value) != 0 + fun contains(product: Product): Boolean = (rawValue and product.code) != 0 - companion object { - const val note = 0x01 - const val tag = 0x02 - const val idCard = 0x04 - const val idIssuer = 0x08 - } +} + +enum class Product(val code: Int) { + NOTE(0x01), + TAG(0x02), + ID_CARD(0x04), + ID_ISSUER(0x08) } class ProductMaskBuilder() { private var productMaskValue = 0 - fun add(productCode: Int) { - productMaskValue = productMaskValue or productCode + fun add(product: Product) { + productMaskValue = productMaskValue or product.code } fun build() = ProductMask(productMaskValue) @@ -299,7 +283,7 @@ class Card( /** * Defines what data should be submitted to SIGN command. */ - val signingMethod: SigningMethod?, + val signingMethods: SigningMethodMask?, /** * Delay in seconds before COS executes commands protected by PIN2. @@ -343,21 +327,21 @@ class Card( val activationSeed: ByteArray?, /** - * Returned only if [SigningMethod.SignPos] enabling POS transactions is supported by card. + * Returned only if [SigningMethod.SIGN_POS] enabling POS transactions is supported by card. */ val paymentFlowVersion: ByteArray?, /** * This value can be initialized by terminal and will be increased by COS on execution of every [SignCommand]. * For example, this field can store blockchain “nonce” for quick one-touch transaction on POS terminals. - * Returned only if [SigningMethod.SignPos] enabling POS transactions is supported by card. + * Returned only if [SigningMethod.SIGN_POS] enabling POS transactions is supported by card. */ val userCounter: Int?, /** * This value can be initialized by App (with PIN2 confirmation) and will be increased by COS * with the execution of each [SignCommand]. For example, this field can store blockchain “nonce” - * for a quick one-touch transaction on POS terminals. Returned only if [SigningMethod.SignPos]. + * for a quick one-touch transaction on POS terminals. Returned only if [SigningMethod.SIGN_POS]. */ val userProtectedCounter: Int?, @@ -415,7 +399,7 @@ class ReadCommand : Command() { issuerPublicKey = decoder.decodeOptional(TlvTag.IssuerDataPublicKey), curve = decoder.decodeOptional(TlvTag.CurveId), maxSignatures = decoder.decodeOptional(TlvTag.MaxSignatures), - signingMethod = decoder.decodeOptional(TlvTag.SigningMethod), + signingMethods = decoder.decodeOptional(TlvTag.SigningMethod), pauseBeforePin2 = decoder.decodeOptional(TlvTag.PauseBeforePin2), walletPublicKey = decoder.decodeOptional(TlvTag.WalletPublicKey), walletRemainingSignatures = decoder.decodeOptional(TlvTag.RemainingSignatures), diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt index 24a118322a..e4fe1a8b15 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt @@ -60,7 +60,7 @@ class PersonalizeCommand( issuerPublicKey = decoder.decodeOptional(TlvTag.IssuerDataPublicKey), curve = decoder.decodeOptional(TlvTag.CurveId), maxSignatures = decoder.decodeOptional(TlvTag.MaxSignatures), - signingMethod = decoder.decodeOptional(TlvTag.SigningMethod), + signingMethods = decoder.decodeOptional(TlvTag.SigningMethod), pauseBeforePin2 = decoder.decodeOptional(TlvTag.PauseBeforePin2), walletPublicKey = decoder.decodeOptional(TlvTag.WalletPublicKey), walletRemainingSignatures = decoder.decodeOptional(TlvTag.RemainingSignatures), @@ -105,7 +105,7 @@ class PersonalizeCommand( tlvBuilder.append(TlvTag.CardId, cardId) tlvBuilder.append(TlvTag.CurveId, config.curveID) tlvBuilder.append(TlvTag.MaxSignatures, config.maxSignatures) - tlvBuilder.append(TlvTag.SigningMethod, config.signingMethod) + tlvBuilder.append(TlvTag.SigningMethod, config.signingMethods) tlvBuilder.append(TlvTag.SettingsMask, config.createSettingsMask()) tlvBuilder.append(TlvTag.PauseBeforePin2, config.pauseBeforePin2 / 10) tlvBuilder.append(TlvTag.Cvc, config.cvc.toByteArray()) diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfig.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfig.kt index bc1a082237..2cfd173be1 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfig.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfig.kt @@ -2,7 +2,7 @@ package com.tangem.commands.personalization.entities import com.tangem.commands.CardData import com.tangem.commands.EllipticCurve -import com.tangem.commands.SigningMethod +import com.tangem.commands.SigningMethodMask data class NdefRecord( val type: Type, @@ -33,7 +33,7 @@ data class CardConfig( val pauseBeforePin2: Int, val smartSecurityDelay: Boolean, val curveID: EllipticCurve, - val signingMethod: SigningMethod, + val signingMethods: SigningMethodMask, val maxSignatures: Int, val isReusable: Boolean, val allowSwapPin: Boolean, diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt index 48c14edec4..4954c38c46 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt @@ -122,9 +122,9 @@ class TlvDecoder(val tlvList: List) { } } TlvValueType.SigningMethod -> { - typeCheck(tag) + typeCheck(tag) try { - SigningMethod(tlvValue.toInt()) as T + SigningMethodMask(tlvValue.toInt()) as T } catch (exception: Exception) { logException(tag, tlvValue.toInt().toString(), exception) throw SessionError.DecodingFailed() diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt index 1855badcab..057b0807e8 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt @@ -87,8 +87,8 @@ class TlvEncoder { (value as CardStatus).code.toByteArray() } TlvValueType.SigningMethod -> { - typeCheck(tag) - byteArrayOf((value as SigningMethod).rawValue.toByte()) + typeCheck(tag) + byteArrayOf((value as SigningMethodMask).rawValue.toByte()) } TlvValueType.IssuerDataMode -> { typeCheck(tag) diff --git a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt index 207cb6f705..3ce71ce30e 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt @@ -20,7 +20,7 @@ internal class ScanTask : CardSessionRunnable { if (card == null) { callback(CompletionResult.Failure(SessionError.MissingPreflightRead())) - } else if (card.cardData?.productMask?.contains(ProductMask.tag) != false) { + } else if (card.cardData?.productMask?.contains(Product.TAG) != false) { callback(CompletionResult.Success(card)) } else if (card.status != CardStatus.Loaded) { diff --git a/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt b/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt index f9915fc7b8..cbb9780c47 100644 --- a/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt +++ b/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt @@ -82,8 +82,8 @@ class TlvDecoderTest { @Test fun `map SigningMethods single value returns correct value`() { - val signingMethods: SigningMethod = tlvMapper.decode(TlvTag.SigningMethod) - assertThat(signingMethods.contains(SigningMethod.signHash)) + val signingMethods: SigningMethodMask = tlvMapper.decode(TlvTag.SigningMethod) + assertThat(signingMethods.contains(SigningMethod.SIGN_HASH)) .isTrue() } @@ -91,20 +91,20 @@ class TlvDecoderTest { fun `map SigningMethods set of methods returns correct value`() { val localMapper = TlvDecoder(Tlv.deserialize("070195".hexToBytes())!!) - val signingMethod: SigningMethod = localMapper.decode(TlvTag.SigningMethod) - assertThat(signingMethod.contains(SigningMethod.signHash)) + val signingMethods: SigningMethodMask = localMapper.decode(TlvTag.SigningMethod) + assertThat(signingMethods.contains(SigningMethod.SIGN_HASH)) .isTrue() - assertThat(signingMethod.contains(SigningMethod.signHashValidatedByIssuer)) + assertThat(signingMethods.contains(SigningMethod.SIGN_HASH_VALIDATE_BY_ISSUER)) .isTrue() - assertThat(signingMethod.contains(SigningMethod.signHashValidatedByIssuerAndWriteIssuerData)) + assertThat(signingMethods.contains(SigningMethod.SIGN_HASH_VALIDATE_BY_ISSUER_WRITE_ISSUER_DATA)) .isTrue() - assertThat(signingMethod.contains(SigningMethod.signRaw)) + assertThat(signingMethods.contains(SigningMethod.SIGN_RAW)) .isFalse() - assertThat(signingMethod.contains(SigningMethod.signRawValidatedByIssuer)) + assertThat(signingMethods.contains(SigningMethod.SIGN_RAW_VALIDATE_BY_ISSUER)) .isFalse() - assertThat(signingMethod.contains(SigningMethod.signRawValidatedByIssuerAndWriteIssuerData)) + assertThat(signingMethods.contains(SigningMethod.SIGN_RAW_VALIDATE_BY_ISSUER_WRITE_ISSUER_DATA)) .isFalse() - assertThat(signingMethod.contains(SigningMethod.signPos)) + assertThat(signingMethods.contains(SigningMethod.SIGN_POS)) .isFalse() } @@ -119,7 +119,7 @@ class TlvDecoderTest { fun `map ProductMask with raw value 5 returns correct value`() { val localMapper = TlvDecoder(listOf(Tlv(TlvTag.ProductMask, byteArrayOf(5)))) val productMask: ProductMask = localMapper.decode(TlvTag.ProductMask) - assertThat(productMask.contains(ProductMask.note) && productMask.contains(ProductMask.idCard)) + assertThat(productMask.contains(Product.NOTE) && productMask.contains(Product.ID_CARD)) .isTrue() } @@ -127,7 +127,7 @@ class TlvDecoderTest { fun `map ProductMask with raw value 1 returns correct value`() { val localMapper = TlvDecoder(listOf(Tlv(TlvTag.ProductMask, byteArrayOf(1)))) val productMask: ProductMask = localMapper.decode(TlvTag.ProductMask) - assertThat(productMask.contains(ProductMask.note)) + assertThat(productMask.contains(Product.NOTE)) .isTrue() } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt index c8f68bec9c..134f110c7d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt @@ -11,24 +11,38 @@ fun CardConfig.Companion.create(application: Application): CardConfig { val preferences = application.getSharedPreferences("prefs", Context.MODE_PRIVATE) - val signingMethod = SigningMethod.build( - signHash = preferences.getBoolean("personalization_SigningMethod_0", false), - signRaw = preferences.getBoolean("personalization_SigningMethod_1", false), - signHashValidatedByIssuer = preferences.getBoolean("personalization_SigningMethod_2", false), - signRawValidatedByIssuer = preferences.getBoolean("personalization_SigningMethod_3", false), - signHashValidatedByIssuerAndWriteIssuerData = preferences.getBoolean("personalization_SigningMethod_4", false), - signRawValidatedByIssuerAndWriteIssuerData = preferences.getBoolean("personalization_SigningMethod_5", false), - signPos = preferences.getBoolean("personalization_SigningMethod_6", false) - ) + val signingMethodMaskBuilder = SigningMethodMaskBuilder() + if (preferences.getBoolean("personalization_SigningMethod_0", false)) { + signingMethodMaskBuilder.add(SigningMethod.SIGN_HASH) + } + if (preferences.getBoolean("personalization_SigningMethod_1", false)) { + signingMethodMaskBuilder.add(SigningMethod.SIGN_RAW) + } + if (preferences.getBoolean("personalization_SigningMethod_2", false)) { + signingMethodMaskBuilder.add(SigningMethod.SIGN_HASH_VALIDATE_BY_ISSUER) + } + if (preferences.getBoolean("personalization_SigningMethod_3", false)) { + signingMethodMaskBuilder.add(SigningMethod.SIGN_RAW_VALIDATE_BY_ISSUER) + } + if (preferences.getBoolean("personalization_SigningMethod_4", false)) { + signingMethodMaskBuilder.add(SigningMethod.SIGN_HASH_VALIDATE_BY_ISSUER_WRITE_ISSUER_DATA) + } + if (preferences.getBoolean("personalization_SigningMethod_5", false)) { + signingMethodMaskBuilder.add(SigningMethod.SIGN_RAW_VALIDATE_BY_ISSUER_WRITE_ISSUER_DATA) + } + if (preferences.getBoolean("personalization_SigningMethod_6", false)) { + signingMethodMaskBuilder.add(SigningMethod.SIGN_HASH) + } + val signingMethod = signingMethodMaskBuilder.build() val isNote = preferences.getBoolean("personalization_ProductMask_IsNote", true) val isTag = preferences.getBoolean("personalization_ProductMask_IsTag", false) val isIdCard = preferences.getBoolean("personalization_ProductMask_IsIDCard", false) val productMaskBuilder = ProductMaskBuilder() - if (isNote) productMaskBuilder.add(ProductMask.note) - if (isTag) productMaskBuilder.add(ProductMask.tag) - if (isIdCard) productMaskBuilder.add(ProductMask.idCard) + if (isNote) productMaskBuilder.add(Product.NOTE) + if (isTag) productMaskBuilder.add(Product.TAG) + if (isIdCard) productMaskBuilder.add(Product.ID_CARD) val productMask = productMaskBuilder.build() var tokenSymbol: String? = null @@ -80,7 +94,7 @@ fun CardConfig.Companion.create(application: Application): CardConfig { cardData = cardData, curveID = EllipticCurve.byName(preferences.getString("personalization_CurveId", "secp256k1")!!) ?: EllipticCurve.Secp256k1, - signingMethod = signingMethod, + signingMethods = signingMethod, createWallet = preferences.getBoolean("personalization_CreateWallet", true), maxSignatures = preferences.getString("personalization_MaxSignatures", "1000")!!.toInt(), isReusable = preferences.getBoolean("personalization_SettingsMask_IsReusable", true), diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt index d54bc08a5d..444f2bd35a 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt @@ -16,7 +16,7 @@ class ResponseJsonConverter { private fun init(): Gson { val builder = GsonBuilder().apply { registerTypeAdapter(ByteArray::class.java, ByteTypeAdapter()) - registerTypeAdapter(SigningMethod::class.java, SigningMethodTypeAdapter()) + registerTypeAdapter(SigningMethodMask::class.java, SigningMethodTypeAdapter()) registerTypeAdapter(SettingsMask::class.java, SettingsMaskTypeAdapter()) registerTypeAdapter(ProductMask::class.java, ProductMaskTypeAdapter()) registerTypeAdapter(Date::class.java, DateTypeAdapter()) @@ -50,8 +50,8 @@ class ProductMaskTypeAdapter : JsonSerializer { } } -class SigningMethodTypeAdapter : JsonSerializer { - override fun serialize(src: SigningMethod, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { +class SigningMethodTypeAdapter : JsonSerializer { + override fun serialize(src: SigningMethodMask, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { return JsonPrimitive(src.rawValue.toString()) } } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt index fa79df96e0..537c21eba5 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt @@ -1,8 +1,6 @@ package com.tangem.tangemtest.ucase.variants.personalize.converter -import com.tangem.commands.CardData -import com.tangem.commands.EllipticCurve -import com.tangem.commands.ProductMaskBuilder +import com.tangem.commands.* import com.tangem.commands.personalization.entities.CardConfig import com.tangem.commands.personalization.entities.NdefRecord import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig @@ -12,15 +10,29 @@ import java.util.* class PersonalizationConfigToCardConfig : Converter { override fun convert(from: PersonalizationConfig): CardConfig { - val signingMethod = com.tangem.commands.SigningMethod.build( - signHash = from.SigningMethod0, - signRaw = from.SigningMethod1, - signHashValidatedByIssuer = from.SigningMethod2, - signRawValidatedByIssuer = from.SigningMethod3, - signHashValidatedByIssuerAndWriteIssuerData = from.SigningMethod4, - signRawValidatedByIssuerAndWriteIssuerData = from.SigningMethod5, - signPos = from.SigningMethod6 - ) + val signingMethodMaskBuilder = SigningMethodMaskBuilder() + if (from.SigningMethod0) { + signingMethodMaskBuilder.add(SigningMethod.SIGN_HASH) + } + if (from.SigningMethod1) { + signingMethodMaskBuilder.add(SigningMethod.SIGN_RAW) + } + if (from.SigningMethod2) { + signingMethodMaskBuilder.add(SigningMethod.SIGN_HASH_VALIDATE_BY_ISSUER) + } + if (from.SigningMethod3) { + signingMethodMaskBuilder.add(SigningMethod.SIGN_RAW_VALIDATE_BY_ISSUER) + } + if (from.SigningMethod4) { + signingMethodMaskBuilder.add(SigningMethod.SIGN_HASH_VALIDATE_BY_ISSUER_WRITE_ISSUER_DATA) + } + if (from.SigningMethod5) { + signingMethodMaskBuilder.add(SigningMethod.SIGN_RAW_VALIDATE_BY_ISSUER_WRITE_ISSUER_DATA) + } + if (from.SigningMethod6) { + signingMethodMaskBuilder.add(SigningMethod.SIGN_HASH) + } + val signingMethod = signingMethodMaskBuilder.build() val isNote = from.cardData.product_note val isTag = from.cardData.product_tag @@ -28,10 +40,10 @@ class PersonalizationConfigToCardConfig : Converter { group.addItem(TextItem(CardId.issuerPublicKey, from.issuerPublicKey?.toHexString())) group.addItem(TextItem(CardId.curve, stringOf(from.curve))) group.addItem(TextItem(CardId.maxSignatures, stringOf(from.maxSignatures))) - group.addItem(TextItem(CardId.signingMethod, stringOf(from.signingMethod?.rawValue))) + group.addItem(TextItem(CardId.signingMethod, stringOf(from.signingMethods?.rawValue))) group.addItem(TextItem(CardId.pauseBeforePin2, stringOf(from.pauseBeforePin2))) group.addItem(TextItem(CardId.walletPublicKey, stringOf(from.walletPublicKey))) group.addItem(TextItem(CardId.walletRemainingSignatures, stringOf(from.walletRemainingSignatures))) From 00cb3c37d269b9dd65469334008ff7257cb8a8c1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 15 Apr 2020 15:33:38 +0300 Subject: [PATCH 10/10] Updated on 2026-08-14 --- .../java/com/tangem/commands/ReadCommand.kt | 28 +++++++++---------- .../main/java/com/tangem/tasks/ScanTask.kt | 2 +- .../com/tangem/common/tlv/TlvDecoderTest.kt | 20 ++++++------- .../tangemtest/extensions/CardConfig.kt | 20 ++++++------- .../PersonalizationConfigToCardConfig.kt | 22 +++++++-------- 5 files changed, 46 insertions(+), 46 deletions(-) diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt index 37caa1f0a5..e3b5d4ccee 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt @@ -26,13 +26,13 @@ data class SigningMethodMask(val rawValue: Int) { } enum class SigningMethod(val code: Int) { - SIGN_HASH(0), - SIGN_RAW(1), - SIGN_HASH_VALIDATE_BY_ISSUER(2), - SIGN_RAW_VALIDATE_BY_ISSUER(3), - SIGN_HASH_VALIDATE_BY_ISSUER_WRITE_ISSUER_DATA(4), - SIGN_RAW_VALIDATE_BY_ISSUER_WRITE_ISSUER_DATA(5), - SIGN_POS(6) + SignHash(0), + SignRaw(1), + SignHashValidateByIssuer(2), + SignRawValidateByIssuer(3), + SignHashValidateByIssuerWriteIssuerData(4), + SignRawValidateByIssuerWriteIssuerData(5), + SignPos(6) } class SigningMethodMaskBuilder() { @@ -101,10 +101,10 @@ data class ProductMask(val rawValue: Int) { } enum class Product(val code: Int) { - NOTE(0x01), - TAG(0x02), - ID_CARD(0x04), - ID_ISSUER(0x08) + Note(0x01), + Tag(0x02), + IdCard(0x04), + IdIssuer(0x08) } class ProductMaskBuilder() { @@ -327,21 +327,21 @@ class Card( val activationSeed: ByteArray?, /** - * Returned only if [SigningMethod.SIGN_POS] enabling POS transactions is supported by card. + * Returned only if [SigningMethod.SignPos] enabling POS transactions is supported by card. */ val paymentFlowVersion: ByteArray?, /** * This value can be initialized by terminal and will be increased by COS on execution of every [SignCommand]. * For example, this field can store blockchain “nonce” for quick one-touch transaction on POS terminals. - * Returned only if [SigningMethod.SIGN_POS] enabling POS transactions is supported by card. + * Returned only if [SigningMethod.SignPos] enabling POS transactions is supported by card. */ val userCounter: Int?, /** * This value can be initialized by App (with PIN2 confirmation) and will be increased by COS * with the execution of each [SignCommand]. For example, this field can store blockchain “nonce” - * for a quick one-touch transaction on POS terminals. Returned only if [SigningMethod.SIGN_POS]. + * for a quick one-touch transaction on POS terminals. Returned only if [SigningMethod.SignPos]. */ val userProtectedCounter: Int?, diff --git a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt index 3ce71ce30e..39dea59f0b 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt @@ -20,7 +20,7 @@ internal class ScanTask : CardSessionRunnable { if (card == null) { callback(CompletionResult.Failure(SessionError.MissingPreflightRead())) - } else if (card.cardData?.productMask?.contains(Product.TAG) != false) { + } else if (card.cardData?.productMask?.contains(Product.Tag) != false) { callback(CompletionResult.Success(card)) } else if (card.status != CardStatus.Loaded) { diff --git a/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt b/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt index cbb9780c47..fbd4514c11 100644 --- a/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt +++ b/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt @@ -83,7 +83,7 @@ class TlvDecoderTest { @Test fun `map SigningMethods single value returns correct value`() { val signingMethods: SigningMethodMask = tlvMapper.decode(TlvTag.SigningMethod) - assertThat(signingMethods.contains(SigningMethod.SIGN_HASH)) + assertThat(signingMethods.contains(SigningMethod.SignHash)) .isTrue() } @@ -92,19 +92,19 @@ class TlvDecoderTest { val localMapper = TlvDecoder(Tlv.deserialize("070195".hexToBytes())!!) val signingMethods: SigningMethodMask = localMapper.decode(TlvTag.SigningMethod) - assertThat(signingMethods.contains(SigningMethod.SIGN_HASH)) + assertThat(signingMethods.contains(SigningMethod.SignHash)) .isTrue() - assertThat(signingMethods.contains(SigningMethod.SIGN_HASH_VALIDATE_BY_ISSUER)) + assertThat(signingMethods.contains(SigningMethod.SignHashValidateByIssuer)) .isTrue() - assertThat(signingMethods.contains(SigningMethod.SIGN_HASH_VALIDATE_BY_ISSUER_WRITE_ISSUER_DATA)) + assertThat(signingMethods.contains(SigningMethod.SignHashValidateByIssuerWriteIssuerData)) .isTrue() - assertThat(signingMethods.contains(SigningMethod.SIGN_RAW)) + assertThat(signingMethods.contains(SigningMethod.SignRaw)) .isFalse() - assertThat(signingMethods.contains(SigningMethod.SIGN_RAW_VALIDATE_BY_ISSUER)) + assertThat(signingMethods.contains(SigningMethod.SignRawValidateByIssuer)) .isFalse() - assertThat(signingMethods.contains(SigningMethod.SIGN_RAW_VALIDATE_BY_ISSUER_WRITE_ISSUER_DATA)) + assertThat(signingMethods.contains(SigningMethod.SignRawValidateByIssuerWriteIssuerData)) .isFalse() - assertThat(signingMethods.contains(SigningMethod.SIGN_POS)) + assertThat(signingMethods.contains(SigningMethod.SignPos)) .isFalse() } @@ -119,7 +119,7 @@ class TlvDecoderTest { fun `map ProductMask with raw value 5 returns correct value`() { val localMapper = TlvDecoder(listOf(Tlv(TlvTag.ProductMask, byteArrayOf(5)))) val productMask: ProductMask = localMapper.decode(TlvTag.ProductMask) - assertThat(productMask.contains(Product.NOTE) && productMask.contains(Product.ID_CARD)) + assertThat(productMask.contains(Product.Note) && productMask.contains(Product.IdCard)) .isTrue() } @@ -127,7 +127,7 @@ class TlvDecoderTest { fun `map ProductMask with raw value 1 returns correct value`() { val localMapper = TlvDecoder(listOf(Tlv(TlvTag.ProductMask, byteArrayOf(1)))) val productMask: ProductMask = localMapper.decode(TlvTag.ProductMask) - assertThat(productMask.contains(Product.NOTE)) + assertThat(productMask.contains(Product.Note)) .isTrue() } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt index 134f110c7d..ab8c84334b 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt @@ -13,25 +13,25 @@ fun CardConfig.Companion.create(application: Application): CardConfig { val signingMethodMaskBuilder = SigningMethodMaskBuilder() if (preferences.getBoolean("personalization_SigningMethod_0", false)) { - signingMethodMaskBuilder.add(SigningMethod.SIGN_HASH) + signingMethodMaskBuilder.add(SigningMethod.SignHash) } if (preferences.getBoolean("personalization_SigningMethod_1", false)) { - signingMethodMaskBuilder.add(SigningMethod.SIGN_RAW) + signingMethodMaskBuilder.add(SigningMethod.SignRaw) } if (preferences.getBoolean("personalization_SigningMethod_2", false)) { - signingMethodMaskBuilder.add(SigningMethod.SIGN_HASH_VALIDATE_BY_ISSUER) + signingMethodMaskBuilder.add(SigningMethod.SignHashValidateByIssuer) } if (preferences.getBoolean("personalization_SigningMethod_3", false)) { - signingMethodMaskBuilder.add(SigningMethod.SIGN_RAW_VALIDATE_BY_ISSUER) + signingMethodMaskBuilder.add(SigningMethod.SignRawValidateByIssuer) } if (preferences.getBoolean("personalization_SigningMethod_4", false)) { - signingMethodMaskBuilder.add(SigningMethod.SIGN_HASH_VALIDATE_BY_ISSUER_WRITE_ISSUER_DATA) + signingMethodMaskBuilder.add(SigningMethod.SignHashValidateByIssuerWriteIssuerData) } if (preferences.getBoolean("personalization_SigningMethod_5", false)) { - signingMethodMaskBuilder.add(SigningMethod.SIGN_RAW_VALIDATE_BY_ISSUER_WRITE_ISSUER_DATA) + signingMethodMaskBuilder.add(SigningMethod.SignRawValidateByIssuerWriteIssuerData) } if (preferences.getBoolean("personalization_SigningMethod_6", false)) { - signingMethodMaskBuilder.add(SigningMethod.SIGN_HASH) + signingMethodMaskBuilder.add(SigningMethod.SignHash) } val signingMethod = signingMethodMaskBuilder.build() @@ -40,9 +40,9 @@ fun CardConfig.Companion.create(application: Application): CardConfig { val isIdCard = preferences.getBoolean("personalization_ProductMask_IsIDCard", false) val productMaskBuilder = ProductMaskBuilder() - if (isNote) productMaskBuilder.add(Product.NOTE) - if (isTag) productMaskBuilder.add(Product.TAG) - if (isIdCard) productMaskBuilder.add(Product.ID_CARD) + if (isNote) productMaskBuilder.add(Product.Note) + if (isTag) productMaskBuilder.add(Product.Tag) + if (isIdCard) productMaskBuilder.add(Product.IdCard) val productMask = productMaskBuilder.build() var tokenSymbol: String? = null diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt index 537c21eba5..a1c710527b 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt @@ -12,25 +12,25 @@ class PersonalizationConfigToCardConfig : Converter