diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinTransactionBuilder.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinTransactionBuilder.kt index 24f072b218..ef0cbe5500 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinTransactionBuilder.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinTransactionBuilder.kt @@ -10,20 +10,20 @@ import org.bitcoinj.script.ScriptBuilder import java.math.BigDecimal import java.math.BigInteger -class BitcoinTransactionBuilder( +open class BitcoinTransactionBuilder( private val walletPublicKey: ByteArray, private val testNet: Boolean = false ) { private lateinit var transaction: Transaction - private var networkParameters: NetworkParameters? = null - var unspentOutputs: List? = null + protected var networkParameters: NetworkParameters? = null + var unspentOutputs: List? = null - fun buildToSign( + open fun buildToSign( transactionData: TransactionData): Result> { if (unspentOutputs == null) return Result.Failure(Exception("Currently there's an unconfirmed transaction")) - val change: BigDecimal = calculateChange(transactionData) + val change: BigDecimal = calculateChange(transactionData, unspentOutputs!!) networkParameters = if (testNet) { NetworkParameters.fromID(NetworkParameters.ID_TESTNET) @@ -40,27 +40,13 @@ class BitcoinTransactionBuilder( return Result.Success(hashesForSign) } - private fun calculateChange(transactionData: TransactionData): BigDecimal { - val fullAmount = unspentOutputs!!.map { it.amount }.reduce { acc, number -> acc + number } - return fullAmount - (transactionData.amount.value!! + (transactionData.fee?.value - ?: 0.toBigDecimal())) - } - - fun buildToSend(signedTransaction: ByteArray): ByteArray { + open fun buildToSend(signedTransaction: ByteArray): ByteArray { for (index in transaction.inputs.indices) { transaction.inputs[index].scriptSig = createScript(index, signedTransaction, walletPublicKey) } return transaction.bitcoinSerialize() } - 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 canonicalS = ECKey.ECDSASignature(r, s).toCanonicalised().s - val signature = TransactionSignature(r, canonicalS) - return ScriptBuilder.createInputScript(signature, ECKey.fromPublicOnly(publicKey)) - } - fun getEstimateSize(transactionData: TransactionData): Result { val buildTransactionResult = buildToSign(transactionData) when (buildTransactionResult) { @@ -72,14 +58,28 @@ class BitcoinTransactionBuilder( } } } + + fun calculateChange(transactionData: TransactionData, unspentOutputs: List): BigDecimal { + val fullAmount = unspentOutputs!!.map { it.amount }.reduce { acc, number -> acc + number } + return fullAmount - (transactionData.amount.value!! + (transactionData.fee?.value + ?: 0.toBigDecimal())) + } + + open 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 canonicalS = ECKey.ECDSASignature(r, s).toCanonicalised().s + val signature = TransactionSignature(r, canonicalS) + return ScriptBuilder.createInputScript(signature, ECKey.fromPublicOnly(publicKey)) + } } internal fun TransactionData.toBitcoinJTransaction(networkParameters: NetworkParameters?, - unspentOutputs: List, + unspentOutputs: List, change: BigDecimal): Transaction { val transaction = Transaction(networkParameters) for (utxo in unspentOutputs) { - transaction.addInput(Sha256Hash.wrap(utxo.hash), utxo.outputIndex, Script(utxo.outputScript)) + transaction.addInput(Sha256Hash.wrap(utxo.transactionHash), utxo.outputIndex, Script(utxo.outputScript)) } transaction.addOutput( Coin.parseCoin(this.amount.value!!.toPlainString()), @@ -93,9 +93,9 @@ internal fun TransactionData.toBitcoinJTransaction(networkParameters: NetworkPar return transaction } -class UnspentTransaction( +class BitcoinUnspentOutput( val amount: BigDecimal, val outputIndex: Long, - val hash: ByteArray, + val transactionHash: ByteArray, val outputScript: ByteArray ) \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinWalletManager.kt index 8d38566e23..0670f32ad4 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinWalletManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinWalletManager.kt @@ -2,7 +2,7 @@ package com.tangem.blockchain.blockchains.bitcoin import android.util.Log import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinAddressResponse -import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinNetworkManager +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinProvider import com.tangem.blockchain.common.* import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.SimpleResult @@ -10,14 +10,14 @@ import com.tangem.common.CompletionResult import com.tangem.common.extensions.toHexString import java.math.BigDecimal -class BitcoinWalletManager( +open class BitcoinWalletManager( cardId: String, wallet: Wallet, private val transactionBuilder: BitcoinTransactionBuilder, - private val networkManager: BitcoinNetworkManager + private val networkManager: BitcoinProvider ) : WalletManager(cardId, wallet), TransactionSender { - private val blockchain = wallet.blockchain + protected val blockchain = wallet.blockchain override suspend fun update() { val response = networkManager.getInfo(wallet.address) @@ -30,7 +30,7 @@ class BitcoinWalletManager( private fun updateWallet(response: BitcoinAddressResponse) { Log.d(this::class.java.simpleName, "Balance is ${response.balance}") wallet.amounts[AmountType.Coin]?.value = response.balance - transactionBuilder.unspentOutputs = response.unspentTransactions + transactionBuilder.unspentOutputs = response.unspentOutputs if (response.hasUnconfirmed) { if (wallet.transactions.isEmpty()) wallet.addIncomingTransaction() } else { @@ -44,8 +44,7 @@ class BitcoinWalletManager( } override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult { - val buildTransactionResult = transactionBuilder.buildToSign(transactionData) - when (buildTransactionResult) { + when (val buildTransactionResult = transactionBuilder.buildToSign(transactionData)) { is Result.Failure -> return SimpleResult.Failure(buildTransactionResult.error) is Result.Success -> { when (val signerResponse = signer.sign(buildTransactionResult.data.toTypedArray(), cardId)) { @@ -60,8 +59,8 @@ class BitcoinWalletManager( } override suspend fun getFee(amount: Amount, destination: String): Result> { - when (val result = networkManager.getFee()) { - is Result.Failure -> return result + when (val feeResult = networkManager.getFee()) { + is Result.Failure -> return feeResult is Result.Success -> { val feeValue = BigDecimal.ONE.movePointLeft(blockchain.decimals()) amount.value = amount.value!! - feeValue @@ -72,9 +71,9 @@ class BitcoinWalletManager( is Result.Failure -> return sizeResult is Result.Success -> { val transactionSize = sizeResult.data.toBigDecimal() - val minFee = result.data.minimalPerKb.calculateFee(transactionSize) - val normalFee = result.data.normalPerKb.calculateFee(transactionSize) - val priorityFee = result.data.priorityPerKb.calculateFee(transactionSize) + val minFee = feeResult.data.minimalPerKb.calculateFee(transactionSize) + val normalFee = feeResult.data.normalPerKb.calculateFee(transactionSize) + val priorityFee = feeResult.data.priorityPerKb.calculateFee(transactionSize) return Result.Success( listOf(Amount(minFee, blockchain), Amount(normalFee, blockchain), diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BitcoinNetworkManager.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BitcoinNetworkManager.kt index 5810ede23f..8483883d82 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BitcoinNetworkManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BitcoinNetworkManager.kt @@ -1,6 +1,6 @@ package com.tangem.blockchain.blockchains.bitcoin.network -import com.tangem.blockchain.blockchains.bitcoin.UnspentTransaction +import com.tangem.blockchain.blockchains.bitcoin.BitcoinUnspentOutput import com.tangem.blockchain.blockchains.bitcoin.network.api.BlockchainInfoApi import com.tangem.blockchain.blockchains.bitcoin.network.api.BlockcypherApi import com.tangem.blockchain.blockchains.bitcoin.network.api.EstimatefeeApi @@ -90,7 +90,7 @@ class BitcoinNetworkManager(private val isTestNet: Boolean = false) : BitcoinPro data class BitcoinAddressResponse( val balance: BigDecimal, val hasUnconfirmed: Boolean, - val unspentTransactions: List? + val unspentOutputs: List? ) data class BitcoinFee( diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BlockchainInfoProvider.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BlockchainInfoProvider.kt index b26b05debc..cb9dc12e6a 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BlockchainInfoProvider.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BlockchainInfoProvider.kt @@ -1,6 +1,6 @@ package com.tangem.blockchain.blockchains.bitcoin.network -import com.tangem.blockchain.blockchains.bitcoin.UnspentTransaction +import com.tangem.blockchain.blockchains.bitcoin.BitcoinUnspentOutput import com.tangem.blockchain.blockchains.bitcoin.network.api.BlockchainInfoApi import com.tangem.blockchain.blockchains.bitcoin.network.api.EstimatefeeApi import com.tangem.blockchain.common.Blockchain @@ -29,7 +29,7 @@ class BlockchainInfoProvider( val unconfirmedTransactions = addressData.transactions?.find { it.blockHeight == 0L } != null val bitcoinUnspents = unspents.unspentOutputs.map { - UnspentTransaction( + BitcoinUnspentOutput( it.amount!!.toBigDecimal().movePointLeft(decimals), it.outputIndex!!.toLong(), it.hash!!.hexToBytes(), diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BlockcypherProvider.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BlockcypherProvider.kt index 17da8cced0..58b6bec6a4 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BlockcypherProvider.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BlockcypherProvider.kt @@ -1,6 +1,6 @@ package com.tangem.blockchain.blockchains.bitcoin.network -import com.tangem.blockchain.blockchains.bitcoin.UnspentTransaction +import com.tangem.blockchain.blockchains.bitcoin.BitcoinUnspentOutput import com.tangem.blockchain.blockchains.bitcoin.network.api.BlockcypherApi import com.tangem.blockchain.blockchains.bitcoin.network.api.BlockcypherBody import com.tangem.blockchain.blockchains.bitcoin.network.response.BlockcypherFee @@ -26,7 +26,7 @@ class BlockcypherProvider(private val api: BlockcypherApi, isTestNet: Boolean) : try { val addressData: BlockcypherResponse = retryIO { api.getAddressData(blockchain, network, address) } val unspents = addressData.txrefs?.map { - UnspentTransaction( + BitcoinUnspentOutput( it.amount!!.toBigDecimal().movePointLeft(decimals), it.outputIndex!!.toLong(), it.hash!!.hexToBytes(), diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashAddressService.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashAddressService.kt new file mode 100644 index 0000000000..20e41a0b49 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashAddressService.kt @@ -0,0 +1,23 @@ +package com.tangem.blockchain.blockchains.bitcoincash + +import com.tangem.blockchain.blockchains.bitcoincash.cashaddr.BitcoinCashAddressType +import com.tangem.blockchain.blockchains.bitcoincash.cashaddr.CashAddr +import com.tangem.blockchain.common.AddressService +import com.tangem.common.extensions.calculateRipemd160 +import com.tangem.common.extensions.calculateSha256 +import com.tangem.common.extensions.toCompressedPublicKey + +class BitcoinCashAddressService() : AddressService { + override fun makeAddress(walletPublicKey: ByteArray): String { + val publicKeyHash = walletPublicKey.toCompressedPublicKey().calculateSha256().calculateRipemd160() + return CashAddr.toCashAddress(BitcoinCashAddressType.P2PKH, publicKeyHash) + } + + override fun validate(address: String): Boolean { + return CashAddr.isValidCashAddress(address) + } + + fun getPublicKeyHash(address: String): ByteArray { + return CashAddr.decodeCashAddress(address).hash + } +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashNetworkManager.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashNetworkManager.kt new file mode 100644 index 0000000000..5a594aa312 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashNetworkManager.kt @@ -0,0 +1,33 @@ +package com.tangem.blockchain.blockchains.bitcoincash + +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinAddressResponse +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinFee +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinProvider +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.extensions.Result +import com.tangem.blockchain.extensions.SimpleResult +import com.tangem.blockchain.network.API_BLOCKCHAIR +import com.tangem.blockchain.network.blockchair.BlockchairApi +import com.tangem.blockchain.network.blockchair.BlockchairProvider +import com.tangem.blockchain.network.createRetrofitInstance + +class BitcoinCashNetworkManager : BitcoinProvider { + private val blockchain: Blockchain = Blockchain.BitcoinCash + + private val blockchairProvider by lazy { + val api = createRetrofitInstance(API_BLOCKCHAIR).create(BlockchairApi::class.java) + BlockchairProvider(api, blockchain) + } + + override suspend fun getInfo(address: String): Result { + return blockchairProvider.getInfo(address) + } + + override suspend fun getFee(): Result { + return blockchairProvider.getFee() + } + + override suspend fun sendTransaction(transaction: String): SimpleResult { + return blockchairProvider.sendTransaction(transaction) + } +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashTransaction.java b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashTransaction.java new file mode 100644 index 0000000000..ec339789c1 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashTransaction.java @@ -0,0 +1,110 @@ +package com.tangem.blockchain.blockchains.bitcoincash; + +import org.bitcoinj.core.Coin; +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.Sha256Hash; +import org.bitcoinj.core.Transaction; +import org.bitcoinj.core.TransactionInput; +import org.bitcoinj.core.TransactionOutput; +import org.bitcoinj.core.UnsafeByteArrayOutputStream; +import org.bitcoinj.core.VarInt; +import org.bitcoinj.crypto.TransactionSignature; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.util.List; + +import static org.bitcoinj.core.Utils.uint32ToByteStreamLE; +import static org.bitcoinj.core.Utils.uint64ToByteStreamLE; + +// logic from https://github.com/pokkst/bitcoincashj +public class BitcoinCashTransaction extends Transaction { +// private ArrayList inputs; +// private ArrayList outputs; + +// private long version; +// private long lockTime; + + public final byte SIGHASH_FORK_ID = 0x40; + + public BitcoinCashTransaction(NetworkParameters params) { + super(params); + } + + public synchronized Sha256Hash hashForSignatureWitness( + int inputIndex, + byte[] connectedScript, + Coin prevValue, + SigHash type, + boolean anyoneCanPay) + { + byte sigHashType = (byte) TransactionSignature.calcSigHashValue(type, anyoneCanPay); + sigHashType |= SIGHASH_FORK_ID; + + ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(length == UNKNOWN_LENGTH ? 256 : length + 4); + try { + byte[] hashPrevouts = new byte[32]; + byte[] hashSequence = new byte[32]; + byte[] hashOutputs = new byte[32]; + anyoneCanPay = (sigHashType & SIGHASH_ANYONECANPAY_VALUE) == SIGHASH_ANYONECANPAY_VALUE; + List inputs = getInputs(); + List outputs = getOutputs(); + + if (!anyoneCanPay) { + ByteArrayOutputStream bosHashPrevouts = new UnsafeByteArrayOutputStream(256); + for (int i = 0; i < inputs.size(); ++i) { + bosHashPrevouts.write(inputs.get(i).getOutpoint().getHash().getReversedBytes()); + uint32ToByteStreamLE(inputs.get(i).getOutpoint().getIndex(), bosHashPrevouts); + } + hashPrevouts = Sha256Hash.hashTwice(bosHashPrevouts.toByteArray()); + } + + if (!anyoneCanPay && type != SigHash.SINGLE && type != SigHash.NONE) { + ByteArrayOutputStream bosSequence = new UnsafeByteArrayOutputStream(256); + for (int i = 0; i < inputs.size(); ++i) { + uint32ToByteStreamLE(inputs.get(i).getSequenceNumber(), bosSequence); + } + hashSequence = Sha256Hash.hashTwice(bosSequence.toByteArray()); + } + + if (type != SigHash.SINGLE && type != SigHash.NONE) { + ByteArrayOutputStream bosHashOutputs = new UnsafeByteArrayOutputStream(256); + for (int i = 0; i < outputs.size(); ++i) { + uint64ToByteStreamLE( + BigInteger.valueOf(outputs.get(i).getValue().getValue()), + bosHashOutputs + ); + bosHashOutputs.write(new VarInt(outputs.get(i).getScriptBytes().length).encode()); + bosHashOutputs.write(outputs.get(i).getScriptBytes()); + } + hashOutputs = Sha256Hash.hashTwice(bosHashOutputs.toByteArray()); + } else if (type == SigHash.SINGLE && inputIndex < outputs.size()) { + ByteArrayOutputStream bosHashOutputs = new UnsafeByteArrayOutputStream(256); + uint64ToByteStreamLE( + BigInteger.valueOf(outputs.get(inputIndex).getValue().getValue()), + bosHashOutputs + ); + bosHashOutputs.write(new VarInt(outputs.get(inputIndex).getScriptBytes().length).encode()); + bosHashOutputs.write(outputs.get(inputIndex).getScriptBytes()); + hashOutputs = Sha256Hash.hashTwice(bosHashOutputs.toByteArray()); + } + uint32ToByteStreamLE(getVersion(), bos); + bos.write(hashPrevouts); + bos.write(hashSequence); + bos.write(inputs.get(inputIndex).getOutpoint().getHash().getReversedBytes()); + uint32ToByteStreamLE(inputs.get(inputIndex).getOutpoint().getIndex(), bos); + bos.write(new VarInt(connectedScript.length).encode()); + bos.write(connectedScript); + uint64ToByteStreamLE(BigInteger.valueOf(prevValue.getValue()), bos); + uint32ToByteStreamLE(inputs.get(inputIndex).getSequenceNumber(), bos); + bos.write(hashOutputs); + uint32ToByteStreamLE(getLockTime(), bos); + uint32ToByteStreamLE(0x000000ff & sigHashType, bos); + } catch (IOException e) { + throw new RuntimeException(e); // Cannot happen. + } + + return Sha256Hash.twiceOf(bos.toByteArray()); + } +} diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashTransactionBuilder.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashTransactionBuilder.kt new file mode 100644 index 0000000000..58bec1f6e3 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashTransactionBuilder.kt @@ -0,0 +1,82 @@ +package com.tangem.blockchain.blockchains.bitcoincash + +import com.tangem.blockchain.blockchains.bitcoin.BitcoinTransactionBuilder +import com.tangem.blockchain.blockchains.bitcoin.BitcoinUnspentOutput +import com.tangem.blockchain.common.TransactionData +import com.tangem.blockchain.extensions.Result +import com.tangem.common.extensions.isZero +import org.bitcoinj.core.* +import org.bitcoinj.core.LegacyAddress.fromPubKeyHash +import org.bitcoinj.crypto.TransactionSignature +import org.bitcoinj.script.Script +import org.bitcoinj.script.ScriptBuilder +import java.math.BigDecimal +import java.math.BigInteger + +class BitcoinCashTransactionBuilder(private val walletPublicKey: ByteArray) + : BitcoinTransactionBuilder(walletPublicKey) { + + private lateinit var transaction: BitcoinCashTransaction + + override fun buildToSign( + transactionData: TransactionData): Result> { + + if (unspentOutputs == null) return Result.Failure(Exception("Currently there's an unconfirmed transaction")) + + val change: BigDecimal = calculateChange(transactionData, unspentOutputs!!) + + networkParameters = NetworkParameters.fromID(NetworkParameters.ID_MAINNET) + transaction = transactionData.toBitcoinCashTransaction(networkParameters, unspentOutputs!!, change) + + val hashesForSign: MutableList = MutableList(transaction.inputs.size) { byteArrayOf() } + for (input in transaction.inputs) { + val index = input.index + val value = Coin.parseCoin(unspentOutputs!![index].amount.toString()) + hashesForSign[index] = transaction.hashForSignatureWitness(index, input.scriptBytes, value, Transaction.SigHash.ALL, false).bytes + } + return Result.Success(hashesForSign) + } + + override fun buildToSend(signedTransaction: ByteArray): ByteArray { + for (index in transaction.inputs.indices) { + transaction.inputs[index].scriptSig = createScript(index, signedTransaction, walletPublicKey) + } + return transaction.bitcoinSerialize() + } + + override 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 canonicalS = ECKey.ECDSASignature(r, s).toCanonicalised().s + + val sigHash = 0x41 + val signature = TransactionSignature(r, canonicalS, sigHash) + return ScriptBuilder.createInputScript(signature, ECKey.fromPublicOnly(publicKey)) + } +} + +internal fun TransactionData.toBitcoinCashTransaction(networkParameters: NetworkParameters?, + unspentOutputs: List, + change: BigDecimal): BitcoinCashTransaction { + val transaction = BitcoinCashTransaction(networkParameters) + for (utxo in unspentOutputs) { + transaction.addInput(Sha256Hash.wrap(utxo.transactionHash), utxo.outputIndex, Script(utxo.outputScript)) + } + val addressService = BitcoinCashAddressService() + val sourceLegacyAddress = + fromPubKeyHash(networkParameters, addressService.getPublicKeyHash(this.sourceAddress)) + val destinationLegacyAddress = + fromPubKeyHash(networkParameters, addressService.getPublicKeyHash(this.destinationAddress)) + + transaction.addOutput( + Coin.parseCoin(this.amount.value!!.toPlainString()), + destinationLegacyAddress + ) + if (!change.isZero()) { + transaction.addOutput( + Coin.parseCoin(change.toPlainString()), + sourceLegacyAddress + ) + } + return transaction +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashWalletManager.kt new file mode 100644 index 0000000000..b0793c6cf9 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashWalletManager.kt @@ -0,0 +1,28 @@ +package com.tangem.blockchain.blockchains.bitcoincash + +import com.tangem.blockchain.blockchains.bitcoin.BitcoinWalletManager +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.TransactionSender +import com.tangem.blockchain.common.Wallet +import com.tangem.blockchain.extensions.Result +import java.math.BigDecimal + +class BitcoinCashWalletManager( + cardId: String, + wallet: Wallet, + private val transactionBuilder: BitcoinCashTransactionBuilder, + private val networkManager: BitcoinCashNetworkManager +) : BitcoinWalletManager(cardId, wallet, transactionBuilder, networkManager), TransactionSender { + override suspend fun getFee(amount: Amount, destination: String): Result> { + val minimalFee = BigDecimal("0.00001") + when (val result = super.getFee(amount, destination)) { + is Result.Success -> { + for (fee in result.data) { + if (fee.value!! < minimalFee) fee.value = minimalFee + } + return result + } + is Result.Failure -> return result + } + } +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/BitcoinCashAddressDecodedParts.java b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/BitcoinCashAddressDecodedParts.java new file mode 100644 index 0000000000..b3675c1f52 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/BitcoinCashAddressDecodedParts.java @@ -0,0 +1,37 @@ +package com.tangem.blockchain.blockchains.bitcoincash.cashaddr; + +// Helper class for CashAddr + +public class BitcoinCashAddressDecodedParts { + + String prefix; + + BitcoinCashAddressType addressType; + + byte[] hash; + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public BitcoinCashAddressType getAddressType() { + return addressType; + } + + public void setAddressType(BitcoinCashAddressType addressType) { + this.addressType = addressType; + } + + public byte[] getHash() { + return hash; + } + + public void setHash(byte[] hash) { + this.hash = hash; + } + +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/BitcoinCashAddressType.java b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/BitcoinCashAddressType.java new file mode 100644 index 0000000000..8654b29a1c --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/BitcoinCashAddressType.java @@ -0,0 +1,23 @@ +package com.tangem.blockchain.blockchains.bitcoincash.cashaddr; + + +/** + * Copyright (c) 2018 Tobias Brandt + * + * Distributed under the MIT software license, see the accompanying file LICENSE + * or http://www.opensource.org/licenses/mit-license.php. + */ +public enum BitcoinCashAddressType { + + P2PKH((byte) 0), P2SH((byte) 8); + + private final byte versionByte; + + BitcoinCashAddressType(byte versionByte) { + this.versionByte = versionByte; + } + + public byte getVersionByte() { + return versionByte; + } +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/BitcoinCashBase32.java b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/BitcoinCashBase32.java new file mode 100644 index 0000000000..04f3fef9f4 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/BitcoinCashBase32.java @@ -0,0 +1,74 @@ +package com.tangem.blockchain.blockchains.bitcoincash.cashaddr; + +import java.util.HashMap; +import java.util.Map; + +/** + * Copyright (c) 2018 Tobias Brandt + * + * Distributed under the MIT software license, see the accompanying file LICENSE + * or http://www.opensource.org/licenses/mit-license.php. + */ +public class BitcoinCashBase32 { + + public static final String CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + + private static final char[] CHARS = CHARSET.toCharArray(); + + private static Map charPositionMap; + static { + charPositionMap = new HashMap<>(); + for (int i = 0; i < CHARS.length; i++) { + charPositionMap.put(CHARS[i], i); + } + if (charPositionMap.size() != 32) { + throw new RuntimeException("The charset must contain 32 unique characters."); + } + } + + /** + * Encode a byte array as base32 string. This method assumes that all bytes + * are only from 0-31 + * + * @param byteArray + * @return + */ + public static String encode(byte[] byteArray) { + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < byteArray.length; i++) { + int val = (int) byteArray[i]; + + if (val < 0 || val > 31) { + throw new RuntimeException("This method assumes that all bytes are only from 0-31. Was: " + val); + } + + sb.append(CHARS[val]); + } + + return sb.toString(); + } + + /** + * Decode a base32 string back to the byte array representation + * + * @param base32String + * @return + */ + public static byte[] decode(String base32String) { + byte[] bytes = new byte[base32String.length()]; + + char[] charArray = base32String.toCharArray(); + for (int i = 0; i < charArray.length; i++) { + Integer position = charPositionMap.get(charArray[i]); + if (position == null) { + throw new RuntimeException("There seems to be an invalid char: " + charArray[i]); + } + bytes[i] = (byte) ((int) position); + } + + return bytes; + } +} + + \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/BitcoinCashBitArrayConverter.java b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/BitcoinCashBitArrayConverter.java new file mode 100644 index 0000000000..ec0f980198 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/BitcoinCashBitArrayConverter.java @@ -0,0 +1,60 @@ +package com.tangem.blockchain.blockchains.bitcoincash.cashaddr; +/** + * Copyright (c) 2018 Tobias Brandt + * + * Copyright (c) 2017 Pieter Wuille + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +public class BitcoinCashBitArrayConverter { + + public static byte[] convertBits(byte[] bytes8Bits, int from, int to, boolean strictMode) { + int length = (int) (strictMode ? Math.floor((double) bytes8Bits.length * from / to) + : Math.ceil((double) bytes8Bits.length * from / to)); + int mask = ((1 << to) - 1) & 0xff; + byte[] result = new byte[length]; + int index = 0; + int accumulator = 0; + int bits = 0; + for (int i = 0; i < bytes8Bits.length; i++) { + byte value = bytes8Bits[i]; + accumulator = (((accumulator & 0xff) << from) | (value & 0xff)); + bits += from; + while (bits >= to) { + bits -= to; + result[index] = (byte) ((accumulator >> bits) & mask); + ++index; + } + } + if (!strictMode) { + if (bits > 0) { + result[index] = (byte) ((accumulator << (to - bits)) & mask); + ++index; + } + } else { + if (!(bits < from && ((accumulator << (to - bits)) & mask) == 0)) { + throw new RuntimeException("Strict mode was used but input couldn't be converted without padding"); + } + } + + return result; + } + +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/CashAddr.java b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/CashAddr.java new file mode 100644 index 0000000000..8c2cd1bfab --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/cashaddr/CashAddr.java @@ -0,0 +1,225 @@ +package com.tangem.blockchain.blockchains.bitcoincash.cashaddr; + +import java.math.BigInteger; +import java.util.Arrays; + + +/** + * Copyright (c) 2018 Tobias Brandt + * + * Distributed under the MIT software license, see the accompanying file LICENSE + * or http://www.opensource.org/licenses/mit-license.php. + */ + + +public class CashAddr { + + public static final String CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + + private static final char[] CHARS = CHARSET.toCharArray(); + + + public static final String SEPARATOR = ":"; + + public static final String MAIN_NET_PREFIX = "bitcoincash"; + + public static final String TEST_NET_PREFIX = "bchtest"; + + public static final String ASSUMED_DEFAULT_PREFIX = MAIN_NET_PREFIX; + + private static final BigInteger[] POLYMOD_GENERATORS = new BigInteger[] { new BigInteger("98f2bc8e61", 16), + new BigInteger("79b76d99e2", 16), new BigInteger("f33e5fb3c4", 16), new BigInteger("ae2eabe2a8", 16), + new BigInteger("1e4f43e470", 16) }; + + private static final BigInteger POLYMOD_AND_CONSTANT = new BigInteger("07ffffffff", 16); + + public static String toCashAddress(BitcoinCashAddressType addressType, byte[] hash) { + String prefixString = MAIN_NET_PREFIX; + byte[] prefixBytes = getPrefixBytes(prefixString); + byte[] payloadBytes = concatenateByteArrays(new byte[] { addressType.getVersionByte() }, hash); + payloadBytes = convertBits(payloadBytes, 8, 5, false); + byte[] allChecksumInput = concatenateByteArrays( + concatenateByteArrays(concatenateByteArrays(prefixBytes, new byte[] { 0 }), payloadBytes), + new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }); + byte[] checksumBytes = calculateChecksumBytesPolymod(allChecksumInput); + checksumBytes = convertBits(checksumBytes, 8, 5, true); + String cashAddress = BitcoinCashBase32.encode(concatenateByteArrays(payloadBytes, checksumBytes)); + return prefixString + SEPARATOR + cashAddress; + } + + public static BitcoinCashAddressDecodedParts decodeCashAddress(String bitcoinCashAddress) { + if (!isValidCashAddress(bitcoinCashAddress)) { + throw new RuntimeException("Address wasn't valid: " + bitcoinCashAddress); + } + + BitcoinCashAddressDecodedParts decoded = new BitcoinCashAddressDecodedParts(); + String[] addressParts = bitcoinCashAddress.split(SEPARATOR); + if (addressParts.length == 2) { + decoded.setPrefix(addressParts[0]); + } else { + decoded.setPrefix(MAIN_NET_PREFIX); + } + + byte[] addressData = BitcoinCashBase32.decode(addressParts[addressParts.length - 1]); + addressData = Arrays.copyOfRange(addressData, 0, addressData.length - 8); + addressData = BitcoinCashBitArrayConverter.convertBits(addressData, 5, 8, true); + byte versionByte = addressData[0]; + byte[] hash = Arrays.copyOfRange(addressData, 1, addressData.length); + + decoded.setAddressType(getAddressTypeFromVersionByte(versionByte)); + decoded.setHash(hash); + + return decoded; + } + + private static BitcoinCashAddressType getAddressTypeFromVersionByte(byte versionByte) { + for (BitcoinCashAddressType addressType : BitcoinCashAddressType.values()) { + if (addressType.getVersionByte() == versionByte) { + return addressType; + } + } + + throw new RuntimeException("Unknown version byte: " + versionByte); + } + + + + public static boolean isValidCashAddress(String bitcoinCashAddress ) { + try { + if (!isSingleCase(bitcoinCashAddress)) + return false; + + bitcoinCashAddress = bitcoinCashAddress.toLowerCase(); + String prefix; + + if (bitcoinCashAddress.contains(SEPARATOR)) { + String[] split = bitcoinCashAddress.split(SEPARATOR); + prefix = split[0]; + if (!prefix.equals(MAIN_NET_PREFIX)) {return false;} //for now we use main net only + bitcoinCashAddress = split[1]; + } else { + prefix = MAIN_NET_PREFIX; + } + if (!bitcoinCashAddress.startsWith("q")) {return false;} //for now we use P2PKH addresses only + + byte[] checksumData = concatenateByteArrays( + concatenateByteArrays(getPrefixBytes(prefix ), new byte[] { 0x00 }), + BitcoinCashBase32.decode(bitcoinCashAddress)); + + byte[] calculateChecksumBytesPolymod = calculateChecksumBytesPolymod(checksumData); + return new BigInteger(calculateChecksumBytesPolymod).compareTo(BigInteger.ZERO) == 0; + } catch (RuntimeException re) { + return false; + } + } + + + + private static boolean isSingleCase(String bitcoinCashAddress) { + if (bitcoinCashAddress.equals(bitcoinCashAddress.toLowerCase())) { + return true; + } + if (bitcoinCashAddress.equals(bitcoinCashAddress.toUpperCase())) { + return true; + } + + return false; + } + + /** + * @param checksumInput + * @return Returns a 40 bits checksum in form of 5 8-bit arrays. This still has + * to me mapped to 5-bit array representation + */ + private static byte[] calculateChecksumBytesPolymod(byte[] checksumInput) { + BigInteger c = BigInteger.ONE; + + for (int i = 0; i < checksumInput.length; i++) { + byte c0 = c.shiftRight(35).byteValue(); + c = c.and(POLYMOD_AND_CONSTANT).shiftLeft(5) + .xor(new BigInteger(String.format("%02x", checksumInput[i]), 16)); + + if ((c0 & 0x01) != 0) + c = c.xor(POLYMOD_GENERATORS[0]); + if ((c0 & 0x02) != 0) + c = c.xor(POLYMOD_GENERATORS[1]); + if ((c0 & 0x04) != 0) + c = c.xor(POLYMOD_GENERATORS[2]); + if ((c0 & 0x08) != 0) + c = c.xor(POLYMOD_GENERATORS[3]); + if ((c0 & 0x10) != 0) + c = c.xor(POLYMOD_GENERATORS[4]); + } + + byte[] checksum = c.xor(BigInteger.ONE).toByteArray(); + if (checksum.length == 5) { + return checksum; + } else { + byte[] newChecksumArray = new byte[5]; + + System.arraycopy(checksum, Math.max(0, checksum.length - 5), newChecksumArray, + Math.max(0, 5 - checksum.length), Math.min(5, checksum.length)); + + return newChecksumArray; + } + + } + + private static byte[] getPrefixBytes(String prefixString ) { + byte[] prefixBytes = new byte[prefixString.length()]; + + char[] charArray = prefixString.toCharArray(); + for (int i = 0; i < charArray.length; i++) { + prefixBytes[i] = (byte) (charArray[i] & 0x1f); + } + + return prefixBytes; + } + + private static byte[] concatenateByteArrays(byte[] first, byte[] second) { + byte[] concatenatedBytes = new byte[first.length + second.length]; + + System.arraycopy(first, 0, concatenatedBytes, 0, first.length); + System.arraycopy(second, 0, concatenatedBytes, first.length, second.length); + + return concatenatedBytes; + } + + private static byte[] convertBits(byte[] bytes8Bits, int from, int to, boolean strictMode) { + //Copyright (c) 2017 Pieter Wuille + + int length = (int) (strictMode ? Math.floor((double) bytes8Bits.length * from / to) + : Math.ceil((double) bytes8Bits.length * from / to)); + int mask = ((1 << to) - 1) & 0xff; + byte[] result = new byte[length]; + int index = 0; + int accumulator = 0; + int bits = 0; + for (int i = 0; i < bytes8Bits.length; i++) { + byte value = bytes8Bits[i]; + accumulator = (((accumulator & 0xff) << from) | (value & 0xff)); + bits += from; + while (bits >= to) { + bits -= to; + result[index] = (byte) ((accumulator >> bits) & mask); + ++index; + } + } + if (!strictMode) { + if (bits > 0) { + result[index] = (byte) ((accumulator << (to - bits)) & mask); + ++index; + } + } else { + if (!(bits < from && ((accumulator << (to - bits)) & mask) == 0)) { + throw new RuntimeException("Strict mode was used but input couldn't be converted without padding"); + } + } + + return result; + } + + + + +} 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 f2be55b6f0..34920ecaca 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,7 @@ package com.tangem.blockchain.common import com.tangem.blockchain.blockchains.binance.BinanceAddressService import com.tangem.blockchain.blockchains.bitcoin.BitcoinAddressService +import com.tangem.blockchain.blockchains.bitcoincash.BitcoinCashAddressService import com.tangem.blockchain.blockchains.cardano.CardanoAddressService import com.tangem.blockchain.blockchains.ethereum.EthereumAddressService import com.tangem.blockchain.blockchains.stellar.StellarAddressService @@ -17,6 +18,7 @@ enum class Blockchain( Unknown("", "", ""), Bitcoin("BTC", "BTC", "Bitcoin"), BitcoinTestnet("BTC/test", "BTCt", "Bitcoin Testnet"), + BitcoinCash("BCH", "BCH", "Bitcoin Cash"), Ethereum("ETH", "ETH", "Ethereum"), Rootstock("", "", ""), Cardano("CARDANO", "ADA", "Cardano"), @@ -32,7 +34,7 @@ enum class Blockchain( } fun decimals(): Int = when (this) { - Bitcoin, BitcoinTestnet, Binance, BinanceTestnet -> 8 + Bitcoin, BitcoinTestnet, BitcoinCash, Binance, BinanceTestnet -> 8 Cardano, XRP -> 6 Ethereum, Rootstock -> 18 Stellar -> 7 @@ -51,6 +53,7 @@ enum class Blockchain( Unknown -> throw Exception("unsupported blockchain") Bitcoin -> BitcoinAddressService() BitcoinTestnet -> BitcoinAddressService(true) + BitcoinCash -> BitcoinCashAddressService() Ethereum -> EthereumAddressService() Rootstock -> throw Exception("unsupported blockchain") Cardano -> CardanoAddressService() @@ -70,6 +73,8 @@ enum class Blockchain( fun getExploreUrl(address: String, token: Token? = null): String = when (this) { Binance -> "https://explorer.binance.org/address/$address" Bitcoin -> "https://blockchain.info/address/$address" + BitcoinTestnet -> "https://live.blockcypher.com/btc-testnet/address/$address" + BitcoinCash -> "https://blockchair.com/bitcoin-cash/address/$address" Cardano -> "https://cardanoexplorer.com/address/$address" Ethereum -> if (token == null) { "https://etherscan.io/address/" diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt index b5bf3032f2..b0a50afe28 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt @@ -6,6 +6,9 @@ import com.tangem.blockchain.blockchains.binance.network.BinanceNetworkManager import com.tangem.blockchain.blockchains.bitcoin.BitcoinTransactionBuilder import com.tangem.blockchain.blockchains.bitcoin.BitcoinWalletManager import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinNetworkManager +import com.tangem.blockchain.blockchains.bitcoincash.BitcoinCashNetworkManager +import com.tangem.blockchain.blockchains.bitcoincash.BitcoinCashTransactionBuilder +import com.tangem.blockchain.blockchains.bitcoincash.BitcoinCashWalletManager import com.tangem.blockchain.blockchains.cardano.CardanoTransactionBuilder import com.tangem.blockchain.blockchains.cardano.CardanoWalletManager import com.tangem.blockchain.blockchains.cardano.network.CardanoNetworkManager @@ -21,6 +24,7 @@ import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder import com.tangem.blockchain.blockchains.xrp.XrpWalletManager import com.tangem.blockchain.blockchains.xrp.network.XrpNetworkManager import com.tangem.commands.Card +import com.tangem.common.extensions.toCompressedPublicKey object WalletManagerFactory { @@ -50,6 +54,13 @@ object WalletManagerFactory { BitcoinNetworkManager(true) ) } + Blockchain.BitcoinCash -> { + return BitcoinCashWalletManager( + card.cardId, wallet, + BitcoinCashTransactionBuilder(walletPublicKey.toCompressedPublicKey()), + BitcoinCashNetworkManager() + ) + } Blockchain.Ethereum -> { val chain = Chain.Mainnet return EthereumWalletManager( diff --git a/blockchain/src/main/java/com/tangem/blockchain/network/RetrofitBuilder.kt b/blockchain/src/main/java/com/tangem/blockchain/network/RetrofitBuilder.kt index a3ecaaac41..bdd4eca666 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/network/RetrofitBuilder.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/network/RetrofitBuilder.kt @@ -49,7 +49,8 @@ const val API_STELLAR = "https://horizon.stellar.org/" const val API_STELLAR_RESERVE = "https://horizon.sui.li/" const val API_STELLAR_TESTNET = "https://horizon-testnet.stellar.org/" const val API_BLOCKCHAIN_INFO = "https://blockchain.info/" -const val API_ADALITE = "https://explorer3.adalite.io" -const val API_ADALITE_RESERVE = "https://nodes.southeastasia.cloudapp.azure.com" -const val API_RIPPLED = "https://s1.ripple.com:51234" -const val API_RIPPLED_RESERVE = "https://s2.ripple.com:51234" \ No newline at end of file +const val API_ADALITE = "https://explorer3.adalite.io/" +const val API_ADALITE_RESERVE = "https://nodes.southeastasia.cloudapp.azure.com/" +const val API_RIPPLED = "https://s1.ripple.com:51234/" +const val API_RIPPLED_RESERVE = "https://s2.ripple.com:51234/" +const val API_BLOCKCHAIR = "https://api.blockchair.com/" \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/network/blockchair/BlockchairApi.kt b/blockchain/src/main/java/com/tangem/blockchain/network/blockchair/BlockchairApi.kt new file mode 100644 index 0000000000..b078327847 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/network/blockchair/BlockchairApi.kt @@ -0,0 +1,36 @@ +package com.tangem.blockchain.network.blockchair + +import com.squareup.moshi.JsonClass +import retrofit2.http.* + +interface BlockchairApi { + @GET("{blockchain}/dashboards/address/{address}") + suspend fun getAddressData( + @Path("address") address: String, + @Path("blockchain") blockchain: String, + @Query("key") key: String + ): BlockchairAddress + + @GET("{blockchain}/dashboards/transaction/{transaction}") + suspend fun getTransaction( + @Path("transaction") transactionHash: String, + @Path("blockchain") blockchain: String, + @Query("key") key: String + ): BlockchairTransaction + + @GET("{blockchain}/stats") + suspend fun getBlockchainStats( + @Path("blockchain") blockchain: String, + @Query("key") key: String + ): BlockchairStats + + @POST("{blockchain}/push/transaction") + suspend fun sendTransaction( + @Body sendBody: BlockchairBody, + @Path("blockchain") blockchain: String, + @Query("key") key: String + ) +} + +@JsonClass(generateAdapter = true) +data class BlockchairBody(val data: String) \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/network/blockchair/BlockchairProvider.kt b/blockchain/src/main/java/com/tangem/blockchain/network/blockchair/BlockchairProvider.kt new file mode 100644 index 0000000000..a14c6b8561 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/network/blockchair/BlockchairProvider.kt @@ -0,0 +1,96 @@ +package com.tangem.blockchain.network.blockchair + +import com.tangem.blockchain.blockchains.bitcoin.BitcoinUnspentOutput +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinAddressResponse +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinFee +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinProvider +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.extensions.Result +import com.tangem.blockchain.extensions.SimpleResult +import com.tangem.blockchain.extensions.retryIO +import com.tangem.common.extensions.hexToBytes +import java.math.BigDecimal +import java.math.RoundingMode + +class BlockchairProvider(private val api: BlockchairApi, blockchain: Blockchain) : BitcoinProvider { + private val blockchainPath = when (blockchain) { + Blockchain.BitcoinCash -> "bitcoin-cash" + else -> throw Exception("${blockchain.fullName} blockchain is not supported by BlockchairProvider") + } + private val decimals = blockchain.decimals() + + override suspend fun getInfo(address: String): Result { + return try { + val blockchairAddress = retryIO { api.getAddressData(address, blockchainPath, API_KEY) } + + val addressData = blockchairAddress.data!!.getValue(address) + val addressInfo = addressData.addressInfo!! + val script = addressInfo.script!!.hexToBytes() + + val hasUnconfirmed = checkHasUnconfirmed(addressData) + + val unspentTransactions = addressData.unspentOutputs!!.map { + BitcoinUnspentOutput( + amount = it.amount!!.toBigDecimal().movePointLeft(decimals), + outputIndex = it.index!!.toLong(), + transactionHash = it.transactionHash!!.hexToBytes(), + outputScript = script + ) + } + + Result.Success(BitcoinAddressResponse( + balance = addressInfo.balance!!.toBigDecimal().movePointLeft(decimals), + hasUnconfirmed = hasUnconfirmed, + unspentOutputs = unspentTransactions + )) + } catch (error: Exception) { + Result.Failure(error) + } + + } + + private suspend fun checkHasUnconfirmed(addressData: BlockchairAddressData): Boolean { + for (utxo in addressData.unspentOutputs!!) { //check utxos first + if (utxo.block == -1) return true + } + + return if (addressData.addressInfo!!.balance != 0L) { // if balance is not zero, unconfirmed tx should have unconfirmed utxo + false + } else { + if (addressData.transactions!!.isEmpty()) { // no transactions from this address ever + false + } else { // check last transaction in case it spent all funds + val lastTransactionHash = addressData.transactions[0] + val blockchairTransaction = retryIO { + api.getTransaction(lastTransactionHash, blockchainPath, API_KEY) + } + blockchairTransaction.data!!.getValue(lastTransactionHash).transaction!!.block == -1 + } + } + } + + override suspend fun getFee(): Result { + return try { + val stats = retryIO { api.getBlockchainStats(blockchainPath, API_KEY) } + val feePerKb = (stats.data!!.feePerByte!! * 1024).toBigDecimal().movePointLeft(decimals) + Result.Success(BitcoinFee( + minimalPerKb = (feePerKb * BigDecimal.valueOf(0.8)).setScale(decimals, RoundingMode.DOWN), + normalPerKb = feePerKb.setScale(decimals, RoundingMode.DOWN), + priorityPerKb = (feePerKb * BigDecimal.valueOf(1.2)).setScale(decimals, RoundingMode.DOWN) + )) + } catch (error: Exception) { + Result.Failure(error) + } + } + + override suspend fun sendTransaction(transaction: String): SimpleResult { + return try { + retryIO { api.sendTransaction(BlockchairBody(transaction), blockchainPath, API_KEY) } + SimpleResult.Success + } catch (error: Exception) { + SimpleResult.Failure(error) + } + } +} + +private const val API_KEY = "A___0Shpsu4KagE7oSabrw20DfXAqWlT" \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/network/blockchair/BlockchairResponse.kt b/blockchain/src/main/java/com/tangem/blockchain/network/blockchair/BlockchairResponse.kt new file mode 100644 index 0000000000..8962ce7860 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/network/blockchair/BlockchairResponse.kt @@ -0,0 +1,75 @@ +package com.tangem.blockchain.network.blockchair + +import com.google.gson.annotations.SerializedName +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class BlockchairAddress( + @Json(name = "data") + val data: Map? = null +) + +data class BlockchairAddressData( + @Json(name = "address") + val addressInfo: BlockchairAddressInfo? = null, + + @Json(name = "utxo") + val unspentOutputs: List? = null, + + @Json(name = "transactions") + val transactions: List? = null +) + +data class BlockchairAddressInfo( + @Json(name = "balance") + val balance: Long? = null, + + @Json(name = "script_hex") + val script: String? = null, + + @Json(name = "output_count") + val outputCount: Int? = null, + + @Json(name = "unspent_output_count") + val unspentOutputCount: Int? = null +) + +data class BlockchairUnspentOutput( + @Json(name = "block_id") + val block: Int? = null, + + @Json(name = "transaction_hash") + val transactionHash: String? = null, + + @Json(name = "index") + val index: Int? = null, + + @Json(name = "value") + val amount: Long? = null +) + +data class BlockchairTransaction( + @Json(name = "data") + val data: Map? = null +) + +data class BlockchairTransactionData( + @Json(name = "transaction") + val transaction: BlockchairTransactionInfo? = null +) + +data class BlockchairTransactionInfo( + @Json(name = "block_id") + val block: Int? = null +) + +data class BlockchairStats( + @Json(name = "data") + val data: BlockchairStatsData? = null +) + +data class BlockchairStatsData( + @Json(name = "suggested_transaction_fee_per_byte_sat") + val feePerByte: Int? = null +) \ No newline at end of file diff --git a/blockchain/src/test/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashAddressTest.kt b/blockchain/src/test/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashAddressTest.kt new file mode 100644 index 0000000000..ff0d1a7f95 --- /dev/null +++ b/blockchain/src/test/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashAddressTest.kt @@ -0,0 +1,27 @@ +package com.tangem.blockchain.blockchains.bitcoincash + +import com.google.common.truth.Truth +import com.tangem.common.extensions.hexToBytes +import com.tangem.common.extensions.toCompressedPublicKey +import org.junit.Test + +class BitcoinCashAddressTest { + + private val addressService = BitcoinCashAddressService() + + @Test + fun makeAddressFromCorrectPublicKey() { + val walletPublicKey = "04BE37CD5251C8999EDBBFC759D800EB41E4DCB718289601EB15819404E1B2F2ED90FE50C2A481D06EC790D1EF6184974EB655ABAE4BE56A6D1C9E1A17B1EFDF02".hexToBytes() + val expected = "bitcoincash:qp7atyzvetwq8a0x02y2snvnns5jfwnzacf9vfa4x3" + + Truth.assertThat(addressService.makeAddress(walletPublicKey.toCompressedPublicKey())) + .isEqualTo(expected) + } + + @Test + fun validateCorrectAddress() { + val address = "bitcoincash:qp7atyzvetwq8a0x02y2snvnns5jfwnzacf9vfa4x3" + Truth.assertThat(addressService.validate(address)) + .isTrue() + } +} \ No newline at end of file diff --git a/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt b/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt index 9c15645ed2..ff9edbcdd1 100644 --- a/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt +++ b/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt @@ -4,6 +4,7 @@ import com.google.common.truth.Truth import com.tangem.SessionEnvironment import com.tangem.blockchain.blockchains.binance.BinanceWalletManager import com.tangem.blockchain.blockchains.bitcoin.BitcoinWalletManager +import com.tangem.blockchain.blockchains.bitcoincash.BitcoinCashWalletManager import com.tangem.blockchain.blockchains.cardano.CardanoWalletManager import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager import com.tangem.blockchain.blockchains.stellar.StellarWalletManager @@ -80,4 +81,15 @@ internal class WalletManagerFactoryTest { Truth.assertThat(walletManager) .isInstanceOf(BinanceWalletManager::class.java) } + + @Test + fun createBitcoinCashWalletManager() { + val data = "0108BB00000000000049200754414E47454D00020102800A322E3432642053444B00034104766A1586D164B436E5D420AED01FDAB41B2AE7EDF0C865D7AF1DA995D70AB297E5B94B761CFBB405084C21BC97C02B4A1EA9ED4F515576EAB4D83AD3A0DFAA8A0A04041E76310C618102FFFF8A0101820407E4041B830B54414E47454D2053444B00840342434886408058F0F628C2466B09ECEB13F2A8EFDD4558F5D2DBDA9BD0628EE8C8CC99A778FF0F1AECD35704B9F3518486EA5C1D20F9DFCBAA66184F4CCCD9282E2632882C3041045F16BD1D2EAFE463E62A335A09E6B2BBCBD04452526885CB679FC4D27AF1BD22F553C7DEEFB54FD3D4F361D14E6DC3F11B7D4EA183250A60720EBDF9E110CD26050A736563703235366B31000804000186A0070100604104BE37CD5251C8999EDBBFC759D800EB41E4DCB718289601EB15819404E1B2F2ED90FE50C2A481D06EC790D1EF6184974EB655ABAE4BE56A6D1C9E1A17B1EFDF0262040001869A6304000000060F01009000" + val responseApdu = ResponseApdu(data.hexToBytes()) + val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu) + val walletManager = WalletManagerFactory.makeWalletManager(card!!) + + Truth.assertThat(walletManager) + .isInstanceOf(BitcoinCashWalletManager::class.java) + } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/SessionEnvironment.kt b/tangem-core/src/main/java/com/tangem/SessionEnvironment.kt index 6400e98e39..c1c9a296b2 100644 --- a/tangem-core/src/main/java/com/tangem/SessionEnvironment.kt +++ b/tangem-core/src/main/java/com/tangem/SessionEnvironment.kt @@ -21,7 +21,7 @@ data class SessionEnvironment( var encryptionMode: EncryptionMode = EncryptionMode.NONE, var encryptionKey: ByteArray? = null, val cvc: ByteArray? = null, - var cardFilter: CardFilter + var cardFilter: CardFilter = CardFilter() ) { fun setPin1(pin1: String) { diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt index 4140000621..db748d8ec5 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt @@ -44,7 +44,7 @@ class ReadIssuerExtraDataResponse( val issuerDataSignature: ByteArray?, /** - * An optional counter that protect issuer data against replay attack. + * An optional counter that protects issuer data against replay attack. * When flag [Settings.ProtectIssuerDataAgainstReplay] set in [SettingsMask] * then this value is mandatory and must increase on each execution of [WriteIssuerDataCommand]. */ diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt index e9771d0b87..7b43e879e7 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt @@ -17,7 +17,7 @@ class WriteUserDataResponse( ) : CommandResponse /** - * This command write some of User_Data, User_ProtectedData, User_Counter and User_ProtectedCounter fields. + * This command writes to the card any of User_Data, User_ProtectedData, User_Counter and User_ProtectedCounter fields. * User_Data and User_ProtectedData are never changed or parsed by the executable code the Tangem COS. * The App defines purpose of use, format and it's payload. For example, this field may contain cashed information * from blockchain to accelerate preparing new transaction. diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/_arch/structure/Ids.kt b/tangem-devkit/src/main/java/com/tangem/devkit/_arch/structure/Ids.kt index e3fcb31dbf..bc05f80fca 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/_arch/structure/Ids.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/_arch/structure/Ids.kt @@ -3,7 +3,15 @@ package com.tangem.devkit._arch.structure /** [REDACTED_AUTHOR] */ -interface Id +interface Id { + + companion object { + fun getTag(id: Id): String { + val className = id.javaClass.simpleName + return if (id is Enum<*>) "$className.${id.name}" else className + } + } +} class StringId(val value: String) : Id diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/_arch/widget/abstraction/ViewWidget.kt b/tangem-devkit/src/main/java/com/tangem/devkit/_arch/widget/abstraction/ViewWidget.kt index 752d178400..e414e89cf7 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/_arch/widget/abstraction/ViewWidget.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/_arch/widget/abstraction/ViewWidget.kt @@ -7,6 +7,7 @@ import android.view.View import android.view.ViewGroup import androidx.annotation.ColorRes import com.tangem.devkit.R +import com.tangem.devkit._arch.structure.Id import com.tangem.devkit._arch.structure.StringId import com.tangem.devkit._arch.structure.StringResId import com.tangem.devkit._arch.structure.abstraction.Item @@ -37,6 +38,7 @@ abstract class BaseViewWidget( init { subscribeToViewStateChanges(item.viewModel.viewState) initViewState(item.viewModel.viewState) + view.tag = Id.getTag(item.id) } protected open fun subscribeToViewStateChanges(viewState: ViewState) { diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/_main/entryPoint/ActionListFragment.kt b/tangem-devkit/src/main/java/com/tangem/devkit/_main/entryPoint/ActionListFragment.kt index 78af64f574..5a1008d717 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/_main/entryPoint/ActionListFragment.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/_main/entryPoint/ActionListFragment.kt @@ -63,15 +63,15 @@ class ActionListFragment : BaseFragment() { ActionType.Scan, ActionType.Sign, ActionType.Personalize, - ActionType.Depersonalize -// ActionType.CreateWallet, -// ActionType.PurgeWallet, -// ActionType.ReadIssuerData, -// ActionType.WriteIssuerData, -// ActionType.ReadIssuerExData, -// ActionType.WriteIssuerExData, -// ActionType.ReadUserData, -// ActionType.WriteUserData + ActionType.Depersonalize, + ActionType.CreateWallet, + ActionType.PurgeWallet, + ActionType.ReadIssuerData, + ActionType.WriteIssuerData, + ActionType.ReadIssuerExData, + ActionType.WriteIssuerExData, + ActionType.ReadUserData, + ActionType.WriteUserData ) } } \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/CreateWalletAction.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/CreateWalletAction.kt new file mode 100644 index 0000000000..0e02ca670f --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/CreateWalletAction.kt @@ -0,0 +1,23 @@ +package com.tangem.devkit.ucase.domain.actions + +import com.tangem.devkit._arch.structure.Id +import com.tangem.devkit._arch.structure.PayloadHolder +import com.tangem.devkit._arch.structure.abstraction.findItem +import com.tangem.devkit.ucase.domain.paramsManager.ActionCallback +import com.tangem.devkit.ucase.variants.TlvId + +class CreateWalletAction : BaseAction() { + override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { + val item = attrs.itemList.findItem(TlvId.CardId) ?: return + val cardId = item.viewModel.data as? String ?: return + + attrs.tangemSdk.createWallet(cardId) { handleResult(payload, it, null, attrs, callback) } + } + + override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? { + return when (id) { + TlvId.CardId -> { callback -> ScanAction().executeMainAction(payload, attrs, callback) } + else -> null + } + } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/PersonalizeAction.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/PersonalizeAction.kt index b289159d6c..a6ac650824 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/PersonalizeAction.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/PersonalizeAction.kt @@ -9,7 +9,7 @@ import com.tangem.devkit.ucase.domain.paramsManager.ActionCallback import com.tangem.devkit.ucase.domain.paramsManager.PayloadKey import com.tangem.devkit.ucase.tunnel.ActionView import com.tangem.devkit.ucase.tunnel.ItemError -import com.tangem.devkit.ucase.variants.personalize.CardNumber +import com.tangem.devkit.ucase.variants.personalize.CardNumberId import com.tangem.devkit.ucase.variants.personalize.converter.PersonalizationConfigConverter import com.tangem.devkit.ucase.variants.personalize.converter.PersonalizationConfigToCardConfig import com.tangem.devkit.ucase.variants.personalize.dto.DefaultPersonalizationParams @@ -46,7 +46,7 @@ class PersonalizeAction : BaseAction() { } private fun checkSeries(itemList: List): Boolean { - val seriesItem = itemList.findItem(CardNumber.Series) as? EditTextItem ?: return false + val seriesItem = itemList.findItem(CardNumberId.Series) as? EditTextItem ?: return false val data = seriesItem.getData() as? String ?: return false seriesItem.setData(data.toUpperCase()) @@ -54,8 +54,8 @@ class PersonalizeAction : BaseAction() { } private fun checkNumber(itemList: List): Boolean { - val seriesItem = itemList.findItem(CardNumber.Series) as? EditTextItem ?: return false - val numberItem = itemList.findItem(CardNumber.Number) as? NumberItem ?: return false + val seriesItem = itemList.findItem(CardNumberId.Series) as? EditTextItem ?: return false + val numberItem = itemList.findItem(CardNumberId.Number) as? NumberItem ?: return false val seriesData = seriesItem.getData() as? String ?: return false val numberData = stringOf(numberItem.getData() as? Number) diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/PurgeWalletAction.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/PurgeWalletAction.kt new file mode 100644 index 0000000000..4c2efe395e --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/PurgeWalletAction.kt @@ -0,0 +1,23 @@ +package com.tangem.devkit.ucase.domain.actions + +import com.tangem.devkit._arch.structure.Id +import com.tangem.devkit._arch.structure.PayloadHolder +import com.tangem.devkit._arch.structure.abstraction.findItem +import com.tangem.devkit.ucase.domain.paramsManager.ActionCallback +import com.tangem.devkit.ucase.variants.TlvId + +class PurgeWalletAction : BaseAction() { + override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { + val item = attrs.itemList.findItem(TlvId.CardId) ?: return + val cardId = item.viewModel.data as? String ?: return + + attrs.tangemSdk.purgeWallet(cardId) { handleResult(payload, it, null, attrs, callback) } + } + + override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? { + return when (id) { + TlvId.CardId -> { callback -> ScanAction().executeMainAction(payload, attrs, callback) } + else -> null + } + } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/ReadIssuerDataAction.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/ReadIssuerDataAction.kt new file mode 100644 index 0000000000..580e43d4f2 --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/ReadIssuerDataAction.kt @@ -0,0 +1,23 @@ +package com.tangem.devkit.ucase.domain.actions + +import com.tangem.devkit._arch.structure.Id +import com.tangem.devkit._arch.structure.PayloadHolder +import com.tangem.devkit._arch.structure.abstraction.findItem +import com.tangem.devkit.ucase.domain.paramsManager.ActionCallback +import com.tangem.devkit.ucase.variants.TlvId + +class ReadIssuerDataAction : BaseAction() { + override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { + val item = attrs.itemList.findItem(TlvId.CardId) ?: return + val cardId = item.viewModel.data as? String ?: return + + attrs.tangemSdk.readIssuerData(cardId) { handleResult(payload, it, null, attrs, callback) } + } + + override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? { + return when (id) { + TlvId.CardId -> { callback -> ScanAction().executeMainAction(payload, attrs, callback) } + else -> null + } + } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/ReadIssuerExtraDataAction.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/ReadIssuerExtraDataAction.kt new file mode 100644 index 0000000000..4483a0bd78 --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/ReadIssuerExtraDataAction.kt @@ -0,0 +1,23 @@ +package com.tangem.devkit.ucase.domain.actions + +import com.tangem.devkit._arch.structure.Id +import com.tangem.devkit._arch.structure.PayloadHolder +import com.tangem.devkit._arch.structure.abstraction.findItem +import com.tangem.devkit.ucase.domain.paramsManager.ActionCallback +import com.tangem.devkit.ucase.variants.TlvId + +class ReadIssuerExtraDataAction : BaseAction() { + override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { + val item = attrs.itemList.findItem(TlvId.CardId) ?: return + val cardId = item.viewModel.data as? String ?: return + + attrs.tangemSdk.readIssuerExtraData(cardId) { handleResult(payload, it, null, attrs, callback) } + } + + override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? { + return when (id) { + TlvId.CardId -> { callback -> ScanAction().executeMainAction(payload, attrs, callback) } + else -> null + } + } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/ReadUserDataAction.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/ReadUserDataAction.kt new file mode 100644 index 0000000000..6014c60757 --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/ReadUserDataAction.kt @@ -0,0 +1,23 @@ +package com.tangem.devkit.ucase.domain.actions + +import com.tangem.devkit._arch.structure.Id +import com.tangem.devkit._arch.structure.PayloadHolder +import com.tangem.devkit._arch.structure.abstraction.findItem +import com.tangem.devkit.ucase.domain.paramsManager.ActionCallback +import com.tangem.devkit.ucase.variants.TlvId + +class ReadUserDataAction : BaseAction() { + override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { + val item = attrs.itemList.findItem(TlvId.CardId) ?: return + val cardId = item.viewModel.data as? String ?: return + + attrs.tangemSdk.readUserData(cardId) { handleResult(payload, it, null, attrs, callback) } + } + + override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? { + return when (id) { + TlvId.CardId -> { callback -> ScanAction().executeMainAction(payload, attrs, callback) } + else -> null + } + } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteIssuerDataAction.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteIssuerDataAction.kt new file mode 100644 index 0000000000..c93f5edfc1 --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteIssuerDataAction.kt @@ -0,0 +1,37 @@ +package com.tangem.devkit.ucase.domain.actions + +import com.tangem.common.extensions.hexToBytes +import com.tangem.common.extensions.toByteArray +import com.tangem.crypto.sign +import com.tangem.devkit._arch.structure.Id +import com.tangem.devkit._arch.structure.PayloadHolder +import com.tangem.devkit._arch.structure.abstraction.findItem +import com.tangem.devkit.ucase.domain.paramsManager.ActionCallback +import com.tangem.devkit.ucase.variants.TlvId +import com.tangem.devkit.ucase.variants.personalize.dto.DefaultPersonalizationParams +import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf + +class WriteIssuerDataAction : BaseAction() { + override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { + val cardId = attrs.itemList.findItem(TlvId.CardId)?.viewModel?.data ?: return + val counter = (attrs.itemList.findItem(TlvId.Counter)?.viewModel?.data as? Int) ?: 1 + + val issuerDataItem = attrs.itemList.findItem(TlvId.IssuerData) ?: return + val issuerData = (issuerDataItem.getData() as? String)?.toByteArray() ?: return + + val signedIssuerData = ( + stringOf(cardId).hexToBytes() + issuerData + counter.toByteArray(4)) + .sign(DefaultPersonalizationParams.issuer().dataKeyPair.privateKey) + + attrs.tangemSdk.writeIssuerData(stringOf(cardId), issuerData, signedIssuerData, counter) { + handleResult(payload, it, null, attrs, callback) + } + } + + override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? { + return when (id) { + TlvId.CardId -> { callback -> ScanAction().executeMainAction(payload, attrs, callback) } + else -> null + } + } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteIssuerExtraDataAction.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteIssuerExtraDataAction.kt new file mode 100644 index 0000000000..17c5052f9f --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteIssuerExtraDataAction.kt @@ -0,0 +1,45 @@ +package com.tangem.devkit.ucase.domain.actions + +import com.tangem.commands.WriteIssuerExtraDataCommand +import com.tangem.common.extensions.hexToBytes +import com.tangem.common.extensions.toByteArray +import com.tangem.crypto.CryptoUtils +import com.tangem.crypto.sign +import com.tangem.devkit._arch.structure.Id +import com.tangem.devkit._arch.structure.PayloadHolder +import com.tangem.devkit._arch.structure.abstraction.findItem +import com.tangem.devkit.ucase.domain.paramsManager.ActionCallback +import com.tangem.devkit.ucase.variants.TlvId +import com.tangem.devkit.ucase.variants.personalize.dto.DefaultPersonalizationParams +import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf + +class WriteIssuerExtraDataAction : BaseAction() { + override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { + val cardId = attrs.itemList.findItem(TlvId.CardId)?.viewModel?.data ?: return + val counter = (attrs.itemList.findItem(TlvId.Counter)?.viewModel?.data as? String)?.toInt() ?: 1 + + val issuerData = CryptoUtils.generateRandomBytes(WriteIssuerExtraDataCommand.SINGLE_WRITE_SIZE * 5) + + + val startingSignature = ( + stringOf(cardId).hexToBytes() + counter.toByteArray(4) + issuerData.size.toByteArray(2)) + .sign(DefaultPersonalizationParams.issuer().dataKeyPair.privateKey) + + val finalizingSignature = ( + stringOf(cardId).hexToBytes() + issuerData + counter.toByteArray(4)) + .sign(DefaultPersonalizationParams.issuer().dataKeyPair.privateKey) + + attrs.tangemSdk.writeIssuerExtraData( + stringOf(cardId), issuerData, startingSignature, finalizingSignature, counter + ) { + handleResult(payload, it, null, attrs, callback) + } + } + + override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? { + return when (id) { + TlvId.CardId -> { callback -> ScanAction().executeMainAction(payload, attrs, callback) } + else -> null + } + } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteUserDataAction.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteUserDataAction.kt new file mode 100644 index 0000000000..d7d6da6107 --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteUserDataAction.kt @@ -0,0 +1,31 @@ +package com.tangem.devkit.ucase.domain.actions + +import com.tangem.devkit._arch.structure.Id +import com.tangem.devkit._arch.structure.PayloadHolder +import com.tangem.devkit._arch.structure.abstraction.findItem +import com.tangem.devkit.ucase.domain.paramsManager.ActionCallback +import com.tangem.devkit.ucase.variants.TlvId +import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf + +class WriteUserDataAction : BaseAction() { + override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { + val userData = (attrs.itemList.findItem(TlvId.UserData)?.getData() as? String)?.toByteArray() + ?: return + val protectedUserData = (attrs.itemList.findItem(TlvId.ProtectedUserData)?.getData() as? String)?.toByteArray() + ?: return + val cardId = attrs.itemList.findItem(TlvId.CardId)?.viewModel?.data ?: return + val counter = (attrs.itemList.findItem(TlvId.Counter)?.viewModel?.data as? Int) ?: 1 + + attrs.tangemSdk.writeUserData(stringOf(cardId), userData, protectedUserData, counter, counter) { + handleResult(payload, it, null, attrs, callback) + } + } + + override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? { + return when (id) { + TlvId.CardId -> { callback -> ScanAction().executeMainAction(payload, attrs, callback) } + TlvId.Counter -> { callback -> ReadUserDataAction().executeMainAction(payload, attrs, callback) } + else -> null + } + } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/paramsManager/managers/SimpleManagers.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/paramsManager/managers/SimpleManagers.kt index a2a7ab9e8b..a5085dccb2 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/paramsManager/managers/SimpleManagers.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/paramsManager/managers/SimpleManagers.kt @@ -1,9 +1,7 @@ package com.tangem.devkit.ucase.domain.paramsManager.managers import com.tangem.devkit._arch.structure.impl.EditTextItem -import com.tangem.devkit.ucase.domain.actions.DepersonalizeAction -import com.tangem.devkit.ucase.domain.actions.ScanAction -import com.tangem.devkit.ucase.domain.actions.SignAction +import com.tangem.devkit.ucase.domain.actions.* import com.tangem.devkit.ucase.domain.paramsManager.triggers.changeConsequence.CardIdConsequence import com.tangem.devkit.ucase.variants.TlvId @@ -29,4 +27,78 @@ class SignItemsManager : BaseItemsManager(SignAction()) { EditTextItem(TlvId.TransactionOutHash, "Data used for hashing") )) } +} + +class CreateWalletItemsManager : BaseItemsManager(CreateWalletAction()) { + + init { + setItemChangeConsequences(CardIdConsequence()) + setItems(listOf(EditTextItem(TlvId.CardId, null))) + } +} + +class PurgeWalletItemsManager : BaseItemsManager(PurgeWalletAction()) { + + init { + setItemChangeConsequences(CardIdConsequence()) + setItems(listOf(EditTextItem(TlvId.CardId, null))) + } +} + +class ReadIssuerDataItemsManager : BaseItemsManager(ReadIssuerDataAction()) { + + init { + setItemChangeConsequences(CardIdConsequence()) + setItems(listOf(EditTextItem(TlvId.CardId, null))) + } +} + +class WriteIssuerDataItemsManager : BaseItemsManager(WriteIssuerDataAction()) { + init { + setItemChangeConsequences(CardIdConsequence()) + setItems(listOf( + EditTextItem(TlvId.CardId, null), + EditTextItem(TlvId.IssuerData, "Data to be written on a card as issuer data"), + EditTextItem(TlvId.Counter, "1") + )) + } +} + +class ReadIssuerExtraDataItemsManager : BaseItemsManager(ReadIssuerExtraDataAction()) { + + init { + setItemChangeConsequences(CardIdConsequence()) + setItems(listOf(EditTextItem(TlvId.CardId, null))) + } +} + +class WriteIssuerExtraDataItemsManager : BaseItemsManager(WriteIssuerExtraDataAction()) { + init { + setItemChangeConsequences(CardIdConsequence()) + setItems(listOf( + EditTextItem(TlvId.CardId, null), + EditTextItem(TlvId.Counter, "1") + )) + } +} + +class ReadUserDataItemsManager : BaseItemsManager(ReadUserDataAction()) { + + init { + setItemChangeConsequences(CardIdConsequence()) + setItems(listOf(EditTextItem(TlvId.CardId, null))) + } +} + +class WriteUserDataItemsManager : BaseItemsManager(WriteUserDataAction()) { + init { + setItemChangeConsequences(CardIdConsequence()) + setItems(listOf( + EditTextItem(TlvId.CardId, null), + EditTextItem(TlvId.Counter, "1"), + EditTextItem(TlvId.UserData, "User data to be written on a card"), + EditTextItem(TlvId.ProtectedUserData, "Protected user data to be written on a card") + + )) + } } \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/PersonalizationResources.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/PersonalizationResources.kt index d5a9eb0601..2319076b7b 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/PersonalizationResources.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/PersonalizationResources.kt @@ -40,89 +40,89 @@ class PersonalizationResources() { } private fun initCardNumber(holder: TypedHolder) { - holder.register(CardNumber.Series, Resources(R.string.pers_item_series, R.string.info_pers_item_series)) - holder.register(CardNumber.Number, Resources(R.string.pers_item_number, R.string.info_pers_item_number)) - holder.register(CardNumber.BatchId, Resources(R.string.pers_item_batch_id, R.string.info_pers_item_batch_id)) + holder.register(CardNumberId.Series, Resources(R.string.pers_item_series, R.string.info_pers_item_series)) + holder.register(CardNumberId.Number, Resources(R.string.pers_item_number, R.string.info_pers_item_number)) + holder.register(CardNumberId.BatchId, Resources(R.string.pers_item_batch_id, R.string.info_pers_item_batch_id)) } private fun initCommon(holder: TypedHolder) { - holder.register(Common.Curve, Resources(R.string.pers_item_curve, R.string.info_pers_item_curve)) - holder.register(Common.Blockchain, Resources(R.string.pers_item_blockchain, R.string.info_pers_item_blockchain)) - holder.register(Common.BlockchainCustom, Resources(R.string.pers_item_custom_blockchain, R.string.info_pers_item_custom_blockchain)) - holder.register(Common.MaxSignatures, Resources(R.string.pers_item_max_signatures, R.string.info_pers_item_max_signatures)) - holder.register(Common.CreateWallet, Resources(R.string.pers_item_create_wallet, R.string.info_pers_item_create_wallet)) + holder.register(CommonId.Curve, Resources(R.string.pers_item_curve, R.string.info_pers_item_curve)) + holder.register(CommonId.Blockchain, Resources(R.string.pers_item_blockchain, R.string.info_pers_item_blockchain)) + holder.register(CommonId.BlockchainCustom, Resources(R.string.pers_item_custom_blockchain, R.string.info_pers_item_custom_blockchain)) + holder.register(CommonId.MaxSignatures, Resources(R.string.pers_item_max_signatures, R.string.info_pers_item_max_signatures)) + holder.register(CommonId.CreateWallet, Resources(R.string.pers_item_create_wallet, R.string.info_pers_item_create_wallet)) } private fun initSigningMethod(holder: TypedHolder) { - holder.register(SigningMethod.SignTx, Resources(R.string.pers_item_sign_tx_hashes, R.string.info_pers_item_sign_tx_hashes)) - holder.register(SigningMethod.SignTxRaw, Resources(R.string.pers_item_sign_raw_tx, R.string.info_pers_item_sign_raw_tx)) - holder.register(SigningMethod.SignValidatedTx, Resources(R.string.pers_item_sign_validated_tx_hashes, R.string.info_pers_item_sign_validated_tx_hashes)) - holder.register(SigningMethod.SignValidatedTxRaw, Resources(R.string.pers_item_sign_validated_raw_tx, R.string.info_pers_item_sign_validated_raw_tx)) - holder.register(SigningMethod.SignValidatedTxIssuer, Resources(R.string.pers_item_sign_validated_tx_hashes_with_iss_data, R.string.info_pers_item_sign_validated_tx_hashes_with_iss_data)) - holder.register(SigningMethod.SignValidatedTxRawIssuer, Resources(R.string.pers_item_sign_validated_raw_tx_with_iss_data, R.string.info_pers_item_sign_validated_raw_tx_with_iss_data)) - holder.register(SigningMethod.SignExternal, Resources(R.string.pers_item_sign_hash_ex, R.string.info_pers_item_sign_hash_ex)) + holder.register(SigningMethodId.SignTx, Resources(R.string.pers_item_sign_tx_hashes, R.string.info_pers_item_sign_tx_hashes)) + holder.register(SigningMethodId.SignTxRaw, Resources(R.string.pers_item_sign_raw_tx, R.string.info_pers_item_sign_raw_tx)) + holder.register(SigningMethodId.SignValidatedTx, Resources(R.string.pers_item_sign_validated_tx_hashes, R.string.info_pers_item_sign_validated_tx_hashes)) + holder.register(SigningMethodId.SignValidatedTxRaw, Resources(R.string.pers_item_sign_validated_raw_tx, R.string.info_pers_item_sign_validated_raw_tx)) + holder.register(SigningMethodId.SignValidatedTxIssuer, Resources(R.string.pers_item_sign_validated_tx_hashes_with_iss_data, R.string.info_pers_item_sign_validated_tx_hashes_with_iss_data)) + holder.register(SigningMethodId.SignValidatedTxRawIssuer, Resources(R.string.pers_item_sign_validated_raw_tx_with_iss_data, R.string.info_pers_item_sign_validated_raw_tx_with_iss_data)) + holder.register(SigningMethodId.SignExternal, Resources(R.string.pers_item_sign_hash_ex, R.string.info_pers_item_sign_hash_ex)) } private fun initSignHashExProp(holder: TypedHolder) { - holder.register(SignHashExProp.PinLessFloorLimit, Resources(R.string.pers_item_pin_less_floor_limit, R.string.info_pers_item_pin_less_floor_limit)) - holder.register(SignHashExProp.CryptoExKey, Resources(R.string.pers_item_cr_ex_key, R.string.info_pers_item_cr_ex_key)) - holder.register(SignHashExProp.RequireTerminalCertSig, Resources(R.string.pers_item_require_terminal_cert_sig, R.string.info_pers_item_require_terminal_cert_sig)) - holder.register(SignHashExProp.RequireTerminalTxSig, Resources(R.string.pers_item_require_terminal_tx_sig, R.string.info_pers_item_require_terminal_tx_sig)) - holder.register(SignHashExProp.CheckPin3, Resources(R.string.pers_item_check_pin3_on_card, R.string.info_pers_item_check_pin3_on_card)) + holder.register(SignHashExPropId.PinLessFloorLimit, Resources(R.string.pers_item_pin_less_floor_limit, R.string.info_pers_item_pin_less_floor_limit)) + holder.register(SignHashExPropId.CryptoExKey, Resources(R.string.pers_item_cr_ex_key, R.string.info_pers_item_cr_ex_key)) + holder.register(SignHashExPropId.RequireTerminalCertSig, Resources(R.string.pers_item_require_terminal_cert_sig, R.string.info_pers_item_require_terminal_cert_sig)) + holder.register(SignHashExPropId.RequireTerminalTxSig, Resources(R.string.pers_item_require_terminal_tx_sig, R.string.info_pers_item_require_terminal_tx_sig)) + holder.register(SignHashExPropId.CheckPin3, Resources(R.string.pers_item_check_pin3_on_card, R.string.info_pers_item_check_pin3_on_card)) } private fun initToken(holder: TypedHolder) { - holder.register(Token.ItsToken, Resources(R.string.pers_item_its_token, R.string.info_pers_item_its_token)) - holder.register(Token.Symbol, Resources(R.string.pers_item_symbol, R.string.info_pers_item_symbol)) - holder.register(Token.ContractAddress, Resources(R.string.pers_item_contract_address, R.string.info_pers_item_contract_address)) - holder.register(Token.Decimal, Resources(R.string.pers_item_decimal, R.string.info_pers_item_decimal)) + holder.register(TokenId.ItsToken, Resources(R.string.pers_item_its_token, R.string.info_pers_item_its_token)) + holder.register(TokenId.Symbol, Resources(R.string.pers_item_symbol, R.string.info_pers_item_symbol)) + holder.register(TokenId.ContractAddress, Resources(R.string.pers_item_contract_address, R.string.info_pers_item_contract_address)) + holder.register(TokenId.Decimal, Resources(R.string.pers_item_decimal, R.string.info_pers_item_decimal)) } private fun initProductMask(holder: TypedHolder) { - holder.register(ProductMask.Note, Resources(R.string.pers_item_note, R.string.info_pers_item_note)) - holder.register(ProductMask.Tag, Resources(R.string.pers_item_tag, R.string.info_pers_item_tag)) - holder.register(ProductMask.IdCard, Resources(R.string.pers_item_id_card, R.string.info_pers_item_id_card)) - holder.register(ProductMask.IdIssuerCard, Resources(R.string.pers_item_id_issuer_card, R.string.info_pers_item_id_issuer_card)) + holder.register(ProductMaskId.Note, Resources(R.string.pers_item_note, R.string.info_pers_item_note)) + holder.register(ProductMaskId.Tag, Resources(R.string.pers_item_tag, R.string.info_pers_item_tag)) + holder.register(ProductMaskId.IdCard, Resources(R.string.pers_item_id_card, R.string.info_pers_item_id_card)) + holder.register(ProductMaskId.IdIssuerCard, Resources(R.string.pers_item_id_issuer_card, R.string.info_pers_item_id_issuer_card)) } private fun initSettingsMask(holder: TypedHolder) { - holder.register(SettingsMask.IsReusable, Resources(R.string.pers_item_is_reusable, R.string.info_pers_item_is_reusable)) - holder.register(SettingsMask.NeedActivation, Resources(R.string.pers_item_need_activation, R.string.info_pers_item_need_activation)) - holder.register(SettingsMask.ForbidPurge, Resources(R.string.pers_item_forbid_purge, R.string.info_pers_item_forbid_purge)) - holder.register(SettingsMask.AllowSelectBlockchain, Resources(R.string.pers_item_allow_select_blockchain, R.string.info_pers_item_allow_select_blockchain)) - holder.register(SettingsMask.UseBlock, Resources(R.string.pers_item_use_block, R.string.info_pers_item_use_block)) - holder.register(SettingsMask.OneApdu, Resources(R.string.pers_item_one_apdu_at_once, R.string.info_pers_item_one_apdu_at_once)) - holder.register(SettingsMask.UseCvc, Resources(R.string.pers_item_use_cvc, R.string.info_pers_item_use_cvc)) - holder.register(SettingsMask.AllowSwapPin, Resources(R.string.pers_item_allow_swap_pin, R.string.info_pers_item_allow_swap_pin)) - holder.register(SettingsMask.AllowSwapPin2, Resources(R.string.pers_item_allow_swap_pin2, R.string.info_pers_item_allow_swap_pin2)) - holder.register(SettingsMask.ForbidDefaultPin, Resources(R.string.pers_item_forbid_default_pin, R.string.info_pers_item_forbid_default_pin)) - holder.register(SettingsMask.SmartSecurityDelay, Resources(R.string.pers_item_smart_security_delay, R.string.info_pers_item_smart_security_delay)) - holder.register(SettingsMask.ProtectIssuerDataAgainstReplay, Resources(R.string.pers_item_protect_issuer_data_against_replay, R.string.info_pers_item_protect_issuer_data_against_replay)) - holder.register(SettingsMask.SkipSecurityDelayIfValidated, Resources(R.string.pers_item_skip_security_delay_if_validated, R.string.info_pers_item_skip_security_delay_if_validated)) - holder.register(SettingsMask.SkipPin2CvcIfValidated, Resources(R.string.pers_item_skip_pin2_and_cvc_if_validated, R.string.info_pers_item_skip_pin2_and_cvc_if_validated)) - holder.register(SettingsMask.SkipSecurityDelayOnLinkedTerminal, Resources(R.string.pers_item_skip_security_delay_on_linked_terminal, R.string.info_pers_item_skip_security_delay_on_linked_terminal)) - holder.register(SettingsMask.RestrictOverwriteExtraIssuerData, Resources(R.string.pers_item_restrict_overwrite_ex_issuer_data, R.string.info_pers_item_restrict_overwrite_ex_issuer_data)) + holder.register(SettingsMaskId.IsReusable, Resources(R.string.pers_item_is_reusable, R.string.info_pers_item_is_reusable)) + holder.register(SettingsMaskId.NeedActivation, Resources(R.string.pers_item_need_activation, R.string.info_pers_item_need_activation)) + holder.register(SettingsMaskId.ForbidPurge, Resources(R.string.pers_item_forbid_purge, R.string.info_pers_item_forbid_purge)) + holder.register(SettingsMaskId.AllowSelectBlockchain, Resources(R.string.pers_item_allow_select_blockchain, R.string.info_pers_item_allow_select_blockchain)) + holder.register(SettingsMaskId.UseBlock, Resources(R.string.pers_item_use_block, R.string.info_pers_item_use_block)) + holder.register(SettingsMaskId.OneApdu, Resources(R.string.pers_item_one_apdu_at_once, R.string.info_pers_item_one_apdu_at_once)) + holder.register(SettingsMaskId.UseCvc, Resources(R.string.pers_item_use_cvc, R.string.info_pers_item_use_cvc)) + holder.register(SettingsMaskId.AllowSwapPin, Resources(R.string.pers_item_allow_swap_pin, R.string.info_pers_item_allow_swap_pin)) + holder.register(SettingsMaskId.AllowSwapPin2, Resources(R.string.pers_item_allow_swap_pin2, R.string.info_pers_item_allow_swap_pin2)) + holder.register(SettingsMaskId.ForbidDefaultPin, Resources(R.string.pers_item_forbid_default_pin, R.string.info_pers_item_forbid_default_pin)) + holder.register(SettingsMaskId.SmartSecurityDelay, Resources(R.string.pers_item_smart_security_delay, R.string.info_pers_item_smart_security_delay)) + holder.register(SettingsMaskId.ProtectIssuerDataAgainstReplay, Resources(R.string.pers_item_protect_issuer_data_against_replay, R.string.info_pers_item_protect_issuer_data_against_replay)) + holder.register(SettingsMaskId.SkipSecurityDelayIfValidated, Resources(R.string.pers_item_skip_security_delay_if_validated, R.string.info_pers_item_skip_security_delay_if_validated)) + holder.register(SettingsMaskId.SkipPin2CvcIfValidated, Resources(R.string.pers_item_skip_pin2_and_cvc_if_validated, R.string.info_pers_item_skip_pin2_and_cvc_if_validated)) + holder.register(SettingsMaskId.SkipSecurityDelayOnLinkedTerminal, Resources(R.string.pers_item_skip_security_delay_on_linked_terminal, R.string.info_pers_item_skip_security_delay_on_linked_terminal)) + holder.register(SettingsMaskId.RestrictOverwriteExtraIssuerData, Resources(R.string.pers_item_restrict_overwrite_ex_issuer_data, R.string.info_pers_item_restrict_overwrite_ex_issuer_data)) } private fun initSettingsMaskProtocolEnc(holder: TypedHolder) { - holder.register(SettingsMaskProtocolEnc.AllowUnencrypted, Resources(R.string.pers_item_allow_unencrypted, R.string.info_pers_item_allow_unencrypted)) - holder.register(SettingsMaskProtocolEnc.AllowStaticEncryption, Resources(R.string.pers_item_allow_fast_encryption, R.string.info_pers_item_allow_fast_encryption)) + holder.register(SettingsMaskProtocolEncId.AllowUnencrypted, Resources(R.string.pers_item_allow_unencrypted, R.string.info_pers_item_allow_unencrypted)) + holder.register(SettingsMaskProtocolEncId.AllowStaticEncryption, Resources(R.string.pers_item_allow_fast_encryption, R.string.info_pers_item_allow_fast_encryption)) } private fun initSettingsMaskNdef(holder: TypedHolder) { - holder.register(SettingsMaskNdef.UseNdef, Resources(R.string.pers_item_use_ndef, R.string.info_pers_item_use_ndef)) - holder.register(SettingsMaskNdef.DynamicNdef, Resources(R.string.pers_item_dynamic_ndef, R.string.info_pers_item_dynamic_ndef)) - holder.register(SettingsMaskNdef.DisablePrecomputedNdef, Resources(R.string.pers_item_disable_precomputed_ndef, R.string.info_pers_item_disable_precomputed_ndef)) - holder.register(SettingsMaskNdef.Aar, Resources(R.string.pers_item_aar, R.string.info_pers_item_aar)) - holder.register(SettingsMaskNdef.AarCustom, Resources(R.string.pers_item_custom_aar_package_name, R.string.info_pers_item_custom_aar_package_name)) - holder.register(SettingsMaskNdef.Uri, Resources(R.string.pers_item_uri, R.string.info_pers_item_uri)) + holder.register(SettingsMaskNdefId.UseNdef, Resources(R.string.pers_item_use_ndef, R.string.info_pers_item_use_ndef)) + holder.register(SettingsMaskNdefId.DynamicNdef, Resources(R.string.pers_item_dynamic_ndef, R.string.info_pers_item_dynamic_ndef)) + holder.register(SettingsMaskNdefId.DisablePrecomputedNdef, Resources(R.string.pers_item_disable_precomputed_ndef, R.string.info_pers_item_disable_precomputed_ndef)) + holder.register(SettingsMaskNdefId.Aar, Resources(R.string.pers_item_aar, R.string.info_pers_item_aar)) + holder.register(SettingsMaskNdefId.AarCustom, Resources(R.string.pers_item_custom_aar_package_name, R.string.info_pers_item_custom_aar_package_name)) + holder.register(SettingsMaskNdefId.Uri, Resources(R.string.pers_item_uri, R.string.info_pers_item_uri)) } private fun initPins(holder: TypedHolder) { - holder.register(Pins.Pin, Resources(R.string.pers_item_pin, R.string.info_pers_item_pin)) - holder.register(Pins.Pin2, Resources(R.string.pers_item_pin2, R.string.info_pers_item_pin2)) - holder.register(Pins.Pin3, Resources(R.string.pers_item_pin3, R.string.info_pers_item_pin3)) - holder.register(Pins.Cvc, Resources(R.string.pers_item_cvc, R.string.info_pers_item_cvc)) - holder.register(Pins.PauseBeforePin2, Resources(R.string.pers_item_pause_before_pin2, R.string.info_pers_item_pause_before_pin2)) + holder.register(PinsId.Pin, Resources(R.string.pers_item_pin, R.string.info_pers_item_pin)) + holder.register(PinsId.Pin2, Resources(R.string.pers_item_pin2, R.string.info_pers_item_pin2)) + holder.register(PinsId.Pin3, Resources(R.string.pers_item_pin3, R.string.info_pers_item_pin3)) + holder.register(PinsId.Cvc, Resources(R.string.pers_item_cvc, R.string.info_pers_item_cvc)) + holder.register(PinsId.PauseBeforePin2, Resources(R.string.pers_item_pause_before_pin2, R.string.info_pers_item_pause_before_pin2)) } } \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/ResponseResources.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/ResponseResources.kt index 37a044c9fa..792a4ee0cb 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/ResponseResources.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/ResponseResources.kt @@ -3,10 +3,7 @@ package com.tangem.devkit.ucase.resources.initializers import com.tangem.devkit.R import com.tangem.devkit.ucase.resources.MainResourceHolder import com.tangem.devkit.ucase.resources.Resources -import com.tangem.devkit.ucase.variants.responses.CardDataId -import com.tangem.devkit.ucase.variants.responses.CardId -import com.tangem.devkit.ucase.variants.responses.DepersonalizeId -import com.tangem.devkit.ucase.variants.responses.SignId +import com.tangem.devkit.ucase.variants.responses.* /** [REDACTED_AUTHOR] @@ -17,6 +14,13 @@ class ResponseResources { initCardData(holder) initSignResponse(holder) initDepersonalizeResponse(holder) + initCreateWalletResponse(holder) + initPurgeWalletResponse(holder) + initReadIssuerDataResponse(holder) + initWriteIssuerDataResponse(holder) + initReadIssuerExtraDataResponse(holder) + initReadUserDataResponse(holder) + initWriteUserDataResponse(holder) } private fun initCard(holder: MainResourceHolder) { @@ -65,4 +69,46 @@ class ResponseResources { private fun initDepersonalizeResponse(holder: MainResourceHolder) { holder.register(DepersonalizeId.isSuccess, Resources(R.string.response_depersonalize_is_success, R.string.info_response_depersonalize_is_success)) } + + private fun initCreateWalletResponse(holder: MainResourceHolder) { + holder.register(CreateWalletId.cid, Resources(R.string.response_card_cid, R.string.info_response_card_cid)) + holder.register(CreateWalletId.cardStatus, Resources(R.string.response_card_status, R.string.info_response_card_status)) + holder.register(CreateWalletId.walletPublicKey, Resources(R.string.response_card_wallet_public_key, R.string.info_response_card_wallet_public_key)) + } + + private fun initPurgeWalletResponse(holder: MainResourceHolder) { + holder.register(PurgeWalletId.cid, Resources(R.string.response_card_cid, R.string.info_response_card_cid)) + holder.register(PurgeWalletId.cardStatus, Resources(R.string.response_card_status, R.string.info_response_card_status)) + } + + private fun initReadIssuerDataResponse(holder: MainResourceHolder) { + holder.register(ReadIssuerDataId.cid, Resources(R.string.response_card_cid, R.string.info_response_card_cid)) + holder.register(ReadIssuerDataId.issuerData, Resources(R.string.response_issuer_data, R.string.info_response_issuer_data)) + holder.register(ReadIssuerDataId.issuerDataSignature, Resources(R.string.response_issuer_data_signature, R.string.info_response_issuer_data_signature)) + holder.register(ReadIssuerDataId.issuerDataCounter, Resources(R.string.response_issuer_data_counter, R.string.info_response_issuer_data_counter)) + } + + private fun initWriteIssuerDataResponse(holder: MainResourceHolder) { + holder.register(ReadIssuerDataId.cid, Resources(R.string.response_card_cid, R.string.info_response_card_cid)) + } + + private fun initReadIssuerExtraDataResponse(holder: MainResourceHolder) { + holder.register(ReadIssuerExtraDataId.cid, Resources(R.string.response_card_cid, R.string.info_response_card_cid)) + holder.register(ReadIssuerExtraDataId.size, Resources(R.string.response_issuer_data_size, R.string.info_response_issuer_data_size)) + holder.register(ReadIssuerExtraDataId.issuerData, Resources(R.string.response_issuer_data, R.string.info_response_issuer_data)) + holder.register(ReadIssuerExtraDataId.issuerDataSignature, Resources(R.string.response_issuer_data_signature, R.string.info_response_issuer_data_signature)) + holder.register(ReadIssuerExtraDataId.issuerDataCounter, Resources(R.string.response_issuer_data_counter, R.string.info_response_issuer_data_counter)) + } + + private fun initReadUserDataResponse(holder: MainResourceHolder) { + holder.register(ReadUserDataId.cid, Resources(R.string.response_card_cid, R.string.info_response_card_cid)) + holder.register(ReadUserDataId.userData, Resources(R.string.response_user_data, R.string.info_response_user_data)) + holder.register(ReadUserDataId.userProtectedData, Resources(R.string.response_user_protected_data, R.string.info_response_user_protected_data)) + holder.register(ReadUserDataId.userCounter, Resources(R.string.response_user_data_counter, R.string.info_response_user_data_counter)) + holder.register(ReadUserDataId.userProtectedCounter, Resources(R.string.response_user_data_protected_counter, R.string.info_response_user_data_protected_counter)) + } + + private fun initWriteUserDataResponse(holder: MainResourceHolder) { + holder.register(ReadIssuerDataId.cid, Resources(R.string.response_card_cid, R.string.info_response_card_cid)) + } } \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/TlvResources.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/TlvResources.kt index a1b64dd45d..32bb68d840 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/TlvResources.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/TlvResources.kt @@ -16,5 +16,9 @@ class TlvResources { private fun initScan(holder: MainResourceHolder) { holder.register(TlvId.CardId, Resources(R.string.tlv_card_id, R.string.info_tlv_card_id)) holder.register(TlvId.TransactionOutHash, Resources(R.string.tlv_transaction_out_hash, R.string.info_tlv_transaction_out_hash)) + holder.register(TlvId.Counter, Resources(R.string.tlv_counter, R.string.info_tlv_counter)) + holder.register(TlvId.IssuerData, Resources(R.string.tlv_issuer_data, R.string.info_tlv_issuer_data)) + holder.register(TlvId.UserData, Resources(R.string.tlv_issuer_data, R.string.info_tlv_user_data)) + holder.register(TlvId.ProtectedUserData, Resources(R.string.tlv_user_protected_data, R.string.info_tlv_protected_user_data)) } } \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/ui/BaseCardActionFragment.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/ui/BaseCardActionFragment.kt index 97bb78f808..36cf46321e 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/ui/BaseCardActionFragment.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/ui/BaseCardActionFragment.kt @@ -140,7 +140,7 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { }) } - private fun inflateParamView(where: ViewGroup): ViewGroup { + protected fun inflateParamView(where: ViewGroup): ViewGroup { val inflater = LayoutInflater.from(where.context) val view = inflater.inflate(R.layout.w_card_incoming_param, where, false) where.plusAssign(view) diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/ui/widgets/ParameterWidget.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/ui/widgets/ParameterWidget.kt index 1187a6d9db..366a99e449 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/ui/widgets/ParameterWidget.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/ui/widgets/ParameterWidget.kt @@ -56,6 +56,7 @@ class ParameterWidget( btnAction.setText(getResNameId(ActionType.Scan)) toggleActionBtnVisibility() initDescriptionWidget() + parent.tag = Id.getTag(typedItem.id) } fun changeParamValue(data: Any?, silent: Boolean = true) { diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/Ids.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/Ids.kt index e372276e8a..2200b40724 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/Ids.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/Ids.kt @@ -8,4 +8,8 @@ import com.tangem.devkit._arch.structure.Id enum class TlvId : Id { CardId, TransactionOutHash, + IssuerData, + Counter, + UserData, + ProtectedUserData } \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/createwallet/ui/CreateWalletActionFragment.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/createwallet/ui/CreateWalletActionFragment.kt new file mode 100644 index 0000000000..605f9dd649 --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/createwallet/ui/CreateWalletActionFragment.kt @@ -0,0 +1,10 @@ +package com.tangem.devkit.ucase.variants.createwallet.ui + +import com.tangem.devkit.ucase.domain.paramsManager.ItemsManager +import com.tangem.devkit.ucase.domain.paramsManager.managers.CreateWalletItemsManager +import com.tangem.devkit.ucase.ui.BaseCardActionFragment + +class CreateWalletActionFragment : BaseCardActionFragment() { + + override val itemsManager: ItemsManager by lazy { CreateWalletItemsManager() } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/issuerdata/ui/ReadIssuerDataFragment.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/issuerdata/ui/ReadIssuerDataFragment.kt new file mode 100644 index 0000000000..6d3ac9be86 --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/issuerdata/ui/ReadIssuerDataFragment.kt @@ -0,0 +1,10 @@ +package com.tangem.devkit.ucase.variants.issuerdata.ui + +import com.tangem.devkit.ucase.domain.paramsManager.ItemsManager +import com.tangem.devkit.ucase.domain.paramsManager.managers.ReadIssuerDataItemsManager +import com.tangem.devkit.ucase.ui.BaseCardActionFragment + +class ReadIssuerDataFragment : BaseCardActionFragment() { + + override val itemsManager: ItemsManager by lazy { ReadIssuerDataItemsManager() } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/issuerdata/ui/ReadIssuerExtraDataFragment.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/issuerdata/ui/ReadIssuerExtraDataFragment.kt new file mode 100644 index 0000000000..74024ca176 --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/issuerdata/ui/ReadIssuerExtraDataFragment.kt @@ -0,0 +1,10 @@ +package com.tangem.devkit.ucase.variants.issuerdata.ui + +import com.tangem.devkit.ucase.domain.paramsManager.ItemsManager +import com.tangem.devkit.ucase.domain.paramsManager.managers.ReadIssuerExtraDataItemsManager +import com.tangem.devkit.ucase.ui.BaseCardActionFragment + +class ReadIssuerExtraDataFragment : BaseCardActionFragment() { + + override val itemsManager: ItemsManager by lazy { ReadIssuerExtraDataItemsManager() } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/issuerdata/ui/WriteIssuerDataFragment.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/issuerdata/ui/WriteIssuerDataFragment.kt new file mode 100644 index 0000000000..88e956818c --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/issuerdata/ui/WriteIssuerDataFragment.kt @@ -0,0 +1,10 @@ +package com.tangem.devkit.ucase.variants.issuerdata.ui + +import com.tangem.devkit.ucase.domain.paramsManager.ItemsManager +import com.tangem.devkit.ucase.domain.paramsManager.managers.WriteIssuerDataItemsManager +import com.tangem.devkit.ucase.ui.BaseCardActionFragment + +class WriteIssuerDataFragment : BaseCardActionFragment() { + + override val itemsManager: ItemsManager by lazy { WriteIssuerDataItemsManager() } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/issuerdata/ui/WriteIssuerExtraDataFragment.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/issuerdata/ui/WriteIssuerExtraDataFragment.kt new file mode 100644 index 0000000000..a90fce5e7a --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/issuerdata/ui/WriteIssuerExtraDataFragment.kt @@ -0,0 +1,10 @@ +package com.tangem.devkit.ucase.variants.issuerdata.ui + +import com.tangem.devkit.ucase.domain.paramsManager.ItemsManager +import com.tangem.devkit.ucase.domain.paramsManager.managers.WriteIssuerExtraDataItemsManager +import com.tangem.devkit.ucase.ui.BaseCardActionFragment + +class WriteIssuerExtraDataFragment : BaseCardActionFragment() { + + override val itemsManager: ItemsManager by lazy { WriteIssuerExtraDataItemsManager() } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/Ids.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/Ids.kt index fd186e61f2..fef98a3cbc 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/Ids.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/Ids.kt @@ -22,13 +22,13 @@ enum class BlockId : PersonalizationId { Pins, } -enum class CardNumber : PersonalizationId { +enum class CardNumberId : PersonalizationId { Series, Number, BatchId, } -enum class Common : PersonalizationId { +enum class CommonId : PersonalizationId { Curve, Blockchain, BlockchainCustom, @@ -36,7 +36,7 @@ enum class Common : PersonalizationId { CreateWallet, } -enum class SigningMethod : PersonalizationId { +enum class SigningMethodId : PersonalizationId { SignTx, SignTxRaw, SignValidatedTx, @@ -46,7 +46,7 @@ enum class SigningMethod : PersonalizationId { SignExternal, } -enum class SignHashExProp : PersonalizationId { +enum class SignHashExPropId : PersonalizationId { PinLessFloorLimit, CryptoExKey, RequireTerminalCertSig, @@ -54,21 +54,21 @@ enum class SignHashExProp : PersonalizationId { CheckPin3, } -enum class Token : PersonalizationId { +enum class TokenId : PersonalizationId { ItsToken, Symbol, ContractAddress, Decimal } -enum class ProductMask : PersonalizationId { +enum class ProductMaskId : PersonalizationId { Note, Tag, IdCard, IdIssuerCard } -enum class SettingsMask : PersonalizationId { +enum class SettingsMaskId : PersonalizationId { IsReusable, NeedActivation, ForbidPurge, @@ -87,12 +87,12 @@ enum class SettingsMask : PersonalizationId { RestrictOverwriteExtraIssuerData, } -enum class SettingsMaskProtocolEnc : PersonalizationId { +enum class SettingsMaskProtocolEncId : PersonalizationId { AllowUnencrypted, AllowStaticEncryption } -enum class SettingsMaskNdef : PersonalizationId { +enum class SettingsMaskNdefId : PersonalizationId { UseNdef, DynamicNdef, DisablePrecomputedNdef, @@ -101,7 +101,7 @@ enum class SettingsMaskNdef : PersonalizationId { Uri, } -enum class Pins : PersonalizationId { +enum class PinsId : PersonalizationId { Pin, Pin2, Pin3, diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/DefaultValues.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/DefaultValues.kt index 51747d4156..bf12a14dd4 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/DefaultValues.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/DefaultValues.kt @@ -14,63 +14,63 @@ class ConfigValuesHolder : BaseTypedHolder() { // All registered association values can't be objects fun init(default: PersonalizationConfig) { - register(CardNumber.Series, Value(default.series)) - register(CardNumber.Number, Value(default.startNumber)) - register(CardNumber.BatchId, Value(default.cardData.batch)) - register(Common.Curve, Value(default.curveID, Helper.listOfCurves())) - register(Common.Blockchain, Value(default.cardData.blockchain, Helper.listOfBlockchain())) - register(Common.BlockchainCustom, Value(default.blockchainCustom)) - register(Common.MaxSignatures, Value(default.MaxSignatures)) - register(Common.CreateWallet, Value(default.createWallet)) - register(SigningMethod.SignTx, Value(default.SigningMethod0)) - register(SigningMethod.SignTxRaw, Value(default.SigningMethod1)) - register(SigningMethod.SignValidatedTx, Value(default.SigningMethod2)) - register(SigningMethod.SignValidatedTxRaw, Value(default.SigningMethod3)) - register(SigningMethod.SignValidatedTxIssuer, Value(default.SigningMethod4)) - register(SigningMethod.SignValidatedTxRawIssuer, Value(default.SigningMethod5)) - register(SigningMethod.SignExternal, Value(default.SigningMethod6)) - register(SignHashExProp.PinLessFloorLimit, Value(default.pinLessFloorLimit)) - register(SignHashExProp.CryptoExKey, Value(default.hexCrExKey)) - register(SignHashExProp.RequireTerminalCertSig, Value(default.requireTerminalCertSignature)) - register(SignHashExProp.RequireTerminalTxSig, Value(default.requireTerminalTxSignature)) - register(SignHashExProp.CheckPin3, Value(default.checkPIN3onCard)) - register(Token.ItsToken, Value(default.itsToken)) - register(Token.Symbol, Value(default.cardData.token_symbol)) - register(Token.ContractAddress, Value(default.cardData.token_contract_address)) - register(Token.Decimal, Value(default.cardData.token_decimal)) - register(ProductMask.Note, Value(default.cardData.product_note)) - register(ProductMask.Tag, Value(default.cardData.product_tag)) - register(ProductMask.IdCard, Value(default.cardData.product_id_card)) - register(ProductMask.IdIssuerCard, Value(default.cardData.product_id_issuer)) - register(SettingsMask.IsReusable, Value(default.isReusable)) - register(SettingsMask.NeedActivation, Value(default.useActivation)) - register(SettingsMask.ForbidPurge, Value(default.forbidPurgeWallet)) - register(SettingsMask.AllowSelectBlockchain, Value(default.allowSelectBlockchain)) - register(SettingsMask.UseBlock, Value(default.useBlock)) - register(SettingsMask.OneApdu, Value(default.useOneCommandAtTime)) - register(SettingsMask.UseCvc, Value(default.useCVC)) - register(SettingsMask.AllowSwapPin, Value(default.allowSwapPIN)) - register(SettingsMask.AllowSwapPin2, Value(default.allowSwapPIN2)) - register(SettingsMask.ForbidDefaultPin, Value(default.forbidDefaultPIN)) - register(SettingsMask.SmartSecurityDelay, Value(default.smartSecurityDelay)) - register(SettingsMask.ProtectIssuerDataAgainstReplay, Value(default.protectIssuerDataAgainstReplay)) - register(SettingsMask.SkipSecurityDelayIfValidated, Value(default.skipSecurityDelayIfValidatedByIssuer)) - register(SettingsMask.SkipPin2CvcIfValidated, Value(default.skipCheckPIN2andCVCIfValidatedByIssuer)) - register(SettingsMask.SkipSecurityDelayOnLinkedTerminal, Value(default.skipSecurityDelayIfValidatedByLinkedTerminal)) - register(SettingsMask.RestrictOverwriteExtraIssuerData, Value(default.restrictOverwriteIssuerDataEx)) - register(SettingsMaskProtocolEnc.AllowUnencrypted, Value(default.protocolAllowUnencrypted)) - register(SettingsMaskProtocolEnc.AllowStaticEncryption, Value(default.protocolAllowStaticEncryption)) - register(SettingsMaskNdef.UseNdef, Value(default.useNDEF)) - register(SettingsMaskNdef.DynamicNdef, Value(default.useDynamicNDEF)) - register(SettingsMaskNdef.DisablePrecomputedNdef, Value(default.disablePrecomputedNDEF)) - register(SettingsMaskNdef.Aar, Value(default.aar, Helper.aarList())) - register(SettingsMaskNdef.AarCustom, Value(default.aarCustom)) - register(SettingsMaskNdef.Uri, Value(default.uri)) - register(Pins.Pin, Value(default.PIN)) - register(Pins.Pin2, Value(default.PIN2)) - register(Pins.Pin3, Value(default.PIN3)) - register(Pins.Cvc, Value(default.CVC)) - register(Pins.PauseBeforePin2, Value(default.pauseBeforePIN2, Helper.pauseBeforePin())) + register(CardNumberId.Series, Value(default.series)) + register(CardNumberId.Number, Value(default.startNumber)) + register(CardNumberId.BatchId, Value(default.cardData.batch)) + register(CommonId.Curve, Value(default.curveID, Helper.listOfCurves())) + register(CommonId.Blockchain, Value(default.cardData.blockchain, Helper.listOfBlockchain())) + register(CommonId.BlockchainCustom, Value(default.blockchainCustom)) + register(CommonId.MaxSignatures, Value(default.MaxSignatures)) + register(CommonId.CreateWallet, Value(default.createWallet)) + register(SigningMethodId.SignTx, Value(default.SigningMethod0)) + register(SigningMethodId.SignTxRaw, Value(default.SigningMethod1)) + register(SigningMethodId.SignValidatedTx, Value(default.SigningMethod2)) + register(SigningMethodId.SignValidatedTxRaw, Value(default.SigningMethod3)) + register(SigningMethodId.SignValidatedTxIssuer, Value(default.SigningMethod4)) + register(SigningMethodId.SignValidatedTxRawIssuer, Value(default.SigningMethod5)) + register(SigningMethodId.SignExternal, Value(default.SigningMethod6)) + register(SignHashExPropId.PinLessFloorLimit, Value(default.pinLessFloorLimit)) + register(SignHashExPropId.CryptoExKey, Value(default.hexCrExKey)) + register(SignHashExPropId.RequireTerminalCertSig, Value(default.requireTerminalCertSignature)) + register(SignHashExPropId.RequireTerminalTxSig, Value(default.requireTerminalTxSignature)) + register(SignHashExPropId.CheckPin3, Value(default.checkPIN3onCard)) + register(TokenId.ItsToken, Value(default.itsToken)) + register(TokenId.Symbol, Value(default.cardData.token_symbol)) + register(TokenId.ContractAddress, Value(default.cardData.token_contract_address)) + register(TokenId.Decimal, Value(default.cardData.token_decimal)) + register(ProductMaskId.Note, Value(default.cardData.product_note)) + register(ProductMaskId.Tag, Value(default.cardData.product_tag)) + register(ProductMaskId.IdCard, Value(default.cardData.product_id_card)) + register(ProductMaskId.IdIssuerCard, Value(default.cardData.product_id_issuer)) + register(SettingsMaskId.IsReusable, Value(default.isReusable)) + register(SettingsMaskId.NeedActivation, Value(default.useActivation)) + register(SettingsMaskId.ForbidPurge, Value(default.forbidPurgeWallet)) + register(SettingsMaskId.AllowSelectBlockchain, Value(default.allowSelectBlockchain)) + register(SettingsMaskId.UseBlock, Value(default.useBlock)) + register(SettingsMaskId.OneApdu, Value(default.useOneCommandAtTime)) + register(SettingsMaskId.UseCvc, Value(default.useCVC)) + register(SettingsMaskId.AllowSwapPin, Value(default.allowSwapPIN)) + register(SettingsMaskId.AllowSwapPin2, Value(default.allowSwapPIN2)) + register(SettingsMaskId.ForbidDefaultPin, Value(default.forbidDefaultPIN)) + register(SettingsMaskId.SmartSecurityDelay, Value(default.smartSecurityDelay)) + register(SettingsMaskId.ProtectIssuerDataAgainstReplay, Value(default.protectIssuerDataAgainstReplay)) + register(SettingsMaskId.SkipSecurityDelayIfValidated, Value(default.skipSecurityDelayIfValidatedByIssuer)) + register(SettingsMaskId.SkipPin2CvcIfValidated, Value(default.skipCheckPIN2andCVCIfValidatedByIssuer)) + register(SettingsMaskId.SkipSecurityDelayOnLinkedTerminal, Value(default.skipSecurityDelayIfValidatedByLinkedTerminal)) + register(SettingsMaskId.RestrictOverwriteExtraIssuerData, Value(default.restrictOverwriteIssuerDataEx)) + register(SettingsMaskProtocolEncId.AllowUnencrypted, Value(default.protocolAllowUnencrypted)) + register(SettingsMaskProtocolEncId.AllowStaticEncryption, Value(default.protocolAllowStaticEncryption)) + register(SettingsMaskNdefId.UseNdef, Value(default.useNDEF)) + register(SettingsMaskNdefId.DynamicNdef, Value(default.useDynamicNDEF)) + register(SettingsMaskNdefId.DisablePrecomputedNdef, Value(default.disablePrecomputedNDEF)) + register(SettingsMaskNdefId.Aar, Value(default.aar, Helper.aarList())) + register(SettingsMaskNdefId.AarCustom, Value(default.aarCustom)) + register(SettingsMaskNdefId.Uri, Value(default.uri)) + register(PinsId.Pin, Value(default.PIN)) + register(PinsId.Pin2, Value(default.PIN2)) + register(PinsId.Pin3, Value(default.PIN3)) + register(PinsId.Cvc, Value(default.CVC)) + register(PinsId.PauseBeforePin2, Value(default.pauseBeforePIN2, Helper.pauseBeforePin())) } } diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/ItemTypes.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/ItemTypes.kt index c257887476..f8c304044c 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/ItemTypes.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/ItemTypes.kt @@ -11,37 +11,37 @@ class ItemTypes { BlockId.SettingsMaskNdef, BlockId.Pins ) - val listItemList = mutableListOf(Common.Curve, Common.Blockchain, SettingsMaskNdef.Aar, Pins.PauseBeforePin2) + val listItemList = mutableListOf(CommonId.Curve, CommonId.Blockchain, SettingsMaskNdefId.Aar, PinsId.PauseBeforePin2) val boolList = mutableListOf( - Common.CreateWallet, SigningMethod.SignTx, SigningMethod.SignTxRaw, SigningMethod.SignValidatedTx, - SigningMethod.SignValidatedTxRaw, SigningMethod.SignValidatedTxIssuer, - SigningMethod.SignValidatedTxRawIssuer, SigningMethod.SignExternal, SignHashExProp.RequireTerminalCertSig, - SignHashExProp.RequireTerminalTxSig, SignHashExProp.CheckPin3, Token.ItsToken, - ProductMask.Note, ProductMask.Tag, ProductMask.IdCard, ProductMask.IdIssuerCard, SettingsMask.IsReusable, SettingsMask.NeedActivation, - SettingsMask.ForbidPurge, SettingsMask.AllowSelectBlockchain, SettingsMask.UseBlock, SettingsMask.OneApdu, - SettingsMask.UseCvc, SettingsMask.AllowSwapPin, SettingsMask.AllowSwapPin2, SettingsMask.ForbidDefaultPin, - SettingsMask.SmartSecurityDelay, SettingsMask.ProtectIssuerDataAgainstReplay, - SettingsMask.SkipSecurityDelayIfValidated, SettingsMask.SkipPin2CvcIfValidated, - SettingsMask.SkipSecurityDelayOnLinkedTerminal, SettingsMask.RestrictOverwriteExtraIssuerData, - SettingsMaskProtocolEnc.AllowUnencrypted, SettingsMaskProtocolEnc.AllowStaticEncryption, - SettingsMaskNdef.UseNdef, SettingsMaskNdef.DynamicNdef, SettingsMaskNdef.DisablePrecomputedNdef + CommonId.CreateWallet, SigningMethodId.SignTx, SigningMethodId.SignTxRaw, SigningMethodId.SignValidatedTx, + SigningMethodId.SignValidatedTxRaw, SigningMethodId.SignValidatedTxIssuer, + SigningMethodId.SignValidatedTxRawIssuer, SigningMethodId.SignExternal, SignHashExPropId.RequireTerminalCertSig, + SignHashExPropId.RequireTerminalTxSig, SignHashExPropId.CheckPin3, TokenId.ItsToken, + ProductMaskId.Note, ProductMaskId.Tag, ProductMaskId.IdCard, ProductMaskId.IdIssuerCard, SettingsMaskId.IsReusable, SettingsMaskId.NeedActivation, + SettingsMaskId.ForbidPurge, SettingsMaskId.AllowSelectBlockchain, SettingsMaskId.UseBlock, SettingsMaskId.OneApdu, + SettingsMaskId.UseCvc, SettingsMaskId.AllowSwapPin, SettingsMaskId.AllowSwapPin2, SettingsMaskId.ForbidDefaultPin, + SettingsMaskId.SmartSecurityDelay, SettingsMaskId.ProtectIssuerDataAgainstReplay, + SettingsMaskId.SkipSecurityDelayIfValidated, SettingsMaskId.SkipPin2CvcIfValidated, + SettingsMaskId.SkipSecurityDelayOnLinkedTerminal, SettingsMaskId.RestrictOverwriteExtraIssuerData, + SettingsMaskProtocolEncId.AllowUnencrypted, SettingsMaskProtocolEncId.AllowStaticEncryption, + SettingsMaskNdefId.UseNdef, SettingsMaskNdefId.DynamicNdef, SettingsMaskNdefId.DisablePrecomputedNdef ) val editTextList = mutableListOf( - CardNumber.Series, CardNumber.BatchId, Common.BlockchainCustom, SignHashExProp.CryptoExKey, Token.Symbol, - Token.ContractAddress, Pins.Pin, Pins.Pin2, Pins.Pin3, Pins.Cvc, SettingsMaskNdef.AarCustom, SettingsMaskNdef.Uri + CardNumberId.Series, CardNumberId.BatchId, CommonId.BlockchainCustom, SignHashExPropId.CryptoExKey, TokenId.Symbol, + TokenId.ContractAddress, PinsId.Pin, PinsId.Pin2, PinsId.Pin3, PinsId.Cvc, SettingsMaskNdefId.AarCustom, SettingsMaskNdefId.Uri ) val numberList = mutableListOf( - CardNumber.Number, Common.MaxSignatures, SignHashExProp.PinLessFloorLimit, Token.Decimal + CardNumberId.Number, CommonId.MaxSignatures, SignHashExPropId.PinLessFloorLimit, TokenId.Decimal ) val hiddenList = mutableListOf( - CardNumber.Series, CardNumber.BatchId, Pins.Pin3, SigningMethod.SignExternal, - SignHashExProp.CryptoExKey, SignHashExProp.CheckPin3, SettingsMask.OneApdu, - SettingsMask.UseBlock, SettingsMask.ProtectIssuerDataAgainstReplay, - SignHashExProp.RequireTerminalCertSig, SignHashExProp.RequireTerminalTxSig + CardNumberId.Series, CardNumberId.BatchId, PinsId.Pin3, SigningMethodId.SignExternal, + SignHashExPropId.CryptoExKey, SignHashExPropId.CheckPin3, SettingsMaskId.OneApdu, + SettingsMaskId.UseBlock, SettingsMaskId.ProtectIssuerDataAgainstReplay, + SignHashExPropId.RequireTerminalCertSig, SignHashExPropId.RequireTerminalTxSig ) val oftenUsedList = listOf( @@ -56,11 +56,11 @@ class ItemTypes { // BlockId.SettingsMaskProtocolEnc, BlockId.SettingsMaskNdef, // BlockId.Pins, - Common.Curve, - Common.Blockchain, - Common.BlockchainCustom, + CommonId.Curve, + CommonId.Blockchain, + CommonId.BlockchainCustom, // Common.MaxSignatures, - Common.CreateWallet, + CommonId.CreateWallet, // SigningMethod.SignTx, // SigningMethod.SignTxRaw, // SigningMethod.SignValidatedTx, @@ -79,10 +79,10 @@ class ItemTypes { // Token.Symbol, // Token.ContractAddress, // Token.Decimal, - ProductMask.Note, - ProductMask.Tag, - ProductMask.IdCard, - ProductMask.IdIssuerCard, + ProductMaskId.Note, + ProductMaskId.Tag, + ProductMaskId.IdCard, + ProductMaskId.IdIssuerCard, // SettingsMask.IsReusable, // SettingsMask.NeedActivation, // SettingsMask.ForbidPurge, @@ -104,14 +104,14 @@ class ItemTypes { // SettingsMaskNdef.UseNdef, // SettingsMaskNdef.DynamicNdef, // SettingsMaskNdef.DisablePrecomputedNdef, - SettingsMaskNdef.Aar, - SettingsMaskNdef.AarCustom, - SettingsMaskNdef.Uri, + SettingsMaskNdefId.Aar, + SettingsMaskNdefId.AarCustom, + SettingsMaskNdefId.Uri, // Pins.Pin, // Pins.Pin2, // Pins.Pin3, // Pins.Cvc, - Pins.PauseBeforePin2 + PinsId.PauseBeforePin2 ) } diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt index a2f8b5de05..e6733ed9f4 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt @@ -50,63 +50,63 @@ class ItemsToPersonalizationConfig : ItemsToModel { private fun createModel(): PersonalizationConfig { val export = PersonalizationConfig() - export.series = getTyped(CardNumber.Series) - export.startNumber = getTyped(CardNumber.Number) - export.cardData.batch = getTyped(CardNumber.BatchId) - export.curveID = getTyped(Common.Curve) - export.cardData.blockchain = getTyped(Common.Blockchain) - export.blockchainCustom = getTyped(Common.BlockchainCustom) - export.MaxSignatures = getTyped(Common.MaxSignatures) - export.createWallet = getTyped(Common.CreateWallet) - export.SigningMethod0 = getTyped(SigningMethod.SignTx) - export.SigningMethod1 = getTyped(SigningMethod.SignTxRaw) - export.SigningMethod2 = getTyped(SigningMethod.SignValidatedTx) - export.SigningMethod3 = getTyped(SigningMethod.SignValidatedTxRaw) - export.SigningMethod4 = getTyped(SigningMethod.SignValidatedTxIssuer) - export.SigningMethod5 = getTyped(SigningMethod.SignValidatedTxRawIssuer) - export.SigningMethod6 = getTyped(SigningMethod.SignExternal) - export.pinLessFloorLimit = getTyped(SignHashExProp.PinLessFloorLimit) - export.hexCrExKey = getTyped(SignHashExProp.CryptoExKey) - export.requireTerminalCertSignature = getTyped(SignHashExProp.RequireTerminalCertSig) - export.requireTerminalTxSignature = getTyped(SignHashExProp.RequireTerminalTxSig) - export.checkPIN3onCard = getTyped(SignHashExProp.CheckPin3) - export.itsToken = getTyped(Token.ItsToken) - export.cardData.token_symbol = getTyped(Token.Symbol) - export.cardData.token_contract_address = getTyped(Token.ContractAddress) - export.cardData.token_decimal = getTyped(Token.Decimal) - export.cardData = export.cardData.apply { this.product_note = getTyped(ProductMask.Note) } - export.cardData = export.cardData.apply { this.product_tag = getTyped(ProductMask.Tag) } - export.cardData = export.cardData.apply { this.product_id_card = getTyped(ProductMask.IdCard) } - export.cardData = export.cardData.apply { this.product_id_issuer = getTyped(ProductMask.IdIssuerCard) } - export.isReusable = getTyped(SettingsMask.IsReusable) - export.useActivation = getTyped(SettingsMask.NeedActivation) - export.forbidPurgeWallet = getTyped(SettingsMask.ForbidPurge) - export.allowSelectBlockchain = getTyped(SettingsMask.AllowSelectBlockchain) - export.useBlock = getTyped(SettingsMask.UseBlock) - export.useOneCommandAtTime = getTyped(SettingsMask.OneApdu) - export.useCVC = getTyped(SettingsMask.UseCvc) - export.allowSwapPIN = getTyped(SettingsMask.AllowSwapPin) - export.allowSwapPIN2 = getTyped(SettingsMask.AllowSwapPin2) - export.forbidDefaultPIN = getTyped(SettingsMask.ForbidDefaultPin) - export.smartSecurityDelay = getTyped(SettingsMask.SmartSecurityDelay) - export.protectIssuerDataAgainstReplay = getTyped(SettingsMask.ProtectIssuerDataAgainstReplay) - export.skipSecurityDelayIfValidatedByIssuer = getTyped(SettingsMask.SkipSecurityDelayIfValidated) - export.skipCheckPIN2andCVCIfValidatedByIssuer = getTyped(SettingsMask.SkipPin2CvcIfValidated) - export.skipSecurityDelayIfValidatedByLinkedTerminal = getTyped(SettingsMask.SkipSecurityDelayOnLinkedTerminal) - export.restrictOverwriteIssuerDataEx = getTyped(SettingsMask.RestrictOverwriteExtraIssuerData) - export.protocolAllowUnencrypted = getTyped(SettingsMaskProtocolEnc.AllowUnencrypted) - export.protocolAllowStaticEncryption = getTyped(SettingsMaskProtocolEnc.AllowStaticEncryption) - export.useNDEF = getTyped(SettingsMaskNdef.UseNdef) - export.useDynamicNDEF = getTyped(SettingsMaskNdef.DynamicNdef) - export.disablePrecomputedNDEF = getTyped(SettingsMaskNdef.DisablePrecomputedNdef) - export.aar = getTyped(SettingsMaskNdef.Aar) - export.aarCustom = getTyped(SettingsMaskNdef.AarCustom) - export.uri = getTyped(SettingsMaskNdef.Uri) - export.PIN = getTyped(Pins.Pin) - export.PIN2 = getTyped(Pins.Pin2) - export.PIN3 = getTyped(Pins.Pin3) - export.CVC = getTyped(Pins.Cvc) - export.pauseBeforePIN2 = getTyped(Pins.PauseBeforePin2) + export.series = getTyped(CardNumberId.Series) + export.startNumber = getTyped(CardNumberId.Number) + export.cardData.batch = getTyped(CardNumberId.BatchId) + export.curveID = getTyped(CommonId.Curve) + export.cardData.blockchain = getTyped(CommonId.Blockchain) + export.blockchainCustom = getTyped(CommonId.BlockchainCustom) + export.MaxSignatures = getTyped(CommonId.MaxSignatures) + export.createWallet = getTyped(CommonId.CreateWallet) + export.SigningMethod0 = getTyped(SigningMethodId.SignTx) + export.SigningMethod1 = getTyped(SigningMethodId.SignTxRaw) + export.SigningMethod2 = getTyped(SigningMethodId.SignValidatedTx) + export.SigningMethod3 = getTyped(SigningMethodId.SignValidatedTxRaw) + export.SigningMethod4 = getTyped(SigningMethodId.SignValidatedTxIssuer) + export.SigningMethod5 = getTyped(SigningMethodId.SignValidatedTxRawIssuer) + export.SigningMethod6 = getTyped(SigningMethodId.SignExternal) + export.pinLessFloorLimit = getTyped(SignHashExPropId.PinLessFloorLimit) + export.hexCrExKey = getTyped(SignHashExPropId.CryptoExKey) + export.requireTerminalCertSignature = getTyped(SignHashExPropId.RequireTerminalCertSig) + export.requireTerminalTxSignature = getTyped(SignHashExPropId.RequireTerminalTxSig) + export.checkPIN3onCard = getTyped(SignHashExPropId.CheckPin3) + export.itsToken = getTyped(TokenId.ItsToken) + export.cardData.token_symbol = getTyped(TokenId.Symbol) + export.cardData.token_contract_address = getTyped(TokenId.ContractAddress) + export.cardData.token_decimal = getTyped(TokenId.Decimal) + export.cardData = export.cardData.apply { this.product_note = getTyped(ProductMaskId.Note) } + export.cardData = export.cardData.apply { this.product_tag = getTyped(ProductMaskId.Tag) } + export.cardData = export.cardData.apply { this.product_id_card = getTyped(ProductMaskId.IdCard) } + export.cardData = export.cardData.apply { this.product_id_issuer = getTyped(ProductMaskId.IdIssuerCard) } + export.isReusable = getTyped(SettingsMaskId.IsReusable) + export.useActivation = getTyped(SettingsMaskId.NeedActivation) + export.forbidPurgeWallet = getTyped(SettingsMaskId.ForbidPurge) + export.allowSelectBlockchain = getTyped(SettingsMaskId.AllowSelectBlockchain) + export.useBlock = getTyped(SettingsMaskId.UseBlock) + export.useOneCommandAtTime = getTyped(SettingsMaskId.OneApdu) + export.useCVC = getTyped(SettingsMaskId.UseCvc) + export.allowSwapPIN = getTyped(SettingsMaskId.AllowSwapPin) + export.allowSwapPIN2 = getTyped(SettingsMaskId.AllowSwapPin2) + export.forbidDefaultPIN = getTyped(SettingsMaskId.ForbidDefaultPin) + export.smartSecurityDelay = getTyped(SettingsMaskId.SmartSecurityDelay) + export.protectIssuerDataAgainstReplay = getTyped(SettingsMaskId.ProtectIssuerDataAgainstReplay) + export.skipSecurityDelayIfValidatedByIssuer = getTyped(SettingsMaskId.SkipSecurityDelayIfValidated) + export.skipCheckPIN2andCVCIfValidatedByIssuer = getTyped(SettingsMaskId.SkipPin2CvcIfValidated) + export.skipSecurityDelayIfValidatedByLinkedTerminal = getTyped(SettingsMaskId.SkipSecurityDelayOnLinkedTerminal) + export.restrictOverwriteIssuerDataEx = getTyped(SettingsMaskId.RestrictOverwriteExtraIssuerData) + export.protocolAllowUnencrypted = getTyped(SettingsMaskProtocolEncId.AllowUnencrypted) + export.protocolAllowStaticEncryption = getTyped(SettingsMaskProtocolEncId.AllowStaticEncryption) + export.useNDEF = getTyped(SettingsMaskNdefId.UseNdef) + export.useDynamicNDEF = getTyped(SettingsMaskNdefId.DynamicNdef) + export.disablePrecomputedNDEF = getTyped(SettingsMaskNdefId.DisablePrecomputedNdef) + export.aar = getTyped(SettingsMaskNdefId.Aar) + export.aarCustom = getTyped(SettingsMaskNdefId.AarCustom) + export.uri = getTyped(SettingsMaskNdefId.Uri) + export.PIN = getTyped(PinsId.Pin) + export.PIN2 = getTyped(PinsId.Pin2) + export.PIN3 = getTyped(PinsId.Pin3) + export.CVC = getTyped(PinsId.Cvc) + export.pauseBeforePIN2 = getTyped(PinsId.PauseBeforePin2) return export } @@ -154,9 +154,9 @@ class PersonalizationConfigToItems : ModelToItems { private fun cardNumber(): ItemGroup { val block = createGroup(BlockId.CardNumber) mutableListOf( - CardNumber.Series, - CardNumber.Number, - CardNumber.BatchId + CardNumberId.Series, + CardNumberId.Number, + CardNumberId.BatchId ).forEach { createItem(block, it as Id) } return block } @@ -164,12 +164,12 @@ class PersonalizationConfigToItems : ModelToItems { private fun common(): ItemGroup { val block = createGroup(BlockId.Common) mutableListOf( - Common.Blockchain, - Common.BlockchainCustom, - Common.Curve, - Common.MaxSignatures, - Common.CreateWallet, - Pins.PauseBeforePin2 + CommonId.Blockchain, + CommonId.BlockchainCustom, + CommonId.Curve, + CommonId.MaxSignatures, + CommonId.CreateWallet, + PinsId.PauseBeforePin2 ).forEach { createItem(block, it as Id) } return block } @@ -177,13 +177,13 @@ class PersonalizationConfigToItems : ModelToItems { private fun signingMethod(): ItemGroup { val block = createGroup(BlockId.SigningMethod) mutableListOf( - SigningMethod.SignTx, - SigningMethod.SignTxRaw, - SigningMethod.SignValidatedTx, - SigningMethod.SignValidatedTxRaw, - SigningMethod.SignValidatedTxIssuer, - SigningMethod.SignValidatedTxRawIssuer, - SigningMethod.SignExternal + SigningMethodId.SignTx, + SigningMethodId.SignTxRaw, + SigningMethodId.SignValidatedTx, + SigningMethodId.SignValidatedTxRaw, + SigningMethodId.SignValidatedTxIssuer, + SigningMethodId.SignValidatedTxRawIssuer, + SigningMethodId.SignExternal ).forEach { createItem(block, it) } return block } @@ -191,11 +191,11 @@ class PersonalizationConfigToItems : ModelToItems { private fun signHashExProperties(): ItemGroup { val block = createGroup(BlockId.SignHashExProp) mutableListOf( - SignHashExProp.PinLessFloorLimit, - SignHashExProp.CryptoExKey, - SignHashExProp.RequireTerminalCertSig, - SignHashExProp.RequireTerminalTxSig, - SignHashExProp.CheckPin3 + SignHashExPropId.PinLessFloorLimit, + SignHashExPropId.CryptoExKey, + SignHashExPropId.RequireTerminalCertSig, + SignHashExPropId.RequireTerminalTxSig, + SignHashExPropId.CheckPin3 ).forEach { createItem(block, it) } return block } @@ -203,10 +203,10 @@ class PersonalizationConfigToItems : ModelToItems { private fun token(): ItemGroup { val block = createGroup(BlockId.Token) mutableListOf( - Token.ItsToken, - Token.Symbol, - Token.ContractAddress, - Token.Decimal + TokenId.ItsToken, + TokenId.Symbol, + TokenId.ContractAddress, + TokenId.Decimal ).forEach { createItem(block, it) } return block } @@ -214,10 +214,10 @@ class PersonalizationConfigToItems : ModelToItems { private fun productMask(): ItemGroup { val block = createGroup(BlockId.ProdMask) mutableListOf( - ProductMask.Note, - ProductMask.Tag, - ProductMask.IdCard, - ProductMask.IdIssuerCard + ProductMaskId.Note, + ProductMaskId.Tag, + ProductMaskId.IdCard, + ProductMaskId.IdIssuerCard ).forEach { createItem(block, it) } return block } @@ -225,22 +225,22 @@ class PersonalizationConfigToItems : ModelToItems { private fun settingsMask(): ItemGroup { val block = createGroup(BlockId.SettingsMask) mutableListOf( - SettingsMask.IsReusable, - SettingsMask.NeedActivation, - SettingsMask.ForbidPurge, - SettingsMask.AllowSelectBlockchain, - SettingsMask.UseBlock, - SettingsMask.OneApdu, - SettingsMask.UseCvc, - SettingsMask.AllowSwapPin, - SettingsMask.AllowSwapPin2, - SettingsMask.ForbidDefaultPin, - SettingsMask.SmartSecurityDelay, - SettingsMask.ProtectIssuerDataAgainstReplay, - SettingsMask.SkipSecurityDelayIfValidated, - SettingsMask.SkipPin2CvcIfValidated, - SettingsMask.SkipSecurityDelayOnLinkedTerminal, - SettingsMask.RestrictOverwriteExtraIssuerData + SettingsMaskId.IsReusable, + SettingsMaskId.NeedActivation, + SettingsMaskId.ForbidPurge, + SettingsMaskId.AllowSelectBlockchain, + SettingsMaskId.UseBlock, + SettingsMaskId.OneApdu, + SettingsMaskId.UseCvc, + SettingsMaskId.AllowSwapPin, + SettingsMaskId.AllowSwapPin2, + SettingsMaskId.ForbidDefaultPin, + SettingsMaskId.SmartSecurityDelay, + SettingsMaskId.ProtectIssuerDataAgainstReplay, + SettingsMaskId.SkipSecurityDelayIfValidated, + SettingsMaskId.SkipPin2CvcIfValidated, + SettingsMaskId.SkipSecurityDelayOnLinkedTerminal, + SettingsMaskId.RestrictOverwriteExtraIssuerData ).forEach { createItem(block, it) } return block } @@ -248,8 +248,8 @@ class PersonalizationConfigToItems : ModelToItems { private fun settingsMaskProtocolEnc(): ItemGroup { val block = createGroup(BlockId.SettingsMaskProtocolEnc) mutableListOf( - SettingsMaskProtocolEnc.AllowUnencrypted, - SettingsMaskProtocolEnc.AllowStaticEncryption + SettingsMaskProtocolEncId.AllowUnencrypted, + SettingsMaskProtocolEncId.AllowStaticEncryption ).forEach { createItem(block, it) } return block } @@ -257,12 +257,12 @@ class PersonalizationConfigToItems : ModelToItems { private fun settingsMaskNdef(): ItemGroup { val block = createGroup(BlockId.SettingsMaskNdef) mutableListOf( - SettingsMaskNdef.UseNdef, - SettingsMaskNdef.DynamicNdef, - SettingsMaskNdef.DisablePrecomputedNdef, - SettingsMaskNdef.Aar, - SettingsMaskNdef.AarCustom, - SettingsMaskNdef.Uri + SettingsMaskNdefId.UseNdef, + SettingsMaskNdefId.DynamicNdef, + SettingsMaskNdefId.DisablePrecomputedNdef, + SettingsMaskNdefId.Aar, + SettingsMaskNdefId.AarCustom, + SettingsMaskNdefId.Uri ).forEach { createItem(block, it) } return block } @@ -270,10 +270,10 @@ class PersonalizationConfigToItems : ModelToItems { private fun pins(): ItemGroup { val block = createGroup(BlockId.Pins) mutableListOf( - Pins.Pin, - Pins.Pin2, - Pins.Pin3, - Pins.Cvc + PinsId.Pin, + PinsId.Pin2, + PinsId.Pin3, + PinsId.Cvc ).forEach { createItem(block, it) } return block } diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/dto/DefaultPersonalizationParams.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/dto/DefaultPersonalizationParams.kt index afa1d5f114..aa68fb4215 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/dto/DefaultPersonalizationParams.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/dto/DefaultPersonalizationParams.kt @@ -4,6 +4,7 @@ import com.tangem.KeyPair import com.tangem.commands.personalization.entities.Acquirer import com.tangem.commands.personalization.entities.Issuer import com.tangem.commands.personalization.entities.Manufacturer +import com.tangem.common.extensions.hexToBytes /** [REDACTED_AUTHOR] @@ -18,12 +19,14 @@ interface DefaultPersonalizationParams { name = name, id = name + "\u0000", dataKeyPair = KeyPair( - "".toByteArray(), - "11121314151617184771ED81F2BACF57479E4735EB1405083927372D40DA9E92".toByteArray() + ("045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f" + + "361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd26").hexToBytes(), + "11121314151617184771ED81F2BACF57479E4735EB1405083927372D40DA9E92".hexToBytes() ), transactionKeyPair = KeyPair( - "".toByteArray(), - "11121314151617184771ED81F2BACF57479E4735EB1405081918171615141312".toByteArray() + ("0484c5192e9bfa6c528a344f442137a92b89ea835bfef1d04cb4362eb906b508c5889846cfea71ba6dc7b" + + "3120c2208df9c46127d3d85cb5cfbd1479e97133a39d8").hexToBytes(), + "11121314151617184771ED81F2BACF57479E4735EB1405081918171615141312".hexToBytes() ) ) } @@ -34,8 +37,9 @@ interface DefaultPersonalizationParams { name = name, id = name + "\u0000", keyPair = KeyPair( - "".toByteArray(), - "21222324252627284771ED81F2BACF57479E4735EB1405083927372D40DA9E92".toByteArray() + ("0456ad1a82b22bcb40c38fd08939f87e6b80e40dec5b3bdb351c55fcd709e47f9fb2ed00c2304d3a9" + + "86f79c5ae0ac3c84e88da46dc8f513b7542c716af8c9a2daf").hexToBytes(), + "21222324252627284771ED81F2BACF57479E4735EB1405083927372D40DA9E92".hexToBytes() ) ) } diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/ui/widgets/NumberWidget.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/ui/widgets/NumberWidget.kt index 2fef1e296f..340e1c3ce5 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/ui/widgets/NumberWidget.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/ui/widgets/NumberWidget.kt @@ -8,7 +8,7 @@ import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputLayout import com.tangem.devkit.R import com.tangem.devkit._arch.structure.impl.NumberItem -import com.tangem.devkit.ucase.variants.personalize.CardNumber +import com.tangem.devkit.ucase.variants.personalize.CardNumberId import ru.dev.gbixahue.eu4d.lib.android._android.views.addInputFilter import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf @@ -39,7 +39,7 @@ class NumberWidget( tilItem.hint = getName() //TODO: remove from Widget - if (item.id == CardNumber.Number) etItem.addInputFilter(InputFilter.LengthFilter(13)) + if (item.id == CardNumberId.Number) etItem.addInputFilter(InputFilter.LengthFilter(13)) etItem.setText(stringOf(typedItem.getTypedData())) etItem.addTextChangedListener(watcher) diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/purgewallet/ui/PurgeWalletActionFragment.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/purgewallet/ui/PurgeWalletActionFragment.kt new file mode 100644 index 0000000000..c3b8736e21 --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/purgewallet/ui/PurgeWalletActionFragment.kt @@ -0,0 +1,10 @@ +package com.tangem.devkit.ucase.variants.purgewallet.ui + +import com.tangem.devkit.ucase.domain.paramsManager.ItemsManager +import com.tangem.devkit.ucase.domain.paramsManager.managers.PurgeWalletItemsManager +import com.tangem.devkit.ucase.ui.BaseCardActionFragment + +class PurgeWalletActionFragment : BaseCardActionFragment() { + + override val itemsManager: ItemsManager by lazy { PurgeWalletItemsManager() } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/Ids.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/Ids.kt index 8e00cef963..a383624f76 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/Ids.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/Ids.kt @@ -53,4 +53,38 @@ enum class SignId : ResponseId { enum class DepersonalizeId: ResponseId { isSuccess +} + +enum class CreateWalletId : ResponseId { + cid, + cardStatus, + walletPublicKey, +} + +enum class PurgeWalletId : ResponseId { + cid, + cardStatus +} + +enum class ReadIssuerDataId : ResponseId { + cid, + issuerData, + issuerDataSignature, + issuerDataCounter +} + +enum class ReadIssuerExtraDataId : ResponseId { + cid, + size, + issuerData, + issuerDataSignature, + issuerDataCounter +} + +enum class ReadUserDataId : ResponseId { + cid, + userData, + userProtectedData, + userCounter, + userProtectedCounter } \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/ResponseViewModel.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/ResponseViewModel.kt index 0da3fbceb4..1d94acaf21 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/ResponseViewModel.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/ResponseViewModel.kt @@ -2,9 +2,7 @@ package com.tangem.devkit.ucase.variants.responses import android.view.View import androidx.lifecycle.ViewModel -import com.tangem.commands.Card -import com.tangem.commands.CommandResponse -import com.tangem.commands.SignResponse +import com.tangem.commands.* import com.tangem.commands.personalization.DepersonalizeResponse import com.tangem.devkit.R import com.tangem.devkit._arch.structure.abstraction.Item @@ -44,6 +42,14 @@ class ResponseViewModel : ViewModel() { is Card -> R.string.fg_name_response_personalization is SignResponse -> R.string.fg_name_response_sign is DepersonalizeResponse -> R.string.fg_name_response_depersonalization + is CreateWalletResponse -> R.string.fg_name_response_create_wallet + is PurgeWalletResponse -> R.string.fg_name_response_purge_wallet + is ReadIssuerDataResponse -> R.string.fg_name_response_read_issuer_data + is WriteIssuerDataResponse -> R.string.fg_name_response_write_issuer_data + is ReadIssuerExtraDataResponse -> R.string.fg_name_response_read_issuer_extra_data + is ReadUserDataResponse -> R.string.fg_name_response_read_user_data + is WriteUserDataResponse -> R.string.fg_name_response_write_user_data + else -> R.string.unknown } } diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/converter/ConvertersStore.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/converter/ConvertersStore.kt index 85b9f38442..8953ba2935 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/converter/ConvertersStore.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/converter/ConvertersStore.kt @@ -1,7 +1,6 @@ package com.tangem.devkit.ucase.variants.responses.converter -import com.tangem.commands.Card -import com.tangem.commands.SignResponse +import com.tangem.commands.* import com.tangem.commands.personalization.DepersonalizeResponse import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder import java.lang.reflect.Type @@ -12,5 +11,12 @@ class ConvertersStore : BaseTypedHolder() { register(SignResponse::class.java, SignResponseConverter()) register(Card::class.java, CardConverter()) register(DepersonalizeResponse::class.java, DepersonalizeResponseConverter()) + register(CreateWalletResponse::class.java, CreateWalletResponseConverter()) + register(PurgeWalletResponse::class.java, PurgeWalletResponseConverter()) + register(ReadIssuerDataResponse::class.java, ReadIssuerDataResponseConverter()) + register(ReadIssuerExtraDataResponse::class.java, ReadIssuerExtraDataResponseConverter()) + register(WriteIssuerDataResponse::class.java, WriteIssuerDataResponseConverter()) + register(ReadUserDataResponse::class.java, ReadUserDataResponseConverter()) + register(WriteUserDataResponse::class.java, WriteUserDataResponseConverter()) } } \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/converter/SimpleConverters.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/converter/SimpleConverters.kt index 980c10f01d..e68c77e083 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/converter/SimpleConverters.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/converter/SimpleConverters.kt @@ -1,11 +1,10 @@ package com.tangem.devkit.ucase.variants.responses.converter -import com.tangem.commands.SignResponse +import com.tangem.commands.* import com.tangem.commands.personalization.DepersonalizeResponse import com.tangem.devkit._arch.structure.abstraction.Item import com.tangem.devkit._arch.structure.impl.TextItem -import com.tangem.devkit.ucase.variants.responses.DepersonalizeId -import com.tangem.devkit.ucase.variants.responses.SignId +import com.tangem.devkit.ucase.variants.responses.* import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf /** @@ -27,4 +26,74 @@ class DepersonalizeResponseConverter : BaseResponseConverter { return listOf(TextItem(DepersonalizeId.isSuccess, stringOf(from.success))) } +} + +class CreateWalletResponseConverter : BaseResponseConverter() { + override fun convert(from: CreateWalletResponse): List { + return listOf( + TextItem(CreateWalletId.cid, from.cardId), + TextItem(CreateWalletId.cardStatus, valueToString(from.status)), + TextItem(CreateWalletId.walletPublicKey, fieldConverter.byteArray(from.walletPublicKey)) + ) + } +} + +class PurgeWalletResponseConverter : BaseResponseConverter() { + override fun convert(from: PurgeWalletResponse): List { + return listOf( + TextItem(CreateWalletId.cid, from.cardId), + TextItem(CreateWalletId.cardStatus, valueToString(from.status)) + ) + } +} + +class ReadIssuerDataResponseConverter : BaseResponseConverter() { + override fun convert(from: ReadIssuerDataResponse): List { + return listOf( + TextItem(ReadIssuerDataId.cid, from.cardId), + TextItem(ReadIssuerDataId.issuerData, fieldConverter.byteArray(from.issuerData)), + TextItem(ReadIssuerDataId.issuerDataSignature, fieldConverter.byteArray(from.issuerDataSignature)), + TextItem(ReadIssuerDataId.issuerDataCounter, valueToString(from.issuerDataCounter)) + ) + } +} + +class ReadIssuerExtraDataResponseConverter : BaseResponseConverter() { + override fun convert(from: ReadIssuerExtraDataResponse): List { + return listOf( + TextItem(ReadIssuerExtraDataId.cid, from.cardId), + TextItem(ReadIssuerExtraDataId.size, valueToString(from.size)), + TextItem(ReadIssuerExtraDataId.issuerData, fieldConverter.byteArray(from.issuerData)), + TextItem(ReadIssuerExtraDataId.issuerDataSignature, fieldConverter.byteArray(from.issuerDataSignature)), + TextItem(ReadIssuerExtraDataId.issuerDataCounter, valueToString(from.issuerDataCounter)) + ) + } +} + +class WriteIssuerDataResponseConverter : BaseResponseConverter() { + override fun convert(from: WriteIssuerDataResponse): List { + return listOf( + TextItem(CardId.cardId, from.cardId) + ) + } +} + +class ReadUserDataResponseConverter : BaseResponseConverter() { + override fun convert(from: ReadUserDataResponse): List { + return listOf( + TextItem(ReadUserDataId.cid, from.cardId), + TextItem(ReadUserDataId.userData, fieldConverter.byteArray(from.userData)), + TextItem(ReadUserDataId.userProtectedData, fieldConverter.byteArray(from.userProtectedData)), + TextItem(ReadUserDataId.userCounter, valueToString(from.userCounter)), + TextItem(ReadUserDataId.userProtectedCounter, valueToString(from.userProtectedCounter)) + ) + } +} + +class WriteUserDataResponseConverter : BaseResponseConverter() { + override fun convert(from: WriteUserDataResponse): List { + return listOf( + TextItem(CardId.cardId, from.cardId) + ) + } } \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/userdata/ui/ReadUserDataFragment.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/userdata/ui/ReadUserDataFragment.kt new file mode 100644 index 0000000000..670931d810 --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/userdata/ui/ReadUserDataFragment.kt @@ -0,0 +1,10 @@ +package com.tangem.devkit.ucase.variants.userdata.ui + +import com.tangem.devkit.ucase.domain.paramsManager.ItemsManager +import com.tangem.devkit.ucase.domain.paramsManager.managers.ReadUserDataItemsManager +import com.tangem.devkit.ucase.ui.BaseCardActionFragment + +class ReadUserDataFragment : BaseCardActionFragment() { + + override val itemsManager: ItemsManager by lazy { ReadUserDataItemsManager() } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/userdata/ui/WriteUserDataFragment.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/userdata/ui/WriteUserDataFragment.kt new file mode 100644 index 0000000000..ad0a829f38 --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/userdata/ui/WriteUserDataFragment.kt @@ -0,0 +1,10 @@ +package com.tangem.devkit.ucase.variants.userdata.ui + +import com.tangem.devkit.ucase.domain.paramsManager.ItemsManager +import com.tangem.devkit.ucase.domain.paramsManager.managers.WriteUserDataItemsManager +import com.tangem.devkit.ucase.ui.BaseCardActionFragment + +class WriteUserDataFragment : BaseCardActionFragment() { + + override val itemsManager: ItemsManager by lazy { WriteUserDataItemsManager() } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/res/navigation/navigation.xml b/tangem-devkit/src/main/res/navigation/navigation.xml index a6cd6c3ff8..a08466aff6 100644 --- a/tangem-devkit/src/main/res/navigation/navigation.xml +++ b/tangem-devkit/src/main/res/navigation/navigation.xml @@ -85,51 +85,51 @@ + tools:layout="@layout/fg_base_action_layout" /> + tools:layout="@layout/fg_base_action_layout" /> + tools:layout="@layout/fg_base_action_layout" /> + tools:layout="@layout/fg_base_action_layout" /> + tools:layout="@layout/fg_base_action_layout" /> + tools:layout="@layout/fg_base_action_layout" /> + tools:layout="@layout/fg_base_action_layout" /> + tools:layout="@layout/fg_base_action_layout" /> Nothing to load @string/app_name - Read response - Sign response - Personalization response - Depersonalization response + Read Response + Sign Response + Personalization Response + Depersonalization Response + Create Wallet Response + Purge Wallet Response + Read Issuer Data Response + Write Issuer Data Response + Read Issuer Extra Data Response + Read User Data Response + Write User Data Response Docs diff --git a/tangem-devkit/src/main/res/values/strings_action.xml b/tangem-devkit/src/main/res/values/strings_action.xml index ed962e51fb..32a9d00725 100644 --- a/tangem-devkit/src/main/res/values/strings_action.xml +++ b/tangem-devkit/src/main/res/values/strings_action.xml @@ -5,27 +5,28 @@ Sign Personalize Depersonalize - Create wallet - Purge wallet - Read issuer data - Write issuer data - Read issuer extra data - Write issuer extra data - Read user data - Write user data + Create Wallet + Purge Wallet + Read Issuer Data + Write Issuer Data + Read Issuer Extra Data + Write Issuer Extra Data + Read User Data + Write User DataThis command returns all data about the card and the wallet, including unique card number (CID) that has to be submitted while calling all other commands Depending on Signing_Method parameter defined during personalization, this command signs data using Wallet_PrivateKey Personalization is an initialization procedure, required before start using a card. During this procedure all card setting are performed. During this procedure all data exchange are encrypted because it\'s usually done at the third-party factory. This command reset card to initial state - erase all data, writed during previous personalization and previous usage. \nCommand available on SDK cards only - SomeDescription - SomeDescription - SomeDescription - SomeDescription - SomeDescription - SomeDescription - SomeDescription - SomeDescription + If the card has an ‘Empty’ state, this command will create a new wallet, changing a state to ‘Loaded’. + This command deletes all wallet data. If ‘IsReusable‘ flag was enabled during personalization, the card changes state to ‘Empty’ and a new wallet can be created by Create Wallet Command. + This command returns 512-byte Issuer Data field and its issuer’s signature. + This command writes 512-byte Issuer Data field and its issuer’s signature to the card. + This command retrieves Issuer Extra Data field and its issuer’s signature. + This command writes Issuer Extra Data field and its issuer’s signature to the card. + This command returns User Data and User Protected Data (up to 512-byte each) and two counters: User Counter and User Protected Counter. + This command writes to the card any of User Data, User Protected Data, User Counter and User Protected Counter fields. + \ No newline at end of file diff --git a/tangem-devkit/src/main/res/values/strings_response.xml b/tangem-devkit/src/main/res/values/strings_response.xml index 489e0a7bd5..2fcd8010e4 100644 --- a/tangem-devkit/src/main/res/values/strings_response.xml +++ b/tangem-devkit/src/main/res/values/strings_response.xml @@ -155,4 +155,27 @@ Is success + + + Size of Issuer Data + Issuer Data + Issuer Data Signature + Issuer Data Counter + + A size of issuer extra data written on a card. + Data defined by the issuer. + Issuer data, signed by the issuer with issuer data private key. + An optional counter that protects issuer data against replay attack. + + + User Data + Protected User Data + User Data Counter + Protected User Data Counter + + Data defined by user\'s App + Data defined by user’s App (confirmed by PIN2) + Counter initialized by user’s App and increased on every signing of new transaction + Counter initialized by user’s App (confirmed by PIN2) and increased on every signing of new transaction + \ No newline at end of file diff --git a/tangem-devkit/src/main/res/values/strings_tlv.xml b/tangem-devkit/src/main/res/values/strings_tlv.xml index fe18360367..034648d928 100644 --- a/tangem-devkit/src/main/res/values/strings_tlv.xml +++ b/tangem-devkit/src/main/res/values/strings_tlv.xml @@ -3,8 +3,16 @@ Card Id (CID) Hash to sign + Issuer Data + User Data + Protected User Data + CounterUnique Tangem card ID number Hashed transaction you want to be signed + Issuer Data to be written by this command + User Data to be written by this command + Protected User Data to be written by this command + Counter that protects this type of data against replay attack. \ No newline at end of file