Updated on 2026-08-14

This commit is contained in:
Tangem 2020-04-29 20:21:43 +03:00
commit 6f91d23c2c
68 changed files with 1954 additions and 394 deletions

View file

@ -10,20 +10,20 @@ import org.bitcoinj.script.ScriptBuilder
import java.math.BigDecimal import java.math.BigDecimal
import java.math.BigInteger import java.math.BigInteger
class BitcoinTransactionBuilder( open class BitcoinTransactionBuilder(
private val walletPublicKey: ByteArray, private val testNet: Boolean = false private val walletPublicKey: ByteArray, private val testNet: Boolean = false
) { ) {
private lateinit var transaction: Transaction private lateinit var transaction: Transaction
private var networkParameters: NetworkParameters? = null protected var networkParameters: NetworkParameters? = null
var unspentOutputs: List<UnspentTransaction>? = null var unspentOutputs: List<BitcoinUnspentOutput>? = null
fun buildToSign( open fun buildToSign(
transactionData: TransactionData): Result<List<ByteArray>> { transactionData: TransactionData): Result<List<ByteArray>> {
if (unspentOutputs == null) return Result.Failure(Exception("Currently there's an unconfirmed transaction")) 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 = if (testNet) {
NetworkParameters.fromID(NetworkParameters.ID_TESTNET) NetworkParameters.fromID(NetworkParameters.ID_TESTNET)
@ -40,27 +40,13 @@ class BitcoinTransactionBuilder(
return Result.Success(hashesForSign) return Result.Success(hashesForSign)
} }
private fun calculateChange(transactionData: TransactionData): BigDecimal { open fun buildToSend(signedTransaction: ByteArray): ByteArray {
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 {
for (index in transaction.inputs.indices) { for (index in transaction.inputs.indices) {
transaction.inputs[index].scriptSig = createScript(index, signedTransaction, walletPublicKey) transaction.inputs[index].scriptSig = createScript(index, signedTransaction, walletPublicKey)
} }
return transaction.bitcoinSerialize() 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<Int> { fun getEstimateSize(transactionData: TransactionData): Result<Int> {
val buildTransactionResult = buildToSign(transactionData) val buildTransactionResult = buildToSign(transactionData)
when (buildTransactionResult) { when (buildTransactionResult) {
@ -72,14 +58,28 @@ class BitcoinTransactionBuilder(
} }
} }
} }
fun calculateChange(transactionData: TransactionData, unspentOutputs: List<BitcoinUnspentOutput>): 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?, internal fun TransactionData.toBitcoinJTransaction(networkParameters: NetworkParameters?,
unspentOutputs: List<UnspentTransaction>, unspentOutputs: List<BitcoinUnspentOutput>,
change: BigDecimal): Transaction { change: BigDecimal): Transaction {
val transaction = Transaction(networkParameters) val transaction = Transaction(networkParameters)
for (utxo in unspentOutputs) { 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( transaction.addOutput(
Coin.parseCoin(this.amount.value!!.toPlainString()), Coin.parseCoin(this.amount.value!!.toPlainString()),
@ -93,9 +93,9 @@ internal fun TransactionData.toBitcoinJTransaction(networkParameters: NetworkPar
return transaction return transaction
} }
class UnspentTransaction( class BitcoinUnspentOutput(
val amount: BigDecimal, val amount: BigDecimal,
val outputIndex: Long, val outputIndex: Long,
val hash: ByteArray, val transactionHash: ByteArray,
val outputScript: ByteArray val outputScript: ByteArray
) )

View file

@ -2,7 +2,7 @@ package com.tangem.blockchain.blockchains.bitcoin
import android.util.Log import android.util.Log
import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinAddressResponse 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.common.*
import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult import com.tangem.blockchain.extensions.SimpleResult
@ -10,14 +10,14 @@ import com.tangem.common.CompletionResult
import com.tangem.common.extensions.toHexString import com.tangem.common.extensions.toHexString
import java.math.BigDecimal import java.math.BigDecimal
class BitcoinWalletManager( open class BitcoinWalletManager(
cardId: String, cardId: String,
wallet: Wallet, wallet: Wallet,
private val transactionBuilder: BitcoinTransactionBuilder, private val transactionBuilder: BitcoinTransactionBuilder,
private val networkManager: BitcoinNetworkManager private val networkManager: BitcoinProvider
) : WalletManager(cardId, wallet), TransactionSender { ) : WalletManager(cardId, wallet), TransactionSender {
private val blockchain = wallet.blockchain protected val blockchain = wallet.blockchain
override suspend fun update() { override suspend fun update() {
val response = networkManager.getInfo(wallet.address) val response = networkManager.getInfo(wallet.address)
@ -30,7 +30,7 @@ class BitcoinWalletManager(
private fun updateWallet(response: BitcoinAddressResponse) { private fun updateWallet(response: BitcoinAddressResponse) {
Log.d(this::class.java.simpleName, "Balance is ${response.balance}") Log.d(this::class.java.simpleName, "Balance is ${response.balance}")
wallet.amounts[AmountType.Coin]?.value = response.balance wallet.amounts[AmountType.Coin]?.value = response.balance
transactionBuilder.unspentOutputs = response.unspentTransactions transactionBuilder.unspentOutputs = response.unspentOutputs
if (response.hasUnconfirmed) { if (response.hasUnconfirmed) {
if (wallet.transactions.isEmpty()) wallet.addIncomingTransaction() if (wallet.transactions.isEmpty()) wallet.addIncomingTransaction()
} else { } else {
@ -44,8 +44,7 @@ class BitcoinWalletManager(
} }
override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult { override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult {
val buildTransactionResult = transactionBuilder.buildToSign(transactionData) when (val buildTransactionResult = transactionBuilder.buildToSign(transactionData)) {
when (buildTransactionResult) {
is Result.Failure -> return SimpleResult.Failure(buildTransactionResult.error) is Result.Failure -> return SimpleResult.Failure(buildTransactionResult.error)
is Result.Success -> { is Result.Success -> {
when (val signerResponse = signer.sign(buildTransactionResult.data.toTypedArray(), cardId)) { when (val signerResponse = signer.sign(buildTransactionResult.data.toTypedArray(), cardId)) {
@ -60,8 +59,8 @@ class BitcoinWalletManager(
} }
override suspend fun getFee(amount: Amount, destination: String): Result<List<Amount>> { override suspend fun getFee(amount: Amount, destination: String): Result<List<Amount>> {
when (val result = networkManager.getFee()) { when (val feeResult = networkManager.getFee()) {
is Result.Failure -> return result is Result.Failure -> return feeResult
is Result.Success -> { is Result.Success -> {
val feeValue = BigDecimal.ONE.movePointLeft(blockchain.decimals()) val feeValue = BigDecimal.ONE.movePointLeft(blockchain.decimals())
amount.value = amount.value!! - feeValue amount.value = amount.value!! - feeValue
@ -72,9 +71,9 @@ class BitcoinWalletManager(
is Result.Failure -> return sizeResult is Result.Failure -> return sizeResult
is Result.Success -> { is Result.Success -> {
val transactionSize = sizeResult.data.toBigDecimal() val transactionSize = sizeResult.data.toBigDecimal()
val minFee = result.data.minimalPerKb.calculateFee(transactionSize) val minFee = feeResult.data.minimalPerKb.calculateFee(transactionSize)
val normalFee = result.data.normalPerKb.calculateFee(transactionSize) val normalFee = feeResult.data.normalPerKb.calculateFee(transactionSize)
val priorityFee = result.data.priorityPerKb.calculateFee(transactionSize) val priorityFee = feeResult.data.priorityPerKb.calculateFee(transactionSize)
return Result.Success( return Result.Success(
listOf(Amount(minFee, blockchain), listOf(Amount(minFee, blockchain),
Amount(normalFee, blockchain), Amount(normalFee, blockchain),

View file

@ -1,6 +1,6 @@
package com.tangem.blockchain.blockchains.bitcoin.network 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.BlockchainInfoApi
import com.tangem.blockchain.blockchains.bitcoin.network.api.BlockcypherApi import com.tangem.blockchain.blockchains.bitcoin.network.api.BlockcypherApi
import com.tangem.blockchain.blockchains.bitcoin.network.api.EstimatefeeApi import com.tangem.blockchain.blockchains.bitcoin.network.api.EstimatefeeApi
@ -90,7 +90,7 @@ class BitcoinNetworkManager(private val isTestNet: Boolean = false) : BitcoinPro
data class BitcoinAddressResponse( data class BitcoinAddressResponse(
val balance: BigDecimal, val balance: BigDecimal,
val hasUnconfirmed: Boolean, val hasUnconfirmed: Boolean,
val unspentTransactions: List<UnspentTransaction>? val unspentOutputs: List<BitcoinUnspentOutput>?
) )
data class BitcoinFee( data class BitcoinFee(

View file

@ -1,6 +1,6 @@
package com.tangem.blockchain.blockchains.bitcoin.network 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.BlockchainInfoApi
import com.tangem.blockchain.blockchains.bitcoin.network.api.EstimatefeeApi import com.tangem.blockchain.blockchains.bitcoin.network.api.EstimatefeeApi
import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Blockchain
@ -29,7 +29,7 @@ class BlockchainInfoProvider(
val unconfirmedTransactions = addressData.transactions?.find { it.blockHeight == 0L } != null val unconfirmedTransactions = addressData.transactions?.find { it.blockHeight == 0L } != null
val bitcoinUnspents = unspents.unspentOutputs.map { val bitcoinUnspents = unspents.unspentOutputs.map {
UnspentTransaction( BitcoinUnspentOutput(
it.amount!!.toBigDecimal().movePointLeft(decimals), it.amount!!.toBigDecimal().movePointLeft(decimals),
it.outputIndex!!.toLong(), it.outputIndex!!.toLong(),
it.hash!!.hexToBytes(), it.hash!!.hexToBytes(),

View file

@ -1,6 +1,6 @@
package com.tangem.blockchain.blockchains.bitcoin.network 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.BlockcypherApi
import com.tangem.blockchain.blockchains.bitcoin.network.api.BlockcypherBody import com.tangem.blockchain.blockchains.bitcoin.network.api.BlockcypherBody
import com.tangem.blockchain.blockchains.bitcoin.network.response.BlockcypherFee import com.tangem.blockchain.blockchains.bitcoin.network.response.BlockcypherFee
@ -26,7 +26,7 @@ class BlockcypherProvider(private val api: BlockcypherApi, isTestNet: Boolean) :
try { try {
val addressData: BlockcypherResponse = retryIO { api.getAddressData(blockchain, network, address) } val addressData: BlockcypherResponse = retryIO { api.getAddressData(blockchain, network, address) }
val unspents = addressData.txrefs?.map { val unspents = addressData.txrefs?.map {
UnspentTransaction( BitcoinUnspentOutput(
it.amount!!.toBigDecimal().movePointLeft(decimals), it.amount!!.toBigDecimal().movePointLeft(decimals),
it.outputIndex!!.toLong(), it.outputIndex!!.toLong(),
it.hash!!.hexToBytes(), it.hash!!.hexToBytes(),

View file

@ -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
}
}

View file

@ -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<BitcoinAddressResponse> {
return blockchairProvider.getInfo(address)
}
override suspend fun getFee(): Result<BitcoinFee> {
return blockchairProvider.getFee()
}
override suspend fun sendTransaction(transaction: String): SimpleResult {
return blockchairProvider.sendTransaction(transaction)
}
}

View file

@ -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<TransactionInput> inputs;
// private ArrayList<TransactionOutput> 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<TransactionInput> inputs = getInputs();
List<TransactionOutput> 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());
}
}

View file

@ -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<List<ByteArray>> {
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<ByteArray> = 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<BitcoinUnspentOutput>,
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
}

View file

@ -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<List<Amount>> {
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
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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<Character, Integer> 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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -2,6 +2,7 @@ package com.tangem.blockchain.common
import com.tangem.blockchain.blockchains.binance.BinanceAddressService import com.tangem.blockchain.blockchains.binance.BinanceAddressService
import com.tangem.blockchain.blockchains.bitcoin.BitcoinAddressService 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.cardano.CardanoAddressService
import com.tangem.blockchain.blockchains.ethereum.EthereumAddressService import com.tangem.blockchain.blockchains.ethereum.EthereumAddressService
import com.tangem.blockchain.blockchains.stellar.StellarAddressService import com.tangem.blockchain.blockchains.stellar.StellarAddressService
@ -17,6 +18,7 @@ enum class Blockchain(
Unknown("", "", ""), Unknown("", "", ""),
Bitcoin("BTC", "BTC", "Bitcoin"), Bitcoin("BTC", "BTC", "Bitcoin"),
BitcoinTestnet("BTC/test", "BTCt", "Bitcoin Testnet"), BitcoinTestnet("BTC/test", "BTCt", "Bitcoin Testnet"),
BitcoinCash("BCH", "BCH", "Bitcoin Cash"),
Ethereum("ETH", "ETH", "Ethereum"), Ethereum("ETH", "ETH", "Ethereum"),
Rootstock("", "", ""), Rootstock("", "", ""),
Cardano("CARDANO", "ADA", "Cardano"), Cardano("CARDANO", "ADA", "Cardano"),
@ -32,7 +34,7 @@ enum class Blockchain(
} }
fun decimals(): Int = when (this) { fun decimals(): Int = when (this) {
Bitcoin, BitcoinTestnet, Binance, BinanceTestnet -> 8 Bitcoin, BitcoinTestnet, BitcoinCash, Binance, BinanceTestnet -> 8
Cardano, XRP -> 6 Cardano, XRP -> 6
Ethereum, Rootstock -> 18 Ethereum, Rootstock -> 18
Stellar -> 7 Stellar -> 7
@ -51,6 +53,7 @@ enum class Blockchain(
Unknown -> throw Exception("unsupported blockchain") Unknown -> throw Exception("unsupported blockchain")
Bitcoin -> BitcoinAddressService() Bitcoin -> BitcoinAddressService()
BitcoinTestnet -> BitcoinAddressService(true) BitcoinTestnet -> BitcoinAddressService(true)
BitcoinCash -> BitcoinCashAddressService()
Ethereum -> EthereumAddressService() Ethereum -> EthereumAddressService()
Rootstock -> throw Exception("unsupported blockchain") Rootstock -> throw Exception("unsupported blockchain")
Cardano -> CardanoAddressService() Cardano -> CardanoAddressService()
@ -70,6 +73,8 @@ enum class Blockchain(
fun getExploreUrl(address: String, token: Token? = null): String = when (this) { fun getExploreUrl(address: String, token: Token? = null): String = when (this) {
Binance -> "https://explorer.binance.org/address/$address" Binance -> "https://explorer.binance.org/address/$address"
Bitcoin -> "https://blockchain.info/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" Cardano -> "https://cardanoexplorer.com/address/$address"
Ethereum -> if (token == null) { Ethereum -> if (token == null) {
"https://etherscan.io/address/" "https://etherscan.io/address/"

View file

@ -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.BitcoinTransactionBuilder
import com.tangem.blockchain.blockchains.bitcoin.BitcoinWalletManager import com.tangem.blockchain.blockchains.bitcoin.BitcoinWalletManager
import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinNetworkManager 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.CardanoTransactionBuilder
import com.tangem.blockchain.blockchains.cardano.CardanoWalletManager import com.tangem.blockchain.blockchains.cardano.CardanoWalletManager
import com.tangem.blockchain.blockchains.cardano.network.CardanoNetworkManager 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.XrpWalletManager
import com.tangem.blockchain.blockchains.xrp.network.XrpNetworkManager import com.tangem.blockchain.blockchains.xrp.network.XrpNetworkManager
import com.tangem.commands.Card import com.tangem.commands.Card
import com.tangem.common.extensions.toCompressedPublicKey
object WalletManagerFactory { object WalletManagerFactory {
@ -50,6 +54,13 @@ object WalletManagerFactory {
BitcoinNetworkManager(true) BitcoinNetworkManager(true)
) )
} }
Blockchain.BitcoinCash -> {
return BitcoinCashWalletManager(
card.cardId, wallet,
BitcoinCashTransactionBuilder(walletPublicKey.toCompressedPublicKey()),
BitcoinCashNetworkManager()
)
}
Blockchain.Ethereum -> { Blockchain.Ethereum -> {
val chain = Chain.Mainnet val chain = Chain.Mainnet
return EthereumWalletManager( return EthereumWalletManager(

View file

@ -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_RESERVE = "https://horizon.sui.li/"
const val API_STELLAR_TESTNET = "https://horizon-testnet.stellar.org/" const val API_STELLAR_TESTNET = "https://horizon-testnet.stellar.org/"
const val API_BLOCKCHAIN_INFO = "https://blockchain.info/" const val API_BLOCKCHAIN_INFO = "https://blockchain.info/"
const val API_ADALITE = "https://explorer3.adalite.io" const val API_ADALITE = "https://explorer3.adalite.io/"
const val API_ADALITE_RESERVE = "https://nodes.southeastasia.cloudapp.azure.com" const val API_ADALITE_RESERVE = "https://nodes.southeastasia.cloudapp.azure.com/"
const val API_RIPPLED = "https://s1.ripple.com:51234" const val API_RIPPLED = "https://s1.ripple.com:51234/"
const val API_RIPPLED_RESERVE = "https://s2.ripple.com:51234" const val API_RIPPLED_RESERVE = "https://s2.ripple.com:51234/"
const val API_BLOCKCHAIR = "https://api.blockchair.com/"

View file

@ -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)

View file

@ -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<BitcoinAddressResponse> {
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<BitcoinFee> {
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"

View file

@ -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<String, BlockchairAddressData>? = null
)
data class BlockchairAddressData(
@Json(name = "address")
val addressInfo: BlockchairAddressInfo? = null,
@Json(name = "utxo")
val unspentOutputs: List<BlockchairUnspentOutput>? = null,
@Json(name = "transactions")
val transactions: List<String>? = 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<String, BlockchairTransactionData>? = 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
)

View file

@ -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()
}
}

View file

@ -4,6 +4,7 @@ import com.google.common.truth.Truth
import com.tangem.SessionEnvironment import com.tangem.SessionEnvironment
import com.tangem.blockchain.blockchains.binance.BinanceWalletManager import com.tangem.blockchain.blockchains.binance.BinanceWalletManager
import com.tangem.blockchain.blockchains.bitcoin.BitcoinWalletManager 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.cardano.CardanoWalletManager
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
import com.tangem.blockchain.blockchains.stellar.StellarWalletManager import com.tangem.blockchain.blockchains.stellar.StellarWalletManager
@ -80,4 +81,15 @@ internal class WalletManagerFactoryTest {
Truth.assertThat(walletManager) Truth.assertThat(walletManager)
.isInstanceOf(BinanceWalletManager::class.java) .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)
}
} }

View file

@ -21,7 +21,7 @@ data class SessionEnvironment(
var encryptionMode: EncryptionMode = EncryptionMode.NONE, var encryptionMode: EncryptionMode = EncryptionMode.NONE,
var encryptionKey: ByteArray? = null, var encryptionKey: ByteArray? = null,
val cvc: ByteArray? = null, val cvc: ByteArray? = null,
var cardFilter: CardFilter var cardFilter: CardFilter = CardFilter()
) { ) {
fun setPin1(pin1: String) { fun setPin1(pin1: String) {

View file

@ -44,7 +44,7 @@ class ReadIssuerExtraDataResponse(
val issuerDataSignature: ByteArray?, 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] * When flag [Settings.ProtectIssuerDataAgainstReplay] set in [SettingsMask]
* then this value is mandatory and must increase on each execution of [WriteIssuerDataCommand]. * then this value is mandatory and must increase on each execution of [WriteIssuerDataCommand].
*/ */

View file

@ -17,7 +17,7 @@ class WriteUserDataResponse(
) : CommandResponse ) : 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. * 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 * 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. * from blockchain to accelerate preparing new transaction.

View file

@ -3,7 +3,15 @@ package com.tangem.devkit._arch.structure
/** /**
[REDACTED_AUTHOR] [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 class StringId(val value: String) : Id

View file

@ -7,6 +7,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.annotation.ColorRes import androidx.annotation.ColorRes
import com.tangem.devkit.R 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.StringId
import com.tangem.devkit._arch.structure.StringResId import com.tangem.devkit._arch.structure.StringResId
import com.tangem.devkit._arch.structure.abstraction.Item import com.tangem.devkit._arch.structure.abstraction.Item
@ -37,6 +38,7 @@ abstract class BaseViewWidget(
init { init {
subscribeToViewStateChanges(item.viewModel.viewState) subscribeToViewStateChanges(item.viewModel.viewState)
initViewState(item.viewModel.viewState) initViewState(item.viewModel.viewState)
view.tag = Id.getTag(item.id)
} }
protected open fun subscribeToViewStateChanges(viewState: ViewState) { protected open fun subscribeToViewStateChanges(viewState: ViewState) {

View file

@ -63,15 +63,15 @@ class ActionListFragment : BaseFragment() {
ActionType.Scan, ActionType.Scan,
ActionType.Sign, ActionType.Sign,
ActionType.Personalize, ActionType.Personalize,
ActionType.Depersonalize ActionType.Depersonalize,
// ActionType.CreateWallet, ActionType.CreateWallet,
// ActionType.PurgeWallet, ActionType.PurgeWallet,
// ActionType.ReadIssuerData, ActionType.ReadIssuerData,
// ActionType.WriteIssuerData, ActionType.WriteIssuerData,
// ActionType.ReadIssuerExData, ActionType.ReadIssuerExData,
// ActionType.WriteIssuerExData, ActionType.WriteIssuerExData,
// ActionType.ReadUserData, ActionType.ReadUserData,
// ActionType.WriteUserData ActionType.WriteUserData
) )
} }
} }

View file

@ -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
}
}
}

View file

@ -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.domain.paramsManager.PayloadKey
import com.tangem.devkit.ucase.tunnel.ActionView import com.tangem.devkit.ucase.tunnel.ActionView
import com.tangem.devkit.ucase.tunnel.ItemError 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.PersonalizationConfigConverter
import com.tangem.devkit.ucase.variants.personalize.converter.PersonalizationConfigToCardConfig import com.tangem.devkit.ucase.variants.personalize.converter.PersonalizationConfigToCardConfig
import com.tangem.devkit.ucase.variants.personalize.dto.DefaultPersonalizationParams import com.tangem.devkit.ucase.variants.personalize.dto.DefaultPersonalizationParams
@ -46,7 +46,7 @@ class PersonalizeAction : BaseAction() {
} }
private fun checkSeries(itemList: List<Item>): Boolean { private fun checkSeries(itemList: List<Item>): 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 val data = seriesItem.getData() as? String ?: return false
seriesItem.setData(data.toUpperCase()) seriesItem.setData(data.toUpperCase())
@ -54,8 +54,8 @@ class PersonalizeAction : BaseAction() {
} }
private fun checkNumber(itemList: List<Item>): Boolean { private fun checkNumber(itemList: List<Item>): Boolean {
val seriesItem = itemList.findItem(CardNumber.Series) as? EditTextItem ?: return false val seriesItem = itemList.findItem(CardNumberId.Series) as? EditTextItem ?: return false
val numberItem = itemList.findItem(CardNumber.Number) as? NumberItem ?: return false val numberItem = itemList.findItem(CardNumberId.Number) as? NumberItem ?: return false
val seriesData = seriesItem.getData() as? String ?: return false val seriesData = seriesItem.getData() as? String ?: return false
val numberData = stringOf(numberItem.getData() as? Number) val numberData = stringOf(numberItem.getData() as? Number)

View file

@ -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
}
}
}

View file

@ -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
}
}
}

View file

@ -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
}
}
}

View file

@ -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
}
}
}

View file

@ -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
}
}
}

View file

@ -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
}
}
}

View file

@ -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
}
}
}

View file

@ -1,9 +1,7 @@
package com.tangem.devkit.ucase.domain.paramsManager.managers package com.tangem.devkit.ucase.domain.paramsManager.managers
import com.tangem.devkit._arch.structure.impl.EditTextItem import com.tangem.devkit._arch.structure.impl.EditTextItem
import com.tangem.devkit.ucase.domain.actions.DepersonalizeAction import com.tangem.devkit.ucase.domain.actions.*
import com.tangem.devkit.ucase.domain.actions.ScanAction
import com.tangem.devkit.ucase.domain.actions.SignAction
import com.tangem.devkit.ucase.domain.paramsManager.triggers.changeConsequence.CardIdConsequence import com.tangem.devkit.ucase.domain.paramsManager.triggers.changeConsequence.CardIdConsequence
import com.tangem.devkit.ucase.variants.TlvId import com.tangem.devkit.ucase.variants.TlvId
@ -29,4 +27,78 @@ class SignItemsManager : BaseItemsManager(SignAction()) {
EditTextItem(TlvId.TransactionOutHash, "Data used for hashing") 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")
))
}
} }

View file

@ -40,89 +40,89 @@ class PersonalizationResources() {
} }
private fun initCardNumber(holder: TypedHolder<Id, Resources>) { private fun initCardNumber(holder: TypedHolder<Id, Resources>) {
holder.register(CardNumber.Series, Resources(R.string.pers_item_series, R.string.info_pers_item_series)) holder.register(CardNumberId.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(CardNumberId.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.BatchId, Resources(R.string.pers_item_batch_id, R.string.info_pers_item_batch_id))
} }
private fun initCommon(holder: TypedHolder<Id, Resources>) { private fun initCommon(holder: TypedHolder<Id, Resources>) {
holder.register(Common.Curve, Resources(R.string.pers_item_curve, R.string.info_pers_item_curve)) holder.register(CommonId.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(CommonId.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(CommonId.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(CommonId.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.CreateWallet, Resources(R.string.pers_item_create_wallet, R.string.info_pers_item_create_wallet))
} }
private fun initSigningMethod(holder: TypedHolder<Id, Resources>) { private fun initSigningMethod(holder: TypedHolder<Id, Resources>) {
holder.register(SigningMethod.SignTx, Resources(R.string.pers_item_sign_tx_hashes, R.string.info_pers_item_sign_tx_hashes)) holder.register(SigningMethodId.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(SigningMethodId.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(SigningMethodId.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(SigningMethodId.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(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(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(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(SigningMethod.SignExternal, Resources(R.string.pers_item_sign_hash_ex, R.string.info_pers_item_sign_hash_ex)) 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<Id, Resources>) { private fun initSignHashExProp(holder: TypedHolder<Id, Resources>) {
holder.register(SignHashExProp.PinLessFloorLimit, Resources(R.string.pers_item_pin_less_floor_limit, R.string.info_pers_item_pin_less_floor_limit)) holder.register(SignHashExPropId.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(SignHashExPropId.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(SignHashExPropId.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(SignHashExPropId.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.CheckPin3, Resources(R.string.pers_item_check_pin3_on_card, R.string.info_pers_item_check_pin3_on_card))
} }
private fun initToken(holder: TypedHolder<Id, Resources>) { private fun initToken(holder: TypedHolder<Id, Resources>) {
holder.register(Token.ItsToken, Resources(R.string.pers_item_its_token, R.string.info_pers_item_its_token)) holder.register(TokenId.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(TokenId.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(TokenId.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.Decimal, Resources(R.string.pers_item_decimal, R.string.info_pers_item_decimal))
} }
private fun initProductMask(holder: TypedHolder<Id, Resources>) { private fun initProductMask(holder: TypedHolder<Id, Resources>) {
holder.register(ProductMask.Note, Resources(R.string.pers_item_note, R.string.info_pers_item_note)) holder.register(ProductMaskId.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(ProductMaskId.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(ProductMaskId.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.IdIssuerCard, Resources(R.string.pers_item_id_issuer_card, R.string.info_pers_item_id_issuer_card))
} }
private fun initSettingsMask(holder: TypedHolder<Id, Resources>) { private fun initSettingsMask(holder: TypedHolder<Id, Resources>) {
holder.register(SettingsMask.IsReusable, Resources(R.string.pers_item_is_reusable, R.string.info_pers_item_is_reusable)) holder.register(SettingsMaskId.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(SettingsMaskId.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(SettingsMaskId.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(SettingsMaskId.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(SettingsMaskId.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(SettingsMaskId.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(SettingsMaskId.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(SettingsMaskId.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(SettingsMaskId.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(SettingsMaskId.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(SettingsMaskId.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(SettingsMaskId.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(SettingsMaskId.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(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(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(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(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.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<Id, Resources>) { private fun initSettingsMaskProtocolEnc(holder: TypedHolder<Id, Resources>) {
holder.register(SettingsMaskProtocolEnc.AllowUnencrypted, Resources(R.string.pers_item_allow_unencrypted, R.string.info_pers_item_allow_unencrypted)) holder.register(SettingsMaskProtocolEncId.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.AllowStaticEncryption, Resources(R.string.pers_item_allow_fast_encryption, R.string.info_pers_item_allow_fast_encryption))
} }
private fun initSettingsMaskNdef(holder: TypedHolder<Id, Resources>) { private fun initSettingsMaskNdef(holder: TypedHolder<Id, Resources>) {
holder.register(SettingsMaskNdef.UseNdef, Resources(R.string.pers_item_use_ndef, R.string.info_pers_item_use_ndef)) holder.register(SettingsMaskNdefId.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(SettingsMaskNdefId.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(SettingsMaskNdefId.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(SettingsMaskNdefId.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(SettingsMaskNdefId.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.Uri, Resources(R.string.pers_item_uri, R.string.info_pers_item_uri))
} }
private fun initPins(holder: TypedHolder<Id, Resources>) { private fun initPins(holder: TypedHolder<Id, Resources>) {
holder.register(Pins.Pin, Resources(R.string.pers_item_pin, R.string.info_pers_item_pin)) holder.register(PinsId.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(PinsId.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(PinsId.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(PinsId.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.PauseBeforePin2, Resources(R.string.pers_item_pause_before_pin2, R.string.info_pers_item_pause_before_pin2))
} }
} }

View file

@ -3,10 +3,7 @@ package com.tangem.devkit.ucase.resources.initializers
import com.tangem.devkit.R import com.tangem.devkit.R
import com.tangem.devkit.ucase.resources.MainResourceHolder import com.tangem.devkit.ucase.resources.MainResourceHolder
import com.tangem.devkit.ucase.resources.Resources import com.tangem.devkit.ucase.resources.Resources
import com.tangem.devkit.ucase.variants.responses.CardDataId import com.tangem.devkit.ucase.variants.responses.*
import com.tangem.devkit.ucase.variants.responses.CardId
import com.tangem.devkit.ucase.variants.responses.DepersonalizeId
import com.tangem.devkit.ucase.variants.responses.SignId
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
@ -17,6 +14,13 @@ class ResponseResources {
initCardData(holder) initCardData(holder)
initSignResponse(holder) initSignResponse(holder)
initDepersonalizeResponse(holder) initDepersonalizeResponse(holder)
initCreateWalletResponse(holder)
initPurgeWalletResponse(holder)
initReadIssuerDataResponse(holder)
initWriteIssuerDataResponse(holder)
initReadIssuerExtraDataResponse(holder)
initReadUserDataResponse(holder)
initWriteUserDataResponse(holder)
} }
private fun initCard(holder: MainResourceHolder) { private fun initCard(holder: MainResourceHolder) {
@ -65,4 +69,46 @@ class ResponseResources {
private fun initDepersonalizeResponse(holder: MainResourceHolder) { private fun initDepersonalizeResponse(holder: MainResourceHolder) {
holder.register(DepersonalizeId.isSuccess, Resources(R.string.response_depersonalize_is_success, R.string.info_response_depersonalize_is_success)) 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))
}
} }

View file

@ -16,5 +16,9 @@ class TlvResources {
private fun initScan(holder: MainResourceHolder) { private fun initScan(holder: MainResourceHolder) {
holder.register(TlvId.CardId, Resources(R.string.tlv_card_id, R.string.info_tlv_card_id)) 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.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))
} }
} }

View file

@ -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 inflater = LayoutInflater.from(where.context)
val view = inflater.inflate(R.layout.w_card_incoming_param, where, false) val view = inflater.inflate(R.layout.w_card_incoming_param, where, false)
where.plusAssign(view) where.plusAssign(view)

View file

@ -56,6 +56,7 @@ class ParameterWidget(
btnAction.setText(getResNameId(ActionType.Scan)) btnAction.setText(getResNameId(ActionType.Scan))
toggleActionBtnVisibility() toggleActionBtnVisibility()
initDescriptionWidget() initDescriptionWidget()
parent.tag = Id.getTag(typedItem.id)
} }
fun changeParamValue(data: Any?, silent: Boolean = true) { fun changeParamValue(data: Any?, silent: Boolean = true) {

View file

@ -8,4 +8,8 @@ import com.tangem.devkit._arch.structure.Id
enum class TlvId : Id { enum class TlvId : Id {
CardId, CardId,
TransactionOutHash, TransactionOutHash,
IssuerData,
Counter,
UserData,
ProtectedUserData
} }

View file

@ -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() }
}

View file

@ -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() }
}

View file

@ -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() }
}

View file

@ -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() }
}

View file

@ -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() }
}

View file

@ -22,13 +22,13 @@ enum class BlockId : PersonalizationId {
Pins, Pins,
} }
enum class CardNumber : PersonalizationId { enum class CardNumberId : PersonalizationId {
Series, Series,
Number, Number,
BatchId, BatchId,
} }
enum class Common : PersonalizationId { enum class CommonId : PersonalizationId {
Curve, Curve,
Blockchain, Blockchain,
BlockchainCustom, BlockchainCustom,
@ -36,7 +36,7 @@ enum class Common : PersonalizationId {
CreateWallet, CreateWallet,
} }
enum class SigningMethod : PersonalizationId { enum class SigningMethodId : PersonalizationId {
SignTx, SignTx,
SignTxRaw, SignTxRaw,
SignValidatedTx, SignValidatedTx,
@ -46,7 +46,7 @@ enum class SigningMethod : PersonalizationId {
SignExternal, SignExternal,
} }
enum class SignHashExProp : PersonalizationId { enum class SignHashExPropId : PersonalizationId {
PinLessFloorLimit, PinLessFloorLimit,
CryptoExKey, CryptoExKey,
RequireTerminalCertSig, RequireTerminalCertSig,
@ -54,21 +54,21 @@ enum class SignHashExProp : PersonalizationId {
CheckPin3, CheckPin3,
} }
enum class Token : PersonalizationId { enum class TokenId : PersonalizationId {
ItsToken, ItsToken,
Symbol, Symbol,
ContractAddress, ContractAddress,
Decimal Decimal
} }
enum class ProductMask : PersonalizationId { enum class ProductMaskId : PersonalizationId {
Note, Note,
Tag, Tag,
IdCard, IdCard,
IdIssuerCard IdIssuerCard
} }
enum class SettingsMask : PersonalizationId { enum class SettingsMaskId : PersonalizationId {
IsReusable, IsReusable,
NeedActivation, NeedActivation,
ForbidPurge, ForbidPurge,
@ -87,12 +87,12 @@ enum class SettingsMask : PersonalizationId {
RestrictOverwriteExtraIssuerData, RestrictOverwriteExtraIssuerData,
} }
enum class SettingsMaskProtocolEnc : PersonalizationId { enum class SettingsMaskProtocolEncId : PersonalizationId {
AllowUnencrypted, AllowUnencrypted,
AllowStaticEncryption AllowStaticEncryption
} }
enum class SettingsMaskNdef : PersonalizationId { enum class SettingsMaskNdefId : PersonalizationId {
UseNdef, UseNdef,
DynamicNdef, DynamicNdef,
DisablePrecomputedNdef, DisablePrecomputedNdef,
@ -101,7 +101,7 @@ enum class SettingsMaskNdef : PersonalizationId {
Uri, Uri,
} }
enum class Pins : PersonalizationId { enum class PinsId : PersonalizationId {
Pin, Pin,
Pin2, Pin2,
Pin3, Pin3,

View file

@ -14,63 +14,63 @@ class ConfigValuesHolder : BaseTypedHolder<Id, Value>() {
// All registered association values can't be objects // All registered association values can't be objects
fun init(default: PersonalizationConfig) { fun init(default: PersonalizationConfig) {
register(CardNumber.Series, Value(default.series)) register(CardNumberId.Series, Value(default.series))
register(CardNumber.Number, Value(default.startNumber)) register(CardNumberId.Number, Value(default.startNumber))
register(CardNumber.BatchId, Value(default.cardData.batch)) register(CardNumberId.BatchId, Value(default.cardData.batch))
register(Common.Curve, Value(default.curveID, Helper.listOfCurves())) register(CommonId.Curve, Value(default.curveID, Helper.listOfCurves()))
register(Common.Blockchain, Value(default.cardData.blockchain, Helper.listOfBlockchain())) register(CommonId.Blockchain, Value(default.cardData.blockchain, Helper.listOfBlockchain()))
register(Common.BlockchainCustom, Value(default.blockchainCustom)) register(CommonId.BlockchainCustom, Value(default.blockchainCustom))
register(Common.MaxSignatures, Value(default.MaxSignatures)) register(CommonId.MaxSignatures, Value(default.MaxSignatures))
register(Common.CreateWallet, Value(default.createWallet)) register(CommonId.CreateWallet, Value(default.createWallet))
register(SigningMethod.SignTx, Value(default.SigningMethod0)) register(SigningMethodId.SignTx, Value(default.SigningMethod0))
register(SigningMethod.SignTxRaw, Value(default.SigningMethod1)) register(SigningMethodId.SignTxRaw, Value(default.SigningMethod1))
register(SigningMethod.SignValidatedTx, Value(default.SigningMethod2)) register(SigningMethodId.SignValidatedTx, Value(default.SigningMethod2))
register(SigningMethod.SignValidatedTxRaw, Value(default.SigningMethod3)) register(SigningMethodId.SignValidatedTxRaw, Value(default.SigningMethod3))
register(SigningMethod.SignValidatedTxIssuer, Value(default.SigningMethod4)) register(SigningMethodId.SignValidatedTxIssuer, Value(default.SigningMethod4))
register(SigningMethod.SignValidatedTxRawIssuer, Value(default.SigningMethod5)) register(SigningMethodId.SignValidatedTxRawIssuer, Value(default.SigningMethod5))
register(SigningMethod.SignExternal, Value(default.SigningMethod6)) register(SigningMethodId.SignExternal, Value(default.SigningMethod6))
register(SignHashExProp.PinLessFloorLimit, Value(default.pinLessFloorLimit)) register(SignHashExPropId.PinLessFloorLimit, Value(default.pinLessFloorLimit))
register(SignHashExProp.CryptoExKey, Value(default.hexCrExKey)) register(SignHashExPropId.CryptoExKey, Value(default.hexCrExKey))
register(SignHashExProp.RequireTerminalCertSig, Value(default.requireTerminalCertSignature)) register(SignHashExPropId.RequireTerminalCertSig, Value(default.requireTerminalCertSignature))
register(SignHashExProp.RequireTerminalTxSig, Value(default.requireTerminalTxSignature)) register(SignHashExPropId.RequireTerminalTxSig, Value(default.requireTerminalTxSignature))
register(SignHashExProp.CheckPin3, Value(default.checkPIN3onCard)) register(SignHashExPropId.CheckPin3, Value(default.checkPIN3onCard))
register(Token.ItsToken, Value(default.itsToken)) register(TokenId.ItsToken, Value(default.itsToken))
register(Token.Symbol, Value(default.cardData.token_symbol)) register(TokenId.Symbol, Value(default.cardData.token_symbol))
register(Token.ContractAddress, Value(default.cardData.token_contract_address)) register(TokenId.ContractAddress, Value(default.cardData.token_contract_address))
register(Token.Decimal, Value(default.cardData.token_decimal)) register(TokenId.Decimal, Value(default.cardData.token_decimal))
register(ProductMask.Note, Value(default.cardData.product_note)) register(ProductMaskId.Note, Value(default.cardData.product_note))
register(ProductMask.Tag, Value(default.cardData.product_tag)) register(ProductMaskId.Tag, Value(default.cardData.product_tag))
register(ProductMask.IdCard, Value(default.cardData.product_id_card)) register(ProductMaskId.IdCard, Value(default.cardData.product_id_card))
register(ProductMask.IdIssuerCard, Value(default.cardData.product_id_issuer)) register(ProductMaskId.IdIssuerCard, Value(default.cardData.product_id_issuer))
register(SettingsMask.IsReusable, Value(default.isReusable)) register(SettingsMaskId.IsReusable, Value(default.isReusable))
register(SettingsMask.NeedActivation, Value(default.useActivation)) register(SettingsMaskId.NeedActivation, Value(default.useActivation))
register(SettingsMask.ForbidPurge, Value(default.forbidPurgeWallet)) register(SettingsMaskId.ForbidPurge, Value(default.forbidPurgeWallet))
register(SettingsMask.AllowSelectBlockchain, Value(default.allowSelectBlockchain)) register(SettingsMaskId.AllowSelectBlockchain, Value(default.allowSelectBlockchain))
register(SettingsMask.UseBlock, Value(default.useBlock)) register(SettingsMaskId.UseBlock, Value(default.useBlock))
register(SettingsMask.OneApdu, Value(default.useOneCommandAtTime)) register(SettingsMaskId.OneApdu, Value(default.useOneCommandAtTime))
register(SettingsMask.UseCvc, Value(default.useCVC)) register(SettingsMaskId.UseCvc, Value(default.useCVC))
register(SettingsMask.AllowSwapPin, Value(default.allowSwapPIN)) register(SettingsMaskId.AllowSwapPin, Value(default.allowSwapPIN))
register(SettingsMask.AllowSwapPin2, Value(default.allowSwapPIN2)) register(SettingsMaskId.AllowSwapPin2, Value(default.allowSwapPIN2))
register(SettingsMask.ForbidDefaultPin, Value(default.forbidDefaultPIN)) register(SettingsMaskId.ForbidDefaultPin, Value(default.forbidDefaultPIN))
register(SettingsMask.SmartSecurityDelay, Value(default.smartSecurityDelay)) register(SettingsMaskId.SmartSecurityDelay, Value(default.smartSecurityDelay))
register(SettingsMask.ProtectIssuerDataAgainstReplay, Value(default.protectIssuerDataAgainstReplay)) register(SettingsMaskId.ProtectIssuerDataAgainstReplay, Value(default.protectIssuerDataAgainstReplay))
register(SettingsMask.SkipSecurityDelayIfValidated, Value(default.skipSecurityDelayIfValidatedByIssuer)) register(SettingsMaskId.SkipSecurityDelayIfValidated, Value(default.skipSecurityDelayIfValidatedByIssuer))
register(SettingsMask.SkipPin2CvcIfValidated, Value(default.skipCheckPIN2andCVCIfValidatedByIssuer)) register(SettingsMaskId.SkipPin2CvcIfValidated, Value(default.skipCheckPIN2andCVCIfValidatedByIssuer))
register(SettingsMask.SkipSecurityDelayOnLinkedTerminal, Value(default.skipSecurityDelayIfValidatedByLinkedTerminal)) register(SettingsMaskId.SkipSecurityDelayOnLinkedTerminal, Value(default.skipSecurityDelayIfValidatedByLinkedTerminal))
register(SettingsMask.RestrictOverwriteExtraIssuerData, Value(default.restrictOverwriteIssuerDataEx)) register(SettingsMaskId.RestrictOverwriteExtraIssuerData, Value(default.restrictOverwriteIssuerDataEx))
register(SettingsMaskProtocolEnc.AllowUnencrypted, Value(default.protocolAllowUnencrypted)) register(SettingsMaskProtocolEncId.AllowUnencrypted, Value(default.protocolAllowUnencrypted))
register(SettingsMaskProtocolEnc.AllowStaticEncryption, Value(default.protocolAllowStaticEncryption)) register(SettingsMaskProtocolEncId.AllowStaticEncryption, Value(default.protocolAllowStaticEncryption))
register(SettingsMaskNdef.UseNdef, Value(default.useNDEF)) register(SettingsMaskNdefId.UseNdef, Value(default.useNDEF))
register(SettingsMaskNdef.DynamicNdef, Value(default.useDynamicNDEF)) register(SettingsMaskNdefId.DynamicNdef, Value(default.useDynamicNDEF))
register(SettingsMaskNdef.DisablePrecomputedNdef, Value(default.disablePrecomputedNDEF)) register(SettingsMaskNdefId.DisablePrecomputedNdef, Value(default.disablePrecomputedNDEF))
register(SettingsMaskNdef.Aar, Value(default.aar, Helper.aarList())) register(SettingsMaskNdefId.Aar, Value(default.aar, Helper.aarList()))
register(SettingsMaskNdef.AarCustom, Value(default.aarCustom)) register(SettingsMaskNdefId.AarCustom, Value(default.aarCustom))
register(SettingsMaskNdef.Uri, Value(default.uri)) register(SettingsMaskNdefId.Uri, Value(default.uri))
register(Pins.Pin, Value(default.PIN)) register(PinsId.Pin, Value(default.PIN))
register(Pins.Pin2, Value(default.PIN2)) register(PinsId.Pin2, Value(default.PIN2))
register(Pins.Pin3, Value(default.PIN3)) register(PinsId.Pin3, Value(default.PIN3))
register(Pins.Cvc, Value(default.CVC)) register(PinsId.Cvc, Value(default.CVC))
register(Pins.PauseBeforePin2, Value(default.pauseBeforePIN2, Helper.pauseBeforePin())) register(PinsId.PauseBeforePin2, Value(default.pauseBeforePIN2, Helper.pauseBeforePin()))
} }
} }

View file

@ -11,37 +11,37 @@ class ItemTypes {
BlockId.SettingsMaskNdef, BlockId.Pins 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( val boolList = mutableListOf(
Common.CreateWallet, SigningMethod.SignTx, SigningMethod.SignTxRaw, SigningMethod.SignValidatedTx, CommonId.CreateWallet, SigningMethodId.SignTx, SigningMethodId.SignTxRaw, SigningMethodId.SignValidatedTx,
SigningMethod.SignValidatedTxRaw, SigningMethod.SignValidatedTxIssuer, SigningMethodId.SignValidatedTxRaw, SigningMethodId.SignValidatedTxIssuer,
SigningMethod.SignValidatedTxRawIssuer, SigningMethod.SignExternal, SignHashExProp.RequireTerminalCertSig, SigningMethodId.SignValidatedTxRawIssuer, SigningMethodId.SignExternal, SignHashExPropId.RequireTerminalCertSig,
SignHashExProp.RequireTerminalTxSig, SignHashExProp.CheckPin3, Token.ItsToken, SignHashExPropId.RequireTerminalTxSig, SignHashExPropId.CheckPin3, TokenId.ItsToken,
ProductMask.Note, ProductMask.Tag, ProductMask.IdCard, ProductMask.IdIssuerCard, SettingsMask.IsReusable, SettingsMask.NeedActivation, ProductMaskId.Note, ProductMaskId.Tag, ProductMaskId.IdCard, ProductMaskId.IdIssuerCard, SettingsMaskId.IsReusable, SettingsMaskId.NeedActivation,
SettingsMask.ForbidPurge, SettingsMask.AllowSelectBlockchain, SettingsMask.UseBlock, SettingsMask.OneApdu, SettingsMaskId.ForbidPurge, SettingsMaskId.AllowSelectBlockchain, SettingsMaskId.UseBlock, SettingsMaskId.OneApdu,
SettingsMask.UseCvc, SettingsMask.AllowSwapPin, SettingsMask.AllowSwapPin2, SettingsMask.ForbidDefaultPin, SettingsMaskId.UseCvc, SettingsMaskId.AllowSwapPin, SettingsMaskId.AllowSwapPin2, SettingsMaskId.ForbidDefaultPin,
SettingsMask.SmartSecurityDelay, SettingsMask.ProtectIssuerDataAgainstReplay, SettingsMaskId.SmartSecurityDelay, SettingsMaskId.ProtectIssuerDataAgainstReplay,
SettingsMask.SkipSecurityDelayIfValidated, SettingsMask.SkipPin2CvcIfValidated, SettingsMaskId.SkipSecurityDelayIfValidated, SettingsMaskId.SkipPin2CvcIfValidated,
SettingsMask.SkipSecurityDelayOnLinkedTerminal, SettingsMask.RestrictOverwriteExtraIssuerData, SettingsMaskId.SkipSecurityDelayOnLinkedTerminal, SettingsMaskId.RestrictOverwriteExtraIssuerData,
SettingsMaskProtocolEnc.AllowUnencrypted, SettingsMaskProtocolEnc.AllowStaticEncryption, SettingsMaskProtocolEncId.AllowUnencrypted, SettingsMaskProtocolEncId.AllowStaticEncryption,
SettingsMaskNdef.UseNdef, SettingsMaskNdef.DynamicNdef, SettingsMaskNdef.DisablePrecomputedNdef SettingsMaskNdefId.UseNdef, SettingsMaskNdefId.DynamicNdef, SettingsMaskNdefId.DisablePrecomputedNdef
) )
val editTextList = mutableListOf( val editTextList = mutableListOf(
CardNumber.Series, CardNumber.BatchId, Common.BlockchainCustom, SignHashExProp.CryptoExKey, Token.Symbol, CardNumberId.Series, CardNumberId.BatchId, CommonId.BlockchainCustom, SignHashExPropId.CryptoExKey, TokenId.Symbol,
Token.ContractAddress, Pins.Pin, Pins.Pin2, Pins.Pin3, Pins.Cvc, SettingsMaskNdef.AarCustom, SettingsMaskNdef.Uri TokenId.ContractAddress, PinsId.Pin, PinsId.Pin2, PinsId.Pin3, PinsId.Cvc, SettingsMaskNdefId.AarCustom, SettingsMaskNdefId.Uri
) )
val numberList = mutableListOf( val numberList = mutableListOf(
CardNumber.Number, Common.MaxSignatures, SignHashExProp.PinLessFloorLimit, Token.Decimal CardNumberId.Number, CommonId.MaxSignatures, SignHashExPropId.PinLessFloorLimit, TokenId.Decimal
) )
val hiddenList = mutableListOf<Id>( val hiddenList = mutableListOf<Id>(
CardNumber.Series, CardNumber.BatchId, Pins.Pin3, SigningMethod.SignExternal, CardNumberId.Series, CardNumberId.BatchId, PinsId.Pin3, SigningMethodId.SignExternal,
SignHashExProp.CryptoExKey, SignHashExProp.CheckPin3, SettingsMask.OneApdu, SignHashExPropId.CryptoExKey, SignHashExPropId.CheckPin3, SettingsMaskId.OneApdu,
SettingsMask.UseBlock, SettingsMask.ProtectIssuerDataAgainstReplay, SettingsMaskId.UseBlock, SettingsMaskId.ProtectIssuerDataAgainstReplay,
SignHashExProp.RequireTerminalCertSig, SignHashExProp.RequireTerminalTxSig SignHashExPropId.RequireTerminalCertSig, SignHashExPropId.RequireTerminalTxSig
) )
val oftenUsedList = listOf<Id>( val oftenUsedList = listOf<Id>(
@ -56,11 +56,11 @@ class ItemTypes {
// BlockId.SettingsMaskProtocolEnc, // BlockId.SettingsMaskProtocolEnc,
BlockId.SettingsMaskNdef, BlockId.SettingsMaskNdef,
// BlockId.Pins, // BlockId.Pins,
Common.Curve, CommonId.Curve,
Common.Blockchain, CommonId.Blockchain,
Common.BlockchainCustom, CommonId.BlockchainCustom,
// Common.MaxSignatures, // Common.MaxSignatures,
Common.CreateWallet, CommonId.CreateWallet,
// SigningMethod.SignTx, // SigningMethod.SignTx,
// SigningMethod.SignTxRaw, // SigningMethod.SignTxRaw,
// SigningMethod.SignValidatedTx, // SigningMethod.SignValidatedTx,
@ -79,10 +79,10 @@ class ItemTypes {
// Token.Symbol, // Token.Symbol,
// Token.ContractAddress, // Token.ContractAddress,
// Token.Decimal, // Token.Decimal,
ProductMask.Note, ProductMaskId.Note,
ProductMask.Tag, ProductMaskId.Tag,
ProductMask.IdCard, ProductMaskId.IdCard,
ProductMask.IdIssuerCard, ProductMaskId.IdIssuerCard,
// SettingsMask.IsReusable, // SettingsMask.IsReusable,
// SettingsMask.NeedActivation, // SettingsMask.NeedActivation,
// SettingsMask.ForbidPurge, // SettingsMask.ForbidPurge,
@ -104,14 +104,14 @@ class ItemTypes {
// SettingsMaskNdef.UseNdef, // SettingsMaskNdef.UseNdef,
// SettingsMaskNdef.DynamicNdef, // SettingsMaskNdef.DynamicNdef,
// SettingsMaskNdef.DisablePrecomputedNdef, // SettingsMaskNdef.DisablePrecomputedNdef,
SettingsMaskNdef.Aar, SettingsMaskNdefId.Aar,
SettingsMaskNdef.AarCustom, SettingsMaskNdefId.AarCustom,
SettingsMaskNdef.Uri, SettingsMaskNdefId.Uri,
// Pins.Pin, // Pins.Pin,
// Pins.Pin2, // Pins.Pin2,
// Pins.Pin3, // Pins.Pin3,
// Pins.Cvc, // Pins.Cvc,
Pins.PauseBeforePin2 PinsId.PauseBeforePin2
) )
} }

View file

@ -50,63 +50,63 @@ class ItemsToPersonalizationConfig : ItemsToModel<PersonalizationConfig> {
private fun createModel(): PersonalizationConfig { private fun createModel(): PersonalizationConfig {
val export = PersonalizationConfig() val export = PersonalizationConfig()
export.series = getTyped(CardNumber.Series) export.series = getTyped(CardNumberId.Series)
export.startNumber = getTyped(CardNumber.Number) export.startNumber = getTyped(CardNumberId.Number)
export.cardData.batch = getTyped(CardNumber.BatchId) export.cardData.batch = getTyped(CardNumberId.BatchId)
export.curveID = getTyped(Common.Curve) export.curveID = getTyped(CommonId.Curve)
export.cardData.blockchain = getTyped(Common.Blockchain) export.cardData.blockchain = getTyped(CommonId.Blockchain)
export.blockchainCustom = getTyped(Common.BlockchainCustom) export.blockchainCustom = getTyped(CommonId.BlockchainCustom)
export.MaxSignatures = getTyped(Common.MaxSignatures) export.MaxSignatures = getTyped(CommonId.MaxSignatures)
export.createWallet = getTyped(Common.CreateWallet) export.createWallet = getTyped(CommonId.CreateWallet)
export.SigningMethod0 = getTyped(SigningMethod.SignTx) export.SigningMethod0 = getTyped(SigningMethodId.SignTx)
export.SigningMethod1 = getTyped(SigningMethod.SignTxRaw) export.SigningMethod1 = getTyped(SigningMethodId.SignTxRaw)
export.SigningMethod2 = getTyped(SigningMethod.SignValidatedTx) export.SigningMethod2 = getTyped(SigningMethodId.SignValidatedTx)
export.SigningMethod3 = getTyped(SigningMethod.SignValidatedTxRaw) export.SigningMethod3 = getTyped(SigningMethodId.SignValidatedTxRaw)
export.SigningMethod4 = getTyped(SigningMethod.SignValidatedTxIssuer) export.SigningMethod4 = getTyped(SigningMethodId.SignValidatedTxIssuer)
export.SigningMethod5 = getTyped(SigningMethod.SignValidatedTxRawIssuer) export.SigningMethod5 = getTyped(SigningMethodId.SignValidatedTxRawIssuer)
export.SigningMethod6 = getTyped(SigningMethod.SignExternal) export.SigningMethod6 = getTyped(SigningMethodId.SignExternal)
export.pinLessFloorLimit = getTyped(SignHashExProp.PinLessFloorLimit) export.pinLessFloorLimit = getTyped(SignHashExPropId.PinLessFloorLimit)
export.hexCrExKey = getTyped(SignHashExProp.CryptoExKey) export.hexCrExKey = getTyped(SignHashExPropId.CryptoExKey)
export.requireTerminalCertSignature = getTyped(SignHashExProp.RequireTerminalCertSig) export.requireTerminalCertSignature = getTyped(SignHashExPropId.RequireTerminalCertSig)
export.requireTerminalTxSignature = getTyped(SignHashExProp.RequireTerminalTxSig) export.requireTerminalTxSignature = getTyped(SignHashExPropId.RequireTerminalTxSig)
export.checkPIN3onCard = getTyped(SignHashExProp.CheckPin3) export.checkPIN3onCard = getTyped(SignHashExPropId.CheckPin3)
export.itsToken = getTyped(Token.ItsToken) export.itsToken = getTyped(TokenId.ItsToken)
export.cardData.token_symbol = getTyped(Token.Symbol) export.cardData.token_symbol = getTyped(TokenId.Symbol)
export.cardData.token_contract_address = getTyped(Token.ContractAddress) export.cardData.token_contract_address = getTyped(TokenId.ContractAddress)
export.cardData.token_decimal = getTyped(Token.Decimal) export.cardData.token_decimal = getTyped(TokenId.Decimal)
export.cardData = export.cardData.apply { this.product_note = getTyped(ProductMask.Note) } export.cardData = export.cardData.apply { this.product_note = getTyped(ProductMaskId.Note) }
export.cardData = export.cardData.apply { this.product_tag = getTyped(ProductMask.Tag) } export.cardData = export.cardData.apply { this.product_tag = getTyped(ProductMaskId.Tag) }
export.cardData = export.cardData.apply { this.product_id_card = getTyped(ProductMask.IdCard) } export.cardData = export.cardData.apply { this.product_id_card = getTyped(ProductMaskId.IdCard) }
export.cardData = export.cardData.apply { this.product_id_issuer = getTyped(ProductMask.IdIssuerCard) } export.cardData = export.cardData.apply { this.product_id_issuer = getTyped(ProductMaskId.IdIssuerCard) }
export.isReusable = getTyped(SettingsMask.IsReusable) export.isReusable = getTyped(SettingsMaskId.IsReusable)
export.useActivation = getTyped(SettingsMask.NeedActivation) export.useActivation = getTyped(SettingsMaskId.NeedActivation)
export.forbidPurgeWallet = getTyped(SettingsMask.ForbidPurge) export.forbidPurgeWallet = getTyped(SettingsMaskId.ForbidPurge)
export.allowSelectBlockchain = getTyped(SettingsMask.AllowSelectBlockchain) export.allowSelectBlockchain = getTyped(SettingsMaskId.AllowSelectBlockchain)
export.useBlock = getTyped(SettingsMask.UseBlock) export.useBlock = getTyped(SettingsMaskId.UseBlock)
export.useOneCommandAtTime = getTyped(SettingsMask.OneApdu) export.useOneCommandAtTime = getTyped(SettingsMaskId.OneApdu)
export.useCVC = getTyped(SettingsMask.UseCvc) export.useCVC = getTyped(SettingsMaskId.UseCvc)
export.allowSwapPIN = getTyped(SettingsMask.AllowSwapPin) export.allowSwapPIN = getTyped(SettingsMaskId.AllowSwapPin)
export.allowSwapPIN2 = getTyped(SettingsMask.AllowSwapPin2) export.allowSwapPIN2 = getTyped(SettingsMaskId.AllowSwapPin2)
export.forbidDefaultPIN = getTyped(SettingsMask.ForbidDefaultPin) export.forbidDefaultPIN = getTyped(SettingsMaskId.ForbidDefaultPin)
export.smartSecurityDelay = getTyped(SettingsMask.SmartSecurityDelay) export.smartSecurityDelay = getTyped(SettingsMaskId.SmartSecurityDelay)
export.protectIssuerDataAgainstReplay = getTyped(SettingsMask.ProtectIssuerDataAgainstReplay) export.protectIssuerDataAgainstReplay = getTyped(SettingsMaskId.ProtectIssuerDataAgainstReplay)
export.skipSecurityDelayIfValidatedByIssuer = getTyped(SettingsMask.SkipSecurityDelayIfValidated) export.skipSecurityDelayIfValidatedByIssuer = getTyped(SettingsMaskId.SkipSecurityDelayIfValidated)
export.skipCheckPIN2andCVCIfValidatedByIssuer = getTyped(SettingsMask.SkipPin2CvcIfValidated) export.skipCheckPIN2andCVCIfValidatedByIssuer = getTyped(SettingsMaskId.SkipPin2CvcIfValidated)
export.skipSecurityDelayIfValidatedByLinkedTerminal = getTyped(SettingsMask.SkipSecurityDelayOnLinkedTerminal) export.skipSecurityDelayIfValidatedByLinkedTerminal = getTyped(SettingsMaskId.SkipSecurityDelayOnLinkedTerminal)
export.restrictOverwriteIssuerDataEx = getTyped(SettingsMask.RestrictOverwriteExtraIssuerData) export.restrictOverwriteIssuerDataEx = getTyped(SettingsMaskId.RestrictOverwriteExtraIssuerData)
export.protocolAllowUnencrypted = getTyped(SettingsMaskProtocolEnc.AllowUnencrypted) export.protocolAllowUnencrypted = getTyped(SettingsMaskProtocolEncId.AllowUnencrypted)
export.protocolAllowStaticEncryption = getTyped(SettingsMaskProtocolEnc.AllowStaticEncryption) export.protocolAllowStaticEncryption = getTyped(SettingsMaskProtocolEncId.AllowStaticEncryption)
export.useNDEF = getTyped(SettingsMaskNdef.UseNdef) export.useNDEF = getTyped(SettingsMaskNdefId.UseNdef)
export.useDynamicNDEF = getTyped(SettingsMaskNdef.DynamicNdef) export.useDynamicNDEF = getTyped(SettingsMaskNdefId.DynamicNdef)
export.disablePrecomputedNDEF = getTyped(SettingsMaskNdef.DisablePrecomputedNdef) export.disablePrecomputedNDEF = getTyped(SettingsMaskNdefId.DisablePrecomputedNdef)
export.aar = getTyped(SettingsMaskNdef.Aar) export.aar = getTyped(SettingsMaskNdefId.Aar)
export.aarCustom = getTyped(SettingsMaskNdef.AarCustom) export.aarCustom = getTyped(SettingsMaskNdefId.AarCustom)
export.uri = getTyped(SettingsMaskNdef.Uri) export.uri = getTyped(SettingsMaskNdefId.Uri)
export.PIN = getTyped(Pins.Pin) export.PIN = getTyped(PinsId.Pin)
export.PIN2 = getTyped(Pins.Pin2) export.PIN2 = getTyped(PinsId.Pin2)
export.PIN3 = getTyped(Pins.Pin3) export.PIN3 = getTyped(PinsId.Pin3)
export.CVC = getTyped(Pins.Cvc) export.CVC = getTyped(PinsId.Cvc)
export.pauseBeforePIN2 = getTyped(Pins.PauseBeforePin2) export.pauseBeforePIN2 = getTyped(PinsId.PauseBeforePin2)
return export return export
} }
@ -154,9 +154,9 @@ class PersonalizationConfigToItems : ModelToItems<PersonalizationConfig> {
private fun cardNumber(): ItemGroup { private fun cardNumber(): ItemGroup {
val block = createGroup(BlockId.CardNumber) val block = createGroup(BlockId.CardNumber)
mutableListOf( mutableListOf(
CardNumber.Series, CardNumberId.Series,
CardNumber.Number, CardNumberId.Number,
CardNumber.BatchId CardNumberId.BatchId
).forEach { createItem(block, it as Id) } ).forEach { createItem(block, it as Id) }
return block return block
} }
@ -164,12 +164,12 @@ class PersonalizationConfigToItems : ModelToItems<PersonalizationConfig> {
private fun common(): ItemGroup { private fun common(): ItemGroup {
val block = createGroup(BlockId.Common) val block = createGroup(BlockId.Common)
mutableListOf( mutableListOf(
Common.Blockchain, CommonId.Blockchain,
Common.BlockchainCustom, CommonId.BlockchainCustom,
Common.Curve, CommonId.Curve,
Common.MaxSignatures, CommonId.MaxSignatures,
Common.CreateWallet, CommonId.CreateWallet,
Pins.PauseBeforePin2 PinsId.PauseBeforePin2
).forEach { createItem(block, it as Id) } ).forEach { createItem(block, it as Id) }
return block return block
} }
@ -177,13 +177,13 @@ class PersonalizationConfigToItems : ModelToItems<PersonalizationConfig> {
private fun signingMethod(): ItemGroup { private fun signingMethod(): ItemGroup {
val block = createGroup(BlockId.SigningMethod) val block = createGroup(BlockId.SigningMethod)
mutableListOf( mutableListOf(
SigningMethod.SignTx, SigningMethodId.SignTx,
SigningMethod.SignTxRaw, SigningMethodId.SignTxRaw,
SigningMethod.SignValidatedTx, SigningMethodId.SignValidatedTx,
SigningMethod.SignValidatedTxRaw, SigningMethodId.SignValidatedTxRaw,
SigningMethod.SignValidatedTxIssuer, SigningMethodId.SignValidatedTxIssuer,
SigningMethod.SignValidatedTxRawIssuer, SigningMethodId.SignValidatedTxRawIssuer,
SigningMethod.SignExternal SigningMethodId.SignExternal
).forEach { createItem(block, it) } ).forEach { createItem(block, it) }
return block return block
} }
@ -191,11 +191,11 @@ class PersonalizationConfigToItems : ModelToItems<PersonalizationConfig> {
private fun signHashExProperties(): ItemGroup { private fun signHashExProperties(): ItemGroup {
val block = createGroup(BlockId.SignHashExProp) val block = createGroup(BlockId.SignHashExProp)
mutableListOf( mutableListOf(
SignHashExProp.PinLessFloorLimit, SignHashExPropId.PinLessFloorLimit,
SignHashExProp.CryptoExKey, SignHashExPropId.CryptoExKey,
SignHashExProp.RequireTerminalCertSig, SignHashExPropId.RequireTerminalCertSig,
SignHashExProp.RequireTerminalTxSig, SignHashExPropId.RequireTerminalTxSig,
SignHashExProp.CheckPin3 SignHashExPropId.CheckPin3
).forEach { createItem(block, it) } ).forEach { createItem(block, it) }
return block return block
} }
@ -203,10 +203,10 @@ class PersonalizationConfigToItems : ModelToItems<PersonalizationConfig> {
private fun token(): ItemGroup { private fun token(): ItemGroup {
val block = createGroup(BlockId.Token) val block = createGroup(BlockId.Token)
mutableListOf( mutableListOf(
Token.ItsToken, TokenId.ItsToken,
Token.Symbol, TokenId.Symbol,
Token.ContractAddress, TokenId.ContractAddress,
Token.Decimal TokenId.Decimal
).forEach { createItem(block, it) } ).forEach { createItem(block, it) }
return block return block
} }
@ -214,10 +214,10 @@ class PersonalizationConfigToItems : ModelToItems<PersonalizationConfig> {
private fun productMask(): ItemGroup { private fun productMask(): ItemGroup {
val block = createGroup(BlockId.ProdMask) val block = createGroup(BlockId.ProdMask)
mutableListOf( mutableListOf(
ProductMask.Note, ProductMaskId.Note,
ProductMask.Tag, ProductMaskId.Tag,
ProductMask.IdCard, ProductMaskId.IdCard,
ProductMask.IdIssuerCard ProductMaskId.IdIssuerCard
).forEach { createItem(block, it) } ).forEach { createItem(block, it) }
return block return block
} }
@ -225,22 +225,22 @@ class PersonalizationConfigToItems : ModelToItems<PersonalizationConfig> {
private fun settingsMask(): ItemGroup { private fun settingsMask(): ItemGroup {
val block = createGroup(BlockId.SettingsMask) val block = createGroup(BlockId.SettingsMask)
mutableListOf( mutableListOf(
SettingsMask.IsReusable, SettingsMaskId.IsReusable,
SettingsMask.NeedActivation, SettingsMaskId.NeedActivation,
SettingsMask.ForbidPurge, SettingsMaskId.ForbidPurge,
SettingsMask.AllowSelectBlockchain, SettingsMaskId.AllowSelectBlockchain,
SettingsMask.UseBlock, SettingsMaskId.UseBlock,
SettingsMask.OneApdu, SettingsMaskId.OneApdu,
SettingsMask.UseCvc, SettingsMaskId.UseCvc,
SettingsMask.AllowSwapPin, SettingsMaskId.AllowSwapPin,
SettingsMask.AllowSwapPin2, SettingsMaskId.AllowSwapPin2,
SettingsMask.ForbidDefaultPin, SettingsMaskId.ForbidDefaultPin,
SettingsMask.SmartSecurityDelay, SettingsMaskId.SmartSecurityDelay,
SettingsMask.ProtectIssuerDataAgainstReplay, SettingsMaskId.ProtectIssuerDataAgainstReplay,
SettingsMask.SkipSecurityDelayIfValidated, SettingsMaskId.SkipSecurityDelayIfValidated,
SettingsMask.SkipPin2CvcIfValidated, SettingsMaskId.SkipPin2CvcIfValidated,
SettingsMask.SkipSecurityDelayOnLinkedTerminal, SettingsMaskId.SkipSecurityDelayOnLinkedTerminal,
SettingsMask.RestrictOverwriteExtraIssuerData SettingsMaskId.RestrictOverwriteExtraIssuerData
).forEach { createItem(block, it) } ).forEach { createItem(block, it) }
return block return block
} }
@ -248,8 +248,8 @@ class PersonalizationConfigToItems : ModelToItems<PersonalizationConfig> {
private fun settingsMaskProtocolEnc(): ItemGroup { private fun settingsMaskProtocolEnc(): ItemGroup {
val block = createGroup(BlockId.SettingsMaskProtocolEnc) val block = createGroup(BlockId.SettingsMaskProtocolEnc)
mutableListOf( mutableListOf(
SettingsMaskProtocolEnc.AllowUnencrypted, SettingsMaskProtocolEncId.AllowUnencrypted,
SettingsMaskProtocolEnc.AllowStaticEncryption SettingsMaskProtocolEncId.AllowStaticEncryption
).forEach { createItem(block, it) } ).forEach { createItem(block, it) }
return block return block
} }
@ -257,12 +257,12 @@ class PersonalizationConfigToItems : ModelToItems<PersonalizationConfig> {
private fun settingsMaskNdef(): ItemGroup { private fun settingsMaskNdef(): ItemGroup {
val block = createGroup(BlockId.SettingsMaskNdef) val block = createGroup(BlockId.SettingsMaskNdef)
mutableListOf( mutableListOf(
SettingsMaskNdef.UseNdef, SettingsMaskNdefId.UseNdef,
SettingsMaskNdef.DynamicNdef, SettingsMaskNdefId.DynamicNdef,
SettingsMaskNdef.DisablePrecomputedNdef, SettingsMaskNdefId.DisablePrecomputedNdef,
SettingsMaskNdef.Aar, SettingsMaskNdefId.Aar,
SettingsMaskNdef.AarCustom, SettingsMaskNdefId.AarCustom,
SettingsMaskNdef.Uri SettingsMaskNdefId.Uri
).forEach { createItem(block, it) } ).forEach { createItem(block, it) }
return block return block
} }
@ -270,10 +270,10 @@ class PersonalizationConfigToItems : ModelToItems<PersonalizationConfig> {
private fun pins(): ItemGroup { private fun pins(): ItemGroup {
val block = createGroup(BlockId.Pins) val block = createGroup(BlockId.Pins)
mutableListOf( mutableListOf(
Pins.Pin, PinsId.Pin,
Pins.Pin2, PinsId.Pin2,
Pins.Pin3, PinsId.Pin3,
Pins.Cvc PinsId.Cvc
).forEach { createItem(block, it) } ).forEach { createItem(block, it) }
return block return block
} }

View file

@ -4,6 +4,7 @@ import com.tangem.KeyPair
import com.tangem.commands.personalization.entities.Acquirer import com.tangem.commands.personalization.entities.Acquirer
import com.tangem.commands.personalization.entities.Issuer import com.tangem.commands.personalization.entities.Issuer
import com.tangem.commands.personalization.entities.Manufacturer import com.tangem.commands.personalization.entities.Manufacturer
import com.tangem.common.extensions.hexToBytes
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
@ -18,12 +19,14 @@ interface DefaultPersonalizationParams {
name = name, name = name,
id = name + "\u0000", id = name + "\u0000",
dataKeyPair = KeyPair( dataKeyPair = KeyPair(
"".toByteArray(), ("045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f" +
"11121314151617184771ED81F2BACF57479E4735EB1405083927372D40DA9E92".toByteArray() "361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd26").hexToBytes(),
"11121314151617184771ED81F2BACF57479E4735EB1405083927372D40DA9E92".hexToBytes()
), ),
transactionKeyPair = KeyPair( transactionKeyPair = KeyPair(
"".toByteArray(), ("0484c5192e9bfa6c528a344f442137a92b89ea835bfef1d04cb4362eb906b508c5889846cfea71ba6dc7b" +
"11121314151617184771ED81F2BACF57479E4735EB1405081918171615141312".toByteArray() "3120c2208df9c46127d3d85cb5cfbd1479e97133a39d8").hexToBytes(),
"11121314151617184771ED81F2BACF57479E4735EB1405081918171615141312".hexToBytes()
) )
) )
} }
@ -34,8 +37,9 @@ interface DefaultPersonalizationParams {
name = name, name = name,
id = name + "\u0000", id = name + "\u0000",
keyPair = KeyPair( keyPair = KeyPair(
"".toByteArray(), ("0456ad1a82b22bcb40c38fd08939f87e6b80e40dec5b3bdb351c55fcd709e47f9fb2ed00c2304d3a9" +
"21222324252627284771ED81F2BACF57479E4735EB1405083927372D40DA9E92".toByteArray() "86f79c5ae0ac3c84e88da46dc8f513b7542c716af8c9a2daf").hexToBytes(),
"21222324252627284771ED81F2BACF57479E4735EB1405083927372D40DA9E92".hexToBytes()
) )
) )
} }

View file

@ -8,7 +8,7 @@ import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import com.tangem.devkit.R import com.tangem.devkit.R
import com.tangem.devkit._arch.structure.impl.NumberItem 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.addInputFilter
import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
@ -39,7 +39,7 @@ class NumberWidget(
tilItem.hint = getName() tilItem.hint = getName()
//TODO: remove from Widget //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.setText(stringOf(typedItem.getTypedData()))
etItem.addTextChangedListener(watcher) etItem.addTextChangedListener(watcher)

View file

@ -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() }
}

View file

@ -53,4 +53,38 @@ enum class SignId : ResponseId {
enum class DepersonalizeId: ResponseId { enum class DepersonalizeId: ResponseId {
isSuccess 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
} }

View file

@ -2,9 +2,7 @@ package com.tangem.devkit.ucase.variants.responses
import android.view.View import android.view.View
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.tangem.commands.Card import com.tangem.commands.*
import com.tangem.commands.CommandResponse
import com.tangem.commands.SignResponse
import com.tangem.commands.personalization.DepersonalizeResponse import com.tangem.commands.personalization.DepersonalizeResponse
import com.tangem.devkit.R import com.tangem.devkit.R
import com.tangem.devkit._arch.structure.abstraction.Item import com.tangem.devkit._arch.structure.abstraction.Item
@ -44,6 +42,14 @@ class ResponseViewModel : ViewModel() {
is Card -> R.string.fg_name_response_personalization is Card -> R.string.fg_name_response_personalization
is SignResponse -> R.string.fg_name_response_sign is SignResponse -> R.string.fg_name_response_sign
is DepersonalizeResponse -> R.string.fg_name_response_depersonalization 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 else -> R.string.unknown
} }
} }

View file

@ -1,7 +1,6 @@
package com.tangem.devkit.ucase.variants.responses.converter package com.tangem.devkit.ucase.variants.responses.converter
import com.tangem.commands.Card import com.tangem.commands.*
import com.tangem.commands.SignResponse
import com.tangem.commands.personalization.DepersonalizeResponse import com.tangem.commands.personalization.DepersonalizeResponse
import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
import java.lang.reflect.Type import java.lang.reflect.Type
@ -12,5 +11,12 @@ class ConvertersStore : BaseTypedHolder<Type, Any>() {
register(SignResponse::class.java, SignResponseConverter()) register(SignResponse::class.java, SignResponseConverter())
register(Card::class.java, CardConverter()) register(Card::class.java, CardConverter())
register(DepersonalizeResponse::class.java, DepersonalizeResponseConverter()) 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())
} }
} }

View file

@ -1,11 +1,10 @@
package com.tangem.devkit.ucase.variants.responses.converter 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.commands.personalization.DepersonalizeResponse
import com.tangem.devkit._arch.structure.abstraction.Item import com.tangem.devkit._arch.structure.abstraction.Item
import com.tangem.devkit._arch.structure.impl.TextItem import com.tangem.devkit._arch.structure.impl.TextItem
import com.tangem.devkit.ucase.variants.responses.DepersonalizeId import com.tangem.devkit.ucase.variants.responses.*
import com.tangem.devkit.ucase.variants.responses.SignId
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/** /**
@ -27,4 +26,74 @@ class DepersonalizeResponseConverter : BaseResponseConverter<DepersonalizeRespon
override fun convert(from: DepersonalizeResponse): List<Item> { override fun convert(from: DepersonalizeResponse): List<Item> {
return listOf(TextItem(DepersonalizeId.isSuccess, stringOf(from.success))) return listOf(TextItem(DepersonalizeId.isSuccess, stringOf(from.success)))
} }
}
class CreateWalletResponseConverter : BaseResponseConverter<CreateWalletResponse>() {
override fun convert(from: CreateWalletResponse): List<Item> {
return listOf(
TextItem(CreateWalletId.cid, from.cardId),
TextItem(CreateWalletId.cardStatus, valueToString(from.status)),
TextItem(CreateWalletId.walletPublicKey, fieldConverter.byteArray(from.walletPublicKey))
)
}
}
class PurgeWalletResponseConverter : BaseResponseConverter<PurgeWalletResponse>() {
override fun convert(from: PurgeWalletResponse): List<Item> {
return listOf(
TextItem(CreateWalletId.cid, from.cardId),
TextItem(CreateWalletId.cardStatus, valueToString(from.status))
)
}
}
class ReadIssuerDataResponseConverter : BaseResponseConverter<ReadIssuerDataResponse>() {
override fun convert(from: ReadIssuerDataResponse): List<Item> {
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<ReadIssuerExtraDataResponse>() {
override fun convert(from: ReadIssuerExtraDataResponse): List<Item> {
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<WriteIssuerDataResponse>() {
override fun convert(from: WriteIssuerDataResponse): List<Item> {
return listOf(
TextItem(CardId.cardId, from.cardId)
)
}
}
class ReadUserDataResponseConverter : BaseResponseConverter<ReadUserDataResponse>() {
override fun convert(from: ReadUserDataResponse): List<Item> {
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<WriteUserDataResponse>() {
override fun convert(from: WriteUserDataResponse): List<Item> {
return listOf(
TextItem(CardId.cardId, from.cardId)
)
}
} }

View file

@ -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() }
}

View file

@ -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() }
}

View file

@ -85,51 +85,51 @@
<fragment <fragment
android:id="@+id/nav_issuer_read_data" android:id="@+id/nav_issuer_read_data"
android:name="com.tangem.devkit.ucase.ui.IssuerReadDataActionFragment" android:name="com.tangem.devkit.ucase.variants.issuerdata.ui.ReadIssuerDataFragment"
android:label="@string/action_issuer_read_data" android:label="@string/action_issuer_read_data"
tools:layout="@layout/fg_action_issuer_read_data" /> tools:layout="@layout/fg_base_action_layout" />
<fragment <fragment
android:id="@+id/nav_issuer_write_data" android:id="@+id/nav_issuer_write_data"
android:name="com.tangem.devkit.ucase.ui.IssuerWriteDataActionFragment" android:name="com.tangem.devkit.ucase.variants.issuerdata.ui.WriteIssuerDataFragment"
android:label="@string/action_issuer_write_data" android:label="@string/action_issuer_write_data"
tools:layout="@layout/fg_action_issuer_write_data" /> tools:layout="@layout/fg_base_action_layout" />
<fragment <fragment
android:id="@+id/nav_issuer_read_ex_data" android:id="@+id/nav_issuer_read_ex_data"
android:name="com.tangem.devkit.ucase.ui.IssuerReadExDataActionFragment" android:name="com.tangem.devkit.ucase.variants.issuerdata.ui.ReadIssuerExtraDataFragment"
android:label="@string/action_issuer_read_ex_data" android:label="@string/action_issuer_read_ex_data"
tools:layout="@layout/fg_action_issuer_read_ex_data" /> tools:layout="@layout/fg_base_action_layout" />
<fragment <fragment
android:id="@+id/nav_issuer_write_ex_data" android:id="@+id/nav_issuer_write_ex_data"
android:name="com.tangem.devkit.ucase.ui.IssuerWriteExDataActionFragment" android:name="com.tangem.devkit.ucase.variants.issuerdata.ui.WriteIssuerExtraDataFragment"
android:label="@string/action_issuer_write_ex_data" android:label="@string/action_issuer_write_ex_data"
tools:layout="@layout/fg_action_issuer_write_ex_data" /> tools:layout="@layout/fg_base_action_layout" />
<fragment <fragment
android:id="@+id/nav_user_read_data" android:id="@+id/nav_user_read_data"
android:name="com.tangem.devkit.ucase.ui.UserReadDataActionFragment" android:name="com.tangem.devkit.ucase.variants.userdata.ui.ReadUserDataFragment"
android:label="@string/action_user_read_data" android:label="@string/action_user_read_data"
tools:layout="@layout/fg_action_user_read_data" /> tools:layout="@layout/fg_base_action_layout" />
<fragment <fragment
android:id="@+id/nav_user_write_data" android:id="@+id/nav_user_write_data"
android:name="com.tangem.devkit.ucase.ui.UserWriteDataActionFragment" android:name="com.tangem.devkit.ucase.variants.userdata.ui.WriteUserDataFragment"
android:label="@string/action_user_write_data" android:label="@string/action_user_write_data"
tools:layout="@layout/fg_action_user_write_data" /> tools:layout="@layout/fg_base_action_layout" />
<fragment <fragment
android:id="@+id/nav_wallet_create" android:id="@+id/nav_wallet_create"
android:name="com.tangem.devkit.ucase.ui.WalletCreateActionFragment" android:name="com.tangem.devkit.ucase.variants.createwallet.ui.CreateWalletActionFragment"
android:label="@string/action_wallet_create" android:label="@string/action_wallet_create"
tools:layout="@layout/fg_action_wallet_create" /> tools:layout="@layout/fg_base_action_layout" />
<fragment <fragment
android:id="@+id/nav_wallet_purge" android:id="@+id/nav_wallet_purge"
android:name="com.tangem.devkit.ucase.ui.WalletPurgeActionFragment" android:name="com.tangem.devkit.ucase.variants.purgewallet.ui.PurgeWalletActionFragment"
android:label="@string/action_wallet_purge" android:label="@string/action_wallet_purge"
tools:layout="@layout/fg_action_wallet_purge" /> tools:layout="@layout/fg_base_action_layout" />
<fragment <fragment
android:id="@+id/nav_card_response" android:id="@+id/nav_card_response"

View file

@ -14,10 +14,17 @@
<string name="error_nothing_to_load">Nothing to load</string> <string name="error_nothing_to_load">Nothing to load</string>
<string name="fg_name_entry_point">@string/app_name</string> <string name="fg_name_entry_point">@string/app_name</string>
<string name="fg_name_response_scan">Read response</string> <string name="fg_name_response_scan">Read Response</string>
<string name="fg_name_response_sign">Sign response</string> <string name="fg_name_response_sign">Sign Response</string>
<string name="fg_name_response_personalization">Personalization response</string> <string name="fg_name_response_personalization">Personalization Response</string>
<string name="fg_name_response_depersonalization">Depersonalization response</string> <string name="fg_name_response_depersonalization">Depersonalization Response</string>
<string name="fg_name_response_create_wallet">Create Wallet Response</string>
<string name="fg_name_response_purge_wallet">Purge Wallet Response</string>
<string name="fg_name_response_read_issuer_data">Read Issuer Data Response</string>
<string name="fg_name_response_write_issuer_data">Write Issuer Data Response</string>
<string name="fg_name_response_read_issuer_extra_data">Read Issuer Extra Data Response</string>
<string name="fg_name_response_read_user_data">Read User Data Response</string>
<string name="fg_name_response_write_user_data">Write User Data Response</string>
<string name="switch_description">Docs</string> <string name="switch_description">Docs</string>

View file

@ -5,27 +5,28 @@
<string name="action_card_sign">Sign</string> <string name="action_card_sign">Sign</string>
<string name="action_personalize">Personalize</string> <string name="action_personalize">Personalize</string>
<string name="action_depersonalize">Depersonalize</string> <string name="action_depersonalize">Depersonalize</string>
<string name="action_wallet_create">Create wallet</string> <string name="action_wallet_create">Create Wallet</string>
<string name="action_wallet_purge">Purge wallet</string> <string name="action_wallet_purge">Purge Wallet</string>
<string name="action_issuer_read_data">Read issuer data</string> <string name="action_issuer_read_data">Read Issuer Data</string>
<string name="action_issuer_write_data">Write issuer data</string> <string name="action_issuer_write_data">Write Issuer Data</string>
<string name="action_issuer_read_ex_data">Read issuer extra data</string> <string name="action_issuer_read_ex_data">Read Issuer Extra Data</string>
<string name="action_issuer_write_ex_data">Write issuer extra data</string> <string name="action_issuer_write_ex_data">Write Issuer Extra Data</string>
<string name="action_user_read_data">Read user data</string> <string name="action_user_read_data">Read User Data</string>
<string name="action_user_write_data">Write user data</string> <string name="action_user_write_data">Write User Data</string>
<string name="info_action_card_scan">This 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</string> <string name="info_action_card_scan">This 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</string>
<string name="info_action_card_sign">Depending on Signing_Method parameter defined during personalization, this command signs data using Wallet_PrivateKey</string> <string name="info_action_card_sign">Depending on Signing_Method parameter defined during personalization, this command signs data using Wallet_PrivateKey</string>
<string name="info_action_personalize">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.</string> <string name="info_action_personalize">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.</string>
<string name="info_action_depersonalize">This command reset card to initial state - erase all data, writed during previous personalization and previous usage. <string name="info_action_depersonalize">This command reset card to initial state - erase all data, writed during previous personalization and previous usage.
\nCommand available on SDK cards only</string> \nCommand available on SDK cards only</string>
<string name="info_action_wallet_create">SomeDescription</string> <string name="info_action_wallet_create">If the card has an Empty state, this command will create a new wallet, changing a state to Loaded.</string>
<string name="info_action_wallet_purge">SomeDescription</string> <string name="info_action_wallet_purge">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.</string>
<string name="info_action_issuer_read_data">SomeDescription</string> <string name="info_action_issuer_read_data">This command returns 512-byte Issuer Data field and its issuers signature.</string>
<string name="info_action_issuer_write_data">SomeDescription</string> <string name="info_action_issuer_write_data">This command writes 512-byte Issuer Data field and its issuers signature to the card.</string>
<string name="info_action_issuer_read_ex_data">SomeDescription</string> <string name="info_action_issuer_read_ex_data">This command retrieves Issuer Extra Data field and its issuers signature.</string>
<string name="info_action_issuer_write_ex_data">SomeDescription</string> <string name="info_action_issuer_write_ex_data">This command writes Issuer Extra Data field and its issuers signature to the card.</string>
<string name="info_action_user_read_data">SomeDescription</string> <string name="info_action_user_read_data">This command returns User Data and User Protected Data (up to 512-byte each) and two counters: User Counter and User Protected Counter.</string>
<string name="info_action_user_write_data">SomeDescription</string> <string name="info_action_user_write_data"> This command writes to the card any of User Data, User Protected Data, User Counter and User Protected Counter fields.
</string>
</resources> </resources>

View file

@ -155,4 +155,27 @@
<string name="info_response_depersonalize_is_success">Is success</string> <string name="info_response_depersonalize_is_success">Is success</string>
<!-- Field names - Response: Read Issuer (Extra) Data -->
<string name="response_issuer_data_size">Size of Issuer Data</string>
<string name="response_issuer_data">Issuer Data</string>
<string name="response_issuer_data_signature">Issuer Data Signature</string>
<string name="response_issuer_data_counter">Issuer Data Counter</string>
<string name="info_response_issuer_data_size">A size of issuer extra data written on a card.</string>
<string name="info_response_issuer_data">Data defined by the issuer.</string>
<string name="info_response_issuer_data_signature">Issuer data, signed by the issuer with issuer data private key.</string>
<string name="info_response_issuer_data_counter">An optional counter that protects issuer data against replay attack.</string>
<!-- Field names - Response: Read User Data -->
<string name="response_user_data">User Data</string>
<string name="response_user_protected_data">Protected User Data</string>
<string name="response_user_data_counter">User Data Counter</string>
<string name="response_user_data_protected_counter">Protected User Data Counter</string>
<string name="info_response_user_data">Data defined by user\'s App</string>
<string name="info_response_user_protected_data">Data defined by users App (confirmed by PIN2)</string>
<string name="info_response_user_data_counter">Counter initialized by users App and increased on every signing of new transaction</string>
<string name="info_response_user_data_protected_counter">Counter initialized by users App (confirmed by PIN2) and increased on every signing of new transaction</string>
</resources> </resources>

View file

@ -3,8 +3,16 @@
<string name="tlv_card_id">Card Id (CID)</string> <string name="tlv_card_id">Card Id (CID)</string>
<string name="tlv_transaction_out_hash">Hash to sign</string> <string name="tlv_transaction_out_hash">Hash to sign</string>
<string name="tlv_issuer_data">Issuer Data</string>
<string name="tlv_user_data">User Data</string>
<string name="tlv_user_protected_data">Protected User Data</string>
<string name="tlv_counter">Counter</string>
<string name="info_tlv_card_id">Unique Tangem card ID number</string> <string name="info_tlv_card_id">Unique Tangem card ID number</string>
<string name="info_tlv_transaction_out_hash">Hashed transaction you want to be signed </string> <string name="info_tlv_transaction_out_hash">Hashed transaction you want to be signed </string>
<string name="info_tlv_issuer_data">Issuer Data to be written by this command</string>
<string name="info_tlv_user_data">User Data to be written by this command</string>
<string name="info_tlv_protected_user_data">Protected User Data to be written by this command</string>
<string name="info_tlv_counter">Counter that protects this type of data against replay attack.</string>
</resources> </resources>