diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 48893c85b7..49dd20b24c 100644 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ diff --git a/.idea/misc.xml b/.idea/misc.xml index cc51e58ed3..c0f68eddd7 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -25,7 +25,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index d7ffe39557..ea315c4fb5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -61,6 +61,7 @@ dependencies { implementation 'org.bitcoinj:bitcoinj-core:0.14.4' implementation 'me.dm7.barcodescanner:zxing:1.9.8' implementation 'info.hoang8f:android-segmented:1.0.6' + implementation 'com.google.code.gson:gson:2.8.5' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' diff --git a/app/src/main/java/com/tangem/data/network/request/VerificationServerProtocol.java b/app/src/main/java/com/tangem/data/network/request/VerificationServerProtocol.java new file mode 100644 index 0000000000..0b008ac56d --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/request/VerificationServerProtocol.java @@ -0,0 +1,193 @@ +package com.tangem.data.network.request; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.tangem.domain.wallet.TangemCard; +import com.tangem.util.Util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.lang.reflect.Type; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.sql.Date; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Locale; + +public class VerificationServerProtocol { + + public static class Request + { + public Request(CustomCommand command, Class answerClass) + { + this.command=command; + this.answerClass=answerClass; + } + public String error; + public CustomCommand command; + public CustomAnswer answer; + public Type answerClass; + + public void doPost(String hostURL) throws IOException { + URL url = new URL(hostURL+"/"+command.getURL()); + URLConnection con = url.openConnection(); + HttpURLConnection http = (HttpURLConnection) con; + try { + http.setConnectTimeout(30000); + http.setReadTimeout(30000); + http.setRequestMethod("POST"); // PUT is another valid option + http.setDoOutput(true); + + String sRequestBody = getGson().toJson(this.command); + + byte[] out = sRequestBody.getBytes(StandardCharsets.UTF_8); + int length = out.length; + + http.setFixedLengthStreamingMode(length); + http.setRequestProperty("Content-Type", "application/json"); + http.connect(); + try (OutputStream os = http.getOutputStream()) { + os.write(out); + } + try (InputStream is = http.getInputStream()) { + try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { + answer = getGson().fromJson(br, answerClass); + } + } + } + finally { + http.disconnect(); + } + } + } + + static abstract class CustomCommand { + public abstract String getURL(); + + public byte[] toBytes() + { + return getGson().toJson(this).getBytes(StandardCharsets.UTF_8); + } + + @Override + public String toString() { + return getGson().toJson(this); + } + + } + + static class CustomAnswer { + public String error; + + @Override + public String toString() { + return getGson().toJson(this); + } + } + + public static class Verify { + + static class RequestItem { + public String CID; + public String publicKey; + } + + static class Command extends CustomCommand { + public RequestItem[] requests; + + @Override + public String getURL() { + return "verify"; + } + } + + public static class ResultItem { + public ResultItem(RequestItem request) { + CID = request.CID; + } + + public String error; + public String CID; + public Boolean passed; + } + + public static class Answer extends CustomAnswer { + public ResultItem[] results; + } + + public static Request prepare(TangemCard card) + { + Command c=new Command(); + c.requests=new RequestItem[1]; + c.requests[0]=new RequestItem(); + c.requests[0].CID=Util.bytesToHex(card.getCID()); + c.requests[0].publicKey=Util.bytesToHex(card.getCardPublicKey()); + + return new Request(c,Answer.class); + } + } + + public static class Validate { + + static class RequestItem { + public String CID; + public int counter; + public String signature; + } + + static class Command extends CustomCommand { + public RequestItem[] requests; + + @Override + public String getURL() { + return "validate"; + } + } + + static class ResultItem { + public ResultItem(RequestItem request) { + CID = request.CID; + } + public String error; + public String CID; + public Integer previousCounter; + public Boolean passed; + } + + static class Answer extends CustomAnswer { + public ResultItem[] results; + } + + public Request prepare(TangemCard card, int ValidationCounter, byte[] ValidationSignature) + { + Command c=new Command(); + c.requests=new RequestItem[1]; + c.requests[0].CID= Util.bytesToHex(card.getCID()); + c.requests[0].counter=ValidationCounter; + c.requests[0].signature=Util.bytesToHex(ValidationSignature); + return new Request(new Command(),Answer.class); + } + } + + + public static Date strToDate(String date) throws ParseException { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US); + return new Date(formatter.parse(date).getTime()); + } + + public static String dateToStr(Date date) { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US); + return formatter.format(date); + } + + public static Gson getGson() { + return new GsonBuilder().create(); + } + +} diff --git a/app/src/main/java/com/tangem/data/network/task/VerificationServerTask.java b/app/src/main/java/com/tangem/data/network/task/VerificationServerTask.java new file mode 100644 index 0000000000..6e83e322ac --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/task/VerificationServerTask.java @@ -0,0 +1,42 @@ +package com.tangem.data.network.task; + +import android.os.AsyncTask; + +import com.tangem.data.network.request.VerificationServerProtocol; + +import java.util.ArrayList; +import java.util.List; + +public class VerificationServerTask extends AsyncTask> { + public static final String hostURL ="https://tangem-webapp.appspot.com"; + + public VerificationServerTask() { + + } + + protected List doInBackground(VerificationServerProtocol.Request... requests) { + List result = new ArrayList<>(); + for (int i = 0; i < requests.length; i++) { + result.add(requests[i]); + } + + for (VerificationServerProtocol.Request request : result) { + try { + request.doPost(hostURL); + } catch (Exception e) { + request.error = e.getMessage(); + if( request.error==null || request.error.isEmpty() ) + { + request.error = e.getClass().getName(); + } + } + } + + return result; + } + + public String getValidationNodeDescription() { + return hostURL; + } + + } diff --git a/app/src/main/java/com/tangem/data/network/task/loaded_wallet/UpdateWalletInfoTask.java b/app/src/main/java/com/tangem/data/network/task/loaded_wallet/UpdateWalletInfoTask.java index 5e5389de4d..d690db0021 100644 --- a/app/src/main/java/com/tangem/data/network/task/loaded_wallet/UpdateWalletInfoTask.java +++ b/app/src/main/java/com/tangem/data/network/task/loaded_wallet/UpdateWalletInfoTask.java @@ -19,6 +19,7 @@ import org.json.JSONException; import org.json.JSONObject; import java.lang.ref.WeakReference; +import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; @@ -69,20 +70,28 @@ public class UpdateWalletInfoTask extends ElectrumTask { String mWalletAddress = request.getParams().getString(0); Long confBalance = request.getResult().getLong("confirmed"); Long unconf = request.getResult().getLong("unconfirmed"); + loadedWallet.mCard.setBalanceRecieved(true); + if (sharedCounter != null) { + boolean notEqualBalance = !sharedCounter.UpdatePayload(new BigDecimal(String.valueOf(confBalance))); + if (notEqualBalance) + loadedWallet.mCard.setIsBalanceEqual(false); int counter = sharedCounter.requestCounter.incrementAndGet(); if (counter != 1) { continue; } } + loadedWallet.mCard.setBalanceConfirmed(confBalance); loadedWallet.mCard.setBalanceUnconfirmed(unconf); loadedWallet.mCard.setDecimalBalance(String.valueOf(confBalance)); + loadedWallet.mCard.setValidationNodeDescription(getValidationNodeDescription()); } catch (JSONException e) { if (sharedCounter != null) { int errCounter = sharedCounter.errorRequest.incrementAndGet(); + loadedWallet.mCard.incFailedBalanceRequestCounter(); if (errCounter >= sharedCounter.allRequest) { e.printStackTrace(); loadedWallet.errorOnUpdate(e.toString()); @@ -270,6 +279,7 @@ public class UpdateWalletInfoTask extends ElectrumTask { } else { if (sharedCounter != null) { int errCounter = sharedCounter.errorRequest.incrementAndGet(); + loadedWallet.mCard.incFailedBalanceRequestCounter(); if (errCounter >= sharedCounter.allRequest) { loadedWallet.errorOnUpdate(request.error); engine.SwitchNode(loadedWallet.mCard); @@ -285,6 +295,7 @@ public class UpdateWalletInfoTask extends ElectrumTask { } catch (JSONException e) { if (sharedCounter != null) { int errCounter = sharedCounter.errorRequest.incrementAndGet(); + loadedWallet.mCard.incFailedBalanceRequestCounter(); if (errCounter >= sharedCounter.allRequest) { e.printStackTrace(); loadedWallet.errorOnUpdate(e.toString()); diff --git a/app/src/main/java/com/tangem/domain/cardReader/CardProtocol.java b/app/src/main/java/com/tangem/domain/cardReader/CardProtocol.java index e7b3f1801b..063769f3ea 100644 --- a/app/src/main/java/com/tangem/domain/cardReader/CardProtocol.java +++ b/app/src/main/java/com/tangem/domain/cardReader/CardProtocol.java @@ -610,6 +610,10 @@ public class CardProtocol { mCard.setRemainingSignatures(readResult.getTagAsInt(TLV.Tag.TAG_RemainingSignatures)); + if (readResult != null && readResult.getTLV(TLV.Tag.TAG_SignedHashes) != null) { + mCard.setSignHashes(readResult.getTLV(TLV.Tag.TAG_SignedHashes).Value); + } + } else { mCard.setWallet("N/A"); } diff --git a/app/src/main/java/com/tangem/domain/wallet/BalanceValidator.java b/app/src/main/java/com/tangem/domain/wallet/BalanceValidator.java new file mode 100644 index 0000000000..0b99f9501c --- /dev/null +++ b/app/src/main/java/com/tangem/domain/wallet/BalanceValidator.java @@ -0,0 +1,163 @@ +package com.tangem.domain.wallet; + +import android.util.Pair; + +public class BalanceValidator { + private String firstLine; + private String secondLine; + private int score; + public String GetFirstLine() + { + return firstLine; + } + public String GetSecondLine() + { + if(score < 0) score = 0; + return score + "% safe. " + secondLine; + } + + public void Check(TangemCard card) + { + String defaultFirstLine = "Unknown balance."; + String successFirstLine = "Verified balance."; + + // rule 1 + if(!CheckOfflineBalance(card) && !CheckOnlineBalance(card)) { + score = 0; + secondLine = "Do not accept. Balance cannot be verified."; + firstLine = defaultFirstLine; + return; + } + // rule 2.a + if(!VerificationWalletKey(card)) { + score = 0; + secondLine = "Do not accept. Wallet verification failed."; + firstLine = defaultFirstLine; + return; + } + + + // rule 2.c + if(!CheckAttestationServiceResult(card)) { + score = 0; + secondLine = "Do not accept. Tangem Attestation service says card is not genuine."; + firstLine = successFirstLine; + return; + } + + // rule 3 + if(CheckOnlineBalance(card) && !NotConfirmTransaction(card)) + { + if(card.isBalanceRecieved() && card.isBalanceEqual()) { + score = 100; + firstLine = "Verified balance."; + secondLine += " Confirmed balance and banknote identity."; + } + + if(card.getFailedBalanceRequestCounter()!=0) { + score = 100 - 10 * card.getFailedBalanceRequestCounter(); + firstLine = "Verified balance."; + secondLine += " Not all nodes have returned balance. App is requesting more nodes to be 100% sure..."; + if(score <= 0) + return; + } + + if(card.isBalanceRecieved() && !card.isBalanceEqual()) { + score = 0; + firstLine = "Disputed balance."; + secondLine += " Cannot obtaine trusted balance at the moment. Try to tap and check this banknote later."; + return; + } + } + + // rule 7 + if(CheckOfflineBalance(card) && !CheckOnlineBalance(card)) + { + score = -10; + String firstLine = "Virified offline balance."; + secondLine += " Restore internet connection to be more confidient."; + if(score <= 0) + return; + } + + // rule 2.b + if(!CheckAttestationServiceAvailable(card)) + { + score = -15; + secondLine += "Card identity was not verified. Cannot reach attestation service."; + firstLine = successFirstLine; + if(score <= 0) + return; + } + + // rule 5 + if(IsLostSecondRead(card)) + { + score = -30; + secondLine += " Wallet and banknote keys were not verified (tap again)."; + if(score <= 0) + return; + } + + // rule 4 + if(!CheckSignHashes(card)) + { + score = -50; + secondLine = "Unguaranteed balance."; + firstLine += " Potential unsent transaction at the moment. Try to tap and check this banknote later."; + if(score <= 0) + return; + } + + // rule 6 + if(NotConfirmTransaction(card)) + { + score = -50; + String firstLine = "Unguaranted balance."; + secondLine = " Loading in progress. Wait for full confirmation in blockchain."; + if(score <= 0) + return; + } + + } + + boolean CheckOfflineBalance(TangemCard card) + { + return card.getOfflineBalance() != null; + } + + boolean CheckOnlineBalance(TangemCard card) + { + return card.isBalanceRecieved(); + } + + boolean VerificationWalletKey(TangemCard card) + { + return card.isWalletPublicKeyValid(); + } + + boolean CheckSignHashes(TangemCard card) + { + return card.getSignHashes()==null; + } + + boolean CheckAttestationServiceAvailable(TangemCard card) + { + return card.isOnlineVerified() != null; + } + + boolean CheckAttestationServiceResult(TangemCard card) + { + return card.isOnlineVerified() != null && card.isOnlineVerified() == true; + } + + boolean NotConfirmTransaction(TangemCard card) + { + return card.getBalanceUnconfirmed()!=0; + } + + boolean IsLostSecondRead(TangemCard card) + { + return card.isCodeConfirmed() != null; + } +} diff --git a/app/src/main/java/com/tangem/domain/wallet/Issuer.java b/app/src/main/java/com/tangem/domain/wallet/Issuer.java index cf37b68369..09fdafe5b0 100644 --- a/app/src/main/java/com/tangem/domain/wallet/Issuer.java +++ b/app/src/main/java/com/tangem/domain/wallet/Issuer.java @@ -117,4 +117,14 @@ public enum Issuer { return Issuer.Unknown; } + public static Issuer FindIssuer(String ID) { + Issuer[] issuers = Issuer.values(); + for (int i = 1; i < issuers.length; i++) { + if (issuers[i].ID.equals(ID)) { + return issuers[i]; + } + } + return Issuer.Unknown; + } + } diff --git a/app/src/main/java/com/tangem/domain/wallet/SharedData.java b/app/src/main/java/com/tangem/domain/wallet/SharedData.java index f9557360fe..d71e5a5bbe 100644 --- a/app/src/main/java/com/tangem/domain/wallet/SharedData.java +++ b/app/src/main/java/com/tangem/domain/wallet/SharedData.java @@ -1,5 +1,6 @@ package com.tangem.domain.wallet; +import java.math.BigDecimal; import java.util.concurrent.atomic.AtomicInteger; /** @@ -14,8 +15,32 @@ public class SharedData public int allRequest; public AtomicInteger errorRequest; + public BigDecimal payload; + public synchronized boolean UpdatePayload(BigDecimal value) + { + + if(payload == null || payload.compareTo(value)!=0) + { + boolean isChange = true; + if(payload == null) + isChange = false; + if(value!=BigDecimal.ZERO) + payload = value; + + return isChange; + } + + return false; + } + + public void SetErrorRequest(int val) + { + errorRequest = new AtomicInteger(val); + } + public SharedData(int requstCount) { + payload = BigDecimal.ZERO; allRequest = requstCount; errorRequest = new AtomicInteger(0); requestCounter = new AtomicInteger(0); diff --git a/app/src/main/java/com/tangem/domain/wallet/TangemCard.java b/app/src/main/java/com/tangem/domain/wallet/TangemCard.java index 68e0657d1d..aecd04d550 100644 --- a/app/src/main/java/com/tangem/domain/wallet/TangemCard.java +++ b/app/src/main/java/com/tangem/domain/wallet/TangemCard.java @@ -11,10 +11,10 @@ import com.tangem.wallet.R; import java.math.BigInteger; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; /** * Created by dvol on 16.07.2017. @@ -270,6 +270,42 @@ public class TangemCard { } } + private Boolean codeConfirmed; + public void setCodeConfirmed(Boolean codeConfirmed) { + this.codeConfirmed = codeConfirmed; + } + + public Boolean isCodeConfirmed() { + return codeConfirmed; + } + + private Boolean onlineVerified; + public void setOnlineVerified(Boolean verified) { + this.onlineVerified = verified; + } + + public Boolean isOnlineVerified() { + return onlineVerified; + } + + private Boolean onlineValidated; + public void setOnlineValidated(Boolean validated) { + this.onlineValidated = validated; + } + + public Boolean isOnlineValidated() { + return onlineValidated; + } + + private boolean balanceRecieved = false; + public boolean isBalanceRecieved() { + return balanceRecieved; + } + public void setBalanceRecieved(boolean balanceRecieved) + { + this.balanceRecieved = balanceRecieved; + } + public int getRemainingSignatures() { return remainingSignatures; } @@ -666,6 +702,31 @@ public class TangemCard { return pauseBeforePIN2; } + private AtomicInteger failedBalanceRequestCounter; + public int incFailedBalanceRequestCounter() + { + if(failedBalanceRequestCounter == null) + failedBalanceRequestCounter = new AtomicInteger(0); + return failedBalanceRequestCounter.incrementAndGet(); + } + public void resetFailedBalanceRequestCounter() + { + failedBalanceRequestCounter = new AtomicInteger(0); + } + public int getFailedBalanceRequestCounter() + { + if(failedBalanceRequestCounter == null) + return 0; + return failedBalanceRequestCounter.get(); + } + + Boolean balanceEqual; + public Boolean isBalanceEqual(){return balanceEqual;} + public void setIsBalanceEqual(boolean isEqual) + { + balanceEqual = isEqual; + } + private Integer settingsMask = null; public void setSettingsMask(int settingsMask) { @@ -1135,6 +1196,16 @@ public class TangemCard { return Denomination; } + public byte[] SignHashes; + public void setSignHashes(byte[] SignHashes) { + this.SignHashes = SignHashes; + } + + public byte[] getSignHashes() { + return SignHashes; + } + + public void clearDenomination() { Denomination = null; } @@ -1205,12 +1276,16 @@ public class TangemCard { if (encryptionMode != null) B.putString("EncryptionMode", encryptionMode.name()); if (issuer != null) B.putString("Issuer", issuer.name()); if (firmwareVersion != null) B.putString("FirmwareVersion", firmwareVersion); + if(balanceEqual != null) B.putBoolean("isBalanceEqual", balanceEqual); B.putString("BalanceDecimal", balanceDecimal); B.putString("BalanceDecimalAlter", balanceDecimalAlter); B.putBoolean("ManufacturerConfirmed", manufacturerConfirmed); B.putBoolean("CardPublicKeyValid", isCardPublicKeyValid()); B.putByteArray("CardPublicKey", getCardPublicKey()); + if(failedBalanceRequestCounter != null) + B.putInt("FailedBalance", failedBalanceRequestCounter.get()); + if(getSignHashes()!=null) B.putByteArray("SignHashes", getSignHashes()); B.putString("Wallet", wallet); B.putString("Error", error); B.putBoolean("WalletPublicKeyValid", isWalletPublicKeyValid()); @@ -1255,6 +1330,22 @@ public class TangemCard { B.putFloat("rate", rate); B.putFloat("rateAlter", rateAlter); B.putString("confirmTx", GetConfirmTXCount().toString(16)); + + if( codeConfirmed!=null ) + B.putBoolean("codeConfirmed", codeConfirmed); + + if( codeConfirmed!=null ) + B.putBoolean("codeConfirmed", codeConfirmed); + + if( onlineVerified!=null ) + B.putBoolean("onlineVerified", onlineVerified); + + B.putBoolean("balanceRecieved", balanceRecieved); + + + if( onlineValidated!=null ) + B.putBoolean("onlineValidated", onlineValidated); + } public void LoadFromBundle(Bundle B) { @@ -1292,12 +1383,17 @@ public class TangemCard { else encryptionMode = null; - if (B.containsKey("Issuer")) issuer = Issuer.valueOf(B.getString("Issuer")); + if(B.containsKey("SignHashes")) setSignHashes(B.getByteArray("SignHashes")); + + if(B.containsKey("FailedBalance")) failedBalanceRequestCounter = new AtomicInteger(B.getInt("FailedBalance")); + if (B.containsKey("Issuer")) issuer = Issuer.FindIssuer(B.getString("Issuer")); if (B.containsKey("FirmwareVersion")) firmwareVersion = B.getString("FirmwareVersion"); + if(B.containsKey("isBalanceEqual")) setIsBalanceEqual(B.getBoolean("isBalanceEqual")); cardPublicKeyValid = B.getBoolean("CardPublicKeyValid"); if (B.containsKey("CardPublicKey")) setCardPublicKey(B.getByteArray("CardPublicKey")); + if(B.containsKey("balanceRecieved")) setBalanceRecieved(B.getBoolean("balanceRecieved")); if (B.containsKey("BalanceConfirmed")) balanceConfirmed = B.getLong("BalanceConfirmed"); else balanceConfirmed = null; if (B.containsKey("BalanceUnconfirmed")) @@ -1364,38 +1460,27 @@ public class TangemCard { rateAlter = B.getFloat("rateAlter"); if (B.containsKey("confirmTx")) countConfirmTX = new BigInteger(B.getString("confirmTx"), 16); - } - private final static char[] hexArray = "0123456789ABCDEF".toCharArray(); - public static String bytesToHex(byte[] bytes) { - if (bytes == null) return ""; - char[] hexChars = new char[bytes.length * 2]; - for ( int j = 0; j < bytes.length; j++ ) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; - } - return new String(hexChars); + if( B.containsKey("codeConfirmed") ) + codeConfirmed=B.getBoolean("codeConfirmed"); + + if( B.containsKey("onlineVerified") ) + onlineVerified=B.getBoolean("onlineVerified"); + + if( B.containsKey("onlineValidated") ) + onlineValidated=B.getBoolean("onlineValidated"); } public int getCardImageResource() { switch (getBlockchainID()) { case "BTC": - if ( bytesToHex(getDenomination()).equals("40420F0000000000") ) - return R.drawable.card_btc001; - else if (bytesToHex(getDenomination()).equals("404B4C0000000000") ) - return R.drawable.card_btc005; - else - return R.drawable.card_default; + return R.drawable.card_btc001; - case "Token": - if (getTokenSymbol().equals("SEED")) - return R.drawable.card_seed; - else - return R.drawable.card_default; + case "ETH\\XTZ": + return R.drawable.card_seed; default: - return R.drawable.card_default; + return R.drawable.card_btc001; } } diff --git a/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.java b/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.java index 136b837345..73b588e5bd 100644 --- a/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.java +++ b/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.java @@ -272,6 +272,7 @@ public class ConfirmPaymentActivity extends AppCompatActivity implements NfcAdap Log.e("Build Fee error", ex.getMessage()); } + mCard.resetFailedBalanceRequestCounter(); SharedData sharedFee = new SharedData(SharedData.COUNT_REQUEST); progressBar.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.java b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.java index 705c882765..1e6dff7faf 100644 --- a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.java +++ b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.java @@ -36,12 +36,15 @@ import com.google.zxing.WriterException; import com.tangem.data.network.request.ElectrumRequest; import com.tangem.data.network.request.ExchangeRequest; import com.tangem.data.network.request.InfuraRequest; +import com.tangem.data.network.request.VerificationServerProtocol; +import com.tangem.data.network.task.VerificationServerTask; import com.tangem.data.network.task.loaded_wallet.ETHRequestTask; import com.tangem.data.network.task.loaded_wallet.RateInfoTask; import com.tangem.data.network.task.loaded_wallet.UpdateWalletInfoTask; import com.tangem.data.nfc.VerifyCardTask; import com.tangem.domain.cardReader.CardProtocol; import com.tangem.domain.cardReader.NfcManager; +import com.tangem.domain.wallet.BalanceValidator; import com.tangem.domain.wallet.Blockchain; import com.tangem.domain.wallet.CoinEngine; import com.tangem.domain.wallet.CoinEngineFactory; @@ -89,12 +92,12 @@ public class LoadedWallet extends Fragment implements SwipeRefreshLayout.OnRefre public SwipeRefreshLayout mSwipeRefreshLayout; private RelativeLayout rlProgressBar; - private TextView tvCardID, tvBalance, tvOffline, tvBalanceEquivalent, tvWallet, tvInputs, tvError, tvMessage, tvIssuer, tvBlockchain, tvValidationNode, tvHeader, tvCaution; + private TextView tvCardID, tvBalance, tvBalanceLine1, tvBalanceLine2,tvOffline, tvBalanceEquivalent, tvWallet, tvInputs, tvError, tvMessage, tvIssuer, tvBlockchain, tvValidationNode, tvHeader, tvCaution; private ProgressBar progressBar; private ImageView ivBlockchain, ivPIN, ivPIN2orSecurityDelay, ivDeveloperVersion; private AppCompatButton btnExtract; - public List updateTasks = new ArrayList<>(); + public List updateTasks = new ArrayList<>(); private boolean lastReadSuccess = true; private VerifyCardTask verifyCardTask = null; private int requestPIN2Count = 0; @@ -102,11 +105,48 @@ public class LoadedWallet extends Fragment implements SwipeRefreshLayout.OnRefre private String newPIN = "", newPIN2 = ""; private CardProtocol mCardProtocol; private int scanTimes = 0; + OnlineVerifyTask onlineVerifyTask; public LoadedWallet() { } + private class OnlineVerifyTask extends VerificationServerTask { + + @Override + protected void onCancelled() { + super.onCancelled(); + updateTasks.remove(this); + onlineVerifyTask=null; + if (updateTasks.size() == 0) mSwipeRefreshLayout.setRefreshing(false); + } + + @Override + protected void onPostExecute(List requests) { + super.onPostExecute(requests); + Log.i("OnlineVerifyTask", "onPostExecute[" + String.valueOf(updateTasks.size()) + "]"); + updateTasks.remove(this); + onlineVerifyTask=null; + + for (VerificationServerProtocol.Request request : requests) { + if (request.error == null) { + VerificationServerProtocol.Verify.Answer answer = (VerificationServerProtocol.Verify.Answer) request.answer; + if (answer.error == null) { + mCard.setOnlineVerified(answer.results[0].passed); + } else { + mCard.setOnlineVerified(null); + errorOnUpdate(request.error); + } + } else { + mCard.setOnlineVerified(null); + errorOnUpdate(request.error); + } + } + if (updateTasks.size() == 0) mSwipeRefreshLayout.setRefreshing(false); + } + } + + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -127,6 +167,8 @@ public class LoadedWallet extends Fragment implements SwipeRefreshLayout.OnRefre rlProgressBar = v.findViewById(R.id.rlProgressBar); ImageView ivTangemCard = v.findViewById(R.id.ivTangemCard); tvBalance = v.findViewById(R.id.tvBalance); + tvBalanceLine1 = v.findViewById(R.id.tvBalanceLine1); + tvBalanceLine2 = v.findViewById(R.id.tvBalanceLine2); tvOffline = v.findViewById(R.id.tvOffline); tvCardID = v.findViewById(R.id.tvCardID); tvWallet = v.findViewById(R.id.tvWallet); @@ -177,6 +219,12 @@ public class LoadedWallet extends Fragment implements SwipeRefreshLayout.OnRefre mSwipeRefreshLayout.postDelayed(this::onRefresh, 1000); } + if ( (mCard.isOnlineVerified()==null || !mCard.isOnlineVerified()) && onlineVerifyTask ==null) { + onlineVerifyTask = new OnlineVerifyTask(); + updateTasks.add(onlineVerifyTask); + onlineVerifyTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, VerificationServerProtocol.Verify.prepare(mCard)); + } + startVerify(lastTag); tvWallet.setText(mCard.getWallet()); @@ -266,7 +314,7 @@ public class LoadedWallet extends Fragment implements SwipeRefreshLayout.OnRefre @Override public void onStop() { super.onStop(); - for (UpdateWalletInfoTask ut : updateTasks) { + for (AsyncTask ut : updateTasks) { ut.cancel(true); } mNfcManager.onStop(); @@ -465,7 +513,9 @@ public class LoadedWallet extends Fragment implements SwipeRefreshLayout.OnRefre CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); if (mCard.getBlockchain() == Blockchain.Bitcoin || mCard.getBlockchain() == Blockchain.BitcoinTestNet) { + mCard.resetFailedBalanceRequestCounter(); SharedData data = new SharedData(SharedData.COUNT_REQUEST); + mCard.resetFailedBalanceRequestCounter(); for (int i = 0; i < data.allRequest; ++i) { String nodeAddress = Objects.requireNonNull(engine).GetNextNode(mCard); int nodePort = engine.GetNextNodePort(mCard); @@ -483,6 +533,7 @@ public class LoadedWallet extends Fragment implements SwipeRefreshLayout.OnRefre } else if (mCard.getBlockchain() == Blockchain.BitcoinCash || mCard.getBlockchain() == Blockchain.BitcoinCashTestNet) { + mCard.resetFailedBalanceRequestCounter(); SharedData data = new SharedData(SharedData.COUNT_REQUEST); for (int i = 0; i < data.allRequest; ++i) { String nodeAddress = Objects.requireNonNull(engine).GetNextNode(mCard); @@ -689,6 +740,14 @@ public class LoadedWallet extends Fragment implements SwipeRefreshLayout.OnRefre CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); + if (mCard.getBlockchain() == Blockchain.Bitcoin || mCard.getBlockchain() == Blockchain.BitcoinTestNet) { + + BalanceValidator validator = new BalanceValidator(); + validator.Check(mCard); + tvBalanceLine1.setText(validator.GetFirstLine()); + tvBalanceLine2.setText(validator.GetSecondLine()); + } + if (engine.HasBalanceInfo(mCard) || mCard.getOfflineBalance() == null) { if (mCard.getBlockchain() == Blockchain.Token) { Spanned html = Html.fromHtml(engine.GetBalanceWithAlter(mCard)); diff --git a/app/src/main/java/com/tangem/presentation/fragment/Main.java b/app/src/main/java/com/tangem/presentation/fragment/Main.java index 8b0b879adf..563539be90 100644 --- a/app/src/main/java/com/tangem/presentation/fragment/Main.java +++ b/app/src/main/java/com/tangem/presentation/fragment/Main.java @@ -431,6 +431,7 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis taskRate.execute(rate); } else if (card.getBlockchain() == Blockchain.BitcoinTestNet || card.getBlockchain() == Blockchain.Bitcoin) { + card.resetFailedBalanceRequestCounter(); SharedData data = new SharedData(SharedData.COUNT_REQUEST); for (int i = 0; i < data.allRequest; ++i) { @@ -445,6 +446,7 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis } } else if (card.getBlockchain() == Blockchain.BitcoinCashTestNet || card.getBlockchain() == Blockchain.BitcoinCash) { + card.resetFailedBalanceRequestCounter(); SharedData data = new SharedData(SharedData.COUNT_REQUEST); for (int i = 0; i < data.allRequest; ++i) { @@ -515,6 +517,7 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis taskRate.execute(rate); } else if (card.getBlockchain() == Blockchain.BitcoinTestNet || card.getBlockchain() == Blockchain.Bitcoin) { + card.resetFailedBalanceRequestCounter(); SharedData data = new SharedData(SharedData.COUNT_REQUEST); for (int i = 0; i < data.allRequest; ++i) { @@ -538,6 +541,7 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis taskRate.execute(rate); } else if (card.getBlockchain() == Blockchain.BitcoinCashTestNet || card.getBlockchain() == Blockchain.BitcoinCash) { + card.resetFailedBalanceRequestCounter(); SharedData data = new SharedData(SharedData.COUNT_REQUEST); for (int i = 0; i < data.allRequest; ++i) {