diff --git a/app/src/main/java/com/tangem/data/network/BlockchainInfoApi.java b/app/src/main/java/com/tangem/data/network/BlockchainInfoApi.java new file mode 100644 index 0000000000..caa2e14013 --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/BlockchainInfoApi.java @@ -0,0 +1,27 @@ +package com.tangem.data.network; + +import com.tangem.data.network.model.BlockchainInfoAddress; +import com.tangem.data.network.model.BlockchainInfoUnspents; + +import io.reactivex.Observable; + +import io.reactivex.Single; +import okhttp3.ResponseBody; +import retrofit2.http.Field; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.GET; +import retrofit2.http.POST; +import retrofit2.http.Path; +import retrofit2.http.Query; + +public interface BlockchainInfoApi { + @GET(Server.ApiBlockchainInfo.Method.ADDRESS) + Single blockchainInfoAddress(@Path("address") String address); + + @GET(Server.ApiBlockchainInfo.Method.UTXO) + Single blockchainInfoUnspents(@Query("active") String address); + + @FormUrlEncoded + @POST(Server.ApiBlockchainInfo.Method.PUSH) + Single blockchainInfoPush(@Field("tx") String tx); +} 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 943488dc02..1b0570150a 100644 --- a/app/src/main/java/com/tangem/data/network/Server.java +++ b/app/src/main/java/com/tangem/data/network/Server.java @@ -118,4 +118,15 @@ public class Server { static final String PUSH = MAIN + "/txs/push"; } } + + public static class ApiBlockchainInfo { + public static final String URL_BLOCKCHAININFO = ServerURL.API_BLOCKCHAIN_INFO; + + public static class Method { + static final String ADDRESS = URL_BLOCKCHAININFO + "rawaddr/{address}?limit=5"; + static final String UTXO = URL_BLOCKCHAININFO + "unspent"; +// static final String TX = URL_BLOCKCHAININFO + "rawtx/{txHash}"; + static final String PUSH = URL_BLOCKCHAININFO + "pushtx"; + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/data/network/ServerApiBlockchainInfo.java b/app/src/main/java/com/tangem/data/network/ServerApiBlockchainInfo.java new file mode 100644 index 0000000000..42581e3d43 --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/ServerApiBlockchainInfo.java @@ -0,0 +1,48 @@ +package com.tangem.data.network; + +import android.util.Log; + +import com.tangem.App; +import com.tangem.data.network.model.BlockchainInfoAddress; +import com.tangem.data.network.model.BlockchainInfoAddressAndUnspents; +import com.tangem.data.network.model.BlockchainInfoUnspents; + +import java.util.ArrayList; + +import io.reactivex.Observable; +import io.reactivex.Observer; +import io.reactivex.Single; +import io.reactivex.SingleObserver; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; +import okhttp3.ResponseBody; + +public class ServerApiBlockchainInfo { + private static String TAG = ServerApiBlockchainInfo.class.getSimpleName(); + + public void getAddressAndUnspents(String wallet, SingleObserver addressAndUnspentsObserver) { + Log.i(TAG, "new getAddressAndUnspents request"); + BlockchainInfoApi api = App.Companion.getNetworkComponent().getRetrofitBlockchainInfo().create(BlockchainInfoApi.class); + + Single addressObservable = api.blockchainInfoAddress(wallet); + + Single unspentsObservable = api.blockchainInfoUnspents(wallet) + .onErrorReturnItem(new BlockchainInfoUnspents(new ArrayList<>())); + + Single.zip(addressObservable, unspentsObservable, BlockchainInfoAddressAndUnspents::new) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(addressAndUnspentsObserver); + } + + public void sendTransaction(String tx, SingleObserver sendObserver) { + Log.i(TAG, "new getAddress request"); + BlockchainInfoApi api = App.Companion.getNetworkComponent().getRetrofitBlockchainInfo().create(BlockchainInfoApi.class); + + Single sendObservable = api.blockchainInfoPush(tx) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + + sendObservable.subscribe(sendObserver); + } +} 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 362e064d50..45515132d2 100644 --- a/app/src/main/java/com/tangem/data/network/ServerURL.java +++ b/app/src/main/java/com/tangem/data/network/ServerURL.java @@ -14,4 +14,5 @@ class ServerURL { static final String API_MATIC_TESTNET = "https://testnet2.matic.network"; static final String API_STELLAR = "https://horizon.stellar.org/"; static final String API_STELLAR_TESTNET = "https://horizon-testnet.stellar.org"; + static final String API_BLOCKCHAIN_INFO = "https://blockchain.info/"; } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/data/network/model/BlockchainInfoResponse.kt b/app/src/main/java/com/tangem/data/network/model/BlockchainInfoResponse.kt new file mode 100644 index 0000000000..fab1d0c88f --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/model/BlockchainInfoResponse.kt @@ -0,0 +1,43 @@ +package com.tangem.data.network.model + +import com.google.gson.annotations.SerializedName + +data class BlockchainInfoAddress( + @SerializedName("final_balance") + var final_balance: Long? = null, + + @SerializedName("txs") + var txs: List? = null +) + +data class BlockchainInfoTransaction( + @SerializedName("hash") + var hash: String? = null, + + @SerializedName("block_height") + var block_height: Long? = null +) + +data class BlockchainInfoUnspents( + @SerializedName("unspent_outputs") + var unspent_outputs: List +) + +data class BlockchainInfoUtxo( + @SerializedName("tx_hash_big_endian") + var tx_hash_big_endian: String? = null, + + @SerializedName("tx_output_n") + var tx_output_n: Int? = null, + + @SerializedName("value") + var value: Long? = null, + + @SerializedName("script") + var script: String? = null +) + +data class BlockchainInfoAddressAndUnspents( + var address: BlockchainInfoAddress, + var unspents: BlockchainInfoUnspents +) \ 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 06182cb4a8..62f5c62650 100644 --- a/app/src/main/java/com/tangem/di/NetworkComponent.kt +++ b/app/src/main/java/com/tangem/di/NetworkComponent.kt @@ -38,6 +38,9 @@ interface NetworkComponent { @get:Named(Server.ApiSoChain.URL) val retrofitSoChain: Retrofit + @get:Named(Server.ApiBlockchainInfo.URL_BLOCKCHAININFO) + val retrofitBlockchainInfo: 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 4f1da7af02..c5ccc558e2 100644 --- a/app/src/main/java/com/tangem/di/NetworkModule.kt +++ b/app/src/main/java/com/tangem/di/NetworkModule.kt @@ -18,6 +18,7 @@ import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory @Module internal class NetworkModule { @@ -121,6 +122,20 @@ internal class NetworkModule { return builder.build() } + @Singleton + @Provides + @Named(Server.ApiBlockchainInfo.URL_BLOCKCHAININFO) + fun provideRetrofitBlockchainInfo(): Retrofit { + val builder = Retrofit.Builder() + .baseUrl(Server.ApiBlockchainInfo.URL_BLOCKCHAININFO) + .addConverterFactory(GsonConverterFactory.create()) + .addConverterFactory(ScalarsConverterFactory.create()) + .addCallAdapterFactory(RxJava2CallAdapterFactory.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/BtcData.java b/app/src/main/java/com/tangem/wallet/btc/BtcData.java index 24d82d0ed0..ba4a6da025 100644 --- a/app/src/main/java/com/tangem/wallet/btc/BtcData.java +++ b/app/src/main/java/com/tangem/wallet/btc/BtcData.java @@ -19,17 +19,19 @@ public class BtcData extends CoinData { private boolean useBlockcypher = false; + //for blockchain.info + private boolean hasUnconfirmed = false; + public String getUnspentInputsDescription() { try { int gatheredUnspents = 0; - if( unspentTransactions==null ) return ""; + if (unspentTransactions == null) return ""; for (int i = 0; i < unspentTransactions.size(); i++) { - if (unspentTransactions.get(i).script != null && unspentTransactions.get(i).script.length() > 1) gatheredUnspents++; + if (unspentTransactions.get(i).script != null && unspentTransactions.get(i).script.length() > 1) + gatheredUnspents++; } return unspentTransactions.size() + " unspents (" + gatheredUnspents + " received)"; - } - catch (Exception e) - { + } catch (Exception e) { e.printStackTrace(); return ""; } @@ -86,6 +88,7 @@ public class BtcData extends CoinData { } } if (B.containsKey("UseBlockcypher")) useBlockcypher = B.getBoolean("UseBlockcypher"); + if (B.containsKey("HasUnconfirmed")) useBlockcypher = B.getBoolean("HasUnconfirmed"); } @Override @@ -102,6 +105,7 @@ public class BtcData extends CoinData { if (balanceConfirmed != null) B.putLong("BalanceConfirmed", balanceConfirmed); if (balanceUnconfirmed != null) B.putLong("BalanceUnconfirmed", balanceUnconfirmed); if (useBlockcypher) B.putBoolean("UseBlockcypher", true); + if (hasUnconfirmed) B.putBoolean("HasUnconfirmed", true); } catch (Exception e) { Log.e("Can't save to bundle ", e.getMessage()); } @@ -116,7 +120,11 @@ public class BtcData extends CoinData { } public CoinEngine.InternalAmount getBalanceInInternalUnits() { - return new CoinEngine.InternalAmount(BigDecimal.valueOf(balanceConfirmed).add(BigDecimal.valueOf(balanceUnconfirmed)),"Satoshi"); + if (balanceConfirmed != null && balanceUnconfirmed != null) { + return new CoinEngine.InternalAmount(BigDecimal.valueOf(balanceConfirmed).add(BigDecimal.valueOf(balanceUnconfirmed)), "Satoshi"); + } else { + return null; + } } public Long getBalanceUnconfirmed() { @@ -142,4 +150,12 @@ public class BtcData extends CoinData { public void setUseBlockcypher(boolean useBlockcypher) { this.useBlockcypher = useBlockcypher; } + + public boolean isHasUnconfirmed() { + return hasUnconfirmed; + } + + public void setHasUnconfirmed(boolean hasUnconfirmed) { + this.hasUnconfirmed = hasUnconfirmed; + } } 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 22d1075ccc..618d42f598 100644 --- a/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java +++ b/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java @@ -12,9 +12,15 @@ import com.tangem.card_common.util.Util; import com.tangem.data.Blockchain; import com.tangem.data.local.PendingTransactionsStorage; import com.tangem.data.network.Server; +import com.tangem.data.network.ServerApiBlockchainInfo; import com.tangem.data.network.ServerApiBlockcypher; import com.tangem.data.network.ServerApiCommon; import com.tangem.data.network.ServerApiSoChain; +import com.tangem.data.network.model.BlockchainInfoAddress; +import com.tangem.data.network.model.BlockchainInfoAddressAndUnspents; +import com.tangem.data.network.model.BlockchainInfoTransaction; +import com.tangem.data.network.model.BlockchainInfoUnspents; +import com.tangem.data.network.model.BlockchainInfoUtxo; import com.tangem.data.network.model.BlockcypherFee; import com.tangem.data.network.model.BlockcypherResponse; import com.tangem.data.network.model.BlockcypherTx; @@ -33,9 +39,8 @@ import com.tangem.wallet.TangemContext; import com.tangem.wallet.Transaction; import com.tangem.wallet.UnspentOutputInfo; -import org.json.JSONException; - import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; @@ -46,6 +51,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import io.reactivex.Observer; +import io.reactivex.SingleObserver; +import io.reactivex.observers.DefaultObserver; +import io.reactivex.observers.DisposableSingleObserver; +import okhttp3.ResponseBody; + public class BtcEngine extends CoinEngine { private static final String TAG = BtcEngine.class.getSimpleName(); @@ -263,7 +274,7 @@ public class BtcEngine extends CoinEngine { // return; // } - if (coinData.getBalanceUnconfirmed() != 0) { + if (coinData.getBalanceUnconfirmed() != 0 || coinData.isHasUnconfirmed()) { balanceValidator.setScore(0); balanceValidator.setFirstLine("Transaction in progress"); balanceValidator.setSecondLine("Wait for confirmation in blockchain"); @@ -537,120 +548,67 @@ public class BtcEngine extends CoinEngine { public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) throws Exception { ctx.setError(null); - if (!coinData.isUseBlockcypher()) { - final ServerApiSoChain serverApiSoChain = new ServerApiSoChain(); + //SoChain request can be found at LtcEngine + if (!coinData.isUseBlockcypher() && ctx.getBlockchain() != Blockchain.BitcoinTestNet) { + final ServerApiBlockchainInfo serverApiBlockchainInfo = new ServerApiBlockchainInfo(); - ServerApiSoChain.AddressInfoListener addressInfoListener = new ServerApiSoChain.AddressInfoListener() { + SingleObserver addressAndUnspentsObserver = new DisposableSingleObserver() { @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(); + public void onSuccess(BlockchainInfoAddressAndUnspents blockchainInfoAddressAndUnspents) { + BlockchainInfoAddress blockchainInfoAddress = blockchainInfoAddressAndUnspents.getAddress(); + BlockchainInfoUnspents blockchainInfoUnspents = blockchainInfoAddressAndUnspents.getUnspents(); + + if (blockchainInfoAddress.getFinal_balance() != null) { coinData.setBalanceReceived(true); - coinData.setBalanceConfirmed(confBalance); - coinData.setBalanceUnconfirmed(unconfirmedBalance); - coinData.setValidationNodeDescription(Server.ApiSoChain.URL); - } catch (JSONException e) { - e.printStackTrace(); - Log.e(TAG, "FAIL METHOD_GetBalance JSONException"); - ctx.setError("FAIL METHOD_GetBalance JSONException"); - } catch (Exception e) { - e.printStackTrace(); - Log.e(TAG, "FAIL METHOD_GetBalance Exception"); - ctx.setError("FAIL METHOD_GetBalance Exception"); - } - if (serverApiSoChain.isRequestsSequenceCompleted()) { - if (!coinData.isUseBlockcypher()) { - checkPending(blockchainRequestsCallbacks); - } else { - try { - requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks); - } catch (Exception e) { - ctx.setError(e.getMessage()); - blockchainRequestsCallbacks.onComplete(false); + coinData.setBalanceConfirmed(blockchainInfoAddress.getFinal_balance()); + coinData.setBalanceUnconfirmed(0L); + coinData.setValidationNodeDescription(Server.ApiBlockchainInfo.URL_BLOCKCHAININFO); + + for (BlockchainInfoTransaction tx : blockchainInfoAddress.getTxs()) { + if (tx.getBlock_height() == null) { + coinData.setHasUnconfirmed(true); + } + } + + if (App.pendingTransactionsStorage.hasTransactions(ctx.getCard())) { + for (PendingTransactionsStorage.TransactionInfo pendingTx : App.pendingTransactionsStorage.getTransactions(ctx.getCard()).getTransactions()) { + String pendingTxId = BTCUtils.toHex(BTCUtils.reverse(CryptoUtil.doubleSha256(BTCUtils.fromHex(pendingTx.getTx())))); + for (BlockchainInfoTransaction responseTx : blockchainInfoAddress.getTxs()) { + if (responseTx.getHash().equals(pendingTxId)) { + App.pendingTransactionsStorage.removeTransaction(ctx.getCard(), pendingTx.getTx()); + } + } } } - } else { - blockchainRequestsCallbacks.onProgress(); } + for (BlockchainInfoUtxo utxo : blockchainInfoUnspents.getUnspent_outputs()) { + BtcData.UnspentTransaction trUnspent = new BtcData.UnspentTransaction(); + trUnspent.txID = utxo.getTx_hash_big_endian(); + trUnspent.amount = utxo.getValue(); + trUnspent.outputN = utxo.getTx_output_n(); + trUnspent.script = utxo.getScript(); + coinData.getUnspentTransactions().add(trUnspent); + } + + blockchainRequestsCallbacks.onComplete(true); } @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.outputN = tx.getOutput_no(); - trUnspent.script = tx.getScript_hex(); - coinData.getUnspentTransactions().add(trUnspent); - -// if (blockchainRequestsCallbacks.allowAdvance()) { -// //serverApiSoChain.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 (serverApiSoChain.isRequestsSequenceCompleted()) { - if (!coinData.isUseBlockcypher()) { - checkPending(blockchainRequestsCallbacks); - } else { - try { - requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks); - } catch (Exception e) { - ctx.setError(e.getMessage()); - blockchainRequestsCallbacks.onComplete(false); - } - } - } else { - blockchainRequestsCallbacks.onProgress(); - } - - } - - @Override - public void onFail(String message) { - Log.i(TAG, "onFail: " + message); + public void onError(Throwable e) { + Log.i(TAG, "onError: getAddress" + e.getMessage()); coinData.setUseBlockcypher(true); -// ctx.setError(R.string.cannot_obtain_data_from_blockchain); - if (serverApiSoChain.isRequestsSequenceCompleted()) { -// blockchainRequestsCallbacks.onComplete(false);//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError()); - try { - requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks); - } catch (Exception e) { - ctx.setError(e.getMessage()); - blockchainRequestsCallbacks.onComplete(false); - } - } else { - blockchainRequestsCallbacks.onProgress(); + try { + requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks); + } catch (Exception ex) { + ctx.setError(ex.getMessage()); + blockchainRequestsCallbacks.onComplete(false); } } }; + serverApiBlockchainInfo.getAddressAndUnspents(coinData.getWallet(), addressAndUnspentsObserver); - serverApiSoChain.setAddressInfoListener(addressInfoListener); - - serverApiSoChain.requestAddressBalance(ctx.getBlockchain(), coinData.getWallet()); - serverApiSoChain.requestUnspentTx(ctx.getBlockchain(), coinData.getWallet()); } else { final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher(); @@ -716,93 +674,48 @@ public class BtcEngine extends CoinEngine { private void checkPending(BlockchainRequestsCallbacks blockchainRequestsCallbacks) { if (App.pendingTransactionsStorage.hasTransactions(ctx.getCard())) { + ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher(); - if (!coinData.isUseBlockcypher()) { - ServerApiSoChain serverApiSoChain = new ServerApiSoChain(); - - ServerApiSoChain.TransactionInfoListener listener = new ServerApiSoChain.TransactionInfoListener() { - @Override - public void onSuccess(SoChain.Response.GetTx response) { - Log.i(TAG, "onSuccess: GetTx"); - try { - if (response.getData() != null && response.getData().getTx_hex() != null && response.getData().getTxid() != null) { - App.pendingTransactionsStorage.removeTransaction(ctx.getCard(), response.getData().getTx_hex()); - } - } catch (Exception e) { - Log.e(TAG, "onFail: GetTx" + response); - } - if (serverApiSoChain.isRequestsSequenceCompleted()) { - blockchainRequestsCallbacks.onComplete(!ctx.hasError()); - } else { - blockchainRequestsCallbacks.onProgress(); - } - } - - @Override - public void onFail(String message) { - Log.i(TAG, "onFail: GetTx " + message); - if (serverApiSoChain.isRequestsSequenceCompleted()) { - blockchainRequestsCallbacks.onComplete(!ctx.hasError());//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError()); - } else { - blockchainRequestsCallbacks.onProgress(); - } - } - }; - - serverApiSoChain.setTransactionInfoListener(listener); - for (PendingTransactionsStorage.TransactionInfo pendingTx : App.pendingTransactionsStorage.getTransactions(ctx.getCard()).getTransactions()) { - String txId = BTCUtils.toHex(BTCUtils.reverse(CryptoUtil.doubleSha256(BTCUtils.fromHex(pendingTx.getTx())))); + ServerApiBlockcypher.TxResponseListener listener = new ServerApiBlockcypher.TxResponseListener() { + @Override + public void onSuccess(BlockcypherTx response) { + Log.i(TAG, "onSuccess: BlockcypherTx"); try { - serverApiSoChain.requestTransactionInfo(ctx.getBlockchain(), txId); + if (response.getHex() != null) { + App.pendingTransactionsStorage.removeTransaction(ctx.getCard(), response.getHex()); + } } catch (Exception e) { - e.printStackTrace(); + Log.e(TAG, "onFail: BlockcypherTx" + response); + } + if (serverApiBlockcypher.isRequestsSequenceCompleted()) { blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + } else { + blockchainRequestsCallbacks.onProgress(); } } - } else { - ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher(); - ServerApiBlockcypher.TxResponseListener listener = new ServerApiBlockcypher.TxResponseListener() { - - @Override - public void onSuccess(BlockcypherTx response) { - Log.i(TAG, "onSuccess: BlockcypherTx"); - try { - if (response.getHex() != null) { - App.pendingTransactionsStorage.removeTransaction(ctx.getCard(), response.getHex()); - } - } catch (Exception e) { - Log.e(TAG, "onFail: BlockcypherTx" + response); - } - if (serverApiBlockcypher.isRequestsSequenceCompleted()) { - blockchainRequestsCallbacks.onComplete(!ctx.hasError()); - } else { - blockchainRequestsCallbacks.onProgress(); - } + @Override + public void onFail(String message) { + Log.i(TAG, "onFail: BlockcypherTx " + message); + if (serverApiBlockcypher.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError());//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError()); + } else { + blockchainRequestsCallbacks.onProgress(); } + } + }; + serverApiBlockcypher.setTxResponseListener(listener); - @Override - public void onFail(String message) { - Log.i(TAG, "onFail: BlockcypherTx " + message); - if (serverApiBlockcypher.isRequestsSequenceCompleted()) { - blockchainRequestsCallbacks.onComplete(!ctx.hasError());//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError()); - } else { - blockchainRequestsCallbacks.onProgress(); - } - } - }; - serverApiBlockcypher.setTxResponseListener(listener); - - for (PendingTransactionsStorage.TransactionInfo pendingTx : App.pendingTransactionsStorage.getTransactions(ctx.getCard()).getTransactions()) { - String txId = BTCUtils.toHex(BTCUtils.reverse(CryptoUtil.doubleSha256(BTCUtils.fromHex(pendingTx.getTx())))); - try { - serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_TXS, "", txId); - } catch (Exception e) { - e.printStackTrace(); - blockchainRequestsCallbacks.onComplete(!ctx.hasError()); - } + for (PendingTransactionsStorage.TransactionInfo pendingTx : App.pendingTransactionsStorage.getTransactions(ctx.getCard()).getTransactions()) { + String txId = BTCUtils.toHex(BTCUtils.reverse(CryptoUtil.doubleSha256(BTCUtils.fromHex(pendingTx.getTx())))); + try { + serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_TXS, "", txId); + } catch (Exception e) { + e.printStackTrace(); + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); } } + } else { blockchainRequestsCallbacks.onComplete(!ctx.hasError()); } @@ -1026,42 +939,40 @@ public class BtcEngine extends CoinEngine { @Override public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) throws Exception { final String txStr = BTCUtils.toHex(txForSend); - if (!coinData.isUseBlockcypher()) { - final ServerApiSoChain serverApiSoChain = new ServerApiSoChain(); - ServerApiSoChain.SendTxListener listener = new ServerApiSoChain.SendTxListener() { + //SoChain request can be found at LtcEngine + if (!coinData.isUseBlockcypher()) { + final ServerApiBlockchainInfo serverApiBlockchainInfo = new ServerApiBlockchainInfo(); + + SingleObserver responseObserver = new DisposableSingleObserver() { @Override - public void onSuccess(SoChain.Response.SendTx response) { + public void onSuccess(ResponseBody response) { try { - if (response.getStatus() == null || response.getData() == null) { - ctx.setError("Rejected by node: invalid answer received"); - blockchainRequestsCallbacks.onComplete(false); - } else if (response.getStatus().equals("fail") || response.getData().getTxid() == null) { - ctx.setError("Rejected by node: " + response.getData()); - 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); + Log.i(TAG, response.string()); + if (!response.string().equals("Transaction Submitted")) { + ctx.setError(response.string()); } + } catch (IOException e) { + e.printStackTrace(); + ctx.setError(e.getMessage()); + } + + if (ctx.hasError()) { + blockchainRequestsCallbacks.onComplete(false); + } else { + blockchainRequestsCallbacks.onComplete(true); } } @Override - public void onFail(String message) { - ctx.setError(message); + public void onError(Throwable e) { + Log.e(TAG, e.getMessage()); + ctx.setError(e.getMessage()); blockchainRequestsCallbacks.onComplete(false); } }; - serverApiSoChain.setSendTxListener(listener); - serverApiSoChain.requestSendTransaction(ctx.getBlockchain(), txStr); + + serverApiBlockchainInfo.sendTransaction(txStr, responseObserver); } else { final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher();