From 6fd870bc9ac1d78f91cec35853ba91125db04aba Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 17 Apr 2019 19:31:19 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../java/com/tangem/data/network/Server.java | 11 + .../tangem/data/network/ServerApiSoChain.java | 104 +++++ .../com/tangem/data/network/ServerURL.java | 1 + .../com/tangem/data/network/SoChainApi.java | 23 ++ .../com/tangem/data/network/model/SoChain.kt | 46 +++ .../java/com/tangem/di/NetworkComponent.kt | 3 + .../main/java/com/tangem/di/NetworkModule.kt | 12 + .../java/com/tangem/wallet/btc/BtcEngine.java | 374 ++++++++++++------ 8 files changed, 450 insertions(+), 124 deletions(-) create mode 100644 app/src/main/java/com/tangem/data/network/ServerApiSoChain.java create mode 100644 app/src/main/java/com/tangem/data/network/SoChainApi.java create mode 100644 app/src/main/java/com/tangem/data/network/model/SoChain.kt diff --git a/app/src/main/java/com/tangem/data/network/Server.java b/app/src/main/java/com/tangem/data/network/Server.java index 832e0495c2..daf83d7554 100644 --- a/app/src/main/java/com/tangem/data/network/Server.java +++ b/app/src/main/java/com/tangem/data/network/Server.java @@ -32,6 +32,17 @@ public class Server { } } + + public static class ApiSoChain { + public static final String URL = ServerURL.API_SOCHAIN_V2; + + public static class Method { + public static final String ADDRESS_BALANCE = "api/v2/get_address_balance/{network}/{address}"; + public static final String UNSPENT_TX = "api/v2/get_tx_unspent/{network}/{address}"; + public static final String SEND_TRANSACTION = "api/v2/send_tx/{network}"; + } + } + /** * https://public-node.rsk.co/ */ diff --git a/app/src/main/java/com/tangem/data/network/ServerApiSoChain.java b/app/src/main/java/com/tangem/data/network/ServerApiSoChain.java new file mode 100644 index 0000000000..862af12b63 --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/ServerApiSoChain.java @@ -0,0 +1,104 @@ +package com.tangem.data.network; + +import android.util.Log; + +import com.tangem.App; +import com.tangem.data.Blockchain; +import com.tangem.data.network.model.SoChain; + +import androidx.annotation.NonNull; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class ServerApiSoChain { + + public static String NETWORK_BTC = "BTC"; + + private static String TAG = ServerApiSoChain.class.getSimpleName(); + + private int requestsCount = 0; + + public boolean isRequestsSequenceCompleted() { + Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount)); + return requestsCount <= 0; + } + + private ResponseListener responseListener; + + public interface ResponseListener { + void onSuccess(SoChain.Response.AddressBalance response); + + void onSuccess(SoChain.Response.TxUnspent response); + + void onFail(String message); + } + + public void setResponseListener(ResponseListener listener) { + responseListener = listener; + } + + private String getNetwork(Blockchain blockchain) throws Exception { + switch (blockchain) { + case Bitcoin: + return "BTC"; + case BitcoinTestNet: + return "BTCTEST"; + case Litecoin: + return "LTC"; + default: + throw new Exception("SoChainAPI don't support blockchain " + blockchain.getID()); + } + } + + public void requestAddressBalance(Blockchain blockchain, String wallet) throws Exception { + requestsCount++; + SoChainApi api = App.Companion.getNetworkComponent().getRetrofitSoChain().create(SoChainApi.class); + + Call call = api.getAddressBalance(getNetwork(blockchain), wallet); + call.enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + Log.i(TAG, "requestAddressBalance onResponse " + response.code()); + if (response.code() == 200) { + requestsCount--; + responseListener.onSuccess(response.body()); + } else { + responseListener.onFail(String.valueOf(response.code())); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + Log.e(TAG, "requestAddressBalance onFailure " + t.getMessage()); + responseListener.onFail(String.valueOf(t.getMessage())); + } + }); + } + + public void requestUnspentTx(Blockchain blockchain, String wallet) throws Exception { + requestsCount++; + SoChainApi api = App.Companion.getNetworkComponent().getRetrofitSoChain().create(SoChainApi.class); + + Call call = api.getUnspentTx(getNetwork(blockchain), wallet); + call.enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + Log.i(TAG, "requestAddressBalance onResponse " + response.code()); + if (response.code() == 200) { + requestsCount--; + responseListener.onSuccess(response.body()); + } else { + responseListener.onFail(String.valueOf(response.code())); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + Log.e(TAG, "requestAddressBalance onFailure " + t.getMessage()); + responseListener.onFail(String.valueOf(t.getMessage())); + } + }); + } + +} diff --git a/app/src/main/java/com/tangem/data/network/ServerURL.java b/app/src/main/java/com/tangem/data/network/ServerURL.java index b33d4c4ba4..8d0c8df96e 100644 --- a/app/src/main/java/com/tangem/data/network/ServerURL.java +++ b/app/src/main/java/com/tangem/data/network/ServerURL.java @@ -4,6 +4,7 @@ class ServerURL { static final String API_TANGEM = "https://verify.tangem.com/"; static final String API_COINMARKETCAP = "https://api.coinmarketcap.com/"; static final String API_INFURA = "https://mainnet.infura.io/"; + static final String API_SOCHAIN_V2 = "https://chain.so/"; static final String API_ESTIMATEFEE = "https://estimatefee.com/"; static final String API_UPDATE_VERSION = "https://raw.githubusercontent.com/"; static final String API_ROOTSTOCK = "https://public-node.rsk.co/"; diff --git a/app/src/main/java/com/tangem/data/network/SoChainApi.java b/app/src/main/java/com/tangem/data/network/SoChainApi.java new file mode 100644 index 0000000000..b9c956a000 --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/SoChainApi.java @@ -0,0 +1,23 @@ +package com.tangem.data.network; + + +import com.tangem.data.network.model.SoChain; + +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.GET; +import retrofit2.http.Headers; +import retrofit2.http.POST; +import retrofit2.http.Path; + +public interface SoChainApi { + @GET(Server.ApiSoChain.Method.ADDRESS_BALANCE) + Call getAddressBalance(@Path("network") String network, @Path("address") String address); + + @GET(Server.ApiSoChain.Method.UNSPENT_TX) + Call getUnspentTx(@Path("network") String network, @Path("address") String address); + + @Headers("Content-Type: application/json") + @POST(Server.ApiSoChain.Method.SEND_TRANSACTION) + Call sendTransaction(@Path("network") String network, @Body SoChain.Request.SendTx body); +} diff --git a/app/src/main/java/com/tangem/data/network/model/SoChain.kt b/app/src/main/java/com/tangem/data/network/model/SoChain.kt new file mode 100644 index 0000000000..2730df7776 --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/model/SoChain.kt @@ -0,0 +1,46 @@ +package com.tangem.data.network.model + +class SoChain { + class Request { + class SendTx{ + var tx_hex: String? = null + } + } + + class Response { + class AddressBalance { + class Data { + var network: String? = null + var address: String? = null + var confirmed_balance: String? = null + var unconfirmed_balance: String? = null + var confirmations: String? = null + } + + var status: String? = null + var data: Data? = null + } + + class TxUnspent { + class Data { + class Tx { + var txid: String? = null //"9b5c8fbeb1e42bb2a6da40e2eab49c368d1a205707a1ec88aa13f0f2ecdfe944", + var output_no: Int? = null // 0, + var script_asm: String? = null // "OP_DUP OP_HASH160 8541eb0593bb19c3755198e7d2a71e134da21a97 OP_EQUALVERIFY OP_CHECKSIG", + var script_hex: String? = null // "76a9148541eb0593bb19c3755198e7d2a71e134da21a9788ac", + var value: String? = null // "11.38404832", + var confirmations: Long? = null + var time: Long? = null// : 1555509495 + } + + var network: String? = null + var address: String? = null + var txs: Array? = null + } + + var status: String? = null + var data: Data? = null + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/di/NetworkComponent.kt b/app/src/main/java/com/tangem/di/NetworkComponent.kt index 7621faf5a2..d04db07e22 100644 --- a/app/src/main/java/com/tangem/di/NetworkComponent.kt +++ b/app/src/main/java/com/tangem/di/NetworkComponent.kt @@ -29,6 +29,9 @@ interface NetworkComponent { @get:Named(Server.ApiRootstock.URL_ROOTSTOCK) val retrofitRootstock: Retrofit + @get:Named(Server.ApiSoChain.URL) + val retrofitSoChain: Retrofit + @get:Named("socket") val socket: Socket diff --git a/app/src/main/java/com/tangem/di/NetworkModule.kt b/app/src/main/java/com/tangem/di/NetworkModule.kt index 7e982995e5..93bb0aa3eb 100644 --- a/app/src/main/java/com/tangem/di/NetworkModule.kt +++ b/app/src/main/java/com/tangem/di/NetworkModule.kt @@ -84,6 +84,18 @@ internal class NetworkModule { return builder.build() } + @Singleton + @Provides + @Named(Server.ApiSoChain.URL) + fun provideRetrofitSoChain(): Retrofit { + val builder = Retrofit.Builder() + .baseUrl(Server.ApiSoChain.URL) + .addConverterFactory(GsonConverterFactory.create()) + if (BuildConfig.DEBUG) + builder.client(createOkHttpClient()) + return builder.build() + } + private fun createOkHttpClient(): OkHttpClient { return OkHttpClient.Builder().addInterceptor(createHttpLoggingInterceptor()).build() } diff --git a/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java b/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java index 7245c9c4ba..73b6871752 100644 --- a/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java +++ b/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java @@ -12,7 +12,10 @@ import com.tangem.card_common.util.Util; import com.tangem.data.Blockchain; import com.tangem.data.local.PendingTransactionsStorage; import com.tangem.data.network.ElectrumRequest; +import com.tangem.data.network.Server; import com.tangem.data.network.ServerApiElectrum; +import com.tangem.data.network.ServerApiSoChain; +import com.tangem.data.network.model.SoChain; import com.tangem.wallet.BTCUtils; import com.tangem.wallet.BalanceValidator; import com.tangem.wallet.Base58; @@ -45,6 +48,8 @@ public class BtcEngine extends CoinEngine { private static final String TAG = BtcEngine.class.getSimpleName(); + private static boolean useElectrum = false; + public BtcData coinData = null; public BtcEngine(TangemContext context) throws Exception { @@ -427,13 +432,20 @@ public class BtcEngine extends CoinEngine { 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 - unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); + if (useElectrum) { + // Build script for our address + List rawTxList = coinData.getUnspentTransactions(); + byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; + // Collect unspent + unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); + } else { + unspentOutputs = new ArrayList<>(); + for (BtcData.UnspentTransaction utxo : coinData.getUnspentTransactions()) { + unspentOutputs.add(new UnspentOutputInfo(BTCUtils.fromHex(utxo.txID), new Transaction.Script(BTCUtils.fromHex(utxo.Raw)), utxo.Amount, utxo.Height, -1, utxo.txID, null)); + } + } long fullAmount = 0; for (int i = 0; i < unspentOutputs.size(); ++i) { fullAmount += unspentOutputs.get(i).value; @@ -523,123 +535,82 @@ public class BtcEngine extends CoinEngine { } @Override - public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) { - final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); - - ServerApiElectrum.ResponseListener electrumListener = new ServerApiElectrum.ResponseListener() { - @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); - 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()); - checkPending(blockchainRequestsCallbacks); - } catch (JSONException e) { - e.printStackTrace(); - Log.e(TAG, "FAIL METHOD_GetBalance JSONException"); - } catch (Exception e) { - e.printStackTrace(); - Log.e(TAG, "FAIL METHOD_GetBalance Exception"); - } - } else 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.getLong("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 (blockchainRequestsCallbacks.allowAdvance()) { - serverApiElectrum.requestData(ctx, ElectrumRequest.getTransaction(walletAddress, hash)); - } else { - ctx.setError("Terminated by user"); - } - } - } - } catch (JSONException e) { - e.printStackTrace(); - } - } else 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.isRequestsSequenceCompleted()) { - blockchainRequestsCallbacks.onComplete(!ctx.hasError()); - } else { - blockchainRequestsCallbacks.onProgress(); - } - } - - @Override - public void onFail(ElectrumRequest electrumRequest) { - Log.i(TAG, "onFail: " + electrumRequest.getMethod() + " " + electrumRequest.getError()); - ctx.setError(electrumRequest.getError()); -// ctx.setError(R.string.cannot_obtain_data_from_blockchain); - if (serverApiElectrum.isRequestsSequenceCompleted()) { - blockchainRequestsCallbacks.onComplete(false);//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError()); - } else { - blockchainRequestsCallbacks.onProgress(); - } - } - }; - - serverApiElectrum.setResponseListener(electrumListener); - - serverApiElectrum.requestData(ctx, ElectrumRequest.checkBalance(coinData.getWallet())); - serverApiElectrum.requestData(ctx, ElectrumRequest.listUnspent(coinData.getWallet())); - } - - private void checkPending(BlockchainRequestsCallbacks blockchainRequestsCallbacks) { - if (App.pendingTransactionsStorage.hasTransactions(ctx.getCard())) { - ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); + public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) throws Exception { + if (useElectrum) { + final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); ServerApiElectrum.ResponseListener electrumListener = new ServerApiElectrum.ResponseListener() { @Override public void onSuccess(ElectrumRequest electrumRequest) { Log.i(TAG, "onSuccess: " + electrumRequest.getMethod()); - try { - if (electrumRequest.getResultString() != null) { - App.pendingTransactionsStorage.removeTransaction(ctx.getCard(), electrumRequest.txHash); + 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()); + checkPending(blockchainRequestsCallbacks); + } catch (JSONException e) { + e.printStackTrace(); + Log.e(TAG, "FAIL METHOD_GetBalance JSONException"); + } catch (Exception e) { + e.printStackTrace(); + Log.e(TAG, "FAIL METHOD_GetBalance Exception"); } + } else 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.getLong("value"); + trUnspent.Height = jsUnspent.getInt("height"); + coinData.getUnspentTransactions().add(trUnspent); + } + } catch (JSONException e) { + e.printStackTrace(); + Log.e(TAG, "FAIL METHOD_ListUnspent JSONException"); + } - } catch (Exception e) { - Log.e(TAG, "onFail: " + electrumRequest.getMethod() + " " + electrumRequest.getError()); + 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 (blockchainRequestsCallbacks.allowAdvance()) { + serverApiElectrum.requestData(ctx, ElectrumRequest.getTransaction(walletAddress, hash)); + } else { + ctx.setError("Terminated by user"); + } + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + } else 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.isRequestsSequenceCompleted()) { blockchainRequestsCallbacks.onComplete(!ctx.hasError()); } else { @@ -650,6 +621,7 @@ public class BtcEngine extends CoinEngine { @Override public void onFail(ElectrumRequest electrumRequest) { Log.i(TAG, "onFail: " + electrumRequest.getMethod() + " " + electrumRequest.getError()); + ctx.setError(electrumRequest.getError()); // ctx.setError(R.string.cannot_obtain_data_from_blockchain); if (serverApiElectrum.isRequestsSequenceCompleted()) { blockchainRequestsCallbacks.onComplete(false);//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError()); @@ -660,9 +632,163 @@ public class BtcEngine extends CoinEngine { }; serverApiElectrum.setResponseListener(electrumListener); - for (PendingTransactionsStorage.TransactionInfo pendingTx : App.pendingTransactionsStorage.getTransactions(ctx.getCard()).getTransactions()) { - String txHash = BTCUtils.toHex(CryptoUtil.doubleSha256(BTCUtils.fromHex(pendingTx.getTx()))); - serverApiElectrum.requestData(ctx, ElectrumRequest.getTransaction(ctx.getCoinData().getWallet(), txHash)); + + serverApiElectrum.requestData(ctx, ElectrumRequest.checkBalance(coinData.getWallet())); + serverApiElectrum.requestData(ctx, ElectrumRequest.listUnspent(coinData.getWallet())); + + } else { + final ServerApiSoChain serverApi = new ServerApiSoChain(); + + ServerApiSoChain.ResponseListener responseListener = new ServerApiSoChain.ResponseListener() { + @Override + public void onSuccess(SoChain.Response.AddressBalance response) { + try { + String walletAddress = response.getData().getAddress(); + if (!walletAddress.equals(coinData.getWallet())) { + // todo - check + throw new Exception("Invalid wallet address in answer!"); + } + Long confBalance = convertToInternalAmount(convertToAmount(response.getData().getConfirmed_balance(), getBalanceCurrency())).longValueExact(); + Long unconfirmedBalance = convertToInternalAmount(convertToAmount(response.getData().getUnconfirmed_balance(), getBalanceCurrency())).longValueExact(); + coinData.setBalanceReceived(true); + coinData.setBalanceConfirmed(confBalance); + coinData.setBalanceUnconfirmed(unconfirmedBalance); + coinData.setValidationNodeDescription(Server.ApiSoChain.URL); + checkPending(blockchainRequestsCallbacks); + } 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 (serverApi.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + } else { + blockchainRequestsCallbacks.onProgress(); + } + + } + + @Override + public void onSuccess(SoChain.Response.TxUnspent response) { + String walletAddress = response.getData().getAddress(); + try { + if (!walletAddress.equals(coinData.getWallet())) { + // todo - check + throw new Exception("Invalid wallet address in answer!"); + } + coinData.getUnspentTransactions().clear(); + if (response.getData().getTxs() != null) + for (SoChain.Response.TxUnspent.Data.Tx tx : response.getData().getTxs()) { + BtcData.UnspentTransaction trUnspent = new BtcData.UnspentTransaction(); + trUnspent.txID = tx.getTxid(); + trUnspent.Amount = convertToInternalAmount(convertToAmount(tx.getValue(), getBalanceCurrency())).longValueExact(); + trUnspent.Height = tx.getOutput_no(); + trUnspent.Raw = tx.getScript_hex(); + coinData.getUnspentTransactions().add(trUnspent); + +// if (blockchainRequestsCallbacks.allowAdvance()) { +// //serverApi.requestData(ctx, ElectrumRequest.getTransaction(walletAddress, hash)); +// } else { +// ctx.setError("Terminated by user"); +// } + } + } catch (Exception e) { + e.printStackTrace(); + Log.e(TAG, "FAIL METHOD_ListUnspent JSONException"); + } + + if (serverApi.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + } else { + blockchainRequestsCallbacks.onProgress(); + } + + } + +// +// } else 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(); +// } +// } + +// } + + + @Override + public void onFail(String message) { + Log.i(TAG, "onFail: " + message); + ctx.setError(message); +// ctx.setError(R.string.cannot_obtain_data_from_blockchain); + if (serverApi.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(false);//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError()); + } else { + blockchainRequestsCallbacks.onProgress(); + } + } + }; + + + serverApi.setResponseListener(responseListener); + + serverApi.requestAddressBalance(ctx.getBlockchain(), coinData.getWallet()); + serverApi.requestUnspentTx(ctx.getBlockchain(), coinData.getWallet()); + } + } + + private void checkPending(BlockchainRequestsCallbacks blockchainRequestsCallbacks) throws Exception { + if (App.pendingTransactionsStorage.hasTransactions(ctx.getCard())) { + + if (useElectrum) { + ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); + + ServerApiElectrum.ResponseListener electrumListener = new ServerApiElectrum.ResponseListener() { + @Override + public void onSuccess(ElectrumRequest electrumRequest) { + Log.i(TAG, "onSuccess: " + electrumRequest.getMethod()); + try { + if (electrumRequest.getResultString() != null) { + App.pendingTransactionsStorage.removeTransaction(ctx.getCard(), electrumRequest.txHash); + } + + } catch (Exception e) { + Log.e(TAG, "onFail: " + electrumRequest.getMethod() + " " + electrumRequest.getError()); + } + if (serverApiElectrum.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + } else { + blockchainRequestsCallbacks.onProgress(); + } + } + + @Override + public void onFail(ElectrumRequest electrumRequest) { + Log.i(TAG, "onFail: " + electrumRequest.getMethod() + " " + electrumRequest.getError()); +// ctx.setError(R.string.cannot_obtain_data_from_blockchain); + if (serverApiElectrum.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(false);//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError()); + } else { + blockchainRequestsCallbacks.onProgress(); + } + } + }; + + serverApiElectrum.setResponseListener(electrumListener); + for (PendingTransactionsStorage.TransactionInfo pendingTx : App.pendingTransactionsStorage.getTransactions(ctx.getCard()).getTransactions()) { + String txHash = BTCUtils.toHex(CryptoUtil.doubleSha256(BTCUtils.fromHex(pendingTx.getTx()))); + serverApiElectrum.requestData(ctx, ElectrumRequest.getTransaction(ctx.getCoinData().getWallet(), txHash)); + } + } else { + throw new Exception("Not supported!"); } } } @@ -758,13 +884,13 @@ public class BtcEngine extends CoinEngine { public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception { 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; + coinData.minFee = null; + coinData.maxFee = null; + coinData.normalFee = null; final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); - final ServerApiElectrum.ResponseListener electrumListener = new ServerApiElectrum.ResponseListener() { + final ServerApiElectrum.ResponseListener electrumListener = new ServerApiElectrum.ResponseListener() { @Override public void onSuccess(ElectrumRequest electrumRequest) { BigDecimal kbFee; @@ -784,9 +910,9 @@ public class BtcEngine extends CoinEngine { BigDecimal normalFee = normalByteFee.multiply(new BigDecimal(calcSize)).setScale(8, RoundingMode.DOWN); BigDecimal maxFee = maxByteFee.multiply(new BigDecimal(calcSize)).setScale(8, RoundingMode.DOWN); - CoinEngine.Amount minAmount = new CoinEngine.Amount(minFee, ctx.getBlockchain().getCurrency()); - CoinEngine.Amount normalAmount = new CoinEngine.Amount(normalFee, ctx.getBlockchain().getCurrency()); - CoinEngine.Amount maxAmount = new CoinEngine.Amount(maxFee, ctx.getBlockchain().getCurrency()); + CoinEngine.Amount minAmount = new CoinEngine.Amount(minFee, ctx.getBlockchain().getCurrency()); + CoinEngine.Amount normalAmount = new CoinEngine.Amount(normalFee, ctx.getBlockchain().getCurrency()); + CoinEngine.Amount maxAmount = new CoinEngine.Amount(maxFee, ctx.getBlockchain().getCurrency()); coinData.minFee = minAmount; coinData.normalFee = normalAmount;