Updated on 2026-08-14
This commit is contained in:
commit
312c8aaf3f
11 changed files with 134 additions and 29 deletions
|
|
@ -8,6 +8,7 @@ import android.nfc.NfcAdapter
|
|||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
|
@ -231,6 +232,8 @@ class IdFragment : BaseFragment(), NfcAdapter.ReaderCallback,
|
|||
mpSecondScanSound.start()
|
||||
}
|
||||
} else {
|
||||
Log.d(this.javaClass.simpleName,
|
||||
"Error while reading ID card: ${cardProtocol.error.localizedMessage}")
|
||||
// remove last UIDs because of error and no card read
|
||||
FirebaseCrashlytics.getInstance().recordException(cardProtocol.error)
|
||||
rlProgressBar?.post {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import android.os.Bundle
|
|||
import android.os.Parcelable
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.RadioButton
|
||||
|
|
@ -22,7 +23,6 @@ import com.tangem.wallet.ethID.EthIdEngine
|
|||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.android.synthetic.main.fragment_issue_new_id.*
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.lang.Exception
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
|
|
@ -54,6 +54,8 @@ class IssueNewIdFragment : BaseFragment(), NavigationResultListener {
|
|||
try {
|
||||
processData()
|
||||
} catch (exception: Exception) {
|
||||
Log.e(this.javaClass.simpleName,
|
||||
"Exception while processing data: ${exception.localizedMessage}")
|
||||
Toast.makeText(context, "Check for correctness of data", Toast.LENGTH_LONG).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
|
@ -123,6 +125,9 @@ class IssueNewIdFragment : BaseFragment(), NavigationResultListener {
|
|||
val inputs = "$name $lastName;$birthDate${gender.toString()}"
|
||||
val info = inputs.toByteArray() + photoInBytes!!
|
||||
ctx.card.idHash = Util.calculateSHA256(info)
|
||||
|
||||
Log.d(this.javaClass.simpleName,
|
||||
"ID info provided: $inputs,\n photo of size ${photoInBytes?.size}")
|
||||
}
|
||||
|
||||
private fun convertDate(inputDate: CharSequence): String {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import android.nfc.NfcAdapter
|
|||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
|
|
@ -114,6 +115,10 @@ class ValidateIdFragment : BaseFragment(), NavigationResultListener,
|
|||
val data = Bundle()
|
||||
ctx.saveToBundle(data)
|
||||
data.putByteArray(Constant.EXTRA_TX, tx)
|
||||
|
||||
Log.d(this.javaClass.simpleName,
|
||||
"Transaction is ready to be sent.")
|
||||
|
||||
navigateForResult(
|
||||
Constant.REQUEST_CODE_SEND_TRANSACTION_,
|
||||
R.id.action_validateIdFragment_to_writeIdFragment,
|
||||
|
|
@ -161,6 +166,8 @@ class ValidateIdFragment : BaseFragment(), NavigationResultListener,
|
|||
}
|
||||
|
||||
PINStorage.setPIN2(PINStorage.getDefaultPIN2())
|
||||
Log.d(this.javaClass.simpleName,
|
||||
"Ready to sing transaction, launching OneTouchSignTask")
|
||||
signTransactionTask = OneTouchSignTask(NfcReader((activity as MainActivity).nfcManager, isoDep),
|
||||
App.localStorage, App.pinStorage, this@ValidateIdFragment, transaction)
|
||||
signTransactionTask?.start()
|
||||
|
|
@ -216,6 +223,8 @@ class ValidateIdFragment : BaseFragment(), NavigationResultListener,
|
|||
|
||||
mpFinishSignSound.start()
|
||||
} else {
|
||||
Log.d(this.javaClass.simpleName,
|
||||
"Error while signing with ID Issuer card: ${cardProtocol.error.localizedMessage}")
|
||||
FirebaseCrashlytics.getInstance().recordException(cardProtocol.error)
|
||||
lastReadSuccess = false
|
||||
if (cardProtocol.error.javaClass == CardProtocol.TangemException_InvalidPIN::class.java) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import android.nfc.NfcAdapter
|
|||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
|
|
@ -186,6 +187,8 @@ class WriteIdFragment : BaseFragment(), NavigationResultListener,
|
|||
)
|
||||
|
||||
} else {
|
||||
Log.d(this.javaClass.simpleName,
|
||||
"Error while writing on ID card: ${cardProtocol.error}")
|
||||
lastReadSuccess = false
|
||||
FirebaseCrashlytics.getInstance().recordException(cardProtocol.error)
|
||||
if (cardProtocol.error.javaClass == CardProtocol.TangemException_InvalidPIN::class.java) {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ import java.security.NoSuchAlgorithmException;
|
|||
import java.security.NoSuchProviderException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.CompletableObserver;
|
||||
import io.reactivex.SingleObserver;
|
||||
|
|
@ -425,8 +424,6 @@ public class BtcCashEngine extends CoinEngine {
|
|||
@Override
|
||||
public byte[][] getHashesToSign() throws Exception {
|
||||
byte[][] dataForSign = new byte[unspentOutputs.size()][];
|
||||
if (txForSign.length > 10)
|
||||
throw new Exception("To much hashes in one transaction!");
|
||||
for (int i = 0; i < unspentOutputs.size(); ++i) {
|
||||
dataForSign[i] = bodyDoubleHash[i];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -517,8 +517,6 @@ public class BtcEngine extends CoinEngine {
|
|||
@Override
|
||||
public byte[][] getHashesToSign() throws Exception {
|
||||
byte[][] dataForSign = new byte[unspentOutputs.size()][];
|
||||
if (txForSign.length > 10)
|
||||
throw new Exception("To much hashes in one transaction!");
|
||||
for (int i = 0; i < unspentOutputs.size(); ++i) {
|
||||
dataForSign[i] = bodyDoubleHash[i];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,9 @@ import android.util.Log;
|
|||
|
||||
import com.tangem.App;
|
||||
import com.tangem.data.network.ServerApiBitcore;
|
||||
import com.tangem.data.network.ServerApiInsight;
|
||||
import com.tangem.data.network.model.BitcoreBalanceAndUnspents;
|
||||
import com.tangem.data.network.model.BitcoreSendResponse;
|
||||
import com.tangem.data.network.model.BitcoreUtxo;
|
||||
import com.tangem.data.network.model.InsightResponse;
|
||||
import com.tangem.data.network.model.InsightUtxo;
|
||||
import com.tangem.tangem_card.data.TangemCard;
|
||||
import com.tangem.tangem_card.reader.CardProtocol;
|
||||
import com.tangem.tangem_card.tasks.SignTask;
|
||||
|
|
@ -39,7 +36,6 @@ import java.security.NoSuchAlgorithmException;
|
|||
import java.security.NoSuchProviderException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.SingleObserver;
|
||||
import io.reactivex.observers.DisposableSingleObserver;
|
||||
|
|
@ -449,8 +445,6 @@ public class DucatusEngine extends BtcEngine {
|
|||
@Override
|
||||
public byte[][] getHashesToSign() throws Exception {
|
||||
byte[][] dataForSign = new byte[unspentOutputs.size()][];
|
||||
if (txForSign.length > 10)
|
||||
throw new Exception("To much hashes in one transaction!");
|
||||
for (int i = 0; i < unspentOutputs.size(); ++i) {
|
||||
dataForSign[i] = bodyDoubleHash[i];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -277,6 +277,9 @@ public class EthIdEngine extends CoinEngine {
|
|||
@Override
|
||||
public SignTask.TransactionToSign constructTransaction(Amount amountValue, Amount feeValue, boolean IncFee, String targetAddress) {
|
||||
|
||||
Log.d(this.getClass().getSimpleName(),
|
||||
"Constructing transaction for signature");
|
||||
|
||||
final EthTransaction tx = constructIdTxForSign(coinData.getApprovalAddressNonce());
|
||||
|
||||
return new SignTask.TransactionToSign() {
|
||||
|
|
@ -309,6 +312,9 @@ public class EthIdEngine extends CoinEngine {
|
|||
|
||||
@Override
|
||||
public byte[] onSignCompleted(byte[] signFromCard) throws Exception {
|
||||
Log.d(this.getClass().getSimpleName(),
|
||||
"Transaction has been signed, now preparing transaction to send");
|
||||
|
||||
byte[] for_hash = tx.getRawHash();
|
||||
BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32));
|
||||
BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64));
|
||||
|
|
@ -330,6 +336,8 @@ public class EthIdEngine extends CoinEngine {
|
|||
Log.e(this.getClass().getSimpleName(), " V: " + String.valueOf(v));
|
||||
|
||||
byte[] txForSend = tx.getEncoded();
|
||||
Log.d(this.getClass().getSimpleName(),
|
||||
"Tx is ready to be sent");
|
||||
notifyOnNeedSendTransaction(txForSend);
|
||||
return txForSend;
|
||||
}
|
||||
|
|
@ -434,9 +442,13 @@ public class EthIdEngine extends CoinEngine {
|
|||
if (method.equals(ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION)) {
|
||||
if (infuraResponse.getResult() == null || infuraResponse.getResult().isEmpty()) {
|
||||
ctx.setError("Rejected by node: " + infuraResponse.getError());
|
||||
Log.d(this.getClass().getSimpleName(),
|
||||
"Rejected by node:" + infuraResponse.getError());
|
||||
blockchainRequestsCallbacks.onComplete(false);
|
||||
} else {
|
||||
ctx.setError(null);
|
||||
Log.d(this.getClass().getSimpleName(),
|
||||
"Transaction has been sent successfully");
|
||||
blockchainRequestsCallbacks.onComplete(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -444,6 +456,8 @@ public class EthIdEngine extends CoinEngine {
|
|||
|
||||
@Override
|
||||
public void onFail(String method, String message) {
|
||||
Log.d(this.getClass().getSimpleName(),
|
||||
"requestSendTransaction failed with message: " + message);
|
||||
if (method.equals(ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION)) {
|
||||
ctx.setError(message);
|
||||
blockchainRequestsCallbacks.onComplete(false);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.tangem.wallet.BTCUtils;
|
|||
import com.tangem.wallet.BalanceValidator;
|
||||
import com.tangem.wallet.Base58;
|
||||
import com.tangem.wallet.CoinData;
|
||||
import com.tangem.wallet.CoinEngine;
|
||||
import com.tangem.wallet.R;
|
||||
import com.tangem.wallet.TangemContext;
|
||||
import com.tangem.wallet.Transaction;
|
||||
|
|
@ -348,7 +349,7 @@ public class LtcEngine extends BtcEngine {
|
|||
|
||||
@Override
|
||||
public Amount convertToAmount(InternalAmount internalAmount) {
|
||||
BigDecimal d = internalAmount.divide(new BigDecimal("100000000"));
|
||||
BigDecimal d = internalAmount.divide(new BigDecimal("100000000")).setScale(getDecimals(), RoundingMode.DOWN);
|
||||
return new Amount(d, getBalanceCurrency());
|
||||
}
|
||||
|
||||
|
|
@ -450,8 +451,6 @@ public class LtcEngine extends BtcEngine {
|
|||
@Override
|
||||
public byte[][] getHashesToSign() throws Exception {
|
||||
byte[][] dataForSign = new byte[unspentOutputs.size()][];
|
||||
if (txForSign.length > 10)
|
||||
throw new Exception("To much hashes in one transaction!");
|
||||
for (int i = 0; i < unspentOutputs.size(); ++i) {
|
||||
dataForSign[i] = bodyDoubleHash[i];
|
||||
}
|
||||
|
|
@ -623,12 +622,74 @@ public class LtcEngine extends BtcEngine {
|
|||
}
|
||||
}
|
||||
|
||||
private final static BigDecimal relayFee = new BigDecimal(0.00001).setScale(8, RoundingMode.DOWN);
|
||||
private final static Amount relayFee = new Amount(new BigDecimal(0.00001).setScale(8, RoundingMode.DOWN), "LTC");
|
||||
|
||||
private void forceRelayFeeMinimum(Amount fee) {
|
||||
if (fee.compareTo(relayFee) < 0) {
|
||||
fee = relayFee;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception {
|
||||
coinData.minFee = coinData.normalFee = coinData.maxFee = new Amount(relayFee, "LTC");
|
||||
blockchainRequestsCallbacks.onComplete(true);
|
||||
final int calcSize = calculateEstimatedTransactionSize(targetAddress, amount.toValueString());
|
||||
Log.e(TAG, String.format("Estimated tx size %d", calcSize));
|
||||
coinData.minFee = null;
|
||||
coinData.maxFee = null;
|
||||
coinData.normalFee = null;
|
||||
|
||||
final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher();
|
||||
|
||||
ServerApiBlockcypher.ResponseListener blockcypherListener = new ServerApiBlockcypher.ResponseListener() {
|
||||
@Override
|
||||
public void onSuccess(String method, BlockcypherResponse blockcypherResponse) {
|
||||
Log.e(TAG, "Wrong response type for requestFee");
|
||||
ctx.setError("Wrong response type for requestFee");
|
||||
blockchainRequestsCallbacks.onComplete(false);
|
||||
}
|
||||
|
||||
public void onSuccess(String method, BlockcypherFee blockcypherFee) {
|
||||
Log.i(TAG, "onSuccess: " + method);
|
||||
try {
|
||||
BigDecimal minByteFee = new BigDecimal(blockcypherFee.getLow_fee_per_kb()).divide(BigDecimal.valueOf(1024));
|
||||
BigDecimal normalByteFee = new BigDecimal(blockcypherFee.getMedium_fee_per_kb()).divide(BigDecimal.valueOf(1024));
|
||||
BigDecimal maxByteFee = new BigDecimal(blockcypherFee.getHigh_fee_per_kb()).divide(BigDecimal.valueOf(1024));
|
||||
|
||||
CoinEngine.InternalAmount minIntAmount = new CoinEngine.InternalAmount(minByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi");
|
||||
CoinEngine.InternalAmount normalIntAmount = new CoinEngine.InternalAmount(normalByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi");
|
||||
CoinEngine.InternalAmount maxIntAmount = new CoinEngine.InternalAmount(maxByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi");
|
||||
|
||||
coinData.minFee = convertToAmount(minIntAmount);
|
||||
coinData.normalFee = convertToAmount(normalIntAmount);
|
||||
coinData.maxFee = convertToAmount(maxIntAmount);
|
||||
|
||||
forceRelayFeeMinimum(coinData.minFee);
|
||||
forceRelayFeeMinimum(coinData.normalFee);
|
||||
forceRelayFeeMinimum(coinData.maxFee);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "FAIL BLOCKCYPHER_FEE Exception");
|
||||
}
|
||||
|
||||
if (serverApiBlockcypher.isRequestsSequenceCompleted()) {
|
||||
blockchainRequestsCallbacks.onComplete(!ctx.hasError());
|
||||
} else {
|
||||
blockchainRequestsCallbacks.onProgress();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFail(String method, String message) {
|
||||
Log.i(TAG, "onFail: " + method + " " + message);
|
||||
ctx.setError(message);
|
||||
blockchainRequestsCallbacks.onComplete(false);
|
||||
}
|
||||
};
|
||||
|
||||
serverApiBlockcypher.setResponseListener(blockcypherListener);
|
||||
|
||||
serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_FEE, "", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -677,9 +738,4 @@ public class LtcEngine extends BtcEngine {
|
|||
|
||||
serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_SEND, "", txStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowSelectFeeLevel() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,8 @@ import java.io.ByteArrayOutputStream;
|
|||
public class SignTask extends CustomReadCardTask {
|
||||
public static final String TAG = SignTask.class.getSimpleName();
|
||||
|
||||
private static final int MAX_HASHES_TO_SIGN = 10;
|
||||
|
||||
/**
|
||||
* Transaction Engine request/notifications during sign process
|
||||
*/
|
||||
|
|
@ -58,13 +60,31 @@ public class SignTask extends CustomReadCardTask {
|
|||
TLVList signResult;
|
||||
switch (mCard.getSigningMethod()) {
|
||||
case Sign_Hash:
|
||||
signResult = protocol.run_SignHashes(pinsProvider.getPIN2(), transactionToSign.getHashesToSign(), null, null, null);
|
||||
break;
|
||||
byte[][] remainingHashes = transactionToSign.getHashesToSign();
|
||||
ByteArrayOutputStream signatures = new ByteArrayOutputStream();
|
||||
while (remainingHashes.length > 0) {
|
||||
byte[][] hashesToSign;
|
||||
if (remainingHashes.length <= MAX_HASHES_TO_SIGN) {
|
||||
hashesToSign = remainingHashes.clone();
|
||||
remainingHashes = new byte[0][];
|
||||
} else {
|
||||
hashesToSign = new byte[MAX_HASHES_TO_SIGN][];
|
||||
System.arraycopy(remainingHashes, 0, hashesToSign, 0, MAX_HASHES_TO_SIGN);
|
||||
remainingHashes = removeFirstTenEntries(remainingHashes);
|
||||
}
|
||||
|
||||
signResult = protocol.run_SignHashes(pinsProvider.getPIN2(), hashesToSign, null, null, null);
|
||||
signatures.write(signResult.getTLV(TLV.Tag.TAG_Signature).Value);
|
||||
}
|
||||
mNotifications.onReadProgress(protocol, 100);
|
||||
transactionToSign.onSignCompleted(signatures.toByteArray());
|
||||
return;
|
||||
|
||||
case Sign_Hash_Validated_By_Issuer:
|
||||
case Sign_Hash_Validated_By_Issuer_And_WriteIssuerData:
|
||||
ByteArrayOutputStream bs = new ByteArrayOutputStream();
|
||||
byte[][] hashes = transactionToSign.getHashesToSign();
|
||||
if (hashes.length > 10) throw new CardProtocol.TangemException("To much hashes in one transaction!");
|
||||
if (hashes.length > MAX_HASHES_TO_SIGN) throw new CardProtocol.TangemException("To much hashes in one transaction!");
|
||||
for (int i = 0; i < hashes.length; i++) {
|
||||
if (i != 0 && hashes[0].length != hashes[i].length)
|
||||
throw new CardProtocol.TangemException("Hashes length must be identical!");
|
||||
|
|
@ -89,4 +109,10 @@ public class SignTask extends CustomReadCardTask {
|
|||
mNotifications.onReadProgress(protocol, 100);
|
||||
|
||||
}
|
||||
|
||||
private byte[][] removeFirstTenEntries(byte[][] array) {
|
||||
byte[][] temp = new byte[array.length - MAX_HASHES_TO_SIGN][];
|
||||
System.arraycopy(array, MAX_HASHES_TO_SIGN, temp, 0, temp.length);
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package com.tangem.tangem_sdk_new
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.app.Activity
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.View
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.tangem.Log
|
||||
import com.tangem.LoggerInterface
|
||||
|
|
@ -25,7 +25,7 @@ import kotlinx.android.synthetic.main.nfc_bottom_sheet.*
|
|||
*/
|
||||
class DefaultSessionViewDelegate(private val reader: NfcReader) : SessionViewDelegate {
|
||||
|
||||
lateinit var activity: FragmentActivity
|
||||
lateinit var activity: Activity
|
||||
private var readingDialog: BottomSheetDialog? = null
|
||||
|
||||
init {
|
||||
|
|
@ -38,7 +38,7 @@ class DefaultSessionViewDelegate(private val reader: NfcReader) : SessionViewDel
|
|||
if (!reader.nfcEnabled) NfcEnableDialog().show(activity)
|
||||
}
|
||||
|
||||
private fun showReadingDialog(activity: FragmentActivity, cardId: String?, message: Message?) {
|
||||
private fun showReadingDialog(activity: Activity, cardId: String?, message: Message?) {
|
||||
val dialogView = activity.layoutInflater.inflate(R.layout.nfc_bottom_sheet, null)
|
||||
readingDialog = BottomSheetDialog(activity)
|
||||
readingDialog?.setContentView(dialogView)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue