From ef0a5f379d8832a954e75609b961cbcb933c162b Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 12 Dec 2018 11:24:53 +0300 Subject: [PATCH 1/7] Updated on 2026-08-14 --- .../tangem/data/network/ElectrumRequest.java | 4 +- .../data/network/ServerApiElectrum.java | 18 ++ .../com/tangem/domain/wallet/CoinEngine.java | 16 +- .../domain/wallet/bch/BtcCashEngine.java | 13 ++ .../tangem/domain/wallet/btc/BtcEngine.java | 197 ++++++++++++++++- .../presentation/fragment/LoadedWallet.kt | 198 ++++++++++-------- 6 files changed, 338 insertions(+), 108 deletions(-) diff --git a/app/src/main/java/com/tangem/data/network/ElectrumRequest.java b/app/src/main/java/com/tangem/data/network/ElectrumRequest.java index 4c3f05a3c2..7fc556cf62 100644 --- a/app/src/main/java/com/tangem/data/network/ElectrumRequest.java +++ b/app/src/main/java/com/tangem/data/network/ElectrumRequest.java @@ -143,8 +143,8 @@ public class ElectrumRequest { return ""; } - public boolean isMethod(String methodName) throws JSONException { - return jsRequestData.getString("method").equals(methodName); + public boolean isMethod(String methodName) { + return getMethod().equals(methodName); } public JSONArray getParams() throws JSONException { diff --git a/app/src/main/java/com/tangem/data/network/ServerApiElectrum.java b/app/src/main/java/com/tangem/data/network/ServerApiElectrum.java index 227a6d9812..19b0404fed 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiElectrum.java +++ b/app/src/main/java/com/tangem/data/network/ServerApiElectrum.java @@ -52,6 +52,21 @@ public class ServerApiElectrum { private String host; private int port; + private int requestsCount=0; + + public boolean hasRequests() { + return requestsCount>0; + } + + private String error=null; + public boolean isErrorOccured() { + return error!=null; + } + + public void setErrorOccured(String error) { + this.error=error; + } + public interface ElectrumRequestDataListener { void onSuccess(ElectrumRequest electrumRequest); @@ -63,6 +78,7 @@ public class ServerApiElectrum { } public void electrumRequestData(TangemContext ctx, ElectrumRequest electrumRequest) { + requestsCount++; Observable checkElectrumDataObserver = Observable.just(electrumRequest) .doOnNext(electrumRequest1 -> doElectrumRequest(ctx, electrumRequest)) @@ -84,6 +100,7 @@ public class ServerApiElectrum { @Override public void onNext(ElectrumRequest v) { if (electrumRequest.answerData != null) { + requestsCount--; electrumRequestDataListener.onSuccess(electrumRequest); // Log.i(TAG, "electrumRequestData " + electrumRequest.getMethod() + " onNext != null"); } else { @@ -282,4 +299,5 @@ public class ServerApiElectrum { return "Electrum, " + host + ":" + String.valueOf(port); } + } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java index f97c7c217e..ee179d4a5c 100644 --- a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java @@ -3,7 +3,6 @@ package com.tangem.domain.wallet; import android.net.Uri; import android.text.InputFilter; -import com.tangem.data.Blockchain; import com.tangem.tangemcard.reader.CardProtocol; import com.tangem.tangemcard.tasks.SignTask; @@ -236,12 +235,7 @@ public abstract class CoinEngine { public void defineWallet() throws CardProtocol.TangemException { try { - String wallet; - if (ctx.getBlockchain() == Blockchain.BitcoinCash) { - wallet = calculateAddress(ctx.getCard().getWalletPublicKeyRar()); - } else { - wallet = calculateAddress(ctx.getCard().getWalletPublicKey()); - } + String wallet = calculateAddress(ctx.getCard().getWalletPublicKey()); ctx.getCoinData().setWallet(wallet); } catch (Exception e) @@ -270,4 +264,12 @@ public abstract class CoinEngine { onNeedSendPayment.onPaymentPrepared(txForSend); } + + + public interface BlockchainRequestsNotifications + { + void onComplete(Boolean success); + boolean needTerminate(); + } + public abstract void requestBalanceAndUnspentTransactions(BlockchainRequestsNotifications blockchainRequestsNotifications); } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java b/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java index ae60967b3c..52bb1f6f65 100644 --- a/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java @@ -431,6 +431,19 @@ public class BtcCashEngine extends CoinEngine { // return mCard.getAmountDescription(Double.parseDouble(amount)); // } + @Override + public void defineWallet() throws CardProtocol.TangemException { + try { + String wallet = calculateAddress(ctx.getCard().getWalletPublicKeyRar()); + ctx.getCoinData().setWallet(wallet); + } + catch (Exception e) + { + ctx.getCoinData().setWallet("ERROR"); + throw new CardProtocol.TangemException("Can't define wallet address"); + } + } + @Override public SignTask.PaymentToSign constructPayment(Amount amountValue, Amount feeValue, boolean IncFee, String targetAddress) throws Exception { diff --git a/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java b/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java index 778a88d06d..c8fae61d76 100644 --- a/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java @@ -2,6 +2,7 @@ package com.tangem.domain.wallet.btc; import android.net.Uri; import android.text.InputFilter; +import android.util.Log; import com.tangem.tangemcard.reader.CardProtocol; import com.tangem.domain.wallet.BalanceValidator; @@ -20,6 +21,13 @@ import com.tangem.util.DecimalDigitsInputFilter; import com.tangem.util.DerEncodingUtil; import com.tangem.tangemcard.util.Util; import com.tangem.wallet.R; +import com.tangem.data.network.ElectrumRequest; +import com.tangem.data.network.ServerApiElectrum; + + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; import java.io.ByteArrayOutputStream; import java.math.BigDecimal; @@ -33,6 +41,8 @@ import java.util.List; public class BtcEngine extends CoinEngine { + private static final String TAG = BtcEngine.class.getSimpleName(); + public BtcData coinData = null; public BtcEngine(TangemContext context) throws Exception { @@ -431,8 +441,8 @@ public class BtcEngine extends CoinEngine { change = change - fees; } - final long amountFinal=amount; - final long changeFinal=change; + final long amountFinal = amount; + final long changeFinal = change; if (amount + fees > fullAmount) { throw new CardProtocol.TangemException_WrongAmount(String.format("Balance (%d) < change (%d) + amount (%d)", fullAmount, change, amount)); @@ -440,7 +450,7 @@ public class BtcEngine extends CoinEngine { final byte[][] txForSign = new byte[unspentOutputs.size()][]; final byte[][] bodyDoubleHash = new byte[unspentOutputs.size()][]; - final byte[][] bodyHash= new byte[unspentOutputs.size()][]; + final byte[][] bodyHash = new byte[unspentOutputs.size()][]; for (int i = 0; i < unspentOutputs.size(); ++i) { txForSign[i] = BTCUtils.buildTXForSign(myAddress, targetAddress, myAddress, unspentOutputs, i, amount, change); @@ -452,12 +462,12 @@ public class BtcEngine extends CoinEngine { @Override public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) { - return signingMethod==TangemCard.SigningMethod.Sign_Hash || signingMethod==TangemCard.SigningMethod.Sign_Raw; + return signingMethod == TangemCard.SigningMethod.Sign_Hash || signingMethod == TangemCard.SigningMethod.Sign_Raw; } @Override public byte[][] getHashesToSign() throws Exception { - byte[][] dataForSign=new byte[unspentOutputs.size()][]; + 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]; @@ -497,12 +507,187 @@ public class BtcEngine extends CoinEngine { unspentOutputs.get(i).scriptForBuild = DerEncodingUtil.packSignDer(r, s, pbKey); } - byte[] txForSend=BTCUtils.buildTXForSend(targetAddress, myAddress, unspentOutputs, amountFinal, changeFinal); + byte[] txForSend = BTCUtils.buildTXForSend(targetAddress, myAddress, unspentOutputs, amountFinal, changeFinal); notifyOnNeedSendPayment(txForSend); } }; } + @Override + public void requestBalanceAndUnspentTransactions(BlockchainRequestsNotifications blockchainRequestsNotifications) { + final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); + + ServerApiElectrum.ElectrumRequestDataListener electrumBodyListener = new ServerApiElectrum.ElectrumRequestDataListener() { + @Override + public void onSuccess(ElectrumRequest electrumRequest) { + if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetBalance)) { + try { + String walletAddress = electrumRequest.getParams().getString(0); + if( !walletAddress.equals(coinData.getWallet())) + { + // todo - check + throw new Exception("Invalid wallet address in answer!"); + } + Long confBalance = electrumRequest.getResult().getLong("confirmed"); + Long unconfirmedBalance = electrumRequest.getResult().getLong("unconfirmed"); + coinData.setBalanceReceived(true); + coinData.setBalanceConfirmed(confBalance); + coinData.setBalanceUnconfirmed(unconfirmedBalance); + coinData.setValidationNodeDescription(serverApiElectrum.getValidationNodeDescription()); + } catch (JSONException e) { + e.printStackTrace(); + Log.e(TAG, "FAIL METHOD_GetBalance JSONException"); + } + catch (Exception e) { + e.printStackTrace(); + Log.e(TAG, "FAIL METHOD_GetBalance Exception"); + } + } + + if (electrumRequest.isMethod(ElectrumRequest.METHOD_ListUnspent)) { + try { + String walletAddress = electrumRequest.getParams().getString(0); + JSONArray jsUnspentArray = electrumRequest.getResultArray(); + try { + coinData.getUnspentTransactions().clear(); + for (int i = 0; i < jsUnspentArray.length(); i++) { + JSONObject jsUnspent = jsUnspentArray.getJSONObject(i); + BtcData.UnspentTransaction trUnspent = new BtcData.UnspentTransaction(); + trUnspent.txID = jsUnspent.getString("tx_hash"); + trUnspent.Amount = jsUnspent.getInt("value"); + trUnspent.Height = jsUnspent.getInt("height"); + coinData.getUnspentTransactions().add(trUnspent); + } + } catch (JSONException e) { + e.printStackTrace(); + Log.e(TAG, "FAIL METHOD_ListUnspent JSONException"); + } + + for (int i = 0; i < jsUnspentArray.length(); i++) { + JSONObject jsUnspent = jsUnspentArray.getJSONObject(i); + Integer height = jsUnspent.getInt("height"); + String hash = jsUnspent.getString("tx_hash"); + if (height != -1) { + if( !blockchainRequestsNotifications.needTerminate() ) { + serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.getTransaction(walletAddress, hash)); + }else{ + serverApiElectrum.setErrorOccured("Terminated by user"); + } + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + + if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetTransaction)) { + try { + String txHash = electrumRequest.txHash; + String raw = electrumRequest.getResultString(); + for (BtcData.UnspentTransaction tx : coinData.getUnspentTransactions()) { + if (tx.txID.equals(txHash)) + tx.Raw = raw; + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + + if( !serverApiElectrum.hasRequests() ) + { + blockchainRequestsNotifications.onComplete(serverApiElectrum.isErrorOccured()); + } + } + + @Override + public void onFail(String method) { + if( !serverApiElectrum.hasRequests() ) + { + blockchainRequestsNotifications.onComplete(serverApiElectrum.isErrorOccured()); + } + } + }; + +// override fun onSuccess(electrumRequest: ElectrumRequest?) { +// if (electrumRequest!!.isMethod(ElectrumRequest.METHOD_GetBalance)) { +// try { +// val walletAddress = electrumRequest.params.getString(0) +// val confBalance = electrumRequest.result.getLong("confirmed") +// val unconfirmedBalance = electrumRequest.result.getLong("unconfirmed") +// ctx.coinData!!.isBalanceReceived = true +// (ctx.coinData!! as BtcData).setBalanceConfirmed(confBalance) +// (ctx.coinData!! as BtcData).balanceUnconfirmed = unconfirmedBalance +// (ctx.coinData!! as BtcData).validationNodeDescription = serverApiElectrum.validationNodeDescription +// } catch (e: JSONException) { +// e.printStackTrace() +// Log.e(TAG, "FAIL METHOD_GetBalance JSONException") +// } +// } +// +// if (electrumRequest.isMethod(ElectrumRequest.METHOD_ListUnspent)) { +// try { +// val walletAddress = electrumRequest.params.getString(0) +// val jsUnspentArray = electrumRequest.resultArray +// try { +// (ctx.coinData!! as BtcData).unspentTransactions.clear() +// for (i in 0 until jsUnspentArray.length()) { +// val jsUnspent = jsUnspentArray.getJSONObject(i) +// val trUnspent = BtcData.UnspentTransaction() +// trUnspent.txID = jsUnspent.getString("tx_hash") +// trUnspent.Amount = jsUnspent.getInt("value") +// trUnspent.Height = jsUnspent.getInt("height") +// (ctx.coinData!! as BtcData).unspentTransactions.add(trUnspent) +// } +// } catch (e: JSONException) { +// e.printStackTrace() +// Log.e(TAG, "FAIL METHOD_ListUnspent JSONException") +// } +// +// for (i in 0 until jsUnspentArray.length()) { +// val jsUnspent = jsUnspentArray.getJSONObject(i) +// val height = jsUnspent.getInt("height") +// val hash = jsUnspent.getString("tx_hash") +// if (height != -1) { +// requestElectrum(ElectrumRequest.getTransaction(walletAddress, hash)) +// } +// } +// } catch (e: JSONException) { +// e.printStackTrace() +// } +// } +// +// if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetTransaction)) { +// try { +// val txHash = electrumRequest.txHash +// val raw = electrumRequest.resultString +// val listTx = (ctx.coinData!! as BtcData).unspentTransactions +// for (tx in listTx) { +// if (tx.txID == txHash) +// tx.Raw = raw +// } +// } catch (e: JSONException) { +// e.printStackTrace() +// } +// } +// +// if (electrumRequest.isMethod(ElectrumRequest.METHOD_SendTransaction)) { +// +// } +// +// counterMinus() +// } +// +// override fun onFail(method: String?) { +// +// } + + + serverApiElectrum.setElectrumRequestData(electrumBodyListener); + + serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.checkBalance(coinData.getWallet())); + serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.listUnspent(coinData.getWallet())); + } + // @Override // public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { // diff --git a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt index 528d9299af..f1a88f9160 100644 --- a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt +++ b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt @@ -21,9 +21,7 @@ import android.view.ViewGroup import android.widget.Toast import com.tangem.App import com.tangem.Constant -import com.tangem.data.network.ElectrumRequest import com.tangem.data.network.ServerApiCommon -import com.tangem.data.network.ServerApiElectrum import com.tangem.data.network.ServerApiInfura import com.tangem.tangemserver.android.model.CardVerifyAndGetInfo import com.tangem.data.network.model.InfuraResponse @@ -32,7 +30,6 @@ import com.tangem.tangemcard.reader.CardProtocol import com.tangem.tangemcard.android.reader.NfcManager import com.tangem.domain.wallet.* import com.tangem.domain.wallet.bch.BtcCashEngine -import com.tangem.domain.wallet.btc.BtcData import com.tangem.domain.wallet.eth.EthData import com.tangem.domain.wallet.token.TokenData import com.tangem.domain.wallet.token.TokenEngine @@ -42,6 +39,7 @@ import com.tangem.presentation.dialog.PINSwapWarningDialog import com.tangem.presentation.dialog.ShowQRCodeDialog import com.tangem.presentation.dialog.WaitSecurityDelayDialog import com.tangem.data.Blockchain +import com.tangem.data.network.ElectrumRequest import com.tangem.tangemcard.android.reader.NfcReader import com.tangem.tangemcard.data.EXTRA_TANGEM_CARD import com.tangem.tangemcard.data.EXTRA_TANGEM_CARD_UID @@ -66,7 +64,6 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific private var serverApiCommon: ServerApiCommon = ServerApiCommon() private var serverApiInfura: ServerApiInfura = ServerApiInfura() - private var serverApiElectrum: ServerApiElectrum = ServerApiElectrum() private var serverApiTangem: ServerApiTangem = ServerApiTangem() private var singleToast: Toast? = null @@ -190,7 +187,6 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific if (cardProtocol != null) // openVerifyCard(cardProtocol!!) (activity as LoadedWalletActivity).navigator.showVerifyCard(context as Activity, ctx) - else showSingleToast(R.string.need_attach_card_again) } @@ -212,81 +208,81 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific } // request electrum listener - val electrumBodyListener: ServerApiElectrum.ElectrumRequestDataListener = object : ServerApiElectrum.ElectrumRequestDataListener { - override fun onSuccess(electrumRequest: ElectrumRequest?) { - if (electrumRequest!!.isMethod(ElectrumRequest.METHOD_GetBalance)) { - try { - val walletAddress = electrumRequest.params.getString(0) - val confBalance = electrumRequest.result.getLong("confirmed") - val unconfirmedBalance = electrumRequest.result.getLong("unconfirmed") - ctx.coinData!!.isBalanceReceived = true - (ctx.coinData!! as BtcData).setBalanceConfirmed(confBalance) - (ctx.coinData!! as BtcData).balanceUnconfirmed = unconfirmedBalance - (ctx.coinData!! as BtcData).validationNodeDescription = serverApiElectrum.validationNodeDescription - } catch (e: JSONException) { - e.printStackTrace() - Log.e(TAG, "FAIL METHOD_GetBalance JSONException") - } - } - - if (electrumRequest.isMethod(ElectrumRequest.METHOD_ListUnspent)) { - try { - val walletAddress = electrumRequest.params.getString(0) - val jsUnspentArray = electrumRequest.resultArray - try { - (ctx.coinData!! as BtcData).unspentTransactions.clear() - for (i in 0 until jsUnspentArray.length()) { - val jsUnspent = jsUnspentArray.getJSONObject(i) - val trUnspent = BtcData.UnspentTransaction() - trUnspent.txID = jsUnspent.getString("tx_hash") - trUnspent.Amount = jsUnspent.getInt("value") - trUnspent.Height = jsUnspent.getInt("height") - (ctx.coinData!! as BtcData).unspentTransactions.add(trUnspent) - } - } catch (e: JSONException) { - e.printStackTrace() - Log.e(TAG, "FAIL METHOD_ListUnspent JSONException") - } - - for (i in 0 until jsUnspentArray.length()) { - val jsUnspent = jsUnspentArray.getJSONObject(i) - val height = jsUnspent.getInt("height") - val hash = jsUnspent.getString("tx_hash") - if (height != -1) { - requestElectrum(ElectrumRequest.getTransaction(walletAddress, hash)) - } - } - } catch (e: JSONException) { - e.printStackTrace() - } - } - - if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetTransaction)) { - try { - val txHash = electrumRequest.txHash - val raw = electrumRequest.resultString - val listTx = (ctx.coinData!! as BtcData).unspentTransactions - for (tx in listTx) { - if (tx.txID == txHash) - tx.Raw = raw - } - } catch (e: JSONException) { - e.printStackTrace() - } - } - - if (electrumRequest.isMethod(ElectrumRequest.METHOD_SendTransaction)) { - - } - - counterMinus() - } - - override fun onFail(method: String?) { - - } - } - serverApiElectrum.setElectrumRequestData(electrumBodyListener) +// val electrumBodyListener: ServerApiElectrum.ElectrumRequestDataListener = object : ServerApiElectrum.ElectrumRequestDataListener { +// override fun onSuccess(electrumRequest: ElectrumRequest?) { +// if (electrumRequest!!.isMethod(ElectrumRequest.METHOD_GetBalance)) { +// try { +// val walletAddress = electrumRequest.params.getString(0) +// val confBalance = electrumRequest.result.getLong("confirmed") +// val unconfirmedBalance = electrumRequest.result.getLong("unconfirmed") +// ctx.coinData!!.isBalanceReceived = true +// (ctx.coinData!! as BtcData).setBalanceConfirmed(confBalance) +// (ctx.coinData!! as BtcData).balanceUnconfirmed = unconfirmedBalance +// (ctx.coinData!! as BtcData).validationNodeDescription = serverApiElectrum.validationNodeDescription +// } catch (e: JSONException) { +// e.printStackTrace() +// Log.e(TAG, "FAIL METHOD_GetBalance JSONException") +// } +// } +// +// if (electrumRequest.isMethod(ElectrumRequest.METHOD_ListUnspent)) { +// try { +// val walletAddress = electrumRequest.params.getString(0) +// val jsUnspentArray = electrumRequest.resultArray +// try { +// (ctx.coinData!! as BtcData).unspentTransactions.clear() +// for (i in 0 until jsUnspentArray.length()) { +// val jsUnspent = jsUnspentArray.getJSONObject(i) +// val trUnspent = BtcData.UnspentTransaction() +// trUnspent.txID = jsUnspent.getString("tx_hash") +// trUnspent.Amount = jsUnspent.getInt("value") +// trUnspent.Height = jsUnspent.getInt("height") +// (ctx.coinData!! as BtcData).unspentTransactions.add(trUnspent) +// } +// } catch (e: JSONException) { +// e.printStackTrace() +// Log.e(TAG, "FAIL METHOD_ListUnspent JSONException") +// } +// +// for (i in 0 until jsUnspentArray.length()) { +// val jsUnspent = jsUnspentArray.getJSONObject(i) +// val height = jsUnspent.getInt("height") +// val hash = jsUnspent.getString("tx_hash") +// if (height != -1) { +// requestElectrum(ElectrumRequest.getTransaction(walletAddress, hash)) +// } +// } +// } catch (e: JSONException) { +// e.printStackTrace() +// } +// } +// +// if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetTransaction)) { +// try { +// val txHash = electrumRequest.txHash +// val raw = electrumRequest.resultString +// val listTx = (ctx.coinData!! as BtcData).unspentTransactions +// for (tx in listTx) { +// if (tx.txID == txHash) +// tx.Raw = raw +// } +// } catch (e: JSONException) { +// e.printStackTrace() +// } +// } +// +// if (electrumRequest.isMethod(ElectrumRequest.METHOD_SendTransaction)) { +// +// } +// +// counterMinus() +// } +// +// override fun onFail(method: String?) { +// +// } +// } +// serverApiElectrum.setElectrumRequestData(electrumBodyListener) // request infura listener val infuraBodyListener: ServerApiInfura.InfuraBodyListener = object : ServerApiInfura.InfuraBodyListener { @@ -428,8 +424,8 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific // if (ctx.blockchain == Blockchain.Token || ctx.blockchain == Blockchain.Ethereum) { // ctx.card!!.setBlockchainIDFromCard(Blockchain.Ethereum.id) - //ctx.blockchain=Blockchain.Ethereum - //engine=engine!!.swithToOtherEngine(Blockchain.Ethereum) + //ctx.blockchain=Blockchain.Ethereum + //engine=engine!!.swithToOtherEngine(Blockchain.Ethereum) // } refresh() } @@ -807,12 +803,28 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific requestVerifyAndGetInfo() + val coinEngine = CoinEngineFactory.create(ctx) + requestCounter++ + coinEngine!!.requestBalanceAndUnspentTransactions ( + object : CoinEngine.BlockchainRequestsNotifications { + override fun onComplete(success: Boolean?) { + counterMinus() + updateViews() + } + + override fun needTerminate(): Boolean { + return !UtilHelper.isOnline(context as Activity) + } + } + ) + + // Bitcoin if (ctx.blockchain == Blockchain.Bitcoin || ctx.blockchain == Blockchain.BitcoinTestNet) { ctx.coinData.setIsBalanceEqual(true) - requestElectrum(ElectrumRequest.checkBalance(ctx.coinData!!.wallet)) - requestElectrum(ElectrumRequest.listUnspent(ctx.coinData!!.wallet)) +// requestElectrum(ElectrumRequest.checkBalance(ctx.coinData!!.wallet)) +// requestElectrum(ElectrumRequest.listUnspent(ctx.coinData!!.wallet)) requestRateInfo("bitcoin") } @@ -842,15 +854,15 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific } } - private fun requestElectrum(electrumRequest: ElectrumRequest) { - if (UtilHelper.isOnline(context as Activity)) { - requestCounter++ - serverApiElectrum.electrumRequestData(ctx, electrumRequest) - } else { - Toast.makeText(activity, getString(R.string.no_connection), Toast.LENGTH_SHORT).show() - srl?.isRefreshing = false - } - } +// private fun requestElectrum(electrumRequest: ElectrumRequest) { +// if (UtilHelper.isOnline(context as Activity)) { +// requestCounter++ +// serverApiElectrum.electrumRequestData(ctx, electrumRequest) +// } else { +// Toast.makeText(activity, getString(R.string.no_connection), Toast.LENGTH_SHORT).show() +// srl?.isRefreshing = false +// } +// } private fun requestInfura(method: String, contract: String) { if (UtilHelper.isOnline(context as Activity)) { From dae8c94c484e9c1a29e8143fb4d5ec750b7ab799 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 13 Dec 2018 09:59:00 +0300 Subject: [PATCH 2/7] Updated on 2026-08-14 --- .../com/tangem/domain/wallet/CoinEngine.java | 2 +- .../domain/wallet/bch/BtcCashEngine.java | 130 ++++++++- .../tangem/domain/wallet/btc/BtcEngine.java | 74 ----- .../com/tangem/domain/wallet/eth/EthData.java | 14 +- .../tangem/domain/wallet/eth/EthEngine.java | 198 +++++++++++-- .../domain/wallet/token/TokenEngine.java | 5 + .../presentation/fragment/LoadedWallet.kt | 269 +++++++++--------- 7 files changed, 442 insertions(+), 250 deletions(-) diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java index ee179d4a5c..e789f9ffd3 100644 --- a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java @@ -271,5 +271,5 @@ public abstract class CoinEngine { void onComplete(Boolean success); boolean needTerminate(); } - public abstract void requestBalanceAndUnspentTransactions(BlockchainRequestsNotifications blockchainRequestsNotifications); + public abstract void requestBalanceAndUnspentTransactions(BlockchainRequestsNotifications blockchainRequestsNotifications) throws Exception; } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java b/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java index 52bb1f6f65..e104579b68 100644 --- a/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java @@ -2,7 +2,10 @@ package com.tangem.domain.wallet.bch; import android.net.Uri; import android.text.InputFilter; +import android.util.Log; +import com.tangem.data.network.ElectrumRequest; +import com.tangem.data.network.ServerApiElectrum; import com.tangem.domain.wallet.BCHUtils; import com.tangem.tangemcard.reader.CardProtocol; import com.tangem.domain.wallet.BalanceValidator; @@ -21,6 +24,10 @@ import com.tangem.util.DerEncodingUtil; import com.tangem.tangemcard.util.Util; import com.tangem.wallet.R; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + import java.io.ByteArrayOutputStream; import java.math.BigDecimal; import java.math.BigInteger; @@ -33,6 +40,7 @@ import java.util.List; public class BtcCashEngine extends CoinEngine { + private static final String TAG = BtcCashEngine.class.getSimpleName(); public BtcData coinData = null; public BtcCashEngine(TangemContext context) throws Exception { @@ -167,7 +175,7 @@ public class BtcCashEngine extends CoinEngine { // // return true; - if(CashAddr.isValidCashAddress(address)) + if (CashAddr.isValidCashAddress(address)) return true; return false; } @@ -221,7 +229,7 @@ public class BtcCashEngine extends CoinEngine { if (fee.isZero() || amount.isZero()) return false; - if (isIncludeFee && (amount.compareTo(coinData.getBalanceInInternalUnits()) > 0 || amount.compareTo(fee)<0)) + if (isIncludeFee && (amount.compareTo(coinData.getBalanceInInternalUnits()) > 0 || amount.compareTo(fee) < 0)) return false; if (!isIncludeFee && amount.add(fee).compareTo(coinData.getBalanceInInternalUnits()) > 0) @@ -316,8 +324,8 @@ public class BtcCashEngine extends CoinEngine { @Override public String getBalanceEquivalent() { if (coinData == null || !coinData.getAmountEquivalentDescriptionAvailable()) return ""; - Amount balance=getBalance(); - if( balance==null ) return ""; + Amount balance = getBalance(); + if (balance == null) return ""; return balance.toEquivalentString(coinData.getRate()); } @@ -436,9 +444,7 @@ public class BtcCashEngine extends CoinEngine { try { String wallet = calculateAddress(ctx.getCard().getWalletPublicKeyRar()); ctx.getCoinData().setWallet(wallet); - } - catch (Exception e) - { + } catch (Exception e) { ctx.getCoinData().setWallet("ERROR"); throw new CardProtocol.TangemException("Can't define wallet address"); } @@ -482,9 +488,9 @@ public class BtcCashEngine extends CoinEngine { final long amountFinal = amount; final long changeFinal = change; - byte[][] txForSign= new byte[unspentOutputs.size()][]; - byte[][] bodyHash= new byte[unspentOutputs.size()][]; - byte[][] bodyDoubleHash= new byte[unspentOutputs.size()][]; + byte[][] txForSign = new byte[unspentOutputs.size()][]; + byte[][] bodyHash = new byte[unspentOutputs.size()][]; + byte[][] bodyDoubleHash = new byte[unspentOutputs.size()][]; for (int i = 0; i < unspentOutputs.size(); ++i) { txForSign[i] = BCHUtils.buildTXForSign(srcLegacyAddress, destLegacyAddress, srcLegacyAddress, unspentOutputs, i, amount, change); @@ -496,12 +502,12 @@ public class BtcCashEngine extends CoinEngine { @Override public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) { - return signingMethod==TangemCard.SigningMethod.Sign_Hash || signingMethod==TangemCard.SigningMethod.Sign_Raw; + return signingMethod == TangemCard.SigningMethod.Sign_Hash || signingMethod == TangemCard.SigningMethod.Sign_Raw; } @Override public byte[][] getHashesToSign() throws Exception { - byte[][] dataForSign=new byte[unspentOutputs.size()][]; + 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]; @@ -541,10 +547,108 @@ public class BtcCashEngine extends CoinEngine { unspentOutputs.get(i).scriptForBuild = DerEncodingUtil.packSignDerBitcoinCash(r, s, pbKey); } - byte[] txForSend=BCHUtils.buildTXForSend(destLegacyAddress, srcLegacyAddress, unspentOutputs, amountFinal, changeFinal); + byte[] txForSend = BCHUtils.buildTXForSend(destLegacyAddress, srcLegacyAddress, unspentOutputs, amountFinal, changeFinal); notifyOnNeedSendPayment(txForSend); } }; } + + @Override + public void requestBalanceAndUnspentTransactions(BlockchainRequestsNotifications blockchainRequestsNotifications) throws Exception { + final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); + + ServerApiElectrum.ElectrumRequestDataListener electrumBodyListener = new ServerApiElectrum.ElectrumRequestDataListener() { + @Override + public void onSuccess(ElectrumRequest electrumRequest) { + if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetBalance)) { + try { + String walletAddress = electrumRequest.getParams().getString(0); + if (!walletAddress.equals(coinData.getWallet())) { + // todo - check + throw new Exception("Invalid wallet address in answer!"); + } + Long confBalance = electrumRequest.getResult().getLong("confirmed"); + Long unconfirmedBalance = electrumRequest.getResult().getLong("unconfirmed"); + coinData.setBalanceReceived(true); + coinData.setBalanceConfirmed(confBalance); + coinData.setBalanceUnconfirmed(unconfirmedBalance); + coinData.setValidationNodeDescription(serverApiElectrum.getValidationNodeDescription()); + } catch (JSONException e) { + e.printStackTrace(); + Log.e(TAG, "FAIL METHOD_GetBalance JSONException"); + } catch (Exception e) { + e.printStackTrace(); + Log.e(TAG, "FAIL METHOD_GetBalance Exception"); + } + } + + if (electrumRequest.isMethod(ElectrumRequest.METHOD_ListUnspent)) { + try { + String walletAddress = electrumRequest.getParams().getString(0); + JSONArray jsUnspentArray = electrumRequest.getResultArray(); + try { + coinData.getUnspentTransactions().clear(); + for (int i = 0; i < jsUnspentArray.length(); i++) { + JSONObject jsUnspent = jsUnspentArray.getJSONObject(i); + BtcData.UnspentTransaction trUnspent = new BtcData.UnspentTransaction(); + trUnspent.txID = jsUnspent.getString("tx_hash"); + trUnspent.Amount = jsUnspent.getInt("value"); + trUnspent.Height = jsUnspent.getInt("height"); + coinData.getUnspentTransactions().add(trUnspent); + } + } catch (JSONException e) { + e.printStackTrace(); + Log.e(TAG, "FAIL METHOD_ListUnspent JSONException"); + } + + for (int i = 0; i < jsUnspentArray.length(); i++) { + JSONObject jsUnspent = jsUnspentArray.getJSONObject(i); + Integer height = jsUnspent.getInt("height"); + String hash = jsUnspent.getString("tx_hash"); + if (height != -1) { + if (!blockchainRequestsNotifications.needTerminate()) { + serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.getTransaction(walletAddress, hash)); + } else { + serverApiElectrum.setErrorOccured("Terminated by user"); + } + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + + if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetTransaction)) { + try { + String txHash = electrumRequest.txHash; + String raw = electrumRequest.getResultString(); + for (BtcData.UnspentTransaction tx : coinData.getUnspentTransactions()) { + if (tx.txID.equals(txHash)) + tx.Raw = raw; + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + + if (!serverApiElectrum.hasRequests()) { + blockchainRequestsNotifications.onComplete(serverApiElectrum.isErrorOccured()); + } + } + + @Override + public void onFail(String method) { + if (!serverApiElectrum.hasRequests()) { + blockchainRequestsNotifications.onComplete(serverApiElectrum.isErrorOccured()); + } + } + }; + + serverApiElectrum.setElectrumRequestData(electrumBodyListener); + + serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.checkBalance(convertToLegacyAddress(coinData.getWallet()))); + serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.listUnspent(convertToLegacyAddress(coinData.getWallet()))); + } + } diff --git a/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java b/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java index c8fae61d76..d71d05ae77 100644 --- a/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java @@ -608,80 +608,6 @@ public class BtcEngine extends CoinEngine { } }; -// override fun onSuccess(electrumRequest: ElectrumRequest?) { -// if (electrumRequest!!.isMethod(ElectrumRequest.METHOD_GetBalance)) { -// try { -// val walletAddress = electrumRequest.params.getString(0) -// val confBalance = electrumRequest.result.getLong("confirmed") -// val unconfirmedBalance = electrumRequest.result.getLong("unconfirmed") -// ctx.coinData!!.isBalanceReceived = true -// (ctx.coinData!! as BtcData).setBalanceConfirmed(confBalance) -// (ctx.coinData!! as BtcData).balanceUnconfirmed = unconfirmedBalance -// (ctx.coinData!! as BtcData).validationNodeDescription = serverApiElectrum.validationNodeDescription -// } catch (e: JSONException) { -// e.printStackTrace() -// Log.e(TAG, "FAIL METHOD_GetBalance JSONException") -// } -// } -// -// if (electrumRequest.isMethod(ElectrumRequest.METHOD_ListUnspent)) { -// try { -// val walletAddress = electrumRequest.params.getString(0) -// val jsUnspentArray = electrumRequest.resultArray -// try { -// (ctx.coinData!! as BtcData).unspentTransactions.clear() -// for (i in 0 until jsUnspentArray.length()) { -// val jsUnspent = jsUnspentArray.getJSONObject(i) -// val trUnspent = BtcData.UnspentTransaction() -// trUnspent.txID = jsUnspent.getString("tx_hash") -// trUnspent.Amount = jsUnspent.getInt("value") -// trUnspent.Height = jsUnspent.getInt("height") -// (ctx.coinData!! as BtcData).unspentTransactions.add(trUnspent) -// } -// } catch (e: JSONException) { -// e.printStackTrace() -// Log.e(TAG, "FAIL METHOD_ListUnspent JSONException") -// } -// -// for (i in 0 until jsUnspentArray.length()) { -// val jsUnspent = jsUnspentArray.getJSONObject(i) -// val height = jsUnspent.getInt("height") -// val hash = jsUnspent.getString("tx_hash") -// if (height != -1) { -// requestElectrum(ElectrumRequest.getTransaction(walletAddress, hash)) -// } -// } -// } catch (e: JSONException) { -// e.printStackTrace() -// } -// } -// -// if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetTransaction)) { -// try { -// val txHash = electrumRequest.txHash -// val raw = electrumRequest.resultString -// val listTx = (ctx.coinData!! as BtcData).unspentTransactions -// for (tx in listTx) { -// if (tx.txID == txHash) -// tx.Raw = raw -// } -// } catch (e: JSONException) { -// e.printStackTrace() -// } -// } -// -// if (electrumRequest.isMethod(ElectrumRequest.METHOD_SendTransaction)) { -// -// } -// -// counterMinus() -// } -// -// override fun onFail(method: String?) { -// -// } - - serverApiElectrum.setElectrumRequestData(electrumBodyListener); serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.checkBalance(coinData.getWallet())); diff --git a/app/src/main/java/com/tangem/domain/wallet/eth/EthData.java b/app/src/main/java/com/tangem/domain/wallet/eth/EthData.java index df44ecef13..7921eaf1c6 100644 --- a/app/src/main/java/com/tangem/domain/wallet/eth/EthData.java +++ b/app/src/main/java/com/tangem/domain/wallet/eth/EthData.java @@ -62,8 +62,12 @@ public class EthData extends CoinData { public void loadFromBundle(Bundle B) { super.loadFromBundle(B); - String currency = B.getString("BalanceCurrency"); - balance = new CoinEngine.InternalAmount(B.getString("BalanceDecimal"), currency); + if (B.containsKey("BalanceCurrency") && B.containsKey("BalanceDecimal")) { + String currency = B.getString("BalanceCurrency"); + balance = new CoinEngine.InternalAmount(B.getString("BalanceDecimal"), currency); + } else { + balance = null; + } if (B.containsKey("confirmTx")) countConfirmedTX = new BigInteger(B.getString("confirmTx"), 16); @@ -75,8 +79,10 @@ public class EthData extends CoinData { public void saveToBundle(Bundle B) { super.saveToBundle(B); try { - B.putString("BalanceCurrency", balance.getCurrency()); - B.putString("BalanceDecimal", balance.toString()); + if (balance != null) { + B.putString("BalanceCurrency", balance.getCurrency()); + B.putString("BalanceDecimal", balance.toString()); + } B.putString("confirmTx", getConfirmedTXCount().toString(16)); B.putString("unconfirmTx", getUnconfirmedTXCount().toString(16)); diff --git a/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java b/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java index 67942dc0c1..bf4eeb0c7b 100644 --- a/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java @@ -1,22 +1,30 @@ package com.tangem.domain.wallet.eth; +import android.app.Activity; import android.net.Uri; import android.text.InputFilter; import android.util.Log; +import android.widget.Toast; +import com.tangem.data.network.ServerApiInfura; +import com.tangem.data.network.model.InfuraResponse; import com.tangem.domain.wallet.BalanceValidator; import com.tangem.data.Blockchain; import com.tangem.domain.wallet.CoinData; import com.tangem.domain.wallet.CoinEngine; +import com.tangem.domain.wallet.CoinEngineFactory; import com.tangem.domain.wallet.ECDSASignatureETH; import com.tangem.domain.wallet.EthTransaction; import com.tangem.domain.wallet.Keccak256; +import com.tangem.domain.wallet.token.TokenData; +import com.tangem.domain.wallet.token.TokenEngine; import com.tangem.tangemcard.data.TangemCard; import com.tangem.domain.wallet.TangemContext; import com.tangem.domain.wallet.BTCUtils; import com.tangem.tangemcard.tasks.SignTask; import com.tangem.util.CryptoUtil; import com.tangem.util.DecimalDigitsInputFilter; +import com.tangem.util.UtilHelper; import com.tangem.wallet.R; import org.bitcoinj.core.ECKey; @@ -57,7 +65,7 @@ public class EthEngine extends CoinEngine { } @Override - public boolean awaitingConfirmation(){ + public boolean awaitingConfirmation() { return false; } @@ -71,10 +79,10 @@ public class EthEngine extends CoinEngine { @Override public String getBalanceHTML() { - Amount balance=getBalance(); - if( balance!=null ) { + Amount balance = getBalance(); + if (balance != null) { return balance.toDescriptionString(getDecimals()); - }else{ + } else { return ""; } } @@ -93,7 +101,7 @@ public class EthEngine extends CoinEngine { @Override public boolean isBalanceNotZero() { - if( coinData ==null ) return false; + if (coinData == null) return false; if (coinData.getBalanceInInternalUnits() == null) return false; return coinData.getBalanceInInternalUnits().notZero(); } @@ -183,8 +191,8 @@ public class EthEngine extends CoinEngine { @Override public String getBalanceEquivalent() { - Amount balance=getBalance(); - if( balance==null ) return ""; + Amount balance = getBalance(); + if (balance == null) return ""; return balance.toEquivalentString(coinData.getRate()); } @@ -200,8 +208,8 @@ public class EthEngine extends CoinEngine { } @Override - public InternalAmount convertToInternalAmount(Amount amount){ - return new InternalAmount(amount.multiply(new BigDecimal("1000000000000000000")),"wei"); + public InternalAmount convertToInternalAmount(Amount amount) { + return new InternalAmount(amount.multiply(new BigDecimal("1000000000000000000")), "wei"); } @Override @@ -218,7 +226,7 @@ public class EthEngine extends CoinEngine { @Override public boolean hasBalanceInfo() { - return coinData.getBalanceInInternalUnits()!=null; + return coinData.getBalanceInInternalUnits() != null; } @Override @@ -254,14 +262,14 @@ public class EthEngine extends CoinEngine { @Override public InputFilter[] getAmountInputFilters() { - return new InputFilter[] { new DecimalDigitsInputFilter(getDecimals()) }; + return new InputFilter[]{new DecimalDigitsInputFilter(getDecimals())}; } @Override - public boolean checkNewTransactionAmount(Amount amount){ - if( coinData ==null ) return false; - Amount balance=getBalance(); - if (balance==null || amount.compareTo(balance) > 0) { + public boolean checkNewTransactionAmount(Amount amount) { + if (coinData == null) return false; + Amount balance = getBalance(); + if (balance == null || amount.compareTo(balance) > 0) { return false; } return true; @@ -292,7 +300,7 @@ public class EthEngine extends CoinEngine { try { BigDecimal cardBalance = getBalance(); - if (isFeeIncluded && (amount.compareTo(cardBalance) > 0 || amount.compareTo(fee)<0)) + if (isFeeIncluded && (amount.compareTo(cardBalance) > 0 || amount.compareTo(fee) < 0)) return false; if (!isFeeIncluded && amount.add(fee).compareTo(cardBalance) > 0) @@ -346,9 +354,7 @@ public class EthEngine extends CoinEngine { try { Amount feeValue = new Amount(fee, ctx.getBlockchain().getCurrency()); return feeValue.toEquivalentString(coinData.getRate()); - } - catch (Exception e) - { + } catch (Exception e) { e.printStackTrace(); return ""; } @@ -380,8 +386,8 @@ public class EthEngine extends CoinEngine { BigInteger nonceValue = coinData.getConfirmedTXCount(); byte[] pbKey = ctx.getCard().getWalletPublicKey(); - BigInteger weiFee=convertToInternalAmount(feeValue).toBigIntegerExact(); - BigInteger weiAmount=convertToInternalAmount(amountValue).toBigIntegerExact(); + BigInteger weiFee = convertToInternalAmount(feeValue).toBigIntegerExact(); + BigInteger weiAmount = convertToInternalAmount(amountValue).toBigIntegerExact(); if (IncFee) { weiAmount = weiAmount.subtract(weiFee); @@ -403,7 +409,7 @@ public class EthEngine extends CoinEngine { return new SignTask.PaymentToSign() { @Override public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) { - return signingMethod==TangemCard.SigningMethod.Sign_Hash; + return signingMethod == TangemCard.SigningMethod.Sign_Hash; } @Override @@ -430,7 +436,7 @@ public class EthEngine extends CoinEngine { @Override public void onSignCompleted(byte[] signFromCard) throws Exception { - byte[] for_hash=tx.getRawHash(); + 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)); s = CryptoUtil.toCanonicalised(s); @@ -455,6 +461,150 @@ public class EthEngine extends CoinEngine { }; } + @Override + public void requestBalanceAndUnspentTransactions(BlockchainRequestsNotifications blockchainRequestsNotifications) throws Exception { + final ServerApiInfura serverApiInfura = new ServerApiInfura(); + // request infura listener + ServerApiInfura.InfuraBodyListener infuraBodyListener = new ServerApiInfura.InfuraBodyListener() { + @Override + public void onSuccess(String method, InfuraResponse infuraResponse) { + switch (method) { + case ServerApiInfura.INFURA_ETH_GET_BALANCE: { + String balanceCap = infuraResponse.getResult(); + balanceCap = balanceCap.substring(2); + BigInteger l = new BigInteger(balanceCap, 16); + BigInteger d = l.divide(new BigInteger("1000000000000000000", 10)); + Long balance = d.longValue(); + +// (ctx.coinData!! as EthData).setBalanceConfirmed(balance) +// (ctx.coinData!! as EthData).balanceUnconfirmed = 0L + if (ctx.getBlockchain() != Blockchain.Token) { + coinData.setBalanceReceived(true); + coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, "wei")); + } else { + coinData.setBalanceReceived(true); + //(ctx.coinData!! as TokenData).balanceInInternalUnits = CoinEngine.InternalAmount(l.toBigDecimal(),ctx.card.tokenSymbol) + ((TokenData) coinData).setBalanceAlterInInternalUnits(new CoinEngine.InternalAmount(l, "wei")); + } + +// Log.i("$TAG eth_get_balance", balanceCap) + } + break; + + case ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT: { + String nonce = infuraResponse.getResult(); + nonce = nonce.substring(2); + BigInteger count = new BigInteger(nonce, 16); + coinData.setConfirmedTXCount(count); + + +// Log.i("$TAG eth_getTransCount", nonce) + } + break; + + case ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT: { + String pending = infuraResponse.getResult(); + pending = pending.substring(2); + BigInteger count = new BigInteger(pending, 16); + coinData.setUnconfirmedTXCount(count); + +// Log.i("$TAG eth_getPendingTxCount", pending) + } +// + case ServerApiInfura.INFURA_ETH_CALL: { + try { + String balanceCap = infuraResponse.getResult(); + balanceCap = balanceCap.substring(2); + BigInteger l = new BigInteger(balanceCap, 16); + Long balance = l.longValue(); +// if (l.compareTo(BigInteger.ZERO) == 0) { +// //ctx.card!!.blockchainID = Blockchain.Ethereum.id +// ctx.card!!.addTokenToBlockchainName() +// +// //TODO check +// //ctx.blockchain=lBlockchain.Ethereum +// +// requestCounter-- +// if (requestCounter == 0) srl!!.isRefreshing = false +// +// requestInfura(ServerApiCommon.INFURA_ETH_GET_BALANCE, "") +// requestInfura(ServerApiCommon.INFURA_ETH_GET_TRANSACTION_COUNT, "") +// requestInfura(ServerApiCommon.INFURA_ETH_GET_PENDING_COUNT, "") +// return +// } + coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol)); + +// Log.i("$TAG eth_call", balanceCap) + + if (!blockchainRequestsNotifications.needTerminate()) { + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", ""); + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", ""); + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", ""); + } else { + serverApiInfura.setErrorOccured("Terminated by user"); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + +// ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION -> { +// try { +// var hashTX: String +// try { +// val tmp = infuraResponse.result +// hashTX = tmp +// } catch (e: JSONException) { +// return +// } +// +// if (hashTX.startsWith("0x") || hashTX.startsWith("0X")) { +// hashTX = hashTX.substring(2) +// } +// +// Log.e("$TAG TX_RESULT", hashTX) +// +// val nonce = (ctx.coinData!! as EthData).confirmedTXCount +// nonce.add(BigInteger.valueOf(1)) +// (ctx.coinData!! as EthData).confirmedTXCount = nonce +// +// Log.e("$TAG TX_RESULT", hashTX) +// +// } catch (e: Exception) { +// e.printStackTrace() +// } +// } + } +// +// counterMinus() + if (!serverApiInfura.hasRequests()) { + blockchainRequestsNotifications.onComplete(serverApiInfura.isErrorOccured()); + } + } + + @Override + public void onFail(String method, String message) { + if (!serverApiInfura.hasRequests()) { + blockchainRequestsNotifications.onComplete(serverApiInfura.isErrorOccured()); + } + } + }; + serverApiInfura.setInfuraResponse(infuraBodyListener); + + if (ctx.getBlockchain() == Blockchain.Ethereum || ctx.getBlockchain() == Blockchain.EthereumTestNet) { + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", ""); + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", ""); + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", ""); + } + + // Token + else if (ctx.getBlockchain() == Blockchain.Token) { + final CoinEngine engine = CoinEngineFactory.INSTANCE.create(ctx); + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.getWallet(), ((TokenEngine) engine).getContractAddress(ctx.getCard()), ""); + } + + // @Override // public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { // @@ -518,4 +668,4 @@ public class EthEngine extends CoinEngine { // byte[] realTX = tx.getEncoded(); // return realTX; // } -} + } diff --git a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java index 1c62899d86..3e0a8d00c2 100644 --- a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java @@ -430,6 +430,11 @@ public class TokenEngine extends CoinEngine { } } + @Override + public void requestBalanceAndUnspentTransactions(BlockchainRequestsNotifications blockchainRequestsNotifications) throws Exception { + //TODO("NOT IMPLEMENTED") + } + // @Override // public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { // if (amountValue.getCurrency().equals("ETH")) { diff --git a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt index f1a88f9160..a3779a6d78 100644 --- a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt +++ b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt @@ -63,7 +63,6 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific private lateinit var nfcManager: NfcManager private var serverApiCommon: ServerApiCommon = ServerApiCommon() - private var serverApiInfura: ServerApiInfura = ServerApiInfura() private var serverApiTangem: ServerApiTangem = ServerApiTangem() private var singleToast: Toast? = null @@ -284,123 +283,123 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific // } // serverApiElectrum.setElectrumRequestData(electrumBodyListener) - // request infura listener - val infuraBodyListener: ServerApiInfura.InfuraBodyListener = object : ServerApiInfura.InfuraBodyListener { - override fun onSuccess(method: String, infuraResponse: InfuraResponse) { - when (method) { - ServerApiInfura.INFURA_ETH_GET_BALANCE -> { - var balanceCap = infuraResponse.result - balanceCap = balanceCap.substring(2) - val l = BigInteger(balanceCap, 16) -// val d = l.divide(BigInteger("1000000000000000000", 10)) -// val balance = d.toLong() - -// (ctx.coinData!! as EthData).setBalanceConfirmed(balance) -// (ctx.coinData!! as EthData).balanceUnconfirmed = 0L - if (ctx.blockchain != Blockchain.Token) { - (ctx.coinData!! as EthData).isBalanceReceived = true - (ctx.coinData!! as EthData).balanceInInternalUnits = CoinEngine.InternalAmount(l.toBigDecimal(), "wei") - } else { - (ctx.coinData!! as TokenData).isBalanceReceived = true - //(ctx.coinData!! as TokenData).balanceInInternalUnits = CoinEngine.InternalAmount(l.toBigDecimal(),ctx.card.tokenSymbol) - (ctx.coinData!! as TokenData).balanceAlterInInternalUnits = CoinEngine.InternalAmount(l.toBigDecimal(), "wei") - } - -// Log.i("$TAG eth_get_balance", balanceCap) - } - - ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT -> { - var nonce = infuraResponse.result - nonce = nonce.substring(2) - val count = BigInteger(nonce, 16) - (ctx.coinData!! as EthData).confirmedTXCount = count - - -// Log.i("$TAG eth_getTransCount", nonce) - } - - ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT -> { - var pending = infuraResponse.result - pending = pending.substring(2) - val count = BigInteger(pending, 16) - (ctx.coinData!! as EthData).unconfirmedTXCount = count - -// Log.i("$TAG eth_getPendingTxCount", pending) - } - - ServerApiInfura.INFURA_ETH_CALL -> { - try { - var balanceCap = infuraResponse.result - balanceCap = balanceCap.substring(2) - val l = BigInteger(balanceCap, 16) - val balance = l.toLong() -// if (l.compareTo(BigInteger.ZERO) == 0) { -// //ctx.card!!.blockchainID = Blockchain.Ethereum.id -// ctx.card!!.addTokenToBlockchainName() +// // request infura listener +// val infuraBodyListener: ServerApiInfura.InfuraBodyListener = object : ServerApiInfura.InfuraBodyListener { +// override fun onSuccess(method: String, infuraResponse: InfuraResponse) { +// when (method) { +// ServerApiInfura.INFURA_ETH_GET_BALANCE -> { +// var balanceCap = infuraResponse.result +// balanceCap = balanceCap.substring(2) +// val l = BigInteger(balanceCap, 16) +//// val d = l.divide(BigInteger("1000000000000000000", 10)) +//// val balance = d.toLong() // -// //TODO check -// //ctx.blockchain=lBlockchain.Ethereum +//// (ctx.coinData!! as EthData).setBalanceConfirmed(balance) +//// (ctx.coinData!! as EthData).balanceUnconfirmed = 0L +// if (ctx.blockchain != Blockchain.Token) { +// (ctx.coinData!! as EthData).isBalanceReceived = true +// (ctx.coinData!! as EthData).balanceInInternalUnits = CoinEngine.InternalAmount(l.toBigDecimal(), "wei") +// } else { +// (ctx.coinData!! as TokenData).isBalanceReceived = true +// //(ctx.coinData!! as TokenData).balanceInInternalUnits = CoinEngine.InternalAmount(l.toBigDecimal(),ctx.card.tokenSymbol) +// (ctx.coinData!! as TokenData).balanceAlterInInternalUnits = CoinEngine.InternalAmount(l.toBigDecimal(), "wei") +// } // -// requestCounter-- -// if (requestCounter == 0) srl!!.isRefreshing = false +//// Log.i("$TAG eth_get_balance", balanceCap) +// } // -// requestInfura(ServerApiCommon.INFURA_ETH_GET_BALANCE, "") -// requestInfura(ServerApiCommon.INFURA_ETH_GET_TRANSACTION_COUNT, "") -// requestInfura(ServerApiCommon.INFURA_ETH_GET_PENDING_COUNT, "") +// ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT -> { +// var nonce = infuraResponse.result +// nonce = nonce.substring(2) +// val count = BigInteger(nonce, 16) +// (ctx.coinData!! as EthData).confirmedTXCount = count +// +// +//// Log.i("$TAG eth_getTransCount", nonce) +// } +// +// ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT -> { +// var pending = infuraResponse.result +// pending = pending.substring(2) +// val count = BigInteger(pending, 16) +// (ctx.coinData!! as EthData).unconfirmedTXCount = count +// +//// Log.i("$TAG eth_getPendingTxCount", pending) +// } +// +// ServerApiInfura.INFURA_ETH_CALL -> { +// try { +// var balanceCap = infuraResponse.result +// balanceCap = balanceCap.substring(2) +// val l = BigInteger(balanceCap, 16) +// val balance = l.toLong() +//// if (l.compareTo(BigInteger.ZERO) == 0) { +//// //ctx.card!!.blockchainID = Blockchain.Ethereum.id +//// ctx.card!!.addTokenToBlockchainName() +//// +//// //TODO check +//// //ctx.blockchain=lBlockchain.Ethereum +//// +//// requestCounter-- +//// if (requestCounter == 0) srl!!.isRefreshing = false +//// +//// requestInfura(ServerApiCommon.INFURA_ETH_GET_BALANCE, "") +//// requestInfura(ServerApiCommon.INFURA_ETH_GET_TRANSACTION_COUNT, "") +//// requestInfura(ServerApiCommon.INFURA_ETH_GET_PENDING_COUNT, "") +//// return +//// } +// (ctx.coinData!! as EthData).balanceInInternalUnits = CoinEngine.InternalAmount(l.toBigDecimal(), ctx.card.tokenSymbol) +// +//// Log.i("$TAG eth_call", balanceCap) +// +// requestInfura(ServerApiInfura.INFURA_ETH_GET_BALANCE, "") +// requestInfura(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, "") +// requestInfura(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, "") +// } catch (e: JSONException) { +// e.printStackTrace() +// } catch (e: NumberFormatException) { +// e.printStackTrace() +// } catch (e: Exception) { +// e.printStackTrace() +// } +// } +// +// ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION -> { +// try { +// var hashTX: String +// try { +// val tmp = infuraResponse.result +// hashTX = tmp +// } catch (e: JSONException) { // return // } - (ctx.coinData!! as EthData).balanceInInternalUnits = CoinEngine.InternalAmount(l.toBigDecimal(), ctx.card.tokenSymbol) - -// Log.i("$TAG eth_call", balanceCap) - - requestInfura(ServerApiInfura.INFURA_ETH_GET_BALANCE, "") - requestInfura(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, "") - requestInfura(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, "") - } catch (e: JSONException) { - e.printStackTrace() - } catch (e: NumberFormatException) { - e.printStackTrace() - } catch (e: Exception) { - e.printStackTrace() - } - } - - ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION -> { - try { - var hashTX: String - try { - val tmp = infuraResponse.result - hashTX = tmp - } catch (e: JSONException) { - return - } - - if (hashTX.startsWith("0x") || hashTX.startsWith("0X")) { - hashTX = hashTX.substring(2) - } - - Log.e("$TAG TX_RESULT", hashTX) - - val nonce = (ctx.coinData!! as EthData).confirmedTXCount - nonce.add(BigInteger.valueOf(1)) - (ctx.coinData!! as EthData).confirmedTXCount = nonce - - Log.e("$TAG TX_RESULT", hashTX) - - } catch (e: Exception) { - e.printStackTrace() - } - } - } - - counterMinus() - } - - override fun onFail(method: String, message: String) { - - } - } - serverApiInfura.setInfuraResponse(infuraBodyListener) +// +// if (hashTX.startsWith("0x") || hashTX.startsWith("0X")) { +// hashTX = hashTX.substring(2) +// } +// +// Log.e("$TAG TX_RESULT", hashTX) +// +// val nonce = (ctx.coinData!! as EthData).confirmedTXCount +// nonce.add(BigInteger.valueOf(1)) +// (ctx.coinData!! as EthData).confirmedTXCount = nonce +// +// Log.e("$TAG TX_RESULT", hashTX) +// +// } catch (e: Exception) { +// e.printStackTrace() +// } +// } +// } +// +// counterMinus() +// } +// +// override fun onFail(method: String, message: String) { +// +// } +// } +// serverApiInfura.setInfuraResponse(infuraBodyListener) // request card verify and get info listener val cardVerifyAndGetInfoListener: ServerApiTangem.CardVerifyAndGetInfoListener = object : ServerApiTangem.CardVerifyAndGetInfoListener { @@ -803,21 +802,23 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific requestVerifyAndGetInfo() - val coinEngine = CoinEngineFactory.create(ctx) - requestCounter++ - coinEngine!!.requestBalanceAndUnspentTransactions ( - object : CoinEngine.BlockchainRequestsNotifications { - override fun onComplete(success: Boolean?) { - counterMinus() - updateViews() - } + if (ctx.blockchain == Blockchain.Bitcoin || ctx.blockchain == Blockchain.BitcoinTestNet || ctx.blockchain == Blockchain.BitcoinCash) { + val coinEngine = CoinEngineFactory.create(ctx) + requestCounter++ + coinEngine!!.requestBalanceAndUnspentTransactions( + object : CoinEngine.BlockchainRequestsNotifications { + override fun onComplete(success: Boolean?) { + counterMinus() + updateViews() + } - override fun needTerminate(): Boolean { - return !UtilHelper.isOnline(context as Activity) - } - } - ) + override fun needTerminate(): Boolean { + return !UtilHelper.isOnline(context as Activity) + } + } + ) + } // Bitcoin if (ctx.blockchain == Blockchain.Bitcoin || ctx.blockchain == Blockchain.BitcoinTestNet) { @@ -831,25 +832,25 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific // BitcoinCash else if (ctx.blockchain == Blockchain.BitcoinCash) { ctx.coinData.setIsBalanceEqual(true) - val engine = CoinEngineFactory.create(ctx) - - requestElectrum(ElectrumRequest.checkBalance((engine as BtcCashEngine).convertToLegacyAddress(ctx.coinData!!.wallet))) - requestElectrum(ElectrumRequest.listUnspent(engine.convertToLegacyAddress(ctx.coinData!!.wallet))) +// val engine = CoinEngineFactory.create(ctx) +// +// requestElectrum(ElectrumRequest.checkBalance((engine as BtcCashEngine).convertToLegacyAddress(ctx.coinData!!.wallet))) +// requestElectrum(ElectrumRequest.listUnspent(engine.convertToLegacyAddress(ctx.coinData!!.wallet))) requestRateInfo("bitcoin-cash") } // Ethereum else if (ctx.blockchain == Blockchain.Ethereum || ctx.blockchain == Blockchain.EthereumTestNet) { - requestInfura(ServerApiInfura.INFURA_ETH_GET_BALANCE, "") - requestInfura(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, "") - requestInfura(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, "") +// requestInfura(ServerApiInfura.INFURA_ETH_GET_BALANCE, "") +// requestInfura(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, "") +// requestInfura(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, "") requestRateInfo("ethereum") } // Token else if (ctx.blockchain == Blockchain.Token) { val engine = CoinEngineFactory.create(ctx) - requestInfura(ServerApiInfura.INFURA_ETH_CALL, (engine as TokenEngine).getContractAddress(ctx.card)) +// requestInfura(ServerApiInfura.INFURA_ETH_CALL, (engine as TokenEngine).getContractAddress(ctx.card)) requestRateInfo("ethereum") } } From e7c57f1e113c41a1386f80d6e431f2c86c9338ce Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 14 Dec 2018 11:50:13 +0300 Subject: [PATCH 3/7] Updated on 2026-08-14 --- .../tangem/data/network/ServerApiInfura.java | 17 + .../com/tangem/domain/wallet/CoinEngine.java | 14 +- .../domain/wallet/bch/BtcCashEngine.java | 8 +- .../tangem/domain/wallet/btc/BtcEngine.java | 174 ++++++++- .../tangem/domain/wallet/eth/EthEngine.java | 136 +++---- .../domain/wallet/token/TokenEngine.java | 115 +++++- .../activity/ConfirmPaymentActivity.kt | 338 ++++++++---------- .../presentation/activity/MainActivity.kt | 8 +- .../presentation/fragment/LoadedWallet.kt | 55 ++- .../tangemserver/android/data/LocalStorage.kt | 2 +- 10 files changed, 530 insertions(+), 337 deletions(-) diff --git a/app/src/main/java/com/tangem/data/network/ServerApiInfura.java b/app/src/main/java/com/tangem/data/network/ServerApiInfura.java index fb8e407099..c6d33242ca 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiInfura.java +++ b/app/src/main/java/com/tangem/data/network/ServerApiInfura.java @@ -31,6 +31,21 @@ public class ServerApiInfura { public static final String INFURA_ETH_SEND_RAW_TRANSACTION = "eth_sendRawTransaction"; public static final String INFURA_ETH_GAS_PRICE = "eth_gasPrice"; + private int requestsCount=0; + + public boolean hasRequests() { + return requestsCount>0; + } + + private String error=null; + public boolean isErrorOccured() { + return error!=null; + } + + public void setErrorOccured(String error) { + this.error=error; + } + private InfuraBodyListener infuraBodyListener; public interface InfuraBodyListener { @@ -44,6 +59,7 @@ public class ServerApiInfura { } public void infura(String method, int id, String wallet, String contract, String tx) { + requestsCount++; InfuraApi infuraApi = App.getNetworkComponent().getRetrofitInfura().create(InfuraApi.class); InfuraBody infuraBody; @@ -77,6 +93,7 @@ public class ServerApiInfura { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { if (response.code() == 200) { + requestsCount--; infuraBodyListener.onSuccess(method, response.body()); Log.i(TAG, "infura " + method + " onResponse " + response.code()); } else { diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java index e789f9ffd3..2ad484f4c8 100644 --- a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java @@ -265,11 +265,19 @@ public abstract class CoinEngine { } - - public interface BlockchainRequestsNotifications + public interface BalanceAndUnspentTransactionsNotifications { void onComplete(Boolean success); boolean needTerminate(); } - public abstract void requestBalanceAndUnspentTransactions(BlockchainRequestsNotifications blockchainRequestsNotifications) throws Exception; + public abstract void requestBalanceAndUnspentTransactions(BalanceAndUnspentTransactionsNotifications balanceAndUnspentTransactionsNotifications) throws Exception; + + + public interface FeeRequestsNotifications + { + void onComplete(boolean success, Amount minFee, Amount normalFee, Amount maxFee); + boolean needTerminate(); + } + public abstract void requestFee(FeeRequestsNotifications feeRequestsNotifications, CoinEngine.Amount amount) throws Exception; + } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java b/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java index e104579b68..82656791fd 100644 --- a/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java @@ -555,7 +555,7 @@ public class BtcCashEngine extends CoinEngine { } @Override - public void requestBalanceAndUnspentTransactions(BlockchainRequestsNotifications blockchainRequestsNotifications) throws Exception { + public void requestBalanceAndUnspentTransactions(BalanceAndUnspentTransactionsNotifications balanceAndUnspentTransactionsNotifications) throws Exception { final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); ServerApiElectrum.ElectrumRequestDataListener electrumBodyListener = new ServerApiElectrum.ElectrumRequestDataListener() { @@ -607,7 +607,7 @@ public class BtcCashEngine extends CoinEngine { Integer height = jsUnspent.getInt("height"); String hash = jsUnspent.getString("tx_hash"); if (height != -1) { - if (!blockchainRequestsNotifications.needTerminate()) { + if (!balanceAndUnspentTransactionsNotifications.needTerminate()) { serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.getTransaction(walletAddress, hash)); } else { serverApiElectrum.setErrorOccured("Terminated by user"); @@ -633,14 +633,14 @@ public class BtcCashEngine extends CoinEngine { } if (!serverApiElectrum.hasRequests()) { - blockchainRequestsNotifications.onComplete(serverApiElectrum.isErrorOccured()); + balanceAndUnspentTransactionsNotifications.onComplete(serverApiElectrum.isErrorOccured()); } } @Override public void onFail(String method) { if (!serverApiElectrum.hasRequests()) { - blockchainRequestsNotifications.onComplete(serverApiElectrum.isErrorOccured()); + balanceAndUnspentTransactionsNotifications.onComplete(serverApiElectrum.isErrorOccured()); } } }; diff --git a/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java b/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java index d71d05ae77..53d97fe589 100644 --- a/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java @@ -4,6 +4,7 @@ import android.net.Uri; import android.text.InputFilter; import android.util.Log; +import com.tangem.data.network.ServerApiCommon; import com.tangem.tangemcard.reader.CardProtocol; import com.tangem.domain.wallet.BalanceValidator; import com.tangem.domain.wallet.Base58; @@ -20,6 +21,7 @@ import com.tangem.util.CryptoUtil; import com.tangem.util.DecimalDigitsInputFilter; import com.tangem.util.DerEncodingUtil; import com.tangem.tangemcard.util.Util; +import com.tangem.util.FormatUtil; import com.tangem.wallet.R; import com.tangem.data.network.ElectrumRequest; import com.tangem.data.network.ServerApiElectrum; @@ -32,6 +34,7 @@ import org.json.JSONObject; import java.io.ByteArrayOutputStream; import java.math.BigDecimal; import java.math.BigInteger; +import java.math.RoundingMode; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; @@ -514,7 +517,7 @@ public class BtcEngine extends CoinEngine { } @Override - public void requestBalanceAndUnspentTransactions(BlockchainRequestsNotifications blockchainRequestsNotifications) { + public void requestBalanceAndUnspentTransactions(BalanceAndUnspentTransactionsNotifications balanceAndUnspentTransactionsNotifications) { final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); ServerApiElectrum.ElectrumRequestDataListener electrumBodyListener = new ServerApiElectrum.ElectrumRequestDataListener() { @@ -523,8 +526,7 @@ public class BtcEngine extends CoinEngine { if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetBalance)) { try { String walletAddress = electrumRequest.getParams().getString(0); - if( !walletAddress.equals(coinData.getWallet())) - { + if (!walletAddress.equals(coinData.getWallet())) { // todo - check throw new Exception("Invalid wallet address in answer!"); } @@ -537,8 +539,7 @@ public class BtcEngine extends CoinEngine { } catch (JSONException e) { e.printStackTrace(); Log.e(TAG, "FAIL METHOD_GetBalance JSONException"); - } - catch (Exception e) { + } catch (Exception e) { e.printStackTrace(); Log.e(TAG, "FAIL METHOD_GetBalance Exception"); } @@ -568,9 +569,9 @@ public class BtcEngine extends CoinEngine { Integer height = jsUnspent.getInt("height"); String hash = jsUnspent.getString("tx_hash"); if (height != -1) { - if( !blockchainRequestsNotifications.needTerminate() ) { + if (!balanceAndUnspentTransactionsNotifications.needTerminate()) { serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.getTransaction(walletAddress, hash)); - }else{ + } else { serverApiElectrum.setErrorOccured("Terminated by user"); } } @@ -593,17 +594,15 @@ public class BtcEngine extends CoinEngine { } } - if( !serverApiElectrum.hasRequests() ) - { - blockchainRequestsNotifications.onComplete(serverApiElectrum.isErrorOccured()); + if (!serverApiElectrum.hasRequests()) { + balanceAndUnspentTransactionsNotifications.onComplete(serverApiElectrum.isErrorOccured()); } } @Override public void onFail(String method) { - if( !serverApiElectrum.hasRequests() ) - { - blockchainRequestsNotifications.onComplete(serverApiElectrum.isErrorOccured()); + if (!serverApiElectrum.hasRequests()) { + balanceAndUnspentTransactionsNotifications.onComplete(serverApiElectrum.isErrorOccured()); } } }; @@ -614,7 +613,154 @@ public class BtcEngine extends CoinEngine { serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.listUnspent(coinData.getWallet())); } -// @Override + Integer buildSize(String outputAddress, String outFee, String outAmount) throws Exception { + String myAddress = coinData.getWallet(); + byte[] pbKey = ctx.getCard().getWalletPublicKey(); + byte[] pbComprKey = ctx.getCard().getWalletPublicKeyRar(); + + // build script for our address + List rawTxList = coinData.getUnspentTransactions(); + byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; + + // collect unspent + ArrayList unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); + + Long fullAmount = 0L; + for (int i = 0; i < unspentOutputs.size(); i++) { + fullAmount += unspentOutputs.get(i).value; + } + + // get first unspent +// val outPut = unspentOutputs[0] +// val outPutIndex = outPut.outputIndex + + // get prev TX id; +// val prevTXID = rawTxList[0].txID//"f67b838d6e2c0c587f476f583843e93ff20368eaf96a798bdc25e01f53f8f5d2"; + + Long fees = FormatUtil.ConvertStringToLong(outFee); + Long amount = FormatUtil.ConvertStringToLong(outAmount); + amount -= fees; + + Long change = fullAmount - fees - amount; + + if (amount + fees > fullAmount) { + throw new Exception(String.format("Balance (%d) < amount (%d) + (%d)", fullAmount, change, amount)); + } + + byte[][] hashesForSign = new byte[unspentOutputs.size()][]; + + for (int i = 0; i < unspentOutputs.size(); i++) { + byte[] newTX = BTCUtils.buildTXForSign(myAddress, outputAddress, myAddress, unspentOutputs, i, amount, change); + byte[] hashData = Util.calculateSHA256(newTX); + byte[] doubleHashData = Util.calculateSHA256(hashData); +// Log.e("TX_BODY_1", BTCUtils.toHex(newTX)) +// Log.e("TX_HASH_1", BTCUtils.toHex(hashData)) +// Log.e("TX_HASH_2", BTCUtils.toHex(doubleHashData)) + +// unspentOutputs[i].bodyDoubleHash = doubleHashData +// unspentOutputs[i].bodyHash = hashData + hashesForSign[i] = doubleHashData; + } + + byte[] signFromCard = new byte[64 * unspentOutputs.size()]; + + for (int i = 0; i < unspentOutputs.size(); i++) { + BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0 + i * 64, 32 + i * 64)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); + byte[] encodingSign = DerEncodingUtil.packSignDer(r, s, pbKey); + unspentOutputs.get(i).scriptForBuild = encodingSign; + } + + byte[] realTX = BTCUtils.buildTXForSend(outputAddress, myAddress, unspentOutputs, amount, change); + + return realTX.length; + } + + @Override + public void requestFee(FeeRequestsNotifications feeRequestsNotifications, CoinEngine.Amount amount) throws Exception { +// request estimate fee listener +// int calcSize = 256; +// try { + + final int calcSize = buildSize(coinData.getWallet(), "0.00", amount.toValueString()); +// } catch (Exception ex) { +// Log.e(TAG,"Build Fee error: "+ ex.getMessage()); +// } + + final ServerApiCommon serverApiCommon = new ServerApiCommon(); + + final ServerApiCommon.EstimateFeeListener estimateFeeListener = new ServerApiCommon.EstimateFeeListener() { + @Override + public void onSuccess(int blockCount, String estimateFeeResponse) { + BigDecimal fee = new BigDecimal(estimateFeeResponse); // BTC per 1 kb + + if (fee.equals(BigDecimal.ZERO)) { +// progressBar.visibility = View.INVISIBLE + if( !feeRequestsNotifications.needTerminate()) { + serverApiCommon.estimateFee(blockCount); + } + return; + } + + if (calcSize != 0) { + fee = fee.multiply(new BigDecimal(calcSize)).divide(new BigDecimal(1024)); // per Kb -> per byte + } else { + if( !feeRequestsNotifications.needTerminate()) { + serverApiCommon.estimateFee(blockCount); + } + return; + } + +// progressBar.visibility = View.INVISIBLE + + fee = fee.setScale(8, RoundingMode.DOWN); + + switch (blockCount) { + case ServerApiCommon.ESTIMATE_FEE_MINIMAL: { + CoinEngine.Amount minFee = new CoinEngine.Amount(fee, getFeeCurrency()); + feeRequestsNotifications.onComplete(true, minFee, null, null); +// if (rgFee.checkedRadioButtonId == R.id.rbMinimalFee) doSetFee(rgFee.checkedRadioButtonId) + } + break; + + case ServerApiCommon.ESTIMATE_FEE_NORMAL: { + CoinEngine.Amount normalFee = new CoinEngine.Amount(fee, getFeeCurrency()); + feeRequestsNotifications.onComplete(true, null, normalFee, null); +// if (rgFee.checkedRadioButtonId == R.id.rbNormalFee) doSetFee(rgFee.checkedRadioButtonId) + } + break; + + case ServerApiCommon.ESTIMATE_FEE_PRIORITY: { + CoinEngine.Amount maxFee = new CoinEngine.Amount(fee, getFeeCurrency()); + feeRequestsNotifications.onComplete(true, null, null, maxFee); +// if (rgFee.checkedRadioButtonId == R.id.rbMaximumFee) doSetFee(rgFee.checkedRadioButtonId) + } + } + +// etFee.error = null +// feeRequestSuccess = true +// if (feeRequestSuccess) +// if (feeRequestSuccess && balanceRequestSuccess) +// btnSend.visibility = View.VISIBLE +// dtVerified = Date() + } + + @Override + public void onFail(String message) { + feeRequestsNotifications.onComplete(false, null, null, null); + + } + }; + serverApiCommon.setEstimateFee(estimateFeeListener); + + serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_PRIORITY); + serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_NORMAL); + serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_MINIMAL); + + } + + + // @Override // public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { // // checkBlockchainDataExists(); diff --git a/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java b/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java index bf4eeb0c7b..a36467c6a5 100644 --- a/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java @@ -1,10 +1,8 @@ package com.tangem.domain.wallet.eth; -import android.app.Activity; import android.net.Uri; import android.text.InputFilter; import android.util.Log; -import android.widget.Toast; import com.tangem.data.network.ServerApiInfura; import com.tangem.data.network.model.InfuraResponse; @@ -12,19 +10,16 @@ import com.tangem.domain.wallet.BalanceValidator; import com.tangem.data.Blockchain; import com.tangem.domain.wallet.CoinData; import com.tangem.domain.wallet.CoinEngine; -import com.tangem.domain.wallet.CoinEngineFactory; import com.tangem.domain.wallet.ECDSASignatureETH; import com.tangem.domain.wallet.EthTransaction; import com.tangem.domain.wallet.Keccak256; import com.tangem.domain.wallet.token.TokenData; -import com.tangem.domain.wallet.token.TokenEngine; import com.tangem.tangemcard.data.TangemCard; import com.tangem.domain.wallet.TangemContext; import com.tangem.domain.wallet.BTCUtils; import com.tangem.tangemcard.tasks.SignTask; import com.tangem.util.CryptoUtil; import com.tangem.util.DecimalDigitsInputFilter; -import com.tangem.util.UtilHelper; import com.tangem.wallet.R; import org.bitcoinj.core.ECKey; @@ -462,7 +457,7 @@ public class EthEngine extends CoinEngine { } @Override - public void requestBalanceAndUnspentTransactions(BlockchainRequestsNotifications blockchainRequestsNotifications) throws Exception { + public void requestBalanceAndUnspentTransactions(BalanceAndUnspentTransactionsNotifications balanceAndUnspentTransactionsNotifications) { final ServerApiInfura serverApiInfura = new ServerApiInfura(); // request infura listener ServerApiInfura.InfuraBodyListener infuraBodyListener = new ServerApiInfura.InfuraBodyListener() { @@ -473,8 +468,8 @@ public class EthEngine extends CoinEngine { String balanceCap = infuraResponse.getResult(); balanceCap = balanceCap.substring(2); BigInteger l = new BigInteger(balanceCap, 16); - BigInteger d = l.divide(new BigInteger("1000000000000000000", 10)); - Long balance = d.longValue(); +// BigInteger d = l.divide(new BigInteger("1000000000000000000", 10)); +// Long balance = d.longValue(); // (ctx.coinData!! as EthData).setBalanceConfirmed(balance) // (ctx.coinData!! as EthData).balanceUnconfirmed = 0L @@ -510,102 +505,71 @@ public class EthEngine extends CoinEngine { // Log.i("$TAG eth_getPendingTxCount", pending) } -// - case ServerApiInfura.INFURA_ETH_CALL: { - try { - String balanceCap = infuraResponse.getResult(); - balanceCap = balanceCap.substring(2); - BigInteger l = new BigInteger(balanceCap, 16); - Long balance = l.longValue(); -// if (l.compareTo(BigInteger.ZERO) == 0) { -// //ctx.card!!.blockchainID = Blockchain.Ethereum.id -// ctx.card!!.addTokenToBlockchainName() -// -// //TODO check -// //ctx.blockchain=lBlockchain.Ethereum -// -// requestCounter-- -// if (requestCounter == 0) srl!!.isRefreshing = false -// -// requestInfura(ServerApiCommon.INFURA_ETH_GET_BALANCE, "") -// requestInfura(ServerApiCommon.INFURA_ETH_GET_TRANSACTION_COUNT, "") -// requestInfura(ServerApiCommon.INFURA_ETH_GET_PENDING_COUNT, "") -// return -// } - coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol)); - -// Log.i("$TAG eth_call", balanceCap) - - if (!blockchainRequestsNotifications.needTerminate()) { - serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", ""); - serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", ""); - serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", ""); - } else { - serverApiInfura.setErrorOccured("Terminated by user"); - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - -// ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION -> { -// try { -// var hashTX: String -// try { -// val tmp = infuraResponse.result -// hashTX = tmp -// } catch (e: JSONException) { -// return -// } -// -// if (hashTX.startsWith("0x") || hashTX.startsWith("0X")) { -// hashTX = hashTX.substring(2) -// } -// -// Log.e("$TAG TX_RESULT", hashTX) -// -// val nonce = (ctx.coinData!! as EthData).confirmedTXCount -// nonce.add(BigInteger.valueOf(1)) -// (ctx.coinData!! as EthData).confirmedTXCount = nonce -// -// Log.e("$TAG TX_RESULT", hashTX) -// -// } catch (e: Exception) { -// e.printStackTrace() -// } -// } } -// -// counterMinus() + if (!serverApiInfura.hasRequests()) { - blockchainRequestsNotifications.onComplete(serverApiInfura.isErrorOccured()); + balanceAndUnspentTransactionsNotifications.onComplete(serverApiInfura.isErrorOccured()); } } @Override public void onFail(String method, String message) { if (!serverApiInfura.hasRequests()) { - blockchainRequestsNotifications.onComplete(serverApiInfura.isErrorOccured()); + balanceAndUnspentTransactionsNotifications.onComplete(serverApiInfura.isErrorOccured()); } } }; serverApiInfura.setInfuraResponse(infuraBodyListener); - if (ctx.getBlockchain() == Blockchain.Ethereum || ctx.getBlockchain() == Blockchain.EthereumTestNet) { serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", ""); serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", ""); serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", ""); - } + } - // Token - else if (ctx.getBlockchain() == Blockchain.Token) { - final CoinEngine engine = CoinEngineFactory.INSTANCE.create(ctx); - serverApiInfura.infura(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.getWallet(), ((TokenEngine) engine).getContractAddress(ctx.getCard()), ""); - } + @Override + public void requestFee(FeeRequestsNotifications feeRequestsNotifications, CoinEngine.Amount amount) throws Exception { + ServerApiInfura serverApiInfura = new ServerApiInfura(); + // request infura eth gasPrice listener + ServerApiInfura.InfuraBodyListener infuraBodyListener = new ServerApiInfura.InfuraBodyListener() { + @Override + public void onSuccess(String method, InfuraResponse infuraResponse) { + if(method== ServerApiInfura.INFURA_ETH_GAS_PRICE) + { + String gasPrice = infuraResponse.getResult(); + gasPrice = gasPrice.substring(2); + // rounding gas price to integer gwei + BigInteger l = new BigInteger(gasPrice, 16).divide(BigInteger.valueOf(1000000000L)).multiply(BigInteger.valueOf(1000000000L)); + //val m = if (ctx.blockchain==Blockchain.Token) BigInteger.valueOf(60000) else BigInteger.valueOf(21000) + BigInteger m; + if (amount.getCurrency().equals("ETH")) m = BigInteger.valueOf(60000); + else m = BigInteger.valueOf(21000); -// @Override + CoinEngine.InternalAmount weiMinFee = new CoinEngine.InternalAmount(l.multiply(m), "wei"); + CoinEngine.InternalAmount weiNormalFee = new CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(12)).divide(BigDecimal.valueOf(10)), "wei"); + CoinEngine.InternalAmount weiMaxFee = new CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(15)).divide(BigDecimal.valueOf(10)), "wei"); + + CoinEngine.Amount minFee = convertToAmount(weiMinFee); + CoinEngine.Amount normalFee = convertToAmount(weiNormalFee); + CoinEngine.Amount maxFee = convertToAmount(weiMaxFee); + feeRequestsNotifications.onComplete(true, minFee, normalFee, maxFee); + } + + } + + @Override + public void onFail(String method, String message) { + if( method==ServerApiInfura.INFURA_ETH_GAS_PRICE ){ + feeRequestsNotifications.onComplete(false, null, null, null); + } + } + }; + serverApiInfura.setInfuraResponse(infuraBodyListener); + + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GAS_PRICE, 67, coinData.getWallet(), "", ""); + } + + // @Override // public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { // // BigInteger nonceValue = coinData.getConfirmedTXCount(); @@ -668,4 +632,4 @@ public class EthEngine extends CoinEngine { // byte[] realTX = tx.getEncoded(); // return realTX; // } - } +} diff --git a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java index 3e0a8d00c2..ee00616785 100644 --- a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java @@ -5,6 +5,9 @@ import android.text.InputFilter; import android.util.Log; import com.google.common.base.Strings; +import com.tangem.data.Blockchain; +import com.tangem.data.network.ServerApiInfura; +import com.tangem.data.network.model.InfuraResponse; import com.tangem.domain.wallet.BalanceValidator; import com.tangem.domain.wallet.CoinData; import com.tangem.domain.wallet.CoinEngine; @@ -430,11 +433,6 @@ public class TokenEngine extends CoinEngine { } } - @Override - public void requestBalanceAndUnspentTransactions(BlockchainRequestsNotifications blockchainRequestsNotifications) throws Exception { - //TODO("NOT IMPLEMENTED") - } - // @Override // public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { // if (amountValue.getCurrency().equals("ETH")) { @@ -629,6 +627,113 @@ public class TokenEngine extends CoinEngine { } + @Override + public void requestBalanceAndUnspentTransactions(BalanceAndUnspentTransactionsNotifications balanceAndUnspentTransactionsNotifications) { + final ServerApiInfura serverApiInfura = new ServerApiInfura(); + // request infura listener + ServerApiInfura.InfuraBodyListener infuraBodyListener = new ServerApiInfura.InfuraBodyListener() { + @Override + public void onSuccess(String method, InfuraResponse infuraResponse) { + switch (method) { + case ServerApiInfura.INFURA_ETH_GET_BALANCE: { + String balanceCap = infuraResponse.getResult(); + balanceCap = balanceCap.substring(2); + BigInteger l = new BigInteger(balanceCap, 16); +// BigInteger d = l.divide(new BigInteger("1000000000000000000", 10)); +// Long balance = d.longValue(); + +// (ctx.coinData!! as EthData).setBalanceConfirmed(balance) +// (ctx.coinData!! as EthData).balanceUnconfirmed = 0L + if (ctx.getBlockchain() != Blockchain.Token) { + coinData.setBalanceReceived(true); + coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, "wei")); + } else { + coinData.setBalanceReceived(true); + //(ctx.coinData!! as TokenData).balanceInInternalUnits = CoinEngine.InternalAmount(l.toBigDecimal(),ctx.card.tokenSymbol) + ((TokenData) coinData).setBalanceAlterInInternalUnits(new CoinEngine.InternalAmount(l, "wei")); + } + +// Log.i("$TAG eth_get_balance", balanceCap) + } + break; + + case ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT: { + String nonce = infuraResponse.getResult(); + nonce = nonce.substring(2); + BigInteger count = new BigInteger(nonce, 16); + coinData.setConfirmedTXCount(count); + + +// Log.i("$TAG eth_getTransCount", nonce) + } + break; + + case ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT: { + String pending = infuraResponse.getResult(); + pending = pending.substring(2); + BigInteger count = new BigInteger(pending, 16); + coinData.setUnconfirmedTXCount(count); + +// Log.i("$TAG eth_getPendingTxCount", pending) + } +// + case ServerApiInfura.INFURA_ETH_CALL: { + try { + String balanceCap = infuraResponse.getResult(); + balanceCap = balanceCap.substring(2); + BigInteger l = new BigInteger(balanceCap, 16); + Long balance = l.longValue(); +// if (l.compareTo(BigInteger.ZERO) == 0) { +// //ctx.card!!.blockchainID = Blockchain.Ethereum.id +// ctx.card!!.addTokenToBlockchainName() +// +// //TODO check +// //ctx.blockchain=lBlockchain.Ethereum +// +// requestCounter-- +// if (requestCounter == 0) srl!!.isRefreshing = false +// +// requestInfura(ServerApiCommon.INFURA_ETH_GET_BALANCE, "") +// requestInfura(ServerApiCommon.INFURA_ETH_GET_TRANSACTION_COUNT, "") +// requestInfura(ServerApiCommon.INFURA_ETH_GET_PENDING_COUNT, "") +// return +// } + coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol)); + +// Log.i("$TAG eth_call", balanceCap) + + if (!balanceAndUnspentTransactionsNotifications.needTerminate()) { + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", ""); + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", ""); + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", ""); + } else { + serverApiInfura.setErrorOccured("Terminated by user"); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + if (!serverApiInfura.hasRequests()) { + balanceAndUnspentTransactionsNotifications.onComplete(serverApiInfura.isErrorOccured()); + } + } + + @Override + public void onFail(String method, String message) { + if (!serverApiInfura.hasRequests()) { + balanceAndUnspentTransactionsNotifications.onComplete(serverApiInfura.isErrorOccured()); + } + } + }; + serverApiInfura.setInfuraResponse(infuraBodyListener); + + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), ""); + } + + // public byte[] signETH(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { // BigInteger nonceValue = coinData.getConfirmedTXCount(); // byte[] pbKey = ctx.getCard().getWalletPublicKey(); diff --git a/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.kt b/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.kt index 93dc3ab3af..df26fa1b02 100644 --- a/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.kt +++ b/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.kt @@ -42,15 +42,15 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { private var nfcManager: NfcManager? = null - private var serverApiCommon: ServerApiCommon = ServerApiCommon() - private var serverApiInfura: ServerApiInfura = ServerApiInfura() - private var serverApiElectrum: ServerApiElectrum = ServerApiElectrum() +// private var serverApiCommon: ServerApiCommon = ServerApiCommon() +// private var serverApiInfura: ServerApiInfura = ServerApiInfura() +// private var serverApiElectrum: ServerApiElectrum = ServerApiElectrum() private lateinit var ctx: TangemContext private lateinit var amount: CoinEngine.Amount private var feeRequestSuccess = false -// private var balanceRequestSuccess = false + // private var balanceRequestSuccess = false private var minFee: CoinEngine.Amount? = null private var maxFee: CoinEngine.Amount? = null private var normalFee: CoinEngine.Amount? = null @@ -58,7 +58,7 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { private var requestPIN2Count = 0 private var nodeCheck = true private var dtVerified: Date? = null - private var calcSize: Int = 0 +// private var calcSize: Int = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -84,7 +84,7 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { amount = CoinEngine.Amount(intent.getStringExtra(SignPaymentActivity.EXTRA_AMOUNT), intent.getStringExtra(SignPaymentActivity.EXTRA_AMOUNT_CURRENCY)) - if (ctx.blockchain == Blockchain.Token && amount.currency!="ETH") + if (ctx.blockchain == Blockchain.Token && amount.currency != "ETH") tvIncFee.visibility = View.INVISIBLE else tvIncFee.visibility = View.VISIBLE @@ -103,7 +103,7 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { if (ctx.blockchain == Blockchain.Ethereum || ctx.blockchain == Blockchain.EthereumTestNet || ctx.blockchain == Blockchain.Token) { rgFee.isEnabled = false - requestInfura(ServerApiInfura.INFURA_ETH_GAS_PRICE) +// requestInfura(ServerApiInfura.INFURA_ETH_GAS_PRICE) } else { rgFee.isEnabled = true @@ -114,7 +114,7 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { progressBar.visibility = View.VISIBLE - requestEstimateFee() +// requestEstimateFee() } // set listeners @@ -192,6 +192,33 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2) } + val coinEngine = CoinEngineFactory.create(ctx) + coinEngine!!.requestFee( + object : CoinEngine.FeeRequestsNotifications { + override fun onComplete(success: Boolean, minFee: CoinEngine.Amount?, normalFee: CoinEngine.Amount?, maxFee: CoinEngine.Amount?) { + if (success) { + this@ConfirmPaymentActivity.minFee = minFee + this@ConfirmPaymentActivity.normalFee = normalFee + this@ConfirmPaymentActivity.maxFee = maxFee + doSetFee(rgFee.checkedRadioButtonId) + etFee.error = null + btnSend.visibility = View.VISIBLE + feeRequestSuccess = true +// balanceRequestSuccess = true + dtVerified = Date() + } else { + finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain)) + } + } + + override fun needTerminate(): Boolean { + return !UtilHelper.isOnline(this@ConfirmPaymentActivity) + } + }, + amount + ) + + // request electrum listener // val electrumBodyListener: ServerApiHelperElectrum.ElectrumRequestDataListener = object : ServerApiHelperElectrum.ElectrumRequestDataListener { // override fun onSuccess(electrumRequest: ElectrumRequest?) { @@ -227,97 +254,97 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { // serverApiHelperElectrum.setElectrumRequestData(electrumBodyListener) // request infura eth gasPrice listener - val infuraBodyListener: ServerApiInfura.InfuraBodyListener = object : ServerApiInfura.InfuraBodyListener { - override fun onSuccess(method: String, infuraResponse: InfuraResponse) { - when (method) { - ServerApiInfura.INFURA_ETH_GAS_PRICE -> { - var gasPrice = infuraResponse.result - gasPrice = gasPrice.substring(2) - //TODO - remove Gwei - // rounding gas price to integer gwei - val l = BigInteger(gasPrice, 16).divide(BigInteger.valueOf(1000000000L)).multiply(BigInteger.valueOf(1000000000L)) +// val infuraBodyListener: ServerApiInfura.InfuraBodyListener = object : ServerApiInfura.InfuraBodyListener { +// override fun onSuccess(method: String, infuraResponse: InfuraResponse) { +// when (method) { +// ServerApiInfura.INFURA_ETH_GAS_PRICE -> { +// var gasPrice = infuraResponse.result +// gasPrice = gasPrice.substring(2) +// //TODO - remove Gwei +// // rounding gas price to integer gwei +// val l = BigInteger(gasPrice, 16).divide(BigInteger.valueOf(1000000000L)).multiply(BigInteger.valueOf(1000000000L)) +// +// //val m = if (ctx.blockchain==Blockchain.Token) BigInteger.valueOf(60000) else BigInteger.valueOf(21000) +// val m = if (amount.currency != "ETH") BigInteger.valueOf(60000) else BigInteger.valueOf(21000) +// val weiMinFee = CoinEngine.InternalAmount(l.multiply(m), "wei") +// val weiNormalFee = CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(12)).divide(BigDecimal.valueOf(10)), "wei") +// val weiMaxFee = CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(15)).divide(BigDecimal.valueOf(10)), "wei") +// +// minFee = engine.convertToAmount(weiMinFee) +// normalFee = engine.convertToAmount(weiNormalFee) +// maxFee = engine.convertToAmount(weiMaxFee) +// doSetFee(rgFee.checkedRadioButtonId) +// //etFee.setText(weiNormalFee.toValueString()) +// etFee.error = null +// btnSend.visibility = View.VISIBLE +// feeRequestSuccess = true +//// balanceRequestSuccess = true +// dtVerified = Date() +// } +// } +// } +// +// override fun onFail(method: String, message: String) { +// when (method) { +// ServerApiInfura.INFURA_ETH_GAS_PRICE -> { +// finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain)) +// } +// } +// } +// } +// serverApiInfura.setInfuraResponse(infuraBodyListener) - //val m = if (ctx.blockchain==Blockchain.Token) BigInteger.valueOf(60000) else BigInteger.valueOf(21000) - val m = if (amount.currency != "ETH") BigInteger.valueOf(60000) else BigInteger.valueOf(21000) - val weiMinFee = CoinEngine.InternalAmount(l.multiply(m), "wei") - val weiNormalFee = CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(12)).divide(BigDecimal.valueOf(10)), "wei") - val weiMaxFee = CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(15)).divide(BigDecimal.valueOf(10)), "wei") - - minFee = engine.convertToAmount(weiMinFee) - normalFee = engine.convertToAmount(weiNormalFee) - maxFee = engine.convertToAmount(weiMaxFee) - doSetFee(rgFee.checkedRadioButtonId) - //etFee.setText(weiNormalFee.toValueString()) - etFee.error = null - btnSend.visibility = View.VISIBLE - feeRequestSuccess = true -// balanceRequestSuccess = true - dtVerified = Date() - } - } - } - - override fun onFail(method: String, message: String) { - when (method) { - ServerApiInfura.INFURA_ETH_GAS_PRICE -> { - finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain)) - } - } - } - } - serverApiInfura.setInfuraResponse(infuraBodyListener) - - // request estimate fee listener - val estimateFeeListener: ServerApiCommon.EstimateFeeListener = object : ServerApiCommon.EstimateFeeListener { - override fun onSuccess(blockCount: Int, estimateFeeResponse: String?) { - var fee: BigDecimal? - fee = BigDecimal(estimateFeeResponse) // BTC per 1 kb - - if (fee == BigDecimal.ZERO) { - progressBar.visibility = View.INVISIBLE - requestEstimateFee() - } - - if (calcSize.toLong() != 0L) { - fee = fee.multiply(BigDecimal(calcSize.toLong())).divide(BigDecimal(1024)) // per Kb -> per byte - } else { - requestEstimateFee() - } - - progressBar.visibility = View.INVISIBLE - - fee = fee!!.setScale(8, RoundingMode.DOWN) - - when (blockCount) { - ServerApiCommon.ESTIMATE_FEE_MINIMAL -> { - minFee = CoinEngine.Amount(fee, engine.feeCurrency) - if (rgFee.checkedRadioButtonId == R.id.rbMinimalFee) doSetFee(rgFee.checkedRadioButtonId) - } - - ServerApiCommon.ESTIMATE_FEE_NORMAL -> { - normalFee = CoinEngine.Amount(fee, engine.feeCurrency) - if (rgFee.checkedRadioButtonId == R.id.rbNormalFee) doSetFee(rgFee.checkedRadioButtonId) - } - - ServerApiCommon.ESTIMATE_FEE_PRIORITY -> { - maxFee = CoinEngine.Amount(fee, engine.feeCurrency) - if (rgFee.checkedRadioButtonId == R.id.rbMaximumFee) doSetFee(rgFee.checkedRadioButtonId) - } - } - - etFee.error = null - feeRequestSuccess = true - if (feeRequestSuccess) -// if (feeRequestSuccess && balanceRequestSuccess) - btnSend.visibility = View.VISIBLE - dtVerified = Date() - } - - override fun onFail(message: String?) { - finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_calculate_fee_wrong_data_received_from_node)) - } - } - serverApiCommon.setEstimateFee(estimateFeeListener) +// // request estimate fee listener +// val estimateFeeListener: ServerApiCommon.EstimateFeeListener = object : ServerApiCommon.EstimateFeeListener { +// override fun onSuccess(blockCount: Int, estimateFeeResponse: String?) { +// var fee: BigDecimal? +// fee = BigDecimal(estimateFeeResponse) // BTC per 1 kb +// +// if (fee == BigDecimal.ZERO) { +// progressBar.visibility = View.INVISIBLE +// requestEstimateFee() +// } +// +// if (calcSize.toLong() != 0L) { +// fee = fee.multiply(BigDecimal(calcSize.toLong())).divide(BigDecimal(1024)) // per Kb -> per byte +// } else { +// requestEstimateFee() +// } +// +// progressBar.visibility = View.INVISIBLE +// +// fee = fee!!.setScale(8, RoundingMode.DOWN) +// +// when (blockCount) { +// ServerApiCommon.ESTIMATE_FEE_MINIMAL -> { +// minFee = CoinEngine.Amount(fee, engine.feeCurrency) +// if (rgFee.checkedRadioButtonId == R.id.rbMinimalFee) doSetFee(rgFee.checkedRadioButtonId) +// } +// +// ServerApiCommon.ESTIMATE_FEE_NORMAL -> { +// normalFee = CoinEngine.Amount(fee, engine.feeCurrency) +// if (rgFee.checkedRadioButtonId == R.id.rbNormalFee) doSetFee(rgFee.checkedRadioButtonId) +// } +// +// ServerApiCommon.ESTIMATE_FEE_PRIORITY -> { +// maxFee = CoinEngine.Amount(fee, engine.feeCurrency) +// if (rgFee.checkedRadioButtonId == R.id.rbMaximumFee) doSetFee(rgFee.checkedRadioButtonId) +// } +// } +// +// etFee.error = null +// feeRequestSuccess = true +// if (feeRequestSuccess) +//// if (feeRequestSuccess && balanceRequestSuccess) +// btnSend.visibility = View.VISIBLE +// dtVerified = Date() +// } +// +// override fun onFail(message: String?) { +// finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_calculate_fee_wrong_data_received_from_node)) +// } +// } +// serverApiCommon.setEstimateFee(estimateFeeListener) } public override fun onResume() { @@ -394,98 +421,35 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { // TODO - move to BtcEngine @Throws(Exception::class) - internal fun buildSize(outputAddress: String, outFee: String, outAmount: String): Int { - val myAddress = ctx.coinData!!.wallet - val pbKey = ctx.card!!.walletPublicKey - val pbComprKey = ctx.card!!.walletPublicKeyRar - // build script for our address - val rawTxList = (ctx.coinData!! as BtcData).unspentTransactions - val outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes +// private fun requestElectrum(ctx: TangemContext, electrumRequest: ElectrumRequest) { +// if (UtilHelper.isOnline(this)) { +// serverApiElectrum.electrumRequestData(ctx, electrumRequest) +// } else +// finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain)) +// } - // collect unspent - val unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend) +// private fun requestInfura(method: String) { +// if (UtilHelper.isOnline(this)) { +// serverApiInfura.infura(method, 67, ctx.coinData!!.wallet, "", "") +// } else +// finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain)) +// } - var fullAmount: Long = 0 - for (i in unspentOutputs.indices) { - fullAmount += unspentOutputs[i].value - } - - // get first unspent -// val outPut = unspentOutputs[0] -// val outPutIndex = outPut.outputIndex - - // get prev TX id; -// val prevTXID = rawTxList[0].txID//"f67b838d6e2c0c587f476f583843e93ff20368eaf96a798bdc25e01f53f8f5d2"; - - val fees = FormatUtil.ConvertStringToLong(outFee) - var amount = FormatUtil.ConvertStringToLong(outAmount) - amount -= fees - - val change = fullAmount - fees - amount - - if (amount + fees > fullAmount) { - throw Exception(String.format("Balance (%d) < amount (%d) + (%d)", fullAmount, change, amount)) - } - - val hashesForSign = arrayOfNulls(unspentOutputs.size) - - for (i in unspentOutputs.indices) { - val newTX = BTCUtils.buildTXForSign(myAddress, outputAddress, myAddress, unspentOutputs, i, amount, change) - val hashData = Util.calculateSHA256(newTX) - val doubleHashData = Util.calculateSHA256(hashData) -// Log.e("TX_BODY_1", BTCUtils.toHex(newTX)) -// Log.e("TX_HASH_1", BTCUtils.toHex(hashData)) -// Log.e("TX_HASH_2", BTCUtils.toHex(doubleHashData)) - -// unspentOutputs[i].bodyDoubleHash = doubleHashData -// unspentOutputs[i].bodyHash = hashData - hashesForSign[i] = doubleHashData - } - - val signFromCard = ByteArray(64 * unspentOutputs.size) - - for (i in unspentOutputs.indices) { - val r = BigInteger(1, Arrays.copyOfRange(signFromCard, 0 + i * 64, 32 + i * 64)) - val s = BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)) - val encodingSign = DerEncodingUtil.packSignDer(r, s, pbKey) - unspentOutputs[i].scriptForBuild = encodingSign - } - - val realTX = BTCUtils.buildTXForSend(outputAddress, myAddress, unspentOutputs, amount, change) - - return realTX.size - } - - private fun requestElectrum(ctx: TangemContext, electrumRequest: ElectrumRequest) { - if (UtilHelper.isOnline(this)) { - serverApiElectrum.electrumRequestData(ctx, electrumRequest) - } else - finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain)) - } - - private fun requestInfura(method: String) { - if (UtilHelper.isOnline(this)) { - serverApiInfura.infura(method, 67, ctx.coinData!!.wallet, "", "") - } else - finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain)) - } - - private fun requestEstimateFee() { - if( calcSize==0 ) - { - calcSize = 256 - try { - - calcSize = buildSize(etWallet!!.text.toString(), "0.00", etAmount.text.toString()) - } catch (ex: Exception) { - Log.e("Build Fee error", ex.message) - } - } - serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_PRIORITY) - serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_NORMAL) - serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_MINIMAL) - } +// private fun requestEstimateFee() { +// if (calcSize == 0) { +// calcSize = 256 +// try { +// +// calcSize = buildSize(etWallet!!.text.toString(), "0.00", etAmount.text.toString()) +// } catch (ex: Exception) { +// Log.e("Build Fee error", ex.message) +// } +// } +// serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_PRIORITY) +// serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_NORMAL) +// serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_MINIMAL) +// } private fun doSetFee(checkedRadioButtonId: Int) { var txtFee = "" diff --git a/app/src/main/java/com/tangem/presentation/activity/MainActivity.kt b/app/src/main/java/com/tangem/presentation/activity/MainActivity.kt index 86e7dbe7d3..7a9f10344d 100644 --- a/app/src/main/java/com/tangem/presentation/activity/MainActivity.kt +++ b/app/src/main/java/com/tangem/presentation/activity/MainActivity.kt @@ -271,7 +271,7 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtoco } catch (e: Exception) { e.printStackTrace() - nfcManager!!.notifyReadResult(false) + nfcManager.notifyReadResult(false) } } @@ -279,11 +279,11 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtoco super.onResume() animate() ReadCardInfoTask.resetLastReadInfo() - nfcManager!!.onResume() + nfcManager.onResume() } public override fun onPause() { - nfcManager!!.onPause() + nfcManager.onPause() if (readCardInfoTask != null) { readCardInfoTask!!.cancel(true) } @@ -292,7 +292,7 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtoco public override fun onStop() { // dismiss enable NFC dialog - nfcManager!!.onStop() + nfcManager.onStop() if (readCardInfoTask != null) { readCardInfoTask!!.cancel(true) } diff --git a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt index a3779a6d78..48ccfa9006 100644 --- a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt +++ b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt @@ -22,24 +22,17 @@ import android.widget.Toast import com.tangem.App import com.tangem.Constant import com.tangem.data.network.ServerApiCommon -import com.tangem.data.network.ServerApiInfura import com.tangem.tangemserver.android.model.CardVerifyAndGetInfo -import com.tangem.data.network.model.InfuraResponse import com.tangem.tangemcard.tasks.VerifyCardTask import com.tangem.tangemcard.reader.CardProtocol import com.tangem.tangemcard.android.reader.NfcManager import com.tangem.domain.wallet.* -import com.tangem.domain.wallet.bch.BtcCashEngine -import com.tangem.domain.wallet.eth.EthData -import com.tangem.domain.wallet.token.TokenData -import com.tangem.domain.wallet.token.TokenEngine import com.tangem.presentation.activity.* import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog import com.tangem.presentation.dialog.PINSwapWarningDialog import com.tangem.presentation.dialog.ShowQRCodeDialog import com.tangem.presentation.dialog.WaitSecurityDelayDialog import com.tangem.data.Blockchain -import com.tangem.data.network.ElectrumRequest import com.tangem.tangemcard.android.reader.NfcReader import com.tangem.tangemcard.data.EXTRA_TANGEM_CARD import com.tangem.tangemcard.data.EXTRA_TANGEM_CARD_UID @@ -50,9 +43,7 @@ import com.tangem.tangemserver.android.ServerApiTangem import com.tangem.util.UtilHelper import com.tangem.wallet.R import kotlinx.android.synthetic.main.fr_loaded_wallet.* -import org.json.JSONException import java.io.InputStream -import java.math.BigInteger import java.util.* class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notifications, SharedPreferences.OnSharedPreferenceChangeListener { @@ -802,23 +793,21 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific requestVerifyAndGetInfo() - if (ctx.blockchain == Blockchain.Bitcoin || ctx.blockchain == Blockchain.BitcoinTestNet || ctx.blockchain == Blockchain.BitcoinCash) { - val coinEngine = CoinEngineFactory.create(ctx) - requestCounter++ - coinEngine!!.requestBalanceAndUnspentTransactions( - object : CoinEngine.BlockchainRequestsNotifications { - override fun onComplete(success: Boolean?) { - counterMinus() - updateViews() - } - - override fun needTerminate(): Boolean { - return !UtilHelper.isOnline(context as Activity) - } + val coinEngine = CoinEngineFactory.create(ctx) + requestCounter++ + coinEngine!!.requestBalanceAndUnspentTransactions( + object : CoinEngine.BalanceAndUnspentTransactionsNotifications { + override fun onComplete(success: Boolean?) { + counterMinus() + updateViews() } - ) - } + override fun needTerminate(): Boolean { + return !UtilHelper.isOnline(context as Activity) + } + } + ) + // Bitcoin if (ctx.blockchain == Blockchain.Bitcoin || ctx.blockchain == Blockchain.BitcoinTestNet) { @@ -865,15 +854,15 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific // } // } - private fun requestInfura(method: String, contract: String) { - if (UtilHelper.isOnline(context as Activity)) { - requestCounter++ - serverApiInfura.infura(method, 67, ctx.coinData!!.wallet, contract, "") - } else { - Toast.makeText(activity, getString(R.string.no_connection), Toast.LENGTH_SHORT).show() - srl?.isRefreshing = false - } - } +// private fun requestInfura(method: String, contract: String) { +// if (UtilHelper.isOnline(context as Activity)) { +// requestCounter++ +// serverApiInfura.infura(method, 67, ctx.coinData!!.wallet, contract, "") +// } else { +// Toast.makeText(activity, getString(R.string.no_connection), Toast.LENGTH_SHORT).show() +// srl?.isRefreshing = false +// } +// } private fun requestVerifyAndGetInfo() { if (UtilHelper.isOnline(context as Activity)) { diff --git a/tangemserver-android/src/main/java/com/tangem/tangemserver/android/data/LocalStorage.kt b/tangemserver-android/src/main/java/com/tangem/tangemserver/android/data/LocalStorage.kt index 009c26bb75..426ac14e0e 100644 --- a/tangemserver-android/src/main/java/com/tangem/tangemserver/android/data/LocalStorage.kt +++ b/tangemserver-android/src/main/java/com/tangem/tangemserver/android/data/LocalStorage.kt @@ -112,7 +112,7 @@ class LocalStorage var sData: String? = result.substitution?.data var sSignature: String? = result.substitution?.signature if (card.batch != result.batch) { - Log.e("CardDataSubstitutionProvider", "Invalid batch received!") + Log.e("CardDataSubstitution", "Invalid batch received!") return false } if (!BatchInfo.CardDataSubstitution.verifySignature(card, sData, sSignature)) { From 97c60b5f9d07d895687726b6ca4052c1d83c8a1f Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 16 Dec 2018 19:51:54 +0300 Subject: [PATCH 4/7] Updated on 2026-08-14 --- .../tangem/data/network/ElectrumRequest.java | 41 +- .../tangem/data/network/ServerApiCommon.java | 6 +- .../data/network/ServerApiElectrum.java | 108 ++--- .../tangem/data/network/ServerApiInfura.java | 14 +- .../com/tangem/domain/wallet/CoinData.java | 55 ++- .../com/tangem/domain/wallet/CoinEngine.java | 36 +- .../tangem/domain/wallet/TangemContext.java | 4 + .../domain/wallet/bch/BtcCashEngine.java | 208 ++++++++- .../tangem/domain/wallet/btc/BtcEngine.java | 438 +++++++++--------- .../tangem/domain/wallet/eth/EthEngine.java | 126 +++-- .../tangem/domain/wallet/token/TokenData.java | 6 +- .../domain/wallet/token/TokenEngine.java | 170 ++++--- .../activity/ConfirmPaymentActivity.kt | 102 ++-- .../activity/SendTransactionActivity.kt | 170 +++---- .../activity/SignPaymentActivity.kt | 141 +++++- .../presentation/fragment/LoadedWallet.kt | 79 +++- app/src/main/res/values/strings.xml | 3 + .../com/tangem/tangemcard/tasks/SignTask.java | 2 +- 18 files changed, 1098 insertions(+), 611 deletions(-) diff --git a/app/src/main/java/com/tangem/data/network/ElectrumRequest.java b/app/src/main/java/com/tangem/data/network/ElectrumRequest.java index 7fc556cf62..adcb1bac52 100644 --- a/app/src/main/java/com/tangem/data/network/ElectrumRequest.java +++ b/app/src/main/java/com/tangem/data/network/ElectrumRequest.java @@ -17,14 +17,14 @@ public class ElectrumRequest { public static final String METHOD_SendTransaction = "blockchain.transaction.broadcast"; public static final String METHOD_GetFee = "blockchain.estimatefee"; - public JSONObject jsRequestData; - public String answerData; - public String error; - public String walletAddress; + private JSONObject jsRequestData; + String answerData; + private String error = null; + private String walletAddress; public String txHash; - public String TX; - public String host; - public int port; + private String TX; + String host; + int port; private ElectrumRequest() { } @@ -155,15 +155,30 @@ public class ElectrumRequest { return getAnswer().getJSONObject("result"); } - public JSONObject getError() throws JSONException { - JSONObject answer = getAnswer(); - if (answer.has("error")) { - return getAnswer().getJSONObject("error"); - } else { - return null; + public String getError() { + if( answerData!=null ) { + // answer received - return error from it + JSONObject answer = getAnswer(); + if (answer.has("error")) { + try { + return getAnswer().getJSONObject("error").toString(); + } catch (JSONException e) { + e.printStackTrace(); + return null; + } + } else { + return null; + } + }else{ + // no answer received - return saved error reason + return error; } } + public void setError(String error) { + this.error = error; + } + public String getResultString() throws JSONException { if (getAnswer().has("result")) { return getAnswer().getString("result"); diff --git a/app/src/main/java/com/tangem/data/network/ServerApiCommon.java b/app/src/main/java/com/tangem/data/network/ServerApiCommon.java index cc0fc7edcc..7ab8fe42e0 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiCommon.java +++ b/app/src/main/java/com/tangem/data/network/ServerApiCommon.java @@ -28,7 +28,7 @@ public class ServerApiCommon { public interface EstimateFeeListener { void onSuccess(int blockCount, String estimateFeeResponse); - void onFail(String message); + void onFail(int blockCount, String message); } public void setEstimateFee(EstimateFeeListener listener) { @@ -63,13 +63,13 @@ public class ServerApiCommon { estimateFeeListener.onSuccess(blockCount, response.body()); Log.i(TAG, "estimateFee onResponse " + response.code() + " " + response.body()); } else - estimateFeeListener.onFail(response.body()); + estimateFeeListener.onFail(blockCount, response.body()); Log.e(TAG, "estimateFee onResponse " + response.code()); } @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { - estimateFeeListener.onFail(t.getMessage()); + estimateFeeListener.onFail(blockCount, t.getMessage()); Log.e(TAG, "estimateFee onFailure " + t.getMessage()); } }); diff --git a/app/src/main/java/com/tangem/data/network/ServerApiElectrum.java b/app/src/main/java/com/tangem/data/network/ServerApiElectrum.java index 19b0404fed..87c4cedb6c 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiElectrum.java +++ b/app/src/main/java/com/tangem/data/network/ServerApiElectrum.java @@ -8,6 +8,7 @@ import com.tangem.data.Blockchain; import com.tangem.domain.wallet.bch.BitcoinCashNode; import com.tangem.domain.wallet.btc.BitcoinNode; import com.tangem.domain.wallet.btc.BitcoinNodeTestNet; +import com.tangem.wallet.R; import java.io.BufferedReader; import java.io.IOException; @@ -54,23 +55,15 @@ public class ServerApiElectrum { private int requestsCount=0; - public boolean hasRequests() { - return requestsCount>0; - } - - private String error=null; - public boolean isErrorOccured() { - return error!=null; - } - - public void setErrorOccured(String error) { - this.error=error; + public boolean isRequestsSequenceCompleted() { + Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount)); + return requestsCount <= 0; } public interface ElectrumRequestDataListener { void onSuccess(ElectrumRequest electrumRequest); - void onFail(String method); + void onFail(ElectrumRequest electrumRequest); } public void setElectrumRequestData(ElectrumRequestDataListener listener) { @@ -79,6 +72,7 @@ public class ServerApiElectrum { public void electrumRequestData(TangemContext ctx, ElectrumRequest electrumRequest) { requestsCount++; + Log.i(TAG, String.format("New request[%d]: %s", requestsCount,electrumRequest.getMethod())); Observable checkElectrumDataObserver = Observable.just(electrumRequest) .doOnNext(electrumRequest1 -> doElectrumRequest(ctx, electrumRequest)) @@ -97,33 +91,47 @@ public class ServerApiElectrum { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); checkElectrumDataObserver.subscribe(new DefaultObserver() { + //TODO remove onNext @Override public void onNext(ElectrumRequest v) { if (electrumRequest.answerData != null) { - requestsCount--; - electrumRequestDataListener.onSuccess(electrumRequest); -// Log.i(TAG, "electrumRequestData " + electrumRequest.getMethod() + " onNext != null"); + Log.i(TAG, "electrumRequestData " + electrumRequest.getMethod() + " onNext != null"); } else { - electrumRequestDataListener.onFail(electrumRequest.getMethod()); Log.e(TAG, "electrumRequestData " + electrumRequest.getMethod() + " onNext == null"); } } @Override public void onError(Throwable e) { - electrumRequestDataListener.onFail(electrumRequest.getMethod()); + requestsCount--; Log.e(TAG, "electrumRequestData " + electrumRequest.getMethod() + " onError " + e.getMessage()); + Log.e(TAG, String.format("%d requests left in processing",requestsCount)); + electrumRequest.setError(ctx.getString(R.string.cannot_obtain_data_from_blockchain)); + //setErrorOccurred(e.getMessage());//; + electrumRequestDataListener.onFail(electrumRequest); } @Override public void onComplete() { -// Log.i(TAG, "electrumRequestData " + electrumRequest.getMethod() + " onComplete"); + requestsCount--; + if (electrumRequest.answerData != null) { + Log.i(TAG, "electrumRequestData " + electrumRequest.getMethod() + " onComplete, answerData!=null"); + } else { + Log.e(TAG, "electrumRequestData " + electrumRequest.getMethod() + " onComplete, answerData==null"); + } + Log.e(TAG, String.format("%d requests left in processing",requestsCount)); + if (electrumRequest.answerData != null) { + electrumRequestDataListener.onSuccess(electrumRequest); + } else { +// if( error==null || error.isEmpty() ) setErrorOccurred(ctx.getString(R.string.cannot_obtain_data_from_blockchain)); + electrumRequestDataListener.onFail(electrumRequest); + } } }); } - private List doElectrumRequest(TangemContext ctx, ElectrumRequest electrumRequest) { + private void doElectrumRequest(TangemContext ctx, ElectrumRequest electrumRequest) { String host; int port; String proto; @@ -135,8 +143,7 @@ public class ServerApiElectrum { this.host = host; this.port = port; - return doElectrumRequestTcp(electrumRequest, host, port); - + doElectrumRequestTcp(electrumRequest, host, port); } else if (ctx.getBlockchain() == Blockchain.BitcoinCash) { BitcoinCashNode bitcoinCashNode = BitcoinCashNode.values()[new Random().nextInt(BitcoinCashNode.values().length)]; host = bitcoinCashNode.getHost(); @@ -147,9 +154,9 @@ public class ServerApiElectrum { this.port = port; if (proto.equals("tcp")) { - return doElectrumRequestTcp(electrumRequest, host, port); + doElectrumRequestTcp(electrumRequest, host, port); } else { - return doElectrumRequestSsl(electrumRequest, host, port); + doElectrumRequestSsl(electrumRequest, host, port); } } else if (ctx.getBlockchain() == Blockchain.Bitcoin) { @@ -162,23 +169,19 @@ public class ServerApiElectrum { this.port = port; if (proto.equals("tcp")) { - return doElectrumRequestTcp(electrumRequest, host, port); + doElectrumRequestTcp(electrumRequest, host, port); } else { - return doElectrumRequestSsl(electrumRequest, host, port); + doElectrumRequestSsl(electrumRequest, host, port); } } - return null; } - private List doElectrumRequestTcp(ElectrumRequest electrumRequest, String host, int port) { - List result = new ArrayList<>(); - Collections.addAll(result, electrumRequest); - + private void doElectrumRequestTcp(ElectrumRequest electrumRequest, String host, int port) { try { Socket socket = App.getNetworkComponent().getSocket(); socket.setSoTimeout(3000); + Log.i(TAG, "Start process "+electrumRequest.getMethod()+" @ "+host + ":" + port); socket.connect(new InetSocketAddress(InetAddress.getByName(host), port)); - Log.i(TAG, host + " " + port); try { OutputStream os = socket.getOutputStream(); OutputStreamWriter out = new OutputStreamWriter(os, "UTF-8"); @@ -195,37 +198,38 @@ public class ServerApiElectrum { if (electrumRequest.answerData != null) { Log.i(TAG, ">> " + electrumRequest.answerData); } else { - electrumRequest.error = "No answer from server"; + electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_no_answer)); Log.i(TAG, ">> "); } } catch (ConnectException e) { - e.printStackTrace(); - electrumRequestDataListener.onFail(e.getMessage()); - Log.e(TAG, "electrumRequestData " + electrumRequest.getMethod() + " ConnectException " + e.getMessage()); + //e.printStackTrace(); + //electrumRequestDataListener.onFail(e.getMessage()); + electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_no_connection)); + Log.e(TAG, "doElectrumRequestTcp " + electrumRequest.getMethod() + " ConnectException " + e.getMessage()); } finally { - Log.i(TAG, "electrumRequestData " + electrumRequest.getMethod() + " CLOSE"); + Log.i(TAG, "doElectrumRequestTcp " + electrumRequest.getMethod() + " socket.close"); socket.close(); } } catch (IOException e) { - e.printStackTrace(); - electrumRequestDataListener.onFail(e.getMessage()); - Log.e(TAG, "electrumRequestData " + electrumRequest.getMethod() + " IOException " + e.getMessage()); + //e.printStackTrace(); + //electrumRequestDataListener.onFail(e.getMessage()); + electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_communication_error)); + Log.e(TAG, "doElectrumRequestTcp " + electrumRequest.getMethod() + " IOException " + e.getMessage()); } - return result; } - private List doElectrumRequestSsl(ElectrumRequest electrumRequest, String host, int port) { + private void doElectrumRequestSsl(ElectrumRequest electrumRequest, String host, int port) { try { // create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + public void checkClientTrusted(X509Certificate[] chain, String authType) { } @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + public void checkServerTrusted(X509Certificate[] chain, String authType) { } @@ -253,8 +257,8 @@ public class ServerApiElectrum { Collections.addAll(result, electrumRequest); try { - sslSocket = (SSLSocket) sf.createSocket(host, port); Log.i(TAG, host + " " + port); + sslSocket = (SSLSocket) sf.createSocket(host, port); try { OutputStream os = sslSocket.getOutputStream(); OutputStreamWriter out = new OutputStreamWriter(os, "UTF-8"); @@ -269,30 +273,28 @@ public class ServerApiElectrum { if (electrumRequest.answerData != null) { Log.i(TAG, ">> " + electrumRequest.answerData); } else { - electrumRequest.error = "No answer from server"; + electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_no_answer)); Log.i(TAG, ">> "); } } catch (ConnectException e) { e.printStackTrace(); - Log.e(TAG, "electrumRequestData " + electrumRequest.getMethod() + " ConnectException " + e.getMessage()); + electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_no_connection)); + Log.e(TAG, "doElectrumRequestTcp " + electrumRequest.getMethod() + " ConnectException " + e.getMessage()); } finally { - Log.i(TAG, "electrumRequestData " + electrumRequest.getMethod() + " CLOSE"); + Log.i(TAG, "doElectrumRequestTcp " + electrumRequest.getMethod() + " socket.close"); sslSocket.close(); } } catch (IOException e) { e.printStackTrace(); - Log.e(TAG, "electrumRequestData " + electrumRequest.getMethod() + " IOException " + e.getMessage()); + electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_communication_error)); + Log.e(TAG, "doElectrumRequestTcp " + electrumRequest.getMethod() + " IOException " + e.getMessage()); } - - return result; - } catch (NoSuchAlgorithmException | KeyManagementException e) { + electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain)); Log.e(TAG, e.getMessage()); } - - return null; } public String getValidationNodeDescription() { diff --git a/app/src/main/java/com/tangem/data/network/ServerApiInfura.java b/app/src/main/java/com/tangem/data/network/ServerApiInfura.java index c6d33242ca..34b2f33f3f 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiInfura.java +++ b/app/src/main/java/com/tangem/data/network/ServerApiInfura.java @@ -33,17 +33,9 @@ public class ServerApiInfura { private int requestsCount=0; - public boolean hasRequests() { - return requestsCount>0; - } - - private String error=null; - public boolean isErrorOccured() { - return error!=null; - } - - public void setErrorOccured(String error) { - this.error=error; + public boolean isRequestsSequenceCompleted() { + Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount)); + return requestsCount <= 0; } private InfuraBodyListener infuraBodyListener; diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinData.java b/app/src/main/java/com/tangem/domain/wallet/CoinData.java index 1fe6c38df8..9447ee6935 100644 --- a/app/src/main/java/com/tangem/domain/wallet/CoinData.java +++ b/app/src/main/java/com/tangem/domain/wallet/CoinData.java @@ -47,8 +47,8 @@ public abstract class CoinData { validationNodeDescription = B.getString("validationNodeDescription"); - if (B.containsKey("FailedBalance")) - failedBalanceRequestCounter = new AtomicInteger(B.getInt("FailedBalance")); +// if (B.containsKey("FailedBalance")) +// failedBalanceRequestCounter = new AtomicInteger(B.getInt("FailedBalance")); if (B.containsKey("isBalanceEqual")) setIsBalanceEqual(B.getBoolean("isBalanceEqual")); @@ -64,8 +64,8 @@ public abstract class CoinData { if (balanceEqual != null) B.putBoolean("isBalanceEqual", balanceEqual); - if (failedBalanceRequestCounter != null) - B.putInt("FailedBalance", failedBalanceRequestCounter.get()); +// if (failedBalanceRequestCounter != null) +// B.putInt("FailedBalance", failedBalanceRequestCounter.get()); B.putFloat("rate", rate); B.putFloat("rateAlter", rateAlter); @@ -143,25 +143,32 @@ public abstract class CoinData { public void clearInfo() { setIsBalanceEqual(false); + setBalanceReceived(false); // TODO check + setValidationNodeDescription(""); + minFee=null; + maxFee=null; + normalFee=null; + rate=0f; + rateAlter=0f; } - 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(); - } +// 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(); +// } private Boolean balanceEqual; @@ -183,7 +190,7 @@ public abstract class CoinData { this.validationNodeDescription = validationNodeDescription; } - - - + public CoinEngine.Amount minFee = null; + public CoinEngine.Amount normalFee = null; + public CoinEngine.Amount maxFee = null; } diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java index 2ad484f4c8..229593fe4c 100644 --- a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java @@ -252,7 +252,7 @@ public abstract class CoinEngine { { void onPaymentPrepared(byte[] txForSend); } - private OnNeedSendPayment onNeedSendPayment; + protected OnNeedSendPayment onNeedSendPayment; public void setOnNeedSendPayment(OnNeedSendPayment onNeedSendPayment) { this.onNeedSendPayment = onNeedSendPayment; @@ -262,22 +262,34 @@ public abstract class CoinEngine { if(onNeedSendPayment==null) throw new Exception("Payment signed but no callback defined to send!"); onNeedSendPayment.onPaymentPrepared(txForSend); - } - public interface BalanceAndUnspentTransactionsNotifications + public interface BlockchainRequestsCallbacks { + /** + * Notification that the all requests in sequence completed + * Call after a last request completed + * If occurred error return in ctx.error + * @param success -* + */ void onComplete(Boolean success); - boolean needTerminate(); + + /** + * Notification that a new part of data received and it's possible to update view + * May call when some request in the sequence completed but there are still a few requests left + */ + void onProgress(); + + /** + * Return flag that allow to add new or re-requests in the sequence + * Call between requests or when request fail and before re-request + * @return true if not need terminate (e.g. activity is online) + */ + boolean allowAdvance(); } - public abstract void requestBalanceAndUnspentTransactions(BalanceAndUnspentTransactionsNotifications balanceAndUnspentTransactionsNotifications) throws Exception; + public abstract void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) throws Exception; + public abstract void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception; - public interface FeeRequestsNotifications - { - void onComplete(boolean success, Amount minFee, Amount normalFee, Amount maxFee); - boolean needTerminate(); - } - public abstract void requestFee(FeeRequestsNotifications feeRequestsNotifications, CoinEngine.Amount amount) throws Exception; - + public abstract void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) throws Exception; } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/domain/wallet/TangemContext.java b/app/src/main/java/com/tangem/domain/wallet/TangemContext.java index 8f1c9b06a3..5802887489 100644 --- a/app/src/main/java/com/tangem/domain/wallet/TangemContext.java +++ b/app/src/main/java/com/tangem/domain/wallet/TangemContext.java @@ -85,6 +85,10 @@ public class TangemContext { return error; } + public boolean hasError() { + return error!=null && !error.isEmpty(); + } + public void setMessage(String value) { this.message = value; } diff --git a/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java b/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java index 82656791fd..152cca93e5 100644 --- a/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java @@ -5,8 +5,10 @@ import android.text.InputFilter; import android.util.Log; import com.tangem.data.network.ElectrumRequest; +import com.tangem.data.network.ServerApiCommon; import com.tangem.data.network.ServerApiElectrum; import com.tangem.domain.wallet.BCHUtils; +import com.tangem.domain.wallet.BTCUtils; import com.tangem.tangemcard.reader.CardProtocol; import com.tangem.domain.wallet.BalanceValidator; import com.tangem.data.Blockchain; @@ -22,6 +24,7 @@ import com.tangem.util.CryptoUtil; import com.tangem.util.DecimalDigitsInputFilter; import com.tangem.util.DerEncodingUtil; import com.tangem.tangemcard.util.Util; +import com.tangem.util.FormatUtil; import com.tangem.wallet.R; import org.json.JSONArray; @@ -31,6 +34,7 @@ import org.json.JSONObject; import java.io.ByteArrayOutputStream; import java.math.BigDecimal; import java.math.BigInteger; +import java.math.RoundingMode; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; @@ -538,7 +542,7 @@ public class BtcCashEngine extends CoinEngine { } @Override - public void onSignCompleted(byte[] signFromCard) throws Exception { + public byte[] onSignCompleted(byte[] signFromCard) throws Exception { for (int i = 0; i < unspentOutputs.size(); ++i) { BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, i * 64, 32 + i * 64)); BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); @@ -550,12 +554,13 @@ public class BtcCashEngine extends CoinEngine { byte[] txForSend = BCHUtils.buildTXForSend(destLegacyAddress, srcLegacyAddress, unspentOutputs, amountFinal, changeFinal); notifyOnNeedSendPayment(txForSend); + return txForSend; } }; } @Override - public void requestBalanceAndUnspentTransactions(BalanceAndUnspentTransactionsNotifications balanceAndUnspentTransactionsNotifications) throws Exception { + public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) throws Exception { final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); ServerApiElectrum.ElectrumRequestDataListener electrumBodyListener = new ServerApiElectrum.ElectrumRequestDataListener() { @@ -607,10 +612,10 @@ public class BtcCashEngine extends CoinEngine { Integer height = jsUnspent.getInt("height"); String hash = jsUnspent.getString("tx_hash"); if (height != -1) { - if (!balanceAndUnspentTransactionsNotifications.needTerminate()) { + if (blockchainRequestsCallbacks.allowAdvance()) { serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.getTransaction(walletAddress, hash)); } else { - serverApiElectrum.setErrorOccured("Terminated by user"); + ctx.setError("Terminated by user"); } } } @@ -632,15 +637,21 @@ public class BtcCashEngine extends CoinEngine { } } - if (!serverApiElectrum.hasRequests()) { - balanceAndUnspentTransactionsNotifications.onComplete(serverApiElectrum.isErrorOccured()); + if (serverApiElectrum.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + }else{ + blockchainRequestsCallbacks.onProgress(); } } @Override - public void onFail(String method) { - if (!serverApiElectrum.hasRequests()) { - balanceAndUnspentTransactionsNotifications.onComplete(serverApiElectrum.isErrorOccured()); + public void onFail(ElectrumRequest electrumRequest) { + Log.i(TAG, "onFail: "+electrumRequest.getMethod()+" "+electrumRequest.getError()); + ctx.setError(electrumRequest.getError()); + if (serverApiElectrum.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(false);//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError()); + }else{ + blockchainRequestsCallbacks.onProgress(); } } }; @@ -651,4 +662,183 @@ public class BtcCashEngine extends CoinEngine { serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.listUnspent(convertToLegacyAddress(coinData.getWallet()))); } + private Integer buildSize(String outputAddress, String outFee, String outAmount) { + //todo - проверить, правильней было бы использовать constructPayment + try { + String myAddress = coinData.getWallet(); + byte[] pbKey = ctx.getCard().getWalletPublicKey(); + byte[] pbComprKey = ctx.getCard().getWalletPublicKeyRar(); + + // build script for our address + List rawTxList = coinData.getUnspentTransactions(); + byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; + + // collect unspent + ArrayList unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); + + Long fullAmount = 0L; + for (int i = 0; i < unspentOutputs.size(); i++) { + fullAmount += unspentOutputs.get(i).value; + } + + // get first unspent +// val outPut = unspentOutputs[0] +// val outPutIndex = outPut.outputIndex + + // get prev TX id; +// val prevTXID = rawTxList[0].txID//"f67b838d6e2c0c587f476f583843e93ff20368eaf96a798bdc25e01f53f8f5d2"; + + Long fees = FormatUtil.ConvertStringToLong(outFee); + Long amount = FormatUtil.ConvertStringToLong(outAmount); + amount -= fees; + + Long change = fullAmount - fees - amount; + + if (amount + fees > fullAmount) { + throw new Exception(String.format("Balance (%d) < amount (%d) + (%d)", fullAmount, change, amount)); + } + + byte[][] hashesForSign = new byte[unspentOutputs.size()][]; + + for (int i = 0; i < unspentOutputs.size(); i++) { + byte[] newTX = BTCUtils.buildTXForSign(myAddress, outputAddress, myAddress, unspentOutputs, i, amount, change); + byte[] hashData = Util.calculateSHA256(newTX); + byte[] doubleHashData = Util.calculateSHA256(hashData); +// Log.e("TX_BODY_1", BTCUtils.toHex(newTX)) +// Log.e("TX_HASH_1", BTCUtils.toHex(hashData)) +// Log.e("TX_HASH_2", BTCUtils.toHex(doubleHashData)) + +// unspentOutputs[i].bodyDoubleHash = doubleHashData +// unspentOutputs[i].bodyHash = hashData + hashesForSign[i] = doubleHashData; + } + + byte[] signFromCard = new byte[64 * unspentOutputs.size()]; + + for (int i = 0; i < unspentOutputs.size(); i++) { + BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0 + i * 64, 32 + i * 64)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); + byte[] encodingSign = DerEncodingUtil.packSignDer(r, s, pbKey); + unspentOutputs.get(i).scriptForBuild = encodingSign; + } + + byte[] realTX = BTCUtils.buildTXForSend(outputAddress, myAddress, unspentOutputs, amount, change); + + return realTX.length; + } + catch (Exception e) + { + e.printStackTrace(); + Log.e(TAG, "Can't calculate transaction size -> use default!"); + return 256; + } + } + + @Override + public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception { + final int calcSize = buildSize(targetAddress, "0.00", amount.toValueString()); + coinData.minFee=null; + coinData.maxFee=null; + coinData.normalFee=null; + + final ServerApiCommon serverApiCommon = new ServerApiCommon(); + + final ServerApiCommon.EstimateFeeListener estimateFeeListener = new ServerApiCommon.EstimateFeeListener() { + @Override + public void onSuccess(int blockCount, String estimateFeeResponse) { + BigDecimal fee = new BigDecimal(estimateFeeResponse); // BTC per 1 kb + + if (fee.equals(BigDecimal.ZERO)) { + if (blockchainRequestsCallbacks.allowAdvance()) { + serverApiCommon.estimateFee(blockCount); + } + return; + } + + if (calcSize != 0) { + fee = fee.multiply(new BigDecimal(calcSize)).divide(new BigDecimal(1024)); // per Kb -> per byte + } else { + if (blockchainRequestsCallbacks.allowAdvance()) { + serverApiCommon.estimateFee(blockCount); + } + return; + } + + fee = fee.setScale(8, RoundingMode.DOWN); + + switch (blockCount) { + case ServerApiCommon.ESTIMATE_FEE_MINIMAL: + coinData.minFee = new CoinEngine.Amount(fee, getFeeCurrency()); + break; + case ServerApiCommon.ESTIMATE_FEE_NORMAL: + coinData.normalFee = new CoinEngine.Amount(fee, getFeeCurrency()); + break; + case ServerApiCommon.ESTIMATE_FEE_PRIORITY: + coinData.maxFee = new CoinEngine.Amount(fee, getFeeCurrency()); + break; + } + blockchainRequestsCallbacks.onComplete(true); + } + + @Override + public void onFail(int blockCount, String message) { + // TODO - add fail counter to terminate after NNN tries + if (blockchainRequestsCallbacks.allowAdvance()) { + serverApiCommon.estimateFee(blockCount); + } + ctx.setError(ctx.getContext().getString(R.string.cannot_calculate_fee_wrong_data_received_from_node)); + blockchainRequestsCallbacks.onComplete(false); + } + }; + serverApiCommon.setEstimateFee(estimateFeeListener); + + serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_PRIORITY); + serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_NORMAL); + serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_MINIMAL); + + } + + @Override + public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) throws Exception { + final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); + final String txStr = BTCUtils.toHex(txForSend); + + ServerApiElectrum.ElectrumRequestDataListener electrumBodyListener = new ServerApiElectrum.ElectrumRequestDataListener() { + @Override + public void onSuccess(ElectrumRequest electrumRequest) { + if (electrumRequest.isMethod(ElectrumRequest.METHOD_SendTransaction)) { + try { + String resultString = electrumRequest.getResultString(); + if (resultString == null || resultString.isEmpty()) { + ctx.setError("Rejected by node: " + electrumRequest.getError()); + blockchainRequestsCallbacks.onComplete(false); + }else { + ctx.setError(null); + blockchainRequestsCallbacks.onComplete(true); + } + } catch (Exception e) { + if (e.getMessage() != null) { + ctx.setError(e.getMessage()); + blockchainRequestsCallbacks.onComplete(false); + } else { + ctx.setError(e.getClass().getName()); + blockchainRequestsCallbacks.onComplete(false); + } + } + } + } + + @Override + public void onFail(ElectrumRequest electrumRequest) { + ctx.setError(electrumRequest.getError()); + blockchainRequestsCallbacks.onComplete(false); + } + }; + serverApiElectrum.setElectrumRequestData(electrumBodyListener); + + + serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.broadcast(ctx.getCoinData().getWallet(), txStr)); + + } + } diff --git a/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java b/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java index 53d97fe589..01a8818e17 100644 --- a/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java @@ -21,7 +21,6 @@ import com.tangem.util.CryptoUtil; import com.tangem.util.DecimalDigitsInputFilter; import com.tangem.util.DerEncodingUtil; import com.tangem.tangemcard.util.Util; -import com.tangem.util.FormatUtil; import com.tangem.wallet.R; import com.tangem.data.network.ElectrumRequest; import com.tangem.data.network.ServerApiElectrum; @@ -248,14 +247,15 @@ public class BtcEngine extends CoinEngine { @Override public boolean validateBalance(BalanceValidator balanceValidator) { - if (((ctx.getCard().getOfflineBalance() == null) && !ctx.getCoinData().isBalanceReceived()) || (!ctx.getCoinData().isBalanceReceived() && (ctx.getCard().getRemainingSignatures() != ctx.getCard().getMaxSignatures()))) { - balanceValidator.setScore(0); - balanceValidator.setFirstLine("Unknown balance"); - balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh."); - return false; - } + try { + if (((ctx.getCard().getOfflineBalance() == null) && !ctx.getCoinData().isBalanceReceived()) || (!ctx.getCoinData().isBalanceReceived() && (ctx.getCard().getRemainingSignatures() != ctx.getCard().getMaxSignatures()))) { + balanceValidator.setScore(0); + balanceValidator.setFirstLine("Unknown balance"); + balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh."); + return false; + } - // Workaround before new back-end + // Workaround before new back-end // if (card.getRemainingSignatures() == card.getMaxSignatures()) { // firstLine = "Verified balance"; // secondLine = "Balance confirmed in blockchain. "; @@ -263,24 +263,24 @@ public class BtcEngine extends CoinEngine { // return; // } - if (coinData.getBalanceUnconfirmed() != 0) { - balanceValidator.setScore(0); - balanceValidator.setFirstLine("Transaction in progress"); - balanceValidator.setSecondLine("Wait for confirmation in blockchain"); - return false; - } - - if (coinData.isBalanceReceived() && coinData.isBalanceEqual()) { - balanceValidator.setScore(100); - balanceValidator.setFirstLine("Verified balance"); - balanceValidator.setSecondLine("Balance confirmed in blockchain"); - if (coinData.getBalanceInInternalUnits().isZero()) { - balanceValidator.setFirstLine("Empty wallet"); - balanceValidator.setSecondLine(""); + if (coinData.getBalanceUnconfirmed() != 0) { + balanceValidator.setScore(0); + balanceValidator.setFirstLine("Transaction in progress"); + balanceValidator.setSecondLine("Wait for confirmation in blockchain"); + return false; } - } - // rule 4 TODO: need to check SignedHashed against number of outputs in blockchain + if (coinData.isBalanceReceived() && coinData.isBalanceEqual()) { + balanceValidator.setScore(100); + balanceValidator.setFirstLine("Verified balance"); + balanceValidator.setSecondLine("Balance confirmed in blockchain"); + if (coinData.getBalanceInInternalUnits().isZero()) { + balanceValidator.setFirstLine("Empty wallet"); + balanceValidator.setSecondLine(""); + } + } + + // rule 4 TODO: need to check SignedHashed against number of outputs in blockchain // if((card.getRemainingSignatures() != card.getMaxSignatures()) && card.getBalance() != 0) // { // score = 80; @@ -289,11 +289,11 @@ public class BtcEngine extends CoinEngine { // return; // } - if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) { - balanceValidator.setScore(80); - balanceValidator.setFirstLine("Verified offline balance"); - balanceValidator.setSecondLine("Can't obtain balance from blockchain. Restore internet connection to be more confident. "); - } + if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) { + balanceValidator.setScore(80); + balanceValidator.setFirstLine("Verified offline balance"); + balanceValidator.setSecondLine("Can't obtain balance from blockchain. Restore internet connection to be more confident. "); + } // if(card.getFailedBalanceRequestCounter()!=0) { // score -= 5 * card.getFailedBalanceRequestCounter(); @@ -302,7 +302,7 @@ public class BtcEngine extends CoinEngine { // return; // } - // + // // if(card.isBalanceReceived() && !card.isBalanceEqual()) { // score = 0; // firstLine = "Disputed balance"; @@ -310,7 +310,13 @@ public class BtcEngine extends CoinEngine { // return; // } - return true; + return true; + } + catch (Exception e) + { + e.printStackTrace(); + return false; + } } @Override @@ -384,7 +390,7 @@ public class BtcEngine extends CoinEngine { } @Override - public InternalAmount convertToInternalAmount(Amount amount) throws Exception { + public InternalAmount convertToInternalAmount(Amount amount) { BigDecimal d = amount.multiply(new BigDecimal("100000000")); return new InternalAmount(d, "Satoshi"); } @@ -398,7 +404,7 @@ public class BtcEngine extends CoinEngine { } @Override - public byte[] convertToByteArray(InternalAmount internalAmount) throws Exception { + public byte[] convertToByteArray(InternalAmount internalAmount) { byte[] bytes = Util.longToByteArray(internalAmount.longValueExact()); byte[] reversed = new byte[bytes.length]; for (int i = 0; i < bytes.length; i++) reversed[i] = bytes[bytes.length - i - 1]; @@ -501,7 +507,7 @@ public class BtcEngine extends CoinEngine { } @Override - public void onSignCompleted(byte[] signFromCard) throws Exception { + public byte[] onSignCompleted(byte[] signFromCard) throws Exception { for (int i = 0; i < unspentOutputs.size(); ++i) { BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, i * 64, 32 + i * 64)); BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); @@ -512,17 +518,19 @@ public class BtcEngine extends CoinEngine { byte[] txForSend = BTCUtils.buildTXForSend(targetAddress, myAddress, unspentOutputs, amountFinal, changeFinal); notifyOnNeedSendPayment(txForSend); + return txForSend; } }; } @Override - public void requestBalanceAndUnspentTransactions(BalanceAndUnspentTransactionsNotifications balanceAndUnspentTransactionsNotifications) { + public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) { final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); - ServerApiElectrum.ElectrumRequestDataListener electrumBodyListener = new ServerApiElectrum.ElectrumRequestDataListener() { + ServerApiElectrum.ElectrumRequestDataListener electrumListener = new ServerApiElectrum.ElectrumRequestDataListener() { @Override public void onSuccess(ElectrumRequest electrumRequest) { + Log.i(TAG, "onSuccess: "+electrumRequest.getMethod()); if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetBalance)) { try { String walletAddress = electrumRequest.getParams().getString(0); @@ -543,9 +551,7 @@ public class BtcEngine extends CoinEngine { e.printStackTrace(); Log.e(TAG, "FAIL METHOD_GetBalance Exception"); } - } - - if (electrumRequest.isMethod(ElectrumRequest.METHOD_ListUnspent)) { + } else if (electrumRequest.isMethod(ElectrumRequest.METHOD_ListUnspent)) { try { String walletAddress = electrumRequest.getParams().getString(0); JSONArray jsUnspentArray = electrumRequest.getResultArray(); @@ -569,19 +575,17 @@ public class BtcEngine extends CoinEngine { Integer height = jsUnspent.getInt("height"); String hash = jsUnspent.getString("tx_hash"); if (height != -1) { - if (!balanceAndUnspentTransactionsNotifications.needTerminate()) { + if (blockchainRequestsCallbacks.allowAdvance()) { serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.getTransaction(walletAddress, hash)); } else { - serverApiElectrum.setErrorOccured("Terminated by user"); + ctx.setError("Terminated by user"); } } } } catch (JSONException e) { e.printStackTrace(); } - } - - if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetTransaction)) { + } else if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetTransaction)) { try { String txHash = electrumRequest.txHash; String raw = electrumRequest.getResultString(); @@ -594,98 +598,122 @@ public class BtcEngine extends CoinEngine { } } - if (!serverApiElectrum.hasRequests()) { - balanceAndUnspentTransactionsNotifications.onComplete(serverApiElectrum.isErrorOccured()); + if (serverApiElectrum.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + }else{ + blockchainRequestsCallbacks.onProgress(); } } @Override - public void onFail(String method) { - if (!serverApiElectrum.hasRequests()) { - balanceAndUnspentTransactionsNotifications.onComplete(serverApiElectrum.isErrorOccured()); + public void onFail(ElectrumRequest electrumRequest) { + Log.i(TAG, "onFail: "+electrumRequest.getMethod()+" "+electrumRequest.getError()); + ctx.setError(electrumRequest.getError()); + if (serverApiElectrum.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(false);//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError()); + }else{ + blockchainRequestsCallbacks.onProgress(); } } }; - serverApiElectrum.setElectrumRequestData(electrumBodyListener); + serverApiElectrum.setElectrumRequestData(electrumListener); serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.checkBalance(coinData.getWallet())); serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.listUnspent(coinData.getWallet())); } - Integer buildSize(String outputAddress, String outFee, String outAmount) throws Exception { - String myAddress = coinData.getWallet(); - byte[] pbKey = ctx.getCard().getWalletPublicKey(); - byte[] pbComprKey = ctx.getCard().getWalletPublicKeyRar(); + private Integer calculateEstimatedTransactionSize(String outputAddress, String outAmount) { + //todo - правильней было бы использовать constructPayment + try { +// String myAddress = coinData.getWallet(); +// byte[] pbKey = ctx.getCard().getWalletPublicKey(); +// byte[] pbComprKey = ctx.getCard().getWalletPublicKeyRar(); +// +// // build script for our address +// List rawTxList = coinData.getUnspentTransactions(); +// byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; +// +// // collect unspent +// ArrayList unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); +// +// Long fullAmount = 0L; +// for (int i = 0; i < unspentOutputs.size(); i++) { +// fullAmount += unspentOutputs.get(i).value; +// } +// +// // get first unspent +//// val outPut = unspentOutputs[0] +//// val outPutIndex = outPut.outputIndex +// +// // get prev TX id; +//// val prevTXID = rawTxList[0].txID//"f67b838d6e2c0c587f476f583843e93ff20368eaf96a798bdc25e01f53f8f5d2"; +// +// Long fees = FormatUtil.ConvertStringToLong("0.00"); +// Long amount = FormatUtil.ConvertStringToLong(outAmount); +// amount -= fees; +// +// Long change = fullAmount - fees - amount; +// +// if (amount + fees > fullAmount) { +// throw new Exception(String.format("Balance (%d) < amount (%d) + (%d)", fullAmount, change, amount)); +// } +// +// byte[][] hashesForSign = new byte[unspentOutputs.size()][]; +// +// for (int i = 0; i < unspentOutputs.size(); i++) { +// byte[] newTX = BTCUtils.buildTXForSign(myAddress, outputAddress, myAddress, unspentOutputs, i, amount, change); +// byte[] hashData = Util.calculateSHA256(newTX); +// byte[] doubleHashData = Util.calculateSHA256(hashData); +//// Log.e("TX_BODY_1", BTCUtils.toHex(newTX)) +//// Log.e("TX_HASH_1", BTCUtils.toHex(hashData)) +//// Log.e("TX_HASH_2", BTCUtils.toHex(doubleHashData)) +// +//// unspentOutputs[i].bodyDoubleHash = doubleHashData +//// unspentOutputs[i].bodyHash = hashData +// hashesForSign[i] = doubleHashData; +// } +// +// byte[] signFromCard = new byte[64 * unspentOutputs.size()]; +// +// for (int i = 0; i < unspentOutputs.size(); i++) { +// BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, i * 64, 32 + i * 64)); +// BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); +// byte[] encodingSign = DerEncodingUtil.packSignDer(r, s, pbKey); +// unspentOutputs.get(i).scriptForBuild = encodingSign; +// } +// +// byte[] realTX = BTCUtils.buildTXForSend(outputAddress, myAddress, unspentOutputs, amount, change); - // build script for our address - List rawTxList = coinData.getUnspentTransactions(); - byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; + SignTask.PaymentToSign ps=constructPayment(new Amount(outAmount, getBalanceCurrency()),new Amount("0.00",getFeeCurrency()), true, outputAddress ); + OnNeedSendPayment onNeedSendPaymentBackup=onNeedSendPayment; + onNeedSendPayment=(tx)->{}; // empty function to bypass exception - // collect unspent - ArrayList unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); + byte[][] hashesToSign=ps.getHashesToSign(); + byte[] signFromCard = new byte[64 * hashesToSign.length]; + byte[] txForSend=ps.onSignCompleted(signFromCard); + onNeedSendPayment=onNeedSendPaymentBackup; + Log.e(TAG,"txForSend.length="+String.valueOf(txForSend.length)); + return txForSend.length; - Long fullAmount = 0L; - for (int i = 0; i < unspentOutputs.size(); i++) { - fullAmount += unspentOutputs.get(i).value; +// Log.e(TAG,"txForSend.length="+String.valueOf(txForSend.length)+" realTX.length="+String.valueOf(realTX.length)); +// +// return realTX.length; + + } catch (Exception e) { + e.printStackTrace(); + Log.e(TAG, "Can't calculate transaction size -> use default!"); + return 256; } - - // get first unspent -// val outPut = unspentOutputs[0] -// val outPutIndex = outPut.outputIndex - - // get prev TX id; -// val prevTXID = rawTxList[0].txID//"f67b838d6e2c0c587f476f583843e93ff20368eaf96a798bdc25e01f53f8f5d2"; - - Long fees = FormatUtil.ConvertStringToLong(outFee); - Long amount = FormatUtil.ConvertStringToLong(outAmount); - amount -= fees; - - Long change = fullAmount - fees - amount; - - if (amount + fees > fullAmount) { - throw new Exception(String.format("Balance (%d) < amount (%d) + (%d)", fullAmount, change, amount)); - } - - byte[][] hashesForSign = new byte[unspentOutputs.size()][]; - - for (int i = 0; i < unspentOutputs.size(); i++) { - byte[] newTX = BTCUtils.buildTXForSign(myAddress, outputAddress, myAddress, unspentOutputs, i, amount, change); - byte[] hashData = Util.calculateSHA256(newTX); - byte[] doubleHashData = Util.calculateSHA256(hashData); -// Log.e("TX_BODY_1", BTCUtils.toHex(newTX)) -// Log.e("TX_HASH_1", BTCUtils.toHex(hashData)) -// Log.e("TX_HASH_2", BTCUtils.toHex(doubleHashData)) - -// unspentOutputs[i].bodyDoubleHash = doubleHashData -// unspentOutputs[i].bodyHash = hashData - hashesForSign[i] = doubleHashData; - } - - byte[] signFromCard = new byte[64 * unspentOutputs.size()]; - - for (int i = 0; i < unspentOutputs.size(); i++) { - BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0 + i * 64, 32 + i * 64)); - BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); - byte[] encodingSign = DerEncodingUtil.packSignDer(r, s, pbKey); - unspentOutputs.get(i).scriptForBuild = encodingSign; - } - - byte[] realTX = BTCUtils.buildTXForSend(outputAddress, myAddress, unspentOutputs, amount, change); - - return realTX.length; } @Override - public void requestFee(FeeRequestsNotifications feeRequestsNotifications, CoinEngine.Amount amount) throws Exception { -// request estimate fee listener -// int calcSize = 256; -// try { - - final int calcSize = buildSize(coinData.getWallet(), "0.00", amount.toValueString()); -// } catch (Exception ex) { -// Log.e(TAG,"Build Fee error: "+ ex.getMessage()); -// } + public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) { + 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 ServerApiCommon serverApiCommon = new ServerApiCommon(); @@ -695,60 +723,51 @@ public class BtcEngine extends CoinEngine { BigDecimal fee = new BigDecimal(estimateFeeResponse); // BTC per 1 kb if (fee.equals(BigDecimal.ZERO)) { -// progressBar.visibility = View.INVISIBLE - if( !feeRequestsNotifications.needTerminate()) { + if (blockchainRequestsCallbacks.allowAdvance()) { serverApiCommon.estimateFee(blockCount); } return; } if (calcSize != 0) { - fee = fee.multiply(new BigDecimal(calcSize)).divide(new BigDecimal(1024)); // per Kb -> per byte + fee = fee.multiply(new BigDecimal(calcSize)).divide(new BigDecimal(1024), BigDecimal.ROUND_DOWN); // per Kb -> per byte } else { - if( !feeRequestsNotifications.needTerminate()) { + if (blockchainRequestsCallbacks.allowAdvance()) { serverApiCommon.estimateFee(blockCount); } return; } -// progressBar.visibility = View.INVISIBLE - fee = fee.setScale(8, RoundingMode.DOWN); switch (blockCount) { - case ServerApiCommon.ESTIMATE_FEE_MINIMAL: { - CoinEngine.Amount minFee = new CoinEngine.Amount(fee, getFeeCurrency()); - feeRequestsNotifications.onComplete(true, minFee, null, null); -// if (rgFee.checkedRadioButtonId == R.id.rbMinimalFee) doSetFee(rgFee.checkedRadioButtonId) - } - break; - - case ServerApiCommon.ESTIMATE_FEE_NORMAL: { - CoinEngine.Amount normalFee = new CoinEngine.Amount(fee, getFeeCurrency()); - feeRequestsNotifications.onComplete(true, null, normalFee, null); -// if (rgFee.checkedRadioButtonId == R.id.rbNormalFee) doSetFee(rgFee.checkedRadioButtonId) - } - break; - - case ServerApiCommon.ESTIMATE_FEE_PRIORITY: { - CoinEngine.Amount maxFee = new CoinEngine.Amount(fee, getFeeCurrency()); - feeRequestsNotifications.onComplete(true, null, null, maxFee); -// if (rgFee.checkedRadioButtonId == R.id.rbMaximumFee) doSetFee(rgFee.checkedRadioButtonId) - } + case ServerApiCommon.ESTIMATE_FEE_MINIMAL: + coinData.minFee = new CoinEngine.Amount(fee, getFeeCurrency()); + break; + case ServerApiCommon.ESTIMATE_FEE_NORMAL: + coinData.normalFee = new CoinEngine.Amount(fee, getFeeCurrency()); + break; + case ServerApiCommon.ESTIMATE_FEE_PRIORITY: + coinData.maxFee = new CoinEngine.Amount(fee, getFeeCurrency()); + break; } -// etFee.error = null -// feeRequestSuccess = true -// if (feeRequestSuccess) -// if (feeRequestSuccess && balanceRequestSuccess) -// btnSend.visibility = View.VISIBLE -// dtVerified = Date() + if(coinData.minFee!=null && coinData.normalFee!=null && coinData.maxFee!=null ) { + blockchainRequestsCallbacks.onComplete(true); + }else{ + blockchainRequestsCallbacks.onProgress(); + } } @Override - public void onFail(String message) { - feeRequestsNotifications.onComplete(false, null, null, null); - + public void onFail(int blockCount, String message) { + // TODO - add fail counter to terminate after NNN tries + if (blockchainRequestsCallbacks.allowAdvance()) { + serverApiCommon.estimateFee(blockCount); + return; + } + ctx.setError(ctx.getContext().getString(R.string.cannot_calculate_fee_wrong_data_received_from_node)); + blockchainRequestsCallbacks.onComplete(false); } }; serverApiCommon.setEstimateFee(estimateFeeListener); @@ -759,84 +778,47 @@ public class BtcEngine extends CoinEngine { } + @Override + public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) { + final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); + final String txStr = BTCUtils.toHex(txForSend); - // @Override -// public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { -// -// checkBlockchainDataExists(); -// -// String myAddress = ctx.getCoinData().getWallet(); -// byte[] pbKey = ctx.getCard().getWalletPublicKey(); -// -// // Build script for our address -// List rawTxList = coinData.getUnspentTransactions(); -// byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; -// -// // Collect unspent -// ArrayList unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); -// -// long fullAmount = 0; -// for (int i = 0; i < unspentOutputs.size(); ++i) { -// fullAmount += unspentOutputs.get(i).value; -// } -// -// -// long fees = convertToInternalAmount(feeValue).longValueExact(); -// long amount = convertToInternalAmount(amountValue).longValueExact(); -// long change = fullAmount - amount; -// if (IncFee) { -// amount = amount - fees; -// } else { -// change = change - fees; -// } -// -// if (amount + fees > fullAmount) { -// throw new CardProtocol.TangemException_WrongAmount(String.format("Balance (%d) < change (%d) + amount (%d)", fullAmount, change, amount)); -// } -// -// byte[][] dataForSign = new byte[unspentOutputs.size()][]; -// -// for (int i = 0; i < unspentOutputs.size(); ++i) { -// byte[] newTX = BTCUtils.buildTXForSign(myAddress, targetAddress, myAddress, unspentOutputs, i, amount, change); -// -// byte[] hashData = Util.calculateSHA256(newTX); -// byte[] doubleHashData = Util.calculateSHA256(hashData); -// -// unspentOutputs.get(i).bodyDoubleHash = doubleHashData; -// unspentOutputs.get(i).bodyHash = hashData; -// -// if (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw || ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw_Validated_By_Issuer) { -// dataForSign[i] = newTX; -// } else { -// dataForSign[i] = doubleHashData; -// } -// } -// -// byte[] signFromCard; -// if (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw || ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw_Validated_By_Issuer) { -// ByteArrayOutputStream bs = new ByteArrayOutputStream(); -// if (dataForSign.length > 10) throw new Exception("To much hashes in one transaction!"); -// for (int i = 0; i < dataForSign.length; i++) { -// if (i != 0 && dataForSign[0].length != dataForSign[i].length) -// throw new Exception("Hashes length must be identical!"); -// bs.write(dataForSign[i]); -// } -// signFromCard = protocol.run_SignRaw(PINStorage.getPIN2(), "sha-256x2",bs.toByteArray(),null,null,null).getTLV(TLV.Tag.TAG_Signature).Value; -// } else { -// //ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer, null, ctx.getCard().getIssuer() -// signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), dataForSign, null, null, null).getTLV(TLV.Tag.TAG_Signature).Value; -// // TODO slice signFromCard to hashes.length parts -// } -// -// for (int i = 0; i < unspentOutputs.size(); ++i) { -// BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, i * 64, 32 + i * 64)); -// BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); -// s = CryptoUtil.toCanonicalised(s); -// -// unspentOutputs.get(i).scriptForBuild = DerEncodingUtil.packSignDer(r, s, pbKey); -// } -// -// return BTCUtils.buildTXForSend(targetAddress, myAddress, unspentOutputs, amount, change); -// } + ServerApiElectrum.ElectrumRequestDataListener electrumListener = new ServerApiElectrum.ElectrumRequestDataListener() { + @Override + public void onSuccess(ElectrumRequest electrumRequest) { + if (electrumRequest.isMethod(ElectrumRequest.METHOD_SendTransaction)) { + try { + String resultString = electrumRequest.getResultString(); + if (resultString == null || resultString.isEmpty()) { + ctx.setError("Rejected by node: " + electrumRequest.getError()); + blockchainRequestsCallbacks.onComplete(false); + }else { + ctx.setError(null); + blockchainRequestsCallbacks.onComplete(true); + } + } catch (Exception e) { + if (e.getMessage() != null) { + ctx.setError(e.getMessage()); + blockchainRequestsCallbacks.onComplete(false); + } else { + ctx.setError(e.getClass().getName()); + blockchainRequestsCallbacks.onComplete(false); + } + } + } + } + + @Override + public void onFail(ElectrumRequest electrumRequest) { + ctx.setError(electrumRequest.getError()); + blockchainRequestsCallbacks.onComplete(false); + } + }; + serverApiElectrum.setElectrumRequestData(electrumListener); + + + serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.broadcast(ctx.getCoinData().getWallet(), txStr)); + + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java b/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java index a36467c6a5..b6203b0bd7 100644 --- a/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java @@ -13,7 +13,6 @@ import com.tangem.domain.wallet.CoinEngine; import com.tangem.domain.wallet.ECDSASignatureETH; import com.tangem.domain.wallet.EthTransaction; import com.tangem.domain.wallet.Keccak256; -import com.tangem.domain.wallet.token.TokenData; import com.tangem.tangemcard.data.TangemCard; import com.tangem.domain.wallet.TangemContext; import com.tangem.domain.wallet.BTCUtils; @@ -430,7 +429,7 @@ public class EthEngine extends CoinEngine { } @Override - public void onSignCompleted(byte[] signFromCard) throws Exception { + public byte[] onSignCompleted(byte[] signFromCard) throws Exception { 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)); @@ -451,13 +450,15 @@ public class EthEngine extends CoinEngine { tx.signature.v = (byte) v; Log.e("ETH_v", String.valueOf(v)); - notifyOnNeedSendPayment(tx.getEncoded()); + byte[] txForSend = tx.getEncoded(); + notifyOnNeedSendPayment(txForSend); + return txForSend; } }; } @Override - public void requestBalanceAndUnspentTransactions(BalanceAndUnspentTransactionsNotifications balanceAndUnspentTransactionsNotifications) { + public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) { final ServerApiInfura serverApiInfura = new ServerApiInfura(); // request infura listener ServerApiInfura.InfuraBodyListener infuraBodyListener = new ServerApiInfura.InfuraBodyListener() { @@ -468,19 +469,8 @@ public class EthEngine extends CoinEngine { String balanceCap = infuraResponse.getResult(); balanceCap = balanceCap.substring(2); BigInteger l = new BigInteger(balanceCap, 16); -// BigInteger d = l.divide(new BigInteger("1000000000000000000", 10)); -// Long balance = d.longValue(); - -// (ctx.coinData!! as EthData).setBalanceConfirmed(balance) -// (ctx.coinData!! as EthData).balanceUnconfirmed = 0L - if (ctx.getBlockchain() != Blockchain.Token) { - coinData.setBalanceReceived(true); - coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, "wei")); - } else { - coinData.setBalanceReceived(true); - //(ctx.coinData!! as TokenData).balanceInInternalUnits = CoinEngine.InternalAmount(l.toBigDecimal(),ctx.card.tokenSymbol) - ((TokenData) coinData).setBalanceAlterInInternalUnits(new CoinEngine.InternalAmount(l, "wei")); - } + coinData.setBalanceReceived(true); + coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, "wei")); // Log.i("$TAG eth_get_balance", balanceCap) } @@ -505,62 +495,67 @@ public class EthEngine extends CoinEngine { // Log.i("$TAG eth_getPendingTxCount", pending) } + break; } - if (!serverApiInfura.hasRequests()) { - balanceAndUnspentTransactionsNotifications.onComplete(serverApiInfura.isErrorOccured()); + if (serverApiInfura.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + }else{ + blockchainRequestsCallbacks.onProgress(); } } @Override public void onFail(String method, String message) { - if (!serverApiInfura.hasRequests()) { - balanceAndUnspentTransactionsNotifications.onComplete(serverApiInfura.isErrorOccured()); + if (!serverApiInfura.isRequestsSequenceCompleted()) { + ctx.setError(message); + blockchainRequestsCallbacks.onComplete(false); } } }; serverApiInfura.setInfuraResponse(infuraBodyListener); - serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", ""); - serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", ""); - serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", ""); + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", ""); + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", ""); + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", ""); } @Override - public void requestFee(FeeRequestsNotifications feeRequestsNotifications, CoinEngine.Amount amount) throws Exception { + public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception { ServerApiInfura serverApiInfura = new ServerApiInfura(); // request infura eth gasPrice listener - ServerApiInfura.InfuraBodyListener infuraBodyListener = new ServerApiInfura.InfuraBodyListener() { + ServerApiInfura.InfuraBodyListener infuraBodyListener = new ServerApiInfura.InfuraBodyListener() { @Override public void onSuccess(String method, InfuraResponse infuraResponse) { - if(method== ServerApiInfura.INFURA_ETH_GAS_PRICE) - { - String gasPrice = infuraResponse.getResult(); - gasPrice = gasPrice.substring(2); - // rounding gas price to integer gwei - BigInteger l = new BigInteger(gasPrice, 16).divide(BigInteger.valueOf(1000000000L)).multiply(BigInteger.valueOf(1000000000L)); + if (method.equals(ServerApiInfura.INFURA_ETH_GAS_PRICE)) { + String gasPrice = infuraResponse.getResult(); + gasPrice = gasPrice.substring(2); + // rounding gas price to integer gwei + BigInteger l = new BigInteger(gasPrice, 16).divide(BigInteger.valueOf(1000000000L)).multiply(BigInteger.valueOf(1000000000L)); - //val m = if (ctx.blockchain==Blockchain.Token) BigInteger.valueOf(60000) else BigInteger.valueOf(21000) - BigInteger m; - if (amount.getCurrency().equals("ETH")) m = BigInteger.valueOf(60000); - else m = BigInteger.valueOf(21000); + //val m = if (ctx.blockchain==Blockchain.Token) BigInteger.valueOf(60000) else BigInteger.valueOf(21000) +// BigInteger m; +// if (amount.getCurrency().equals("ETH")) m = BigInteger.valueOf(60000); +// else m = BigInteger.valueOf(21000); + BigInteger m = BigInteger.valueOf(60000); - CoinEngine.InternalAmount weiMinFee = new CoinEngine.InternalAmount(l.multiply(m), "wei"); - CoinEngine.InternalAmount weiNormalFee = new CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(12)).divide(BigDecimal.valueOf(10)), "wei"); - CoinEngine.InternalAmount weiMaxFee = new CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(15)).divide(BigDecimal.valueOf(10)), "wei"); + CoinEngine.InternalAmount weiMinFee = new CoinEngine.InternalAmount(l.multiply(m), "wei"); + CoinEngine.InternalAmount weiNormalFee = new CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(12)).divide(BigDecimal.valueOf(10)), "wei"); + CoinEngine.InternalAmount weiMaxFee = new CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(15)).divide(BigDecimal.valueOf(10)), "wei"); - CoinEngine.Amount minFee = convertToAmount(weiMinFee); - CoinEngine.Amount normalFee = convertToAmount(weiNormalFee); - CoinEngine.Amount maxFee = convertToAmount(weiMaxFee); - feeRequestsNotifications.onComplete(true, minFee, normalFee, maxFee); - } + coinData.minFee = convertToAmount(weiMinFee); + coinData.normalFee = convertToAmount(weiNormalFee); + coinData.maxFee = convertToAmount(weiMaxFee); + blockchainRequestsCallbacks.onComplete(true); + } } @Override public void onFail(String method, String message) { - if( method==ServerApiInfura.INFURA_ETH_GAS_PRICE ){ - feeRequestsNotifications.onComplete(false, null, null, null); + if (method == ServerApiInfura.INFURA_ETH_GAS_PRICE) { + ctx.setError(ctx.getContext().getString(R.string.cannot_calculate_fee_wrong_data_received_from_node)); + blockchainRequestsCallbacks.onComplete(false); } } }; @@ -569,6 +564,45 @@ public class EthEngine extends CoinEngine { serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GAS_PRICE, 67, coinData.getWallet(), "", ""); } + @Override + public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) throws Exception { + + String txStr = String.format("0x%s", BTCUtils.toHex(txForSend)); + + ServerApiInfura serverApiInfura = new ServerApiInfura(); + // request infura eth gasPrice listener + ServerApiInfura.InfuraBodyListener infuraBodyListener = new ServerApiInfura.InfuraBodyListener() { + @Override + public void onSuccess(String method, InfuraResponse infuraResponse) { + if (method.equals(ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION)) { + if (infuraResponse.getResult().isEmpty()) { + ctx.setError("Rejected by node: " + infuraResponse.getError()); + blockchainRequestsCallbacks.onComplete(false); + } else { + BigInteger nonce = coinData.getConfirmedTXCount(); + nonce.add(BigInteger.valueOf(1)); + coinData.setConfirmedTXCount(nonce); + ctx.setError(null); + blockchainRequestsCallbacks.onComplete(true); + } + } + } + + @Override + public void onFail(String method, String message) { + if (method.equals(ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION)) { + ctx.setError(message); + blockchainRequestsCallbacks.onComplete(false); + } + } + }; + + serverApiInfura.setInfuraResponse(infuraBodyListener); + + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION, 67, coinData.getWallet(), "", txStr); + + } + // @Override // public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { // diff --git a/app/src/main/java/com/tangem/domain/wallet/token/TokenData.java b/app/src/main/java/com/tangem/domain/wallet/token/TokenData.java index b0bfd6cd50..42f24f682a 100644 --- a/app/src/main/java/com/tangem/domain/wallet/token/TokenData.java +++ b/app/src/main/java/com/tangem/domain/wallet/token/TokenData.java @@ -28,7 +28,11 @@ public class TokenData extends EthData { public void loadFromBundle(Bundle B) { super.loadFromBundle(B); - balanceAlter = new CoinEngine.InternalAmount(B.getString("BalanceDecimalAlter"),"wei"); + if( B.containsKey("BalanceDecimalAlter" )) { + balanceAlter = new CoinEngine.InternalAmount(B.getString("BalanceDecimalAlter"), "wei"); + }else{ + balanceAlter=null; + } } @Override diff --git a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java index ee00616785..fe687057d6 100644 --- a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java @@ -5,7 +5,6 @@ import android.text.InputFilter; import android.util.Log; import com.google.common.base.Strings; -import com.tangem.data.Blockchain; import com.tangem.data.network.ServerApiInfura; import com.tangem.data.network.model.InfuraResponse; import com.tangem.domain.wallet.BalanceValidator; @@ -170,7 +169,7 @@ public class TokenEngine extends CoinEngine { @Override public boolean isBalanceNotZero() { if (coinData == null) return false; - if (coinData.getBalanceInInternalUnits() == null && coinData.getBalanceAlterInInternalUnits() == null ) return false; + if (coinData.getBalanceInInternalUnits() == null && coinData.getBalanceAlterInInternalUnits() == null) return false; return coinData.getBalanceInInternalUnits().notZero() || coinData.getBalanceAlterInInternalUnits().notZero(); } @@ -185,7 +184,7 @@ public class TokenEngine extends CoinEngine { // TODO: check why Rate=EthRate return "";//convertToAmount(coinData.getBalanceInInternalUnits()).toEquivalentString(coinData.getRate()); } else { - if( coinData.getBalanceAlterInInternalUnits()==null ) return ""; + if (coinData.getBalanceAlterInInternalUnits() == null) return ""; return convertToAmount(coinData.getBalanceAlterInInternalUnits()).toEquivalentString(coinData.getRateAlter()); } } catch (Exception e) { @@ -342,7 +341,7 @@ public class TokenEngine extends CoinEngine { if (amount.getCurrency().equals(ctx.getCard().tokenSymbol)) { // token transaction - if( fee.compareTo(balance)>0 ) + if (fee.compareTo(balance) > 0) return false; } else if (amount.getCurrency().equals("ETH") && coinData.getBalanceInInternalUnits().isZero()) { // standard ETH transaction @@ -433,21 +432,12 @@ public class TokenEngine extends CoinEngine { } } -// @Override -// public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { -// if (amountValue.getCurrency().equals("ETH")) { -// return signETH(feeValue, amountValue, IncFee, targetAddress, protocol); -// } else { -// return signToken(feeValue, amountValue, IncFee, targetAddress, protocol); -// } -// } - private SignTask.PaymentToSign constructPaymentETH(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress) throws Exception { BigInteger nonceValue = coinData.getConfirmedTXCount(); byte[] pbKey = ctx.getCard().getWalletPublicKey(); - BigInteger weiFee=convertToInternalAmount(feeValue).toBigIntegerExact(); - BigInteger weiAmount=convertToInternalAmount(amountValue).toBigIntegerExact(); + BigInteger weiFee = convertToInternalAmount(feeValue).toBigIntegerExact(); + BigInteger weiAmount = convertToInternalAmount(amountValue).toBigIntegerExact(); if (IncFee) { weiAmount = weiAmount.subtract(weiFee); @@ -469,7 +459,7 @@ public class TokenEngine extends CoinEngine { return new SignTask.PaymentToSign() { @Override public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) { - return signingMethod==TangemCard.SigningMethod.Sign_Hash; + return signingMethod == TangemCard.SigningMethod.Sign_Hash; } @Override @@ -495,8 +485,8 @@ public class TokenEngine extends CoinEngine { } @Override - public void onSignCompleted(byte[] signFromCard) throws Exception { - byte[] for_hash=tx.getRawHash(); + public byte[] onSignCompleted(byte[] signFromCard) throws Exception { + 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)); s = CryptoUtil.toCanonicalised(s); @@ -516,7 +506,9 @@ public class TokenEngine extends CoinEngine { tx.signature.v = (byte) v; Log.e("ETH_v", String.valueOf(v)); - notifyOnNeedSendPayment(tx.getEncoded()); + byte[] txForSend = tx.getEncoded(); + notifyOnNeedSendPayment(txForSend); + return txForSend; } }; } @@ -532,7 +524,7 @@ public class TokenEngine extends CoinEngine { BigInteger weiFee = convertToInternalAmount(feeValue).toBigIntegerExact(); - InternalAmount amountDec=convertToInternalAmount(amountValue); + InternalAmount amountDec = convertToInternalAmount(amountValue); BigInteger amount = amountDec.toBigInteger(); //new BigInteger(amountValue, 10); @@ -573,7 +565,7 @@ public class TokenEngine extends CoinEngine { return new SignTask.PaymentToSign() { @Override public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) { - return signingMethod==TangemCard.SigningMethod.Sign_Hash; + return signingMethod == TangemCard.SigningMethod.Sign_Hash; } @Override @@ -599,7 +591,7 @@ public class TokenEngine extends CoinEngine { } @Override - public void onSignCompleted(byte[] signFromCard) throws Exception { + public byte[] onSignCompleted(byte[] signFromCard) throws Exception { 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)); @@ -620,7 +612,9 @@ public class TokenEngine extends CoinEngine { tx.signature.v = (byte) v; Log.e("ETH_v", String.valueOf(v)); - notifyOnNeedSendPayment(tx.getEncoded()); + byte[] txForSend = tx.getEncoded(); + notifyOnNeedSendPayment(txForSend); + return txForSend; } }; @@ -628,7 +622,7 @@ public class TokenEngine extends CoinEngine { } @Override - public void requestBalanceAndUnspentTransactions(BalanceAndUnspentTransactionsNotifications balanceAndUnspentTransactionsNotifications) { + public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) { final ServerApiInfura serverApiInfura = new ServerApiInfura(); // request infura listener ServerApiInfura.InfuraBodyListener infuraBodyListener = new ServerApiInfura.InfuraBodyListener() { @@ -639,19 +633,8 @@ public class TokenEngine extends CoinEngine { String balanceCap = infuraResponse.getResult(); balanceCap = balanceCap.substring(2); BigInteger l = new BigInteger(balanceCap, 16); -// BigInteger d = l.divide(new BigInteger("1000000000000000000", 10)); -// Long balance = d.longValue(); - -// (ctx.coinData!! as EthData).setBalanceConfirmed(balance) -// (ctx.coinData!! as EthData).balanceUnconfirmed = 0L - if (ctx.getBlockchain() != Blockchain.Token) { - coinData.setBalanceReceived(true); - coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, "wei")); - } else { - coinData.setBalanceReceived(true); - //(ctx.coinData!! as TokenData).balanceInInternalUnits = CoinEngine.InternalAmount(l.toBigDecimal(),ctx.card.tokenSymbol) - ((TokenData) coinData).setBalanceAlterInInternalUnits(new CoinEngine.InternalAmount(l, "wei")); - } + coinData.setBalanceReceived(true); + coinData.setBalanceAlterInInternalUnits(new CoinEngine.InternalAmount(l, "wei")); // Log.i("$TAG eth_get_balance", balanceCap) } @@ -676,6 +659,7 @@ public class TokenEngine extends CoinEngine { // Log.i("$TAG eth_getPendingTxCount", pending) } + break; // case ServerApiInfura.INFURA_ETH_CALL: { try { @@ -683,48 +667,37 @@ public class TokenEngine extends CoinEngine { balanceCap = balanceCap.substring(2); BigInteger l = new BigInteger(balanceCap, 16); Long balance = l.longValue(); -// if (l.compareTo(BigInteger.ZERO) == 0) { -// //ctx.card!!.blockchainID = Blockchain.Ethereum.id -// ctx.card!!.addTokenToBlockchainName() -// -// //TODO check -// //ctx.blockchain=lBlockchain.Ethereum -// -// requestCounter-- -// if (requestCounter == 0) srl!!.isRefreshing = false -// -// requestInfura(ServerApiCommon.INFURA_ETH_GET_BALANCE, "") -// requestInfura(ServerApiCommon.INFURA_ETH_GET_TRANSACTION_COUNT, "") -// requestInfura(ServerApiCommon.INFURA_ETH_GET_PENDING_COUNT, "") -// return -// } coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol)); // Log.i("$TAG eth_call", balanceCap) - if (!balanceAndUnspentTransactionsNotifications.needTerminate()) { + if (blockchainRequestsCallbacks.allowAdvance()) { serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", ""); serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", ""); serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", ""); } else { - serverApiInfura.setErrorOccured("Terminated by user"); + ctx.setError("Terminated by user"); } } catch (Exception e) { e.printStackTrace(); } } + break; } - if (!serverApiInfura.hasRequests()) { - balanceAndUnspentTransactionsNotifications.onComplete(serverApiInfura.isErrorOccured()); + if (serverApiInfura.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + } else { + blockchainRequestsCallbacks.onProgress(); } } @Override public void onFail(String method, String message) { - if (!serverApiInfura.hasRequests()) { - balanceAndUnspentTransactionsNotifications.onComplete(serverApiInfura.isErrorOccured()); + if (!serverApiInfura.isRequestsSequenceCompleted()) { + ctx.setError(message); + blockchainRequestsCallbacks.onComplete(false); } } }; @@ -733,6 +706,87 @@ public class TokenEngine extends CoinEngine { serverApiInfura.infura(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), ""); } + @Override + public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception { + ServerApiInfura serverApiInfura = new ServerApiInfura(); + // request infura eth gasPrice listener + ServerApiInfura.InfuraBodyListener infuraBodyListener = new ServerApiInfura.InfuraBodyListener() { + @Override + public void onSuccess(String method, InfuraResponse infuraResponse) { + String gasPrice = infuraResponse.getResult(); + gasPrice = gasPrice.substring(2); + // rounding gas price to integer gwei + BigInteger l = new BigInteger(gasPrice, 16).divide(BigInteger.valueOf(1000000000L)).multiply(BigInteger.valueOf(1000000000L)); + + //val m = if (ctx.blockchain==Blockchain.Token) BigInteger.valueOf(60000) else BigInteger.valueOf(21000) + BigInteger m; + if (amount.getCurrency().equals("ETH")) m = BigInteger.valueOf(60000); + else m = BigInteger.valueOf(21000); + + CoinEngine.InternalAmount weiMinFee = new CoinEngine.InternalAmount(l.multiply(m), "wei"); + CoinEngine.InternalAmount weiNormalFee = new CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(12)).divide(BigDecimal.valueOf(10)), "wei"); + CoinEngine.InternalAmount weiMaxFee = new CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(15)).divide(BigDecimal.valueOf(10)), "wei"); + + try { + coinData.minFee = convertToAmount(weiMinFee); + coinData.normalFee = convertToAmount(weiNormalFee); + coinData.maxFee = convertToAmount(weiMaxFee); + } catch (Exception e) { + e.printStackTrace(); + } + blockchainRequestsCallbacks.onComplete(true); + } + + @Override + public void onFail(String method, String message) { + ctx.setError(message); + blockchainRequestsCallbacks.onComplete(false); + } + }; + serverApiInfura.setInfuraResponse(infuraBodyListener); + + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_GAS_PRICE, 67, coinData.getWallet(), "", ""); + } + + @Override + public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) throws Exception { + + String txStr = String.format("0x%s", BTCUtils.toHex(txForSend)); + + ServerApiInfura serverApiInfura = new ServerApiInfura(); + // request infura eth gasPrice listener + ServerApiInfura.InfuraBodyListener infuraBodyListener = new ServerApiInfura.InfuraBodyListener() { + @Override + public void onSuccess(String method, InfuraResponse infuraResponse) { + if (method.equals(ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION)) { + if (infuraResponse.getResult().isEmpty()) { + ctx.setError("Rejected by node: " + infuraResponse.getError()); + blockchainRequestsCallbacks.onComplete(false); + } else { + BigInteger nonce = coinData.getConfirmedTXCount(); + nonce.add(BigInteger.valueOf(1)); + coinData.setConfirmedTXCount(nonce); + ctx.setError(null); + blockchainRequestsCallbacks.onComplete(true); + } + } + } + + @Override + public void onFail(String method, String message) { + if (method.equals(ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION)) { + ctx.setError(message); + blockchainRequestsCallbacks.onComplete(false); + } + } + }; + + serverApiInfura.setInfuraResponse(infuraBodyListener); + + serverApiInfura.infura(ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION, 67, coinData.getWallet(), "", txStr); + + } + // public byte[] signETH(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { // BigInteger nonceValue = coinData.getConfirmedTXCount(); diff --git a/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.kt b/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.kt index df26fa1b02..afcb2662c2 100644 --- a/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.kt +++ b/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.kt @@ -9,29 +9,20 @@ import android.support.v7.app.AppCompatActivity import android.text.Editable import android.text.Html import android.text.TextWatcher -import android.util.Log import android.view.KeyEvent import android.view.View import android.widget.Toast -import com.tangem.data.network.ElectrumRequest -import com.tangem.data.network.ServerApiCommon -import com.tangem.data.network.ServerApiElectrum -import com.tangem.data.network.ServerApiInfura -import com.tangem.data.network.model.InfuraResponse -import com.tangem.tangemcard.android.reader.NfcManager -import com.tangem.domain.wallet.* -import com.tangem.domain.wallet.btc.BtcData import com.tangem.data.Blockchain +import com.tangem.domain.wallet.CoinEngine +import com.tangem.domain.wallet.CoinEngineFactory +import com.tangem.domain.wallet.TangemContext +import com.tangem.tangemcard.android.reader.NfcManager import com.tangem.tangemcard.data.TangemCard import com.tangem.tangemcard.data.loadFromBundle -import com.tangem.tangemcard.util.Util -import com.tangem.util.* +import com.tangem.util.UtilHelper import com.tangem.wallet.R import kotlinx.android.synthetic.main.activity_confirm_payment.* import java.io.IOException -import java.math.BigDecimal -import java.math.BigInteger -import java.math.RoundingMode import java.util.* class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { @@ -49,11 +40,8 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { private lateinit var ctx: TangemContext private lateinit var amount: CoinEngine.Amount - private var feeRequestSuccess = false + // private var feeRequestSuccess = false // private var balanceRequestSuccess = false - private var minFee: CoinEngine.Amount? = null - private var maxFee: CoinEngine.Amount? = null - private var normalFee: CoinEngine.Amount? = null private var isIncludeFee: Boolean = true private var requestPIN2Count = 0 private var nodeCheck = true @@ -97,7 +85,7 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { etFee.setText("") btnSend.visibility = View.INVISIBLE - feeRequestSuccess = false +// feeRequestSuccess = false // balanceRequestSuccess = false if (ctx.blockchain == Blockchain.Ethereum || ctx.blockchain == Blockchain.EthereumTestNet || ctx.blockchain == Blockchain.Token) { @@ -110,9 +98,9 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { // requestElectrum(ctx.card, ElectrumRequest.checkBalance(ctx.card!!.wallet)) - ctx.coinData!!.resetFailedBalanceRequestCounter() +// ctx.coinData!!.resetFailedBalanceRequestCounter() - progressBar.visibility = View.VISIBLE +// progressBar.visibility = View.VISIBLE // requestEstimateFee() } @@ -193,30 +181,37 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { } val coinEngine = CoinEngineFactory.create(ctx) + + progressBar.visibility = View.VISIBLE + coinEngine!!.requestFee( - object : CoinEngine.FeeRequestsNotifications { - override fun onComplete(success: Boolean, minFee: CoinEngine.Amount?, normalFee: CoinEngine.Amount?, maxFee: CoinEngine.Amount?) { + object : CoinEngine.BlockchainRequestsCallbacks { + override fun onComplete(success: Boolean) { if (success) { - this@ConfirmPaymentActivity.minFee = minFee - this@ConfirmPaymentActivity.normalFee = normalFee - this@ConfirmPaymentActivity.maxFee = maxFee - doSetFee(rgFee.checkedRadioButtonId) - etFee.error = null - btnSend.visibility = View.VISIBLE - feeRequestSuccess = true -// balanceRequestSuccess = true + + onProgress() + +// etFee.error = null + +// feeRequestSuccess = true + // balanceRequestSuccess = true + progressBar.visibility = View.INVISIBLE dtVerified = Date() } else { - finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain)) + finishWithError(Activity.RESULT_CANCELED, ctx.error) } } - override fun needTerminate(): Boolean { - return !UtilHelper.isOnline(this@ConfirmPaymentActivity) + override fun onProgress() { + doSetFee(rgFee.checkedRadioButtonId) + } + + override fun allowAdvance(): Boolean { + return UtilHelper.isOnline(this@ConfirmPaymentActivity) } }, - amount - ) + etWallet.text.toString(), + amount) // request electrum listener @@ -420,7 +415,7 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { } // TODO - move to BtcEngine - @Throws(Exception::class) +// @Throws(Exception::class) // private fun requestElectrum(ctx: TangemContext, electrumRequest: ElectrumRequest) { // if (UtilHelper.isOnline(this)) { @@ -455,20 +450,29 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { var txtFee = "" when (checkedRadioButtonId) { R.id.rbMinimalFee -> - if (minFee != null) - txtFee = minFee!!.toValueString() - else - finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain)) + if (ctx.coinData.minFee != null) { + txtFee = ctx.coinData.minFee!!.toValueString() + btnSend.visibility = View.VISIBLE + }else { + btnSend.visibility = View.INVISIBLE +// finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain)) + } R.id.rbNormalFee -> - if (normalFee != null) - txtFee = normalFee!!.toValueString() - else - finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain)) + if (ctx.coinData.normalFee != null) { + txtFee = ctx.coinData.normalFee!!.toValueString() + btnSend.visibility = View.VISIBLE + }else { + btnSend.visibility = View.INVISIBLE +// finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain)) + } R.id.rbMaximumFee -> - if (maxFee != null) - txtFee = maxFee!!.toValueString() - else - finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain)) + if (ctx.coinData.maxFee != null) { + txtFee = ctx.coinData.maxFee!!.toValueString() + btnSend.visibility = View.VISIBLE + }else { +// finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain)) + btnSend.visibility = View.INVISIBLE + } } etFee.setText(txtFee.replace(',', '.')) } diff --git a/app/src/main/java/com/tangem/presentation/activity/SendTransactionActivity.kt b/app/src/main/java/com/tangem/presentation/activity/SendTransactionActivity.kt index f91991a62e..8057a788ac 100644 --- a/app/src/main/java/com/tangem/presentation/activity/SendTransactionActivity.kt +++ b/app/src/main/java/com/tangem/presentation/activity/SendTransactionActivity.kt @@ -7,10 +7,6 @@ import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.view.KeyEvent import android.widget.Toast -import com.tangem.data.network.ElectrumRequest -import com.tangem.data.network.ServerApiElectrum -import com.tangem.data.network.ServerApiInfura -import com.tangem.data.network.model.InfuraResponse import com.tangem.tangemcard.android.reader.NfcManager import com.tangem.domain.wallet.* import com.tangem.domain.wallet.eth.EthData @@ -27,11 +23,11 @@ class SendTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { const val EXTRA_TX: String = "TX" } - private var serverApiInfura: ServerApiInfura = ServerApiInfura() - private var serverApiElectrum: ServerApiElectrum = ServerApiElectrum() +// private var serverApiInfura: ServerApiInfura = ServerApiInfura() +// private var serverApiElectrum: ServerApiElectrum = ServerApiElectrum() private lateinit var ctx: TangemContext - private var tx: String? = null + private var tx: ByteArray? = null private var nfcManager: NfcManager? = null override fun onCreate(savedInstanceState: Bundle?) { @@ -43,71 +39,89 @@ class SendTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { nfcManager = NfcManager(this, this) ctx = TangemContext.loadFromBundle(this, intent.extras) - tx = intent.getStringExtra(EXTRA_TX) + tx = intent.getByteArrayExtra(EXTRA_TX) val engine = CoinEngineFactory.create(ctx) - if (ctx.blockchain == Blockchain.Ethereum || ctx.blockchain == Blockchain.EthereumTestNet || ctx.blockchain == Blockchain.Token) - requestInfura(ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION, "") - else if (ctx.blockchain == Blockchain.Bitcoin || ctx.blockchain == Blockchain.BitcoinTestNet) - requestElectrum(ctx, ElectrumRequest.broadcast(ctx.coinData!!.wallet, tx)) - else if (ctx.blockchain == Blockchain.BitcoinCash) - requestElectrum(ctx, ElectrumRequest.broadcast(ctx.coinData!!.wallet, tx)) + engine!!.requestSendTransaction( + object : CoinEngine.BlockchainRequestsCallbacks { + override fun onComplete(success: Boolean) { + if (success) { + finishWithSuccess() + } else { + finishWithError(this@SendTransactionActivity.getString(R.string.try_again_failed_to_send_transaction)) + } + } + + override fun onProgress() { + } + + override fun allowAdvance(): Boolean { + return UtilHelper.isOnline(this@SendTransactionActivity) + } + }, + tx + ) + + +// if (ctx.blockchain == Blockchain.Ethereum || ctx.blockchain == Blockchain.EthereumTestNet || ctx.blockchain == Blockchain.Token) +// requestInfura(ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION, "") +// else if (ctx.blockchain == Blockchain.Bitcoin || ctx.blockchain == Blockchain.BitcoinTestNet) +// requestElectrum(ctx, ElectrumRequest.broadcast(ctx.coinData!!.wallet, tx)) +// else if (ctx.blockchain == Blockchain.BitcoinCash) +// requestElectrum(ctx, ElectrumRequest.broadcast(ctx.coinData!!.wallet, tx)) // request electrum listener - val electrumBodyListener: ServerApiElectrum.ElectrumRequestDataListener = object : ServerApiElectrum.ElectrumRequestDataListener { - override fun onSuccess(electrumRequest: ElectrumRequest?) { - if (electrumRequest!!.isMethod(ElectrumRequest.METHOD_SendTransaction)) { - try { - if (electrumRequest.resultString.isNullOrEmpty()) - finishWithError("Rejected by node: " + electrumRequest.getError()) - else - finishWithSuccess() - } - catch (e: Exception) - { - if( e.message!=null ) - { - finishWithError(e.message!!) - }else{ - finishWithError(e.javaClass.name) - } - } - } - } - - override fun onFail(message: String?) { - finishWithError(message!!) - } - } - serverApiElectrum.setElectrumRequestData(electrumBodyListener) +// val electrumBodyListener: ServerApiElectrum.ElectrumRequestDataListener = object : ServerApiElectrum.ElectrumRequestDataListener { +// override fun onSuccess(electrumRequest: ElectrumRequest?) { +// if (electrumRequest!!.isMethod(ElectrumRequest.METHOD_SendTransaction)) { +// try { +// if (electrumRequest.resultString.isNullOrEmpty()) +// finishWithError("Rejected by node: " + electrumRequest.getError()) +// else +// finishWithSuccess() +// } catch (e: Exception) { +// if (e.message != null) { +// finishWithError(e.message!!) +// } else { +// finishWithError(e.javaClass.name) +// } +// } +// } +// } +// +// override fun onFail(message: String?) { +// finishWithError(message!!) +// } +// } +// serverApiElectrum.setElectrumRequestData(electrumBodyListener) // request infura listener - val infuraBodyListener: ServerApiInfura.InfuraBodyListener = object : ServerApiInfura.InfuraBodyListener { - override fun onSuccess(method: String, infuraResponse: InfuraResponse) { - when (method) { - ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION -> { - if (infuraResponse.result.isEmpty()) - finishWithError("Rejected by node: " + infuraResponse.error) - else { - val nonce = (ctx.coinData!! as EthData).confirmedTXCount - nonce.add(BigInteger.valueOf(1)) - (ctx.coinData!! as EthData).confirmedTXCount = nonce - finishWithSuccess() - } - } - } - } - - override fun onFail(method: String, message: String) { - when (method) { - ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION -> { - finishWithError(message) - } - } - } - } - serverApiInfura.setInfuraResponse(infuraBodyListener) +// val infuraBodyListener: ServerApiInfura.InfuraBodyListener = object : ServerApiInfura.InfuraBodyListener { +// override fun onSuccess(method: String, infuraResponse: InfuraResponse) { +// when (method) { +// ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION -> { +// if (infuraResponse.result.isEmpty()) +// finishWithError("Rejected by node: " + infuraResponse.error) +// else { +// val nonce = (ctx.coinData!! as EthData).confirmedTXCount +// nonce.add(BigInteger.valueOf(1)) +// (ctx.coinData!! as EthData).confirmedTXCount = nonce +// finishWithSuccess() +// } +// } +// } +// } +// +// override fun onFail(method: String, message: String) { +// when (method) { +// ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION -> { +// finishWithError(message) +// } +// } +// } +// } +// serverApiInfura.setInfuraResponse(infuraBodyListener) } override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { @@ -143,20 +157,20 @@ class SendTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { } } - private fun requestInfura(method: String, contract: String) { - if (UtilHelper.isOnline(this)) { - serverApiInfura.infura(method, 67, ctx.coinData!!.wallet, contract, tx) - } else - finishWithError(getString(R.string.no_connection)) - } - - private fun requestElectrum(ctx: TangemContext, electrumRequest: ElectrumRequest) { - if (UtilHelper.isOnline(this)) { - serverApiElectrum.electrumRequestData(ctx, electrumRequest) - } else - finishWithError(getString(R.string.no_connection)) - } +// private fun requestInfura(method: String, contract: String) { +// if (UtilHelper.isOnline(this)) { +// serverApiInfura.infura(method, 67, ctx.coinData!!.wallet, contract, tx) +// } else +// finishWithError(getString(R.string.no_connection)) +// } +// private fun requestElectrum(ctx: TangemContext, electrumRequest: ElectrumRequest) { +// if (UtilHelper.isOnline(this)) { +// serverApiElectrum.electrumRequestData(ctx, electrumRequest) +// } else +// finishWithError(getString(R.string.no_connection)) +// } +// private fun finishWithSuccess() { val intent = Intent() intent.putExtra("message", getString(R.string.transaction_has_been_successfully_signed)) diff --git a/app/src/main/java/com/tangem/presentation/activity/SignPaymentActivity.kt b/app/src/main/java/com/tangem/presentation/activity/SignPaymentActivity.kt index 4466ea5315..7ba52a40d0 100644 --- a/app/src/main/java/com/tangem/presentation/activity/SignPaymentActivity.kt +++ b/app/src/main/java/com/tangem/presentation/activity/SignPaymentActivity.kt @@ -144,4 +144,143 @@ class SignPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, Card ?: throw CardProtocol.TangemException("Can't create CoinEngine!") coinEngine.setOnNeedSendPayment { tx -> if (tx != null) { - // [REDACTED_TODO_COMMENT] \ No newline at end of file + val intent = Intent(this, SendTransactionActivity::class.java) + ctx.saveToIntent(intent) + intent.putExtra(SendTransactionActivity.EXTRA_TX, tx) + startActivityForResult(intent, SignPaymentActivity.REQUEST_CODE_SEND_PAYMENT) + } + } + val paymentToSign = coinEngine.constructPayment(amount, fee, isIncludeFee, outAddressStr) + + signPaymentTask = SignTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this, paymentToSign) + signPaymentTask!!.start() + } else { +// Log.d(TAG, "Mismatch card UID (" + sUID + " instead of " + card!!.uid + ")") + nfcManager!!.ignoreTag(isoDep.tag) + } + + }catch (e: CardProtocol.TangemException_WrongAmount) + { + try { + val intent = Intent() + intent.putExtra("message", getString(R.string.cannot_sign_transaction_wrong_amount)) + intent.putExtra("UID", ctx.card.uid) + intent.putExtra("Card", ctx.card.asBundle) + setResult(Activity.RESULT_CANCELED, intent) + finish() + } catch (e: Exception) { + e.printStackTrace() + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + override fun onReadStart(cardProtocol: CardProtocol) { + progressBar!!.post { + progressBar!!.visibility = View.VISIBLE + progressBar!!.progress = 5 + } + } + + override fun onReadProgress(protocol: CardProtocol, progress: Int) { + progressBar!!.post { progressBar!!.progress = progress } + } + + override fun onReadFinish(cardProtocol: CardProtocol?) { + signPaymentTask = null + if (cardProtocol != null) { + if (cardProtocol.error == null) { + progressBar!!.post { + progressBar!!.progress = 100 + progressBar!!.progressTintList = ColorStateList.valueOf(Color.GREEN) + } + } else { + lastReadSuccess = false + if (cardProtocol.error.javaClass == CardProtocol.TangemException_InvalidPIN::class.java) { + progressBar!!.post { + progressBar!!.progress = 100 + progressBar!!.progressTintList = ColorStateList.valueOf(Color.RED) + } + progressBar!!.postDelayed({ + try { + progressBar!!.progress = 0 + progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY) + progressBar!!.visibility = View.INVISIBLE + val intent = Intent() + intent.putExtra("message", getString(R.string.cannot_sign_transaction__make_sure_you_enter_correct_pin_2)) + intent.putExtra("UID", cardProtocol.card.uid) + intent.putExtra("Card", cardProtocol.card.asBundle) + setResult(RESULT_INVALID_PIN, intent) + finish() + } catch (e: Exception) { + e.printStackTrace() + } + }, 500) + } else { + if (cardProtocol.error is CardProtocol.TangemException_WrongAmount) { + try { + val intent = Intent() + intent.putExtra("message", getString(R.string.cannot_sign_transaction_wrong_amount)) + intent.putExtra("UID", cardProtocol.card.uid) + intent.putExtra("Card", cardProtocol.card.asBundle) + setResult(Activity.RESULT_CANCELED, intent) + finish() + } catch (e: Exception) { + e.printStackTrace() + } + } + progressBar!!.post { + if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) { + if (!NoExtendedLengthSupportDialog.allReadyShowed) { + NoExtendedLengthSupportDialog.message = getText(R.string.the_nfc_adapter_length_apdu).toString() + "\n" + getText(R.string.the_nfc_adapter_length_apdu_advice).toString() + NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG) + } + } else { + Toast.makeText(baseContext, R.string.try_to_scan_again, Toast.LENGTH_LONG).show() + } + progressBar!!.progress = 100 + progressBar!!.progressTintList = ColorStateList.valueOf(Color.RED) + } + } + } + } + + progressBar!!.postDelayed({ + try { + progressBar!!.progress = 0 + progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY) + progressBar!!.visibility = View.INVISIBLE + } catch (e: Exception) { + e.printStackTrace() + } + }, 500) + } + + override fun onReadCancel() { + signPaymentTask = null + + progressBar!!.postDelayed({ + try { + progressBar!!.progress = 0 + progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY) + progressBar!!.visibility = View.INVISIBLE + } catch (e: Exception) { + e.printStackTrace() + } + }, 500) + } + + override fun onReadWait(msec: Int) { + WaitSecurityDelayDialog.OnReadWait(this, msec) + } + + override fun onReadBeforeRequest(timeout: Int) { + WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout) + } + + override fun onReadAfterRequest() { + WaitSecurityDelayDialog.onReadAfterRequest(this) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt index 48ccfa9006..626a410857 100644 --- a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt +++ b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt @@ -69,7 +69,17 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific private var cardProtocol: CardProtocol? = null private val inactiveColor: ColorStateList by lazy { resources.getColorStateList(R.color.btn_dark) } private val activeColor: ColorStateList by lazy { resources.getColorStateList(R.color.colorAccent) } - private var requestCounter = 0 + private var requestCounter: Int = 0 + set(value) + { + field=value + Log.i(TAG, "requestCounter, set $field") + if (field <= 0 && srl!=null && srl.isRefreshing ) { + Log.e(TAG, "+++++++++++ FINISH REFRESH") + if (srl != null) srl!!.isRefreshing = false + //updateViews() + } + } private var timerRepeatRefresh: Timer? = null override fun onCreate(savedInstanceState: Bundle?) { @@ -395,6 +405,7 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific // request card verify and get info listener val cardVerifyAndGetInfoListener: ServerApiTangem.CardVerifyAndGetInfoListener = object : ServerApiTangem.CardVerifyAndGetInfoListener { override fun onSuccess(cardVerifyAndGetArtworkResponse: CardVerifyAndGetInfo.Response?) { + Log.i(TAG,"cardVerifyAndGetInfoListener onSuccess") val result = cardVerifyAndGetArtworkResponse?.results!![0] if (result.error != null) { ctx.card!!.isOnlineVerified = false @@ -402,7 +413,9 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific } ctx.card!!.isOnlineVerified = result.passed - if (requestCounter == 0) updateViews() +// if (requestCounter == 0) + requestCounter-- + updateViews() if (!result.passed) return @@ -421,6 +434,7 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific } if (result.artwork != null && App.localStorage.checkNeedUpdateArtwork(result.artwork)) { Log.w(TAG, "Artwork '${result.artwork!!.id}' updated, need download") + requestCounter++ serverApiTangem.requestArtwork(result.artwork!!.id, result.artwork!!.getUpdateDate(), ctx.card!!) updateViews() } @@ -428,7 +442,9 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific } override fun onFail(message: String?) { - + Log.i(TAG,"cardVerifyAndGetInfoListener onFail") + requestCounter-- + updateViews() } } serverApiTangem.setCardVerifyAndGetInfoListener(cardVerifyAndGetInfoListener) @@ -436,12 +452,17 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific // request artwork listener val artworkListener: ServerApiTangem.ArtworkListener = object : ServerApiTangem.ArtworkListener { override fun onSuccess(artworkId: String?, inputStream: InputStream?, updateDate: Date?) { + Log.i(TAG,"artworkListener onSuccess") App.localStorage.updateArtwork(artworkId!!, inputStream!!, updateDate!!) + requestCounter-- ivTangemCard.setImageBitmap(App.localStorage.getCardArtworkBitmap(ctx.card!!)) + updateViews() } override fun onFail(message: String?) { - + Log.i(TAG,"artworkListener onFail") + requestCounter-- + updateViews() } } serverApiTangem.setArtworkListener(artworkListener) @@ -454,14 +475,6 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific } } - private fun counterMinus() { - requestCounter-- - if (requestCounter == 0) { - if (srl != null) srl!!.isRefreshing = false - updateViews() - } - } - override fun onResume() { super.onResume() nfcManager!!.onResume() @@ -714,12 +727,12 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific timerHideErrorAndMessage = null } - if (ctx.error == null || ctx.error.isEmpty()) { - tvError.visibility = View.GONE - tvError.text = "" - } else { + if (ctx.hasError()) { tvError.visibility = View.VISIBLE tvError.text = ctx.error + } else { + tvError.visibility = View.GONE + tvError.text = "" } if (ctx.message == null || ctx.message.isEmpty()) { @@ -775,19 +788,22 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific btnExtract.backgroundTintList = inactiveColor } - ctx.error = null - ctx.message = null + //TODO why ??? +// ctx.error = null +// ctx.message = null } private fun refresh() { if (ctx.card == null) return // clear all card data and request again - srl?.isRefreshing = true ctx.coinData.clearInfo() ctx.error = null ctx.message = null + + Log.e(TAG, "============= START REFRESH") requestCounter = 0 + srl?.isRefreshing = true updateViews() @@ -796,14 +812,24 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific val coinEngine = CoinEngineFactory.create(ctx) requestCounter++ coinEngine!!.requestBalanceAndUnspentTransactions( - object : CoinEngine.BalanceAndUnspentTransactionsNotifications { - override fun onComplete(success: Boolean?) { - counterMinus() + object : CoinEngine.BlockchainRequestsCallbacks { + override fun onComplete(success: Boolean) { + Log.i(TAG, "requestBalanceAndUnspentTransactions onComplete: "+success.toString()+", request counter "+requestCounter.toString()) + requestCounter-- + if(! success) + { + Log.e(TAG, "ctx.error: "+ctx.error) + } updateViews() } - override fun needTerminate(): Boolean { - return !UtilHelper.isOnline(context as Activity) + override fun onProgress() { + Log.i(TAG, "requestBalanceAndUnspentTransactions onProgress") + updateViews() + } + + override fun allowAdvance(): Boolean { + return UtilHelper.isOnline(context as Activity) } } ) @@ -867,19 +893,24 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific private fun requestVerifyAndGetInfo() { if (UtilHelper.isOnline(context as Activity)) { if ((ctx.card!!.isOnlineVerified == null || !ctx.card!!.isOnlineVerified)) { + Log.i(TAG, "requestVerifyAndGetInfo") + requestCounter++ serverApiTangem.cardVerifyAndGetInfo(ctx.card) } } else { Toast.makeText(activity, getString(R.string.no_connection), Toast.LENGTH_SHORT).show() + Log.e(TAG, "+++++++++++ Hide refresh 1") srl?.isRefreshing = false } } private fun requestRateInfo(cryptoId: String) { if (UtilHelper.isOnline(context as Activity)) { + Log.i(TAG, "requestRateInfo") serverApiCommon.rateInfoData(cryptoId) } else { Toast.makeText(activity, getString(R.string.no_connection), Toast.LENGTH_SHORT).show() + Log.e(TAG, "+++++++++++ Hide refresh 2") srl?.isRefreshing = false } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dfe00212b1..16073f4101 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -107,6 +107,9 @@ If you forget your new PIN you will lose your money forever! If you use default PIN someone can steal your money! Cannot obtain data from blockchain + Cannot obtain data from blockchain (connection refused) + Cannot obtain data from blockchain (empty answer received) + Cannot obtain data from blockchain (communication error) Sending cached transaction… NOT IMPLEMENTED This banknote is protected by default PIN1 code diff --git a/tangemcard-common/src/main/java/com/tangem/tangemcard/tasks/SignTask.java b/tangemcard-common/src/main/java/com/tangem/tangemcard/tasks/SignTask.java index 9e83d7fd99..512aa917e3 100644 --- a/tangemcard-common/src/main/java/com/tangem/tangemcard/tasks/SignTask.java +++ b/tangemcard-common/src/main/java/com/tangem/tangemcard/tasks/SignTask.java @@ -28,7 +28,7 @@ public class SignTask extends CustomReadCardTask { byte[] getIssuerTransactionSignature(byte[] dataToSignByIssuer) throws Exception; - void onSignCompleted(byte[] signature) throws Exception; + byte[] onSignCompleted(byte[] signature) throws Exception; } private PaymentToSign paymentToSign; From b641f2eac7d56e9195b6f629d27af639c02d2733 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 17 Dec 2018 12:10:58 +0300 Subject: [PATCH 5/7] Updated on 2026-08-14 --- .../com/tangem/domain/wallet/CoinEngine.java | 89 +++++++++++++------ 1 file changed, 62 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java index 229593fe4c..f6a104c7ca 100644 --- a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java @@ -27,36 +27,35 @@ public abstract class CoinEngine { public InternalAmount() { super(0); - currency=""; + currency = ""; } public InternalAmount(String amountString, String currency) { - super(amountString.replace(',','.')); - this.currency=currency; + super(amountString.replace(',', '.')); + this.currency = currency; } public InternalAmount(long amount, String currency) { super(amount); - this.currency=currency; + this.currency = currency; } public InternalAmount(BigDecimal amount, String currency) { super(amount.unscaledValue(), amount.scale()); - this.currency=currency; + this.currency = currency; } public InternalAmount(BigInteger amount, String currency) { super(new BigDecimal(amount).unscaledValue(), new BigDecimal(amount).scale()); - this.currency=currency; + this.currency = currency; } - public boolean notZero() - { - return compareTo(BigDecimal.ZERO)>0; + public boolean notZero() { + return compareTo(BigDecimal.ZERO) > 0; } public boolean isZero() { - return compareTo(BigDecimal.ZERO)==0; + return compareTo(BigDecimal.ZERO) == 0; } public String getCurrency() { @@ -74,7 +73,7 @@ public abstract class CoinEngine { df.setGroupingUsed(false); - BigDecimal bd=new BigDecimal(unscaledValue(), scale()); + BigDecimal bd = new BigDecimal(unscaledValue(), scale()); bd.setScale(decimals, ROUND_DOWN); return df.format(bd); } @@ -95,11 +94,11 @@ public abstract class CoinEngine { public Amount() { super(0); - currency=""; + currency = ""; } public Amount(String amountString, String currency) { - super(amountString.replace(',','.')); + super(amountString.replace(',', '.')); this.currency = currency; } @@ -122,13 +121,12 @@ public abstract class CoinEngine { return super.toString() + " " + currency; } - public boolean notZero() - { - return compareTo(BigDecimal.ZERO)>0; + public boolean notZero() { + return compareTo(BigDecimal.ZERO) > 0; } public String toDescriptionString(int decimals) { - return toValueString(decimals)+ " " + currency; + return toValueString(decimals) + " " + currency; } public String toValueString(int decimals) { @@ -142,7 +140,7 @@ public abstract class CoinEngine { df.setGroupingUsed(false); - BigDecimal bd=new BigDecimal(unscaledValue(), scale()); + BigDecimal bd = new BigDecimal(unscaledValue(), scale()); bd.setScale(decimals, ROUND_DOWN); return df.format(bd); } @@ -163,7 +161,7 @@ public abstract class CoinEngine { } public boolean isZero() { - return compareTo(BigDecimal.ZERO)==0; + return compareTo(BigDecimal.ZERO) == 0; } } @@ -222,9 +220,11 @@ public abstract class CoinEngine { public abstract String calculateAddress(byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException; public abstract Amount convertToAmount(InternalAmount internalAmount) throws Exception; + public abstract Amount convertToAmount(String strAmount, String currency); public abstract InternalAmount convertToInternalAmount(Amount amount) throws Exception; + public abstract InternalAmount convertToInternalAmount(byte[] bytes) throws Exception; public abstract byte[] convertToByteArray(InternalAmount internalAmount) throws Exception; @@ -237,39 +237,66 @@ public abstract class CoinEngine { try { String wallet = calculateAddress(ctx.getCard().getWalletPublicKey()); ctx.getCoinData().setWallet(wallet); - } - catch (Exception e) - { + } catch (Exception e) { ctx.getCoinData().setWallet("ERROR"); throw new CardProtocol.TangemException("Can't define wallet address"); } } + /** + * Create instance of {@link SignTask.PaymentToSign} used for transaction signing and sending + * + * Transaction processing sequence: + * 1. User enter transaction attributes + * 2. Application create instance of {@link SignTask.PaymentToSign} by call {@see constructPayment} + * 3. Application set notification when transaction were prepared {@see setOnNeedSendPayment} and start {@link SignTask} + * 4. User tap card and card sign transaction + * 5. Application receive {@link CoinEngine.OnNeedSendPayment} notification with prepared raw transaction + * 6. Application show user information that transaction ready for sending and start sending procedure by call {@see requestSendTransaction} + * 7. Application receive notification of sending result through {@link CoinEngine.BlockchainRequestsCallbacks} and show result to user + * + * @param amountValue - amount of desired transaction + * @param feeValue - fee amount of desired transaction + * @param IncFee - true if fee amount is included in amountValue (amountValue is total amount of transaction) + * @param targetAddress - target address of transaction + * @return instance of {@link SignTask.PaymentToSign} + * @throws Exception if something goes wrong + */ public abstract SignTask.PaymentToSign constructPayment(Amount amountValue, Amount feeValue, boolean IncFee, String targetAddress) throws Exception; - public interface OnNeedSendPayment - { + /** + * Interface used to notify main application when new transaction is prepared to send + */ + public interface OnNeedSendPayment { void onPaymentPrepared(byte[] txForSend); } + protected OnNeedSendPayment onNeedSendPayment; + + /** + * Set notification callback when new transaction is prepared to send + */ public void setOnNeedSendPayment(OnNeedSendPayment onNeedSendPayment) { this.onNeedSendPayment = onNeedSendPayment; } protected void notifyOnNeedSendPayment(byte[] txForSend) throws Exception { - if(onNeedSendPayment==null) + if (onNeedSendPayment == null) throw new Exception("Payment signed but no callback defined to send!"); onNeedSendPayment.onPaymentPrepared(txForSend); } - public interface BlockchainRequestsCallbacks - { + /** + * Interface used to notify/querying application during processing sequence of request to blockchain nodes/servers + */ + public interface BlockchainRequestsCallbacks { /** * Notification that the all requests in sequence completed * Call after a last request completed * If occurred error return in ctx.error + * * @param success -* */ void onComplete(Boolean success); @@ -283,10 +310,18 @@ public abstract class CoinEngine { /** * Return flag that allow to add new or re-requests in the sequence * Call between requests or when request fail and before re-request + * * @return true if not need terminate (e.g. activity is online) */ boolean allowAdvance(); } + + /** + * Start sequence of request to blockchain nodes needed to get balance and other information (for example unspent transaction) needed to + * show current state of wallet and prepare new withdrawal transaction + * @param blockchainRequestsCallbacks - notifications + * @throws Exception + */ public abstract void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) throws Exception; public abstract void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception; From abc5bc3d145e2ad65e5a5e658675074779c6f0b3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 17 Dec 2018 15:20:25 +0300 Subject: [PATCH 6/7] Updated on 2026-08-14 --- .../dialog/WaitSecurityDelayDialog.java | 153 +++++++++--------- 1 file changed, 77 insertions(+), 76 deletions(-) diff --git a/app/src/main/java/com/tangem/presentation/dialog/WaitSecurityDelayDialog.java b/app/src/main/java/com/tangem/presentation/dialog/WaitSecurityDelayDialog.java index 8e61a1087b..4f0ab501d6 100644 --- a/app/src/main/java/com/tangem/presentation/dialog/WaitSecurityDelayDialog.java +++ b/app/src/main/java/com/tangem/presentation/dialog/WaitSecurityDelayDialog.java @@ -3,12 +3,11 @@ package com.tangem.presentation.dialog; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; +import android.app.DialogFragment; import android.content.DialogInterface; import android.os.Bundle; -import android.support.v4.app.DialogFragment; import android.view.LayoutInflater; import android.view.View; -import android.view.WindowManager; import android.widget.ProgressBar; import com.tangem.wallet.R; @@ -20,24 +19,16 @@ import java.util.TimerTask; * Created by dvol on 06.03.2018. */ public class WaitSecurityDelayDialog extends DialogFragment { - private static final String TAG = WaitSecurityDelayDialog.class.getSimpleName(); - - private ProgressBar progressBar; - private int msTimeout = 60000, msProgress = 0; - private Timer timer; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } + ProgressBar progressBar; + int msTimeout = 60000, msProgress = 0; + Timer timer; @Override public Dialog onCreateDialog(Bundle savedInstanceState) { LayoutInflater inflater = getActivity().getLayoutInflater(); - // Inflate and set t he layout for the dialog + // Inflate and set the layout for the dialog // Pass null as the parent view because its going in the dialog layout View v = inflater.inflate(R.layout.dialog_wait_pin2, null); @@ -49,17 +40,20 @@ public class WaitSecurityDelayDialog extends DialogFragment { timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { - progressBar.post(() -> { - int progress = WaitSecurityDelayDialog.this.progressBar.getProgress(); - if (progress < WaitSecurityDelayDialog.this.progressBar.getMax()) { - WaitSecurityDelayDialog.this.progressBar.setProgress(progress + 1000); + progressBar.post(new Runnable() { + @Override + public void run() { + int progress = WaitSecurityDelayDialog.this.progressBar.getProgress(); + if (progress < WaitSecurityDelayDialog.this.progressBar.getMax()) { + WaitSecurityDelayDialog.this.progressBar.setProgress(progress + 1000); + } } }); } }, 1000, 1000); return new AlertDialog.Builder(getActivity()) .setIcon(R.drawable.tangem_logo_small_new) - .setTitle(R.string.security_delay) + .setTitle("Security delay") .setView(v) .setCancelable(false) .create(); @@ -76,19 +70,22 @@ public class WaitSecurityDelayDialog extends DialogFragment { } public void setRemainingTimeout(final int msec) { - progressBar.post(() -> { - int progress = WaitSecurityDelayDialog.this.progressBar.getProgress(); - if (timer != null) { - // we get delay latency from card for first time - don't change progress by timer, only by card answer - progressBar.setMax(progress + msec); - timer.cancel(); - timer = null; - } else { - int newProgress = progressBar.getMax() - msec; - if (newProgress > progress) { - progressBar.setProgress(newProgress); - } else { + progressBar.post(new Runnable() { + @Override + public void run() { + int progress = WaitSecurityDelayDialog.this.progressBar.getProgress(); + if (timer != null) { + // we get delay latency from card for first time - don't change progress by timer, only by card answer progressBar.setMax(progress + msec); + timer.cancel(); + timer = null; + } else { + int newProgress = progressBar.getMax() - msec; + if (newProgress > progress) { + progressBar.setProgress(newProgress); + } else { + progressBar.setMax(progress + msec); + } } } }); @@ -104,64 +101,68 @@ public class WaitSecurityDelayDialog extends DialogFragment { return instance; } - private final static int MinRemainingDelayToShowDialog = 1000; - private final static int DelayBeforeShowDialog = 5000; + private final static int MinRemainingDelayToShowDialog=1000; + private final static int DelayBeforeShowDialog=5000; public static void onReadBeforeRequest(final Activity activity, final int timeout) { - activity.runOnUiThread(() -> { - if (timerToShowDelayDialog != null || timeout < DelayBeforeShowDialog + MinRemainingDelayToShowDialog) - return; - timerToShowDelayDialog = new Timer(); - timerToShowDelayDialog.schedule(new TimerTask() { - @Override - public void run() { - if (WaitSecurityDelayDialog.instance != null) return; - instance = new WaitSecurityDelayDialog(); - instance.setup(timeout, DelayBeforeShowDialog); - instance.setCancelable(false); - if (instance.getFragmentManager() != null) - instance.show(instance.getFragmentManager(), TAG); - } - }, DelayBeforeShowDialog); + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + if (timerToShowDelayDialog != null || timeout < DelayBeforeShowDialog+MinRemainingDelayToShowDialog) return; + timerToShowDelayDialog = new Timer(); + timerToShowDelayDialog.schedule(new TimerTask() { + @Override + public void run() { + if (WaitSecurityDelayDialog.instance != null) return; + instance = new WaitSecurityDelayDialog(); + instance.setup(timeout, DelayBeforeShowDialog); + instance.setCancelable(false); + instance.show(activity.getFragmentManager(), "WaitSecurityDelayDialog"); + } + }, DelayBeforeShowDialog); + } }); } public static void onReadAfterRequest(final Activity activity) { - activity.runOnUiThread(() -> { - if (timerToShowDelayDialog == null) return; - timerToShowDelayDialog.cancel(); - timerToShowDelayDialog = null; + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + if (timerToShowDelayDialog == null) return; + timerToShowDelayDialog.cancel(); + timerToShowDelayDialog = null; + } }); } public static void OnReadWait(final Activity activity, final int msec) { - activity.runOnUiThread(() -> { - if (timerToShowDelayDialog != null) { - timerToShowDelayDialog.cancel(); - timerToShowDelayDialog = null; - } - - if (msec == 0) { - if (instance != null) { - // TODO java.lang.NullPointerException: Attempt to invoke virtual method 'android.support.v4.app.FragmentTransaction android.support.v4.app.FragmentManager.beginTransaction()' on a null object reference - instance.dismiss(); - instance = null; + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + if (timerToShowDelayDialog != null) { + timerToShowDelayDialog.cancel(); + timerToShowDelayDialog = null; } - return; - } - if (instance == null) { - if (msec > MinRemainingDelayToShowDialog) { - instance = new WaitSecurityDelayDialog(); - // 1000ms - card delay notification interval - instance.setup(msec + 1000, 1000); - instance.setCancelable(false); - if (instance.getFragmentManager() != null) - instance.show(instance.getFragmentManager(), TAG); + if (msec == 0) { + if (instance != null) { + instance.dismiss(); + instance = null; + } + return; } - } else - instance.setRemainingTimeout(msec); + if (instance == null) { + if( msec>MinRemainingDelayToShowDialog ) { + instance = new WaitSecurityDelayDialog(); + // 1000ms - card delay notification interval + instance.setup(msec + 1000, 1000); + instance.setCancelable(false); + instance.show(activity.getFragmentManager(), "WaitSecurityDelayDialog"); + } + } else { + instance.setRemainingTimeout(msec); + } + } }); } - } \ No newline at end of file From 6948a2c04af1a33a38b8366f8d49c77f5920d7d3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 18 Dec 2018 11:42:27 +0300 Subject: [PATCH 7/7] Updated on 2026-08-14 --- .../data/network/ServerApiElectrum.java | 37 ++++++++++++- .../com/tangem/domain/wallet/CoinEngine.java | 18 +++++- .../domain/wallet/token/TokenEngine.java | 9 +++ .../presentation/fragment/LoadedWallet.kt | 55 +++++++++++-------- .../presentation/fragment/VerifyCard.kt | 1 - .../CardDataSubstitutionProvider.java | 5 ++ .../external/FirmwaresDigestsProvider.java | 4 ++ .../data/external/PINsProvider.java | 21 ++++++- 8 files changed, 121 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/tangem/data/network/ServerApiElectrum.java b/app/src/main/java/com/tangem/data/network/ServerApiElectrum.java index 87c4cedb6c..a3267d633a 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiElectrum.java +++ b/app/src/main/java/com/tangem/data/network/ServerApiElectrum.java @@ -42,6 +42,17 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.observers.DefaultObserver; import io.reactivex.schedulers.Schedulers; +/** + * Request processor for Electrum Api + * Every request live cycle: + * 1. In application create request and call {@link ServerApiElectrum}.electrumRequestData(..) + * 2. Try send every request for max 4 times, + * 3. If all 4 times fail call DefaultObserver.onError (defined in .electrumRequestData(..)) and than + * {@link ElectrumRequestDataListener}.onFail(...) callback + * Error can be acquired with {@link ElectrumRequest}.getError() method + * 4. If request network communication finished successfully then call DefaultObserver.onComplete (defined in .electrumRequestData) and than + * {@link ElectrumRequestDataListener}.onSuccess(...) callback + */ public class ServerApiElectrum { private static String TAG = ServerApiElectrum.class.getSimpleName(); @@ -60,16 +71,37 @@ public class ServerApiElectrum { return requestsCount <= 0; } + /** + * Interface for notification every request result + */ public interface ElectrumRequestDataListener { - void onSuccess(ElectrumRequest electrumRequest); + /** + * Notify that request processing was successful + * @param electrumRequest - processed request containing received answer {@see electrumRequest.getAnswer() method} + */ + void onSuccess(ElectrumRequest electrumRequest); + /** + * Notify that request processing was successful + * @param electrumRequest - processed request containing occurred error {@see electrumRequest.getError() method} + */ void onFail(ElectrumRequest electrumRequest); } + /** + * Set notificaion listener + * @param listener + */ public void setElectrumRequestData(ElectrumRequestDataListener listener) { electrumRequestDataListener = listener; } + + /** + * Start process request + * @param ctx + * @param electrumRequest + */ public void electrumRequestData(TangemContext ctx, ElectrumRequest electrumRequest) { requestsCount++; Log.i(TAG, String.format("New request[%d]: %s", requestsCount,electrumRequest.getMethod())); @@ -111,6 +143,9 @@ public class ServerApiElectrum { electrumRequestDataListener.onFail(electrumRequest); } + /** + * Called after completion request processing + */ @Override public void onComplete() { requestsCount--; diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java index f6a104c7ca..83b0f987bf 100644 --- a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java @@ -295,7 +295,7 @@ public abstract class CoinEngine { /** * Notification that the all requests in sequence completed * Call after a last request completed - * If occurred error return in ctx.error + * If occurred error return in {@link TangemContext} {@see TangemContext.getError()} * * @param success -* */ @@ -319,12 +319,26 @@ public abstract class CoinEngine { /** * Start sequence of request to blockchain nodes needed to get balance and other information (for example unspent transaction) needed to * show current state of wallet and prepare new withdrawal transaction + * Save result in {@link CoinData} + * If occurred error can be get at onComplete callback in {@link TangemContext}.getError() * @param blockchainRequestsCallbacks - notifications - * @throws Exception + * @throws Exception if something goes wrong */ public abstract void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) throws Exception; + /** + * Start sequence of request to blockchain nodes needed to get fee amount for a new transaction + * Save result in {@link CoinData} minFee, maxFee, normalFee + * @param blockchainRequestsCallbacks - notifications + * @throws Exception if something goes wrong + */ public abstract void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception; + /** + * Start sequence of request to blockchain nodes needed to send new transaction + * If occurred error can be get at onComplete callback in {@link TangemContext}.getError() + * @param blockchainRequestsCallbacks - notifications + * @throws Exception if something goes wrong + */ public abstract void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) throws Exception; } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java index fe687057d6..847603aa23 100644 --- a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java @@ -1,6 +1,7 @@ package com.tangem.domain.wallet.token; import android.net.Uri; +import android.os.Bundle; import android.text.InputFilter; import android.util.Log; @@ -13,6 +14,7 @@ import com.tangem.domain.wallet.CoinEngine; import com.tangem.domain.wallet.ECDSASignatureETH; import com.tangem.domain.wallet.EthTransaction; import com.tangem.domain.wallet.Keccak256; +import com.tangem.domain.wallet.eth.EthData; import com.tangem.tangemcard.data.TangemCard; import com.tangem.domain.wallet.TangemContext; import com.tangem.domain.wallet.BTCUtils; @@ -45,6 +47,13 @@ public class TokenEngine extends CoinEngine { ctx.setCoinData(coinData); } else if (ctx.getCoinData() instanceof TokenData) { coinData = (TokenData) ctx.getCoinData(); + } else if (ctx.getCoinData() instanceof EthData) { + // special case with receive card data substitution from server at the moment + Bundle B=new Bundle(); + ctx.getCoinData().saveToBundle(B); + coinData = new TokenData(); + coinData.loadFromBundle(B); + ctx.setCoinData(coinData); } else { throw new Exception("Invalid type of Blockchain data for TokenEngine"); } diff --git a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt index 626a410857..96cd64cf83 100644 --- a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt +++ b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt @@ -21,25 +21,28 @@ import android.view.ViewGroup import android.widget.Toast import com.tangem.App import com.tangem.Constant +import com.tangem.data.Blockchain import com.tangem.data.network.ServerApiCommon -import com.tangem.tangemserver.android.model.CardVerifyAndGetInfo -import com.tangem.tangemcard.tasks.VerifyCardTask -import com.tangem.tangemcard.reader.CardProtocol -import com.tangem.tangemcard.android.reader.NfcManager -import com.tangem.domain.wallet.* +import com.tangem.domain.wallet.BalanceValidator +import com.tangem.domain.wallet.CoinEngine +import com.tangem.domain.wallet.CoinEngineFactory +import com.tangem.domain.wallet.TangemContext import com.tangem.presentation.activity.* import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog import com.tangem.presentation.dialog.PINSwapWarningDialog import com.tangem.presentation.dialog.ShowQRCodeDialog import com.tangem.presentation.dialog.WaitSecurityDelayDialog -import com.tangem.data.Blockchain +import com.tangem.tangemcard.android.reader.NfcManager import com.tangem.tangemcard.android.reader.NfcReader import com.tangem.tangemcard.data.EXTRA_TANGEM_CARD import com.tangem.tangemcard.data.EXTRA_TANGEM_CARD_UID import com.tangem.tangemcard.data.TangemCard import com.tangem.tangemcard.data.loadFromBundle +import com.tangem.tangemcard.reader.CardProtocol +import com.tangem.tangemcard.tasks.VerifyCardTask import com.tangem.tangemcard.util.Util import com.tangem.tangemserver.android.ServerApiTangem +import com.tangem.tangemserver.android.model.CardVerifyAndGetInfo import com.tangem.util.UtilHelper import com.tangem.wallet.R import kotlinx.android.synthetic.main.fr_loaded_wallet.* @@ -108,10 +111,6 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific btnExtract.isEnabled = false btnExtract.backgroundTintList = inactiveColor - refresh() - - startVerify(lastTag) - tvWallet.text = ctx.coinData.wallet // set listeners @@ -406,6 +405,8 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific val cardVerifyAndGetInfoListener: ServerApiTangem.CardVerifyAndGetInfoListener = object : ServerApiTangem.CardVerifyAndGetInfoListener { override fun onSuccess(cardVerifyAndGetArtworkResponse: CardVerifyAndGetInfo.Response?) { Log.i(TAG,"cardVerifyAndGetInfoListener onSuccess") + if( activity==null || !UtilHelper.isOnline(activity!!)) return + val result = cardVerifyAndGetArtworkResponse?.results!![0] if (result.error != null) { ctx.card!!.isOnlineVerified = false @@ -413,8 +414,9 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific } ctx.card!!.isOnlineVerified = result.passed -// if (requestCounter == 0) + requestCounter-- +// if (requestCounter == 0) updateViews() if (!result.passed) return @@ -423,13 +425,6 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific Log.w(TAG, "Batch ${result.batch} info changed to '$result'") ivTangemCard.setImageBitmap(App.localStorage.getCardArtworkBitmap(ctx.card!!)) App.localStorage.applySubstitution(ctx.card!!) - //todo - check this is not need after refactoring -// if (ctx.blockchain == Blockchain.Token || ctx.blockchain == Blockchain.Ethereum) { -// ctx.card!!.setBlockchainIDFromCard(Blockchain.Ethereum.id) - - //ctx.blockchain=Blockchain.Ethereum - //engine=engine!!.swithToOtherEngine(Blockchain.Ethereum) -// } refresh() } if (result.artwork != null && App.localStorage.checkNeedUpdateArtwork(result.artwork)) { @@ -443,6 +438,7 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific override fun onFail(message: String?) { Log.i(TAG,"cardVerifyAndGetInfoListener onFail") + if( activity==null || !UtilHelper.isOnline(activity!!)) return requestCounter-- updateViews() } @@ -453,6 +449,7 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific val artworkListener: ServerApiTangem.ArtworkListener = object : ServerApiTangem.ArtworkListener { override fun onSuccess(artworkId: String?, inputStream: InputStream?, updateDate: Date?) { Log.i(TAG,"artworkListener onSuccess") + if( activity==null || !UtilHelper.isOnline(activity!!)) return App.localStorage.updateArtwork(artworkId!!, inputStream!!, updateDate!!) requestCounter-- ivTangemCard.setImageBitmap(App.localStorage.getCardArtworkBitmap(ctx.card!!)) @@ -461,6 +458,7 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific override fun onFail(message: String?) { Log.i(TAG,"artworkListener onFail") + if( activity==null || !UtilHelper.isOnline(activity!!)) return requestCounter-- updateViews() } @@ -469,27 +467,32 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific // request rate info listener serverApiCommon.setRateInfoData { + if( activity==null || !UtilHelper.isOnline(activity!!)) return@setRateInfoData val rate = it.priceUsd.toFloat() ctx.coinData!!.rate = rate ctx.coinData!!.rateAlter = rate } + + refresh() + + startVerify(lastTag) } override fun onResume() { super.onResume() - nfcManager!!.onResume() + nfcManager.onResume() } override fun onPause() { super.onPause() - nfcManager!!.onPause() + nfcManager.onPause() if (timerRepeatRefresh != null) timerRepeatRefresh!!.cancel() } override fun onStop() { super.onStop() - nfcManager!!.onStop() + nfcManager.onStop() } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { @@ -722,6 +725,8 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific } fun updateViews() { + if( activity==null || !UtilHelper.isOnline(activity!!)) return + if (timerHideErrorAndMessage != null) { timerHideErrorAndMessage!!.cancel() timerHideErrorAndMessage = null @@ -780,7 +785,7 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific } else tvBlockchain.text = ctx.blockchainName - if (engine.hasBalanceInfo()) { + if (requestCounter==0 && engine.hasBalanceInfo()) { btnExtract.isEnabled = true btnExtract.backgroundTintList = activeColor } else { @@ -815,6 +820,7 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific object : CoinEngine.BlockchainRequestsCallbacks { override fun onComplete(success: Boolean) { Log.i(TAG, "requestBalanceAndUnspentTransactions onComplete: "+success.toString()+", request counter "+requestCounter.toString()) + if( activity==null || !UtilHelper.isOnline(activity!!)) return requestCounter-- if(! success) { @@ -824,6 +830,7 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific } override fun onProgress() { + if( activity==null || !UtilHelper.isOnline(activity!!)) return Log.i(TAG, "requestBalanceAndUnspentTransactions onProgress") updateViews() } @@ -864,7 +871,7 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific // Token else if (ctx.blockchain == Blockchain.Token) { - val engine = CoinEngineFactory.create(ctx) +// val engine = CoinEngineFactory.create(ctx) // requestInfura(ServerApiInfura.INFURA_ETH_CALL, (engine as TokenEngine).getContractAddress(ctx.card)) requestRateInfo("ethereum") } @@ -923,7 +930,7 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific val sUID = Util.byteArrayToHexString(uid) if (ctx.card.uid != sUID) { // Log.d(TAG, "Invalid UID: $sUID") - nfcManager!!.ignoreTag(isoDep.tag) + nfcManager.ignoreTag(isoDep.tag) return } else { // Log.v(TAG, "UID: $sUID") diff --git a/app/src/main/java/com/tangem/presentation/fragment/VerifyCard.kt b/app/src/main/java/com/tangem/presentation/fragment/VerifyCard.kt index 08ce72d5ab..6067ad6f26 100644 --- a/app/src/main/java/com/tangem/presentation/fragment/VerifyCard.kt +++ b/app/src/main/java/com/tangem/presentation/fragment/VerifyCard.kt @@ -35,7 +35,6 @@ class VerifyCard : Fragment(), NfcAdapter.ReaderCallback { companion object { val TAG: String = VerifyCard::class.java.simpleName - } private var nfcManager: NfcManager? = null diff --git a/tangemcard-common/src/main/java/com/tangem/tangemcard/data/external/CardDataSubstitutionProvider.java b/tangemcard-common/src/main/java/com/tangem/tangemcard/data/external/CardDataSubstitutionProvider.java index fd8c23e624..ceaf756749 100644 --- a/tangemcard-common/src/main/java/com/tangem/tangemcard/data/external/CardDataSubstitutionProvider.java +++ b/tangemcard-common/src/main/java/com/tangem/tangemcard/data/external/CardDataSubstitutionProvider.java @@ -2,6 +2,11 @@ package com.tangem.tangemcard.data.external; import com.tangem.tangemcard.data.TangemCard; + +/** + * This interface provide method to make substitution of read card data (token symbol, contract address) + * if they was unknown when the card was produced + */ public interface CardDataSubstitutionProvider { void applySubstitution(TangemCard card); } diff --git a/tangemcard-common/src/main/java/com/tangem/tangemcard/data/external/FirmwaresDigestsProvider.java b/tangemcard-common/src/main/java/com/tangem/tangemcard/data/external/FirmwaresDigestsProvider.java index 6ee6a58069..86e8ba4175 100644 --- a/tangemcard-common/src/main/java/com/tangem/tangemcard/data/external/FirmwaresDigestsProvider.java +++ b/tangemcard-common/src/main/java/com/tangem/tangemcard/data/external/FirmwaresDigestsProvider.java @@ -1,5 +1,9 @@ package com.tangem.tangemcard.data.external; +/** + * This interfaces provide function to randomly select parameters to run one VerifyCode command, check answer and + * state that card is genuine or not + */ public interface FirmwaresDigestsProvider { VerifyCodeRecord selectRandomVerifyCodeBlock(String firmwareVersion); diff --git a/tangemcard-common/src/main/java/com/tangem/tangemcard/data/external/PINsProvider.java b/tangemcard-common/src/main/java/com/tangem/tangemcard/data/external/PINsProvider.java index 0db22fbbfb..05cabc54af 100644 --- a/tangemcard-common/src/main/java/com/tangem/tangemcard/data/external/PINsProvider.java +++ b/tangemcard-common/src/main/java/com/tangem/tangemcard/data/external/PINsProvider.java @@ -2,10 +2,29 @@ package com.tangem.tangemcard.data.external; import java.util.List; +/** + * Interface of PINsProvider - object that know some list of PINs (used when start first time read), PIN2 (used for protected operation) and store last used PIN + * to use it in following operations + */ public interface PINsProvider { + + /** + * @return list of known PINs + * This PINs used when start reading of card + * When start reading a PINs from this list used sequential in search PIN algorithm until right PIN found + */ + List getPINs(); + + /** + * @return PIN2 for protected operations + */ String getPIN2(); + + /** + * Call after successful first time reading of card to store founded PIN (normally this PIN must be returned in next time {@see getPINs} at first position) + * @param pin + */ void setLastUsedPIN(String pin); - List getPINs(); }