Updated on 2026-08-14

This commit is contained in:
Tangem 2019-08-26 15:38:01 +03:00
parent 38543d26dd
commit 2b2344ea2e
9 changed files with 283 additions and 208 deletions

View file

@ -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> blockchainInfoAddress(@Path("address") String address);
@GET(Server.ApiBlockchainInfo.Method.UTXO)
Single<BlockchainInfoUnspents> blockchainInfoUnspents(@Query("active") String address);
@FormUrlEncoded
@POST(Server.ApiBlockchainInfo.Method.PUSH)
Single<ResponseBody> blockchainInfoPush(@Field("tx") String tx);
}

View file

@ -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";
}
}
}

View file

@ -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<BlockchainInfoAddressAndUnspents> addressAndUnspentsObserver) {
Log.i(TAG, "new getAddressAndUnspents request");
BlockchainInfoApi api = App.Companion.getNetworkComponent().getRetrofitBlockchainInfo().create(BlockchainInfoApi.class);
Single<BlockchainInfoAddress> addressObservable = api.blockchainInfoAddress(wallet);
Single<BlockchainInfoUnspents> 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<ResponseBody> sendObserver) {
Log.i(TAG, "new getAddress request");
BlockchainInfoApi api = App.Companion.getNetworkComponent().getRetrofitBlockchainInfo().create(BlockchainInfoApi.class);
Single<ResponseBody> sendObservable = api.blockchainInfoPush(tx)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
sendObservable.subscribe(sendObserver);
}
}

View file

@ -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/";
}

View file

@ -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<BlockchainInfoTransaction>? = 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<BlockchainInfoUtxo>
)
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
)

View file

@ -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

View file

@ -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()
}

View file

@ -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;
}
}

View file

@ -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<BlockchainInfoAddressAndUnspents> addressAndUnspentsObserver = new DisposableSingleObserver<BlockchainInfoAddressAndUnspents>() {
@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<ResponseBody> responseObserver = new DisposableSingleObserver<ResponseBody>() {
@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();