From 9f9c5db55e07e1df4870962d609bfc27af1331b6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 8 Aug 2019 14:08:10 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tangem/data/network/BlockcypherApi.java | 4 + .../java/com/tangem/data/network/Server.java | 1 + .../data/network/ServerApiBlockcypher.java | 48 +- .../tangem/data/network/ServerApiSoChain.java | 11 +- .../data/network/model/BlockcypherResponse.kt | 5 + .../main/java/com/tangem/wallet/BCHUtils.java | 4 +- .../main/java/com/tangem/wallet/BTCUtils.java | 4 +- .../com/tangem/wallet/bch/BtcCashEngine.java | 6 +- .../java/com/tangem/wallet/btc/BtcData.java | 33 +- .../java/com/tangem/wallet/btc/BtcEngine.java | 920 +++++++++--------- .../tangem/wallet/cardano/CardanoData.java | 2 +- .../tangem/wallet/ducatus/DucatusEngine.java | 6 +- .../java/com/tangem/wallet/eos/EosEngine.java | 4 +- .../java/com/tangem/wallet/ltc/LtcEngine.java | 9 +- 14 files changed, 581 insertions(+), 476 deletions(-) diff --git a/app/src/main/java/com/tangem/data/network/BlockcypherApi.java b/app/src/main/java/com/tangem/data/network/BlockcypherApi.java index 2ef6251651..d8de09d725 100644 --- a/app/src/main/java/com/tangem/data/network/BlockcypherApi.java +++ b/app/src/main/java/com/tangem/data/network/BlockcypherApi.java @@ -3,6 +3,7 @@ package com.tangem.data.network; import com.tangem.data.network.model.BlockcypherBody; import com.tangem.data.network.model.BlockcypherResponse; import com.tangem.data.network.model.BlockcypherFee; +import com.tangem.data.network.model.BlockcypherTx; import retrofit2.Call; import retrofit2.http.Body; @@ -19,6 +20,9 @@ public interface BlockcypherApi { @GET(Server.ApiBlockcypher.Method.ADDRESS) Call blockcypherAddress(@Path("blockchain") String blockchain, @Path("network") String network, @Path("address") String address); + @GET(Server.ApiBlockcypher.Method.TXS) + Call blockcypherTxs(@Path("blockchain") String blockchain, @Path("network") String network, @Path("txHash") String txHash); + @Headers("Content-Type: application/json") @POST(Server.ApiBlockcypher.Method.PUSH) Call blockcypherPush(@Path("blockchain") String blockchain, @Path("network") String network, @Body BlockcypherBody blockcypherBody, @Query("token") String token); 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 8d79d87be5..943488dc02 100644 --- a/app/src/main/java/com/tangem/data/network/Server.java +++ b/app/src/main/java/com/tangem/data/network/Server.java @@ -114,6 +114,7 @@ public class Server { public static class Method { static final String MAIN = URL_BLOCKCYPHER + V1_MAIN; static final String ADDRESS = MAIN + "/addrs/{address}?unspentOnly=true&includeScript=true"; + static final String TXS = MAIN + "/txs/{txHash}?includeHex=true"; static final String PUSH = MAIN + "/txs/push"; } } diff --git a/app/src/main/java/com/tangem/data/network/ServerApiBlockcypher.java b/app/src/main/java/com/tangem/data/network/ServerApiBlockcypher.java index 33add147b9..f593b1545c 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiBlockcypher.java +++ b/app/src/main/java/com/tangem/data/network/ServerApiBlockcypher.java @@ -7,6 +7,7 @@ import com.tangem.data.Blockchain; import com.tangem.data.network.model.BlockcypherBody; import com.tangem.data.network.model.BlockcypherFee; import com.tangem.data.network.model.BlockcypherResponse; +import com.tangem.data.network.model.BlockcypherTx; import java.util.Random; @@ -20,6 +21,7 @@ public class ServerApiBlockcypher { public static final String BLOCKCYPHER_ADDRESS = "blockcypher_address"; public static final String BLOCKCYPHER_FEE = "blockcypher_fee"; + public static final String BLOCKCYPHER_TXS = "blockcypher_txs"; public static final String BLOCKCYPHER_SEND = "blockcypher_send"; private int requestsCount = 0; @@ -31,6 +33,8 @@ public class ServerApiBlockcypher { private ResponseListener responseListener; + private TxResponseListener txResponseListener; + public interface ResponseListener { void onSuccess(String method, BlockcypherResponse blockcypherResponse); @@ -39,10 +43,20 @@ public class ServerApiBlockcypher { void onFail(String method, String message); } + public interface TxResponseListener { + void onSuccess(BlockcypherTx blockcypherTx); + + void onFail(String message); + } + public void setResponseListener(ResponseListener listener) { responseListener = listener; } + public void setTxResponseListener(TxResponseListener txListener) { + txResponseListener = txListener; + } + public void requestData(String blockchainID, String method, String wallet, String tx) { requestsCount++; String blockchain = blockchainID.toLowerCase(); @@ -60,8 +74,8 @@ public class ServerApiBlockcypher { addressCall.enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { + requestsCount--; if (response.code() == 200) { - requestsCount--; responseListener.onSuccess(method, response.body()); Log.i(TAG, "requestData " + method + " onResponse " + response.code()); } else { @@ -72,6 +86,7 @@ public class ServerApiBlockcypher { @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { + requestsCount--; responseListener.onFail(method, String.valueOf(t.getMessage())); Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage()); } @@ -83,8 +98,8 @@ public class ServerApiBlockcypher { feeCall.enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { + requestsCount--; if (response.code() == 200) { - requestsCount--; responseListener.onSuccess(method, response.body()); Log.i(TAG, "requestData " + method + " onResponse " + response.code()); } else { @@ -95,12 +110,37 @@ public class ServerApiBlockcypher { @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { + requestsCount--; responseListener.onFail(method, String.valueOf(t.getMessage())); Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage()); } }); break; + case BLOCKCYPHER_TXS: + Call txsCall = blockcypherApi.blockcypherTxs(blockchain, network, tx); + txsCall.enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call,@NonNull Response response) { + requestsCount--; + if (response.code() == 200) { + txResponseListener.onSuccess(response.body()); + Log.i(TAG, "requestData " + method + " onResponse " + response.code()); + } else { + txResponseListener.onFail(String.valueOf(response.code())); + Log.e(TAG, "requestData " + method + " onResponse " + response.code()); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + requestsCount--; + txResponseListener.onFail(String.valueOf(t.getMessage())); + Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage()); + } + }); + break; + case BLOCKCYPHER_SEND: BlockcypherToken blockcypherToken = BlockcypherToken.values()[new Random().nextInt(BlockcypherToken.values().length)]; @@ -108,8 +148,8 @@ public class ServerApiBlockcypher { sendCall.enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { + requestsCount--; if (response.code() == 201) { - requestsCount--; responseListener.onSuccess(method, response.body()); Log.i(TAG, "requestData " + method + " onResponse " + response.code()); } else { @@ -120,6 +160,7 @@ public class ServerApiBlockcypher { @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { + requestsCount--; responseListener.onFail(method, String.valueOf(t.getMessage())); Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage()); } @@ -127,6 +168,7 @@ public class ServerApiBlockcypher { break; default: + requestsCount--; responseListener.onFail(method, "undeclared method"); Log.e(TAG, "requestData " + method + " onFailure - undeclared method"); break; diff --git a/app/src/main/java/com/tangem/data/network/ServerApiSoChain.java b/app/src/main/java/com/tangem/data/network/ServerApiSoChain.java index 2f4d171a6f..0c73849056 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiSoChain.java +++ b/app/src/main/java/com/tangem/data/network/ServerApiSoChain.java @@ -13,8 +13,6 @@ import retrofit2.Response; public class ServerApiSoChain { - public static String NETWORK_BTC = "BTC"; - private static String TAG = ServerApiSoChain.class.getSimpleName(); private int requestsCount = 0; @@ -86,9 +84,9 @@ public class ServerApiSoChain { call.enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { + requestsCount--; Log.i(TAG, "requestAddressBalance onResponse " + response.code()); if (response.code() == 200) { - requestsCount--; addressInfoListener.onSuccess(response.body()); } else { addressInfoListener.onFail(String.valueOf(response.code())); @@ -97,6 +95,7 @@ public class ServerApiSoChain { @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { + requestsCount--; Log.e(TAG, "requestAddressBalance onFailure " + t.getMessage()); addressInfoListener.onFail(String.valueOf(t.getMessage())); } @@ -111,9 +110,9 @@ public class ServerApiSoChain { call.enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { + requestsCount--; Log.i(TAG, "requestAddressBalance onResponse " + response.code()); if (response.code() == 200) { - requestsCount--; addressInfoListener.onSuccess(response.body()); } else { addressInfoListener.onFail(String.valueOf(response.code())); @@ -122,6 +121,7 @@ public class ServerApiSoChain { @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { + requestsCount--; Log.e(TAG, "requestAddressBalance onFailure " + t.getMessage()); addressInfoListener.onFail(String.valueOf(t.getMessage())); } @@ -138,9 +138,9 @@ public class ServerApiSoChain { call.enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { + requestsCount--; Log.i(TAG, "requestAddressBalance onResponse " + response.code()); if (response.code() == 200) { - requestsCount--; sendTxListener.onSuccess(response.body()); } else { sendTxListener.onFail(String.valueOf(response.code())); @@ -149,6 +149,7 @@ public class ServerApiSoChain { @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { + requestsCount--; Log.e(TAG, "requestAddressBalance onFailure " + t.getMessage()); sendTxListener.onFail(String.valueOf(t.getMessage())); } diff --git a/app/src/main/java/com/tangem/data/network/model/BlockcypherResponse.kt b/app/src/main/java/com/tangem/data/network/model/BlockcypherResponse.kt index d86b7e0840..b470cba5ed 100644 --- a/app/src/main/java/com/tangem/data/network/model/BlockcypherResponse.kt +++ b/app/src/main/java/com/tangem/data/network/model/BlockcypherResponse.kt @@ -33,6 +33,11 @@ data class BlockcypherTxref( var script: String? = null ) +data class BlockcypherTx( + @SerializedName("hex") + var hex: String? = null +) + data class BlockcypherFee( @SerializedName("low_fee_per_kb") var low_fee_per_kb: Long? = null, diff --git a/app/src/main/java/com/tangem/wallet/BCHUtils.java b/app/src/main/java/com/tangem/wallet/BCHUtils.java index 7c36d50153..6276cd5170 100644 --- a/app/src/main/java/com/tangem/wallet/BCHUtils.java +++ b/app/src/main/java/com/tangem/wallet/BCHUtils.java @@ -267,8 +267,8 @@ public final class BCHUtils { ArrayList unspentOutputs = new ArrayList<>(); for (BtcData.UnspentTransaction current : rawTxList) { - byte[] rawTxByte = BCHUtils.fromHex(current.Raw); - if (rawTxByte == null || current.Raw.isEmpty()) { + byte[] rawTxByte = BCHUtils.fromHex(current.script); + if (rawTxByte == null || current.script.isEmpty()) { continue; } diff --git a/app/src/main/java/com/tangem/wallet/BTCUtils.java b/app/src/main/java/com/tangem/wallet/BTCUtils.java index 180495a55e..f08cf2786e 100644 --- a/app/src/main/java/com/tangem/wallet/BTCUtils.java +++ b/app/src/main/java/com/tangem/wallet/BTCUtils.java @@ -190,8 +190,8 @@ public final class BTCUtils { ArrayList unspentOutputs = new ArrayList<>(); for (BtcData.UnspentTransaction current : rawTxList) { - byte[] rawTxByte = BTCUtils.fromHex(current.Raw); - if (rawTxByte == null || current.Raw.isEmpty()) { + byte[] rawTxByte = BTCUtils.fromHex(current.script); + if (rawTxByte == null || current.script.isEmpty()) { continue; } diff --git a/app/src/main/java/com/tangem/wallet/bch/BtcCashEngine.java b/app/src/main/java/com/tangem/wallet/bch/BtcCashEngine.java index f29bc7e7f4..b6ab61edfb 100644 --- a/app/src/main/java/com/tangem/wallet/bch/BtcCashEngine.java +++ b/app/src/main/java/com/tangem/wallet/bch/BtcCashEngine.java @@ -591,8 +591,8 @@ public class BtcCashEngine extends CoinEngine { 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"); + trUnspent.amount = jsUnspent.getLong("value"); + trUnspent.outputN = jsUnspent.getInt("height"); coinData.getUnspentTransactions().add(trUnspent); } } catch (JSONException e) { @@ -623,7 +623,7 @@ public class BtcCashEngine extends CoinEngine { String raw = electrumRequest.getResultString(); for (BtcData.UnspentTransaction tx : coinData.getUnspentTransactions()) { if (tx.txID.equals(txHash)) - tx.Raw = raw; + tx.script = raw; } } catch (JSONException e) { e.printStackTrace(); 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 d024d9d91d..24d82d0ed0 100644 --- a/app/src/main/java/com/tangem/wallet/btc/BtcData.java +++ b/app/src/main/java/com/tangem/wallet/btc/BtcData.java @@ -17,14 +17,16 @@ public class BtcData extends CoinData { private Long balanceConfirmed, balanceUnconfirmed; + private boolean useBlockcypher = false; + public String getUnspentInputsDescription() { try { int gatheredUnspents = 0; if( unspentTransactions==null ) return ""; for (int i = 0; i < unspentTransactions.size(); i++) { - if (unspentTransactions.get(i).Raw != null && unspentTransactions.get(i).Raw.length() > 1) gatheredUnspents++; + if (unspentTransactions.get(i).script != null && unspentTransactions.get(i).script.length() > 1) gatheredUnspents++; } - return String.valueOf(unspentTransactions.size()) + " unspents (" + String.valueOf(gatheredUnspents) + " received)"; + return unspentTransactions.size() + " unspents (" + gatheredUnspents + " received)"; } catch (Exception e) { @@ -35,24 +37,24 @@ public class BtcData extends CoinData { public static class UnspentTransaction { public String txID; - public Long Amount; - public Integer Height; - public String Raw = ""; + public Long amount; + public Integer outputN; + public String script = ""; public Bundle getAsBundle() { Bundle B = new Bundle(); B.putString("txID", txID); - B.putLong("Amount", Amount); - B.putInt("Height", Height); - B.putString("Raw", Raw); + B.putLong("Amount", amount); + B.putInt("OutputN", outputN); + B.putString("Script", script); return B; } public void loadFromBundle(Bundle B) { txID = B.getString("txID"); - Amount = B.getLong("Amount"); - Height = B.getInt("Height"); - Raw = B.getString("Raw"); + amount = B.getLong("Amount"); + outputN = B.getInt("OutputN"); + script = B.getString("Script"); } } @@ -83,6 +85,7 @@ public class BtcData extends CoinData { i++; } } + if (B.containsKey("UseBlockcypher")) useBlockcypher = B.getBoolean("UseBlockcypher"); } @Override @@ -98,6 +101,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); } catch (Exception e) { Log.e("Can't save to bundle ", e.getMessage()); } @@ -131,4 +135,11 @@ public class BtcData extends CoinData { return balanceConfirmed != null || balanceUnconfirmed != null; } + public boolean isUseBlockcypher() { + return useBlockcypher; + } + + public void setUseBlockcypher(boolean useBlockcypher) { + this.useBlockcypher = useBlockcypher; + } } 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 1dddef4063..5f6554ed0d 100644 --- a/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java +++ b/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java @@ -11,11 +11,14 @@ import com.tangem.card_common.tasks.SignTask; 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.ServerApiBlockcypher; import com.tangem.data.network.ServerApiCommon; -import com.tangem.data.network.ServerApiElectrum; import com.tangem.data.network.ServerApiSoChain; +import com.tangem.data.network.model.BlockcypherFee; +import com.tangem.data.network.model.BlockcypherResponse; +import com.tangem.data.network.model.BlockcypherTx; +import com.tangem.data.network.model.BlockcypherTxref; import com.tangem.data.network.model.SoChain; import com.tangem.util.CryptoUtil; import com.tangem.util.DecimalDigitsInputFilter; @@ -30,9 +33,7 @@ import com.tangem.wallet.TangemContext; import com.tangem.wallet.Transaction; import com.tangem.wallet.UnspentOutputInfo; -import org.json.JSONArray; import org.json.JSONException; -import org.json.JSONObject; import java.io.ByteArrayOutputStream; import java.math.BigDecimal; @@ -384,7 +385,7 @@ public class BtcEngine extends CoinEngine { @Override public Amount convertToAmount(InternalAmount internalAmount) { - BigDecimal d = internalAmount.divide(new BigDecimal("100000000")).setScale(8, RoundingMode.DOWN); + BigDecimal d = internalAmount.divide(new BigDecimal("100000000")).setScale(getDecimals(), RoundingMode.DOWN); return new Amount(d, getBalanceCurrency()); } @@ -435,7 +436,7 @@ public class BtcEngine extends CoinEngine { // /blockcypher/ // 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)); +// unspentOutputs.add(new UnspentOutputInfo(BTCUtils.fromHex(utxo.txID), new Transaction.Script(BTCUtils.fromHex(utxo.script)), utxo.amount, utxo.outputN, -1, utxo.txID, null)); // } if (useElectrum) { @@ -448,7 +449,7 @@ public class BtcEngine extends CoinEngine { } 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)); + unspentOutputs.add(new UnspentOutputInfo(BTCUtils.fromHex(utxo.txID), new Transaction.Script(BTCUtils.fromHex(utxo.script)), utxo.amount, utxo.outputN, -1, utxo.txID, null)); } } long fullAmount = 0; @@ -541,108 +542,10 @@ public class BtcEngine extends CoinEngine { @Override public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) throws Exception { - if (useElectrum) { - final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); + ctx.setError(null); - 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())); - - } else { - final ServerApiSoChain serverApi = new ServerApiSoChain(); + if (!coinData.isUseBlockcypher()) { + final ServerApiSoChain serverApiSoChain = new ServerApiSoChain(); ServerApiSoChain.AddressInfoListener addressInfoListener = new ServerApiSoChain.AddressInfoListener() { @Override @@ -659,16 +562,26 @@ public class BtcEngine extends CoinEngine { 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"); + 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 (serverApi.isRequestsSequenceCompleted()) { - blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + 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(); } @@ -688,13 +601,13 @@ public class BtcEngine extends CoinEngine { 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(); + 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()) { -// //serverApi.requestData(ctx, ElectrumRequest.getTransaction(walletAddress, hash)); +// //serverApiSoChain.requestData(ctx, ElectrumRequest.getTransaction(walletAddress, hash)); // } else { // ctx.setError("Terminated by user"); // } @@ -704,38 +617,36 @@ public class BtcEngine extends CoinEngine { Log.e(TAG, "FAIL METHOD_ListUnspent JSONException"); } - if (serverApi.isRequestsSequenceCompleted()) { - blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + 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(); } } -// -// } 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); + coinData.setUseBlockcypher(true); // ctx.setError(R.string.cannot_obtain_data_from_blockchain); - if (serverApi.isRequestsSequenceCompleted()) { - blockchainRequestsCallbacks.onComplete(false);//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError()); + 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(); } @@ -743,57 +654,78 @@ public class BtcEngine extends CoinEngine { }; - serverApi.setAddressInfoListener(addressInfoListener); + serverApiSoChain.setAddressInfoListener(addressInfoListener); - serverApi.requestAddressBalance(ctx.getBlockchain(), coinData.getWallet()); - serverApi.requestUnspentTx(ctx.getBlockchain(), coinData.getWallet()); + serverApiSoChain.requestAddressBalance(ctx.getBlockchain(), coinData.getWallet()); + serverApiSoChain.requestUnspentTx(ctx.getBlockchain(), coinData.getWallet()); + } else { + final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher(); + + ServerApiBlockcypher.ResponseListener blockcypherListener = new ServerApiBlockcypher.ResponseListener() { + @Override + public void onSuccess(String method, BlockcypherResponse blockcypherResponse) { + Log.i(TAG, "onSuccess: " + method); + try { + String walletAddress = blockcypherResponse.getAddress(); + if (!walletAddress.equals(coinData.getWallet())) { + // todo - check + throw new Exception("Invalid wallet address in answer!"); + } + Long confBalance = blockcypherResponse.getBalance(); + Long unconfirmedBalance = blockcypherResponse.getUnconfirmed_balance(); + coinData.setBalanceReceived(true); + coinData.setBalanceConfirmed(confBalance); + coinData.setBalanceUnconfirmed(unconfirmedBalance); + coinData.setValidationNodeDescription(Server.ApiBlockcypher.URL_BLOCKCYPHER); + + coinData.getUnspentTransactions().clear(); + if (blockcypherResponse.getTxrefs() != null) { + for (BlockcypherTxref txref : blockcypherResponse.getTxrefs()) { + BtcData.UnspentTransaction trUnspent = new BtcData.UnspentTransaction(); + trUnspent.txID = txref.getTx_hash(); + trUnspent.amount = txref.getValue(); + trUnspent.outputN = txref.getTx_output_n(); + trUnspent.script = txref.getScript(); + coinData.getUnspentTransactions().add(trUnspent); + } + } + } catch (Exception e) { + e.printStackTrace(); + Log.e(TAG, "FAIL BLOCKCYPHER_ADDRESS Exception"); + } + + if (serverApiBlockcypher.isRequestsSequenceCompleted()) { + checkPending(blockchainRequestsCallbacks); + } else { + blockchainRequestsCallbacks.onProgress(); + } + } + + public void onSuccess(String method, BlockcypherFee blockcypherFee) { + Log.e(TAG, "Wrong response type for requestBalanceAndUnspentTransactions"); + ctx.setError("Wrong response type for requestBalanceAndUnspentTransactions"); + blockchainRequestsCallbacks.onComplete(false); + } + + @Override + public void onFail(String method, String message) { + Log.i(TAG, "onFail: " + method + " " + message); + ctx.setError(message); + blockchainRequestsCallbacks.onComplete(false); + } + }; + + serverApiBlockcypher.setResponseListener(blockcypherListener); + + serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_ADDRESS, ctx.getCoinData().getWallet(), ""); } } - private void checkPending(BlockchainRequestsCallbacks blockchainRequestsCallbacks) throws Exception { + private void checkPending(BlockchainRequestsCallbacks blockchainRequestsCallbacks) { 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); //TODO Need remove transaction by RAW HEX - } - - } 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 { - ServerApiSoChain serverApi = new ServerApiSoChain(); + if (!coinData.isUseBlockcypher()) { + ServerApiSoChain serverApiSoChain = new ServerApiSoChain(); ServerApiSoChain.TransactionInfoListener listener = new ServerApiSoChain.TransactionInfoListener() { @Override @@ -803,11 +735,10 @@ public class BtcEngine extends CoinEngine { 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 (serverApi.isRequestsSequenceCompleted()) { + if (serverApiSoChain.isRequestsSequenceCompleted()) { blockchainRequestsCallbacks.onComplete(!ctx.hasError()); } else { blockchainRequestsCallbacks.onProgress(); @@ -817,87 +748,73 @@ public class BtcEngine extends CoinEngine { @Override public void onFail(String message) { Log.i(TAG, "onFail: GetTx " + message); - if (serverApi.isRequestsSequenceCompleted()) { - blockchainRequestsCallbacks.onComplete(false);//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError()); + if (serverApiSoChain.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError());//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError()); } else { blockchainRequestsCallbacks.onProgress(); } } }; - serverApi.setTransactionInfoListener(listener); + 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())))); - serverApi.requestTransactionInfo(ctx.getBlockchain(), txId); + try { + serverApiSoChain.requestTransactionInfo(ctx.getBlockchain(), txId); + } catch (Exception e) { + e.printStackTrace(); + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + } + } + } 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); + + 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()); } } -// /blockcypher/ -// @Override -// public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) { -// final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher(); -// -// ServerApiBlockcypher.ResponseListener blockcypherListener = new ServerApiBlockcypher.ResponseListener() { -// @Override -// public void onSuccess(String method, BlockcypherResponse blockcypherResponse) { -// Log.i(TAG, "onSuccess: " + method); -// try { -// String walletAddress = blockcypherResponse.getAddress(); -// if (!walletAddress.equals(coinData.getWallet())) { -// // todo - check -// throw new Exception("Invalid wallet address in answer!"); -// } -// Long confBalance = blockcypherResponse.getBalance(); -// Long unconfirmedBalance = blockcypherResponse.getUnconfirmed_balance(); -// coinData.setBalanceReceived(true); -// coinData.setBalanceConfirmed(confBalance); -// coinData.setBalanceUnconfirmed(unconfirmedBalance); -// coinData.setValidationNodeDescription("blockcypher.com"); -// //checkPending(blockchainRequestsCallbacks); TODO: rework checkPending -// -// coinData.getUnspentTransactions().clear(); -// for (BlockcypherTxref txref : blockcypherResponse.getTxrefs()) { -// BtcData.UnspentTransaction trUnspent = new BtcData.UnspentTransaction(); -// trUnspent.txID = txref.getTx_hash(); -// trUnspent.Amount = txref.getValue(); -// trUnspent.Height = txref.getTx_output_n(); //TODO: height -> outIndex -// trUnspent.Raw = txref.getScript(); //TODO: raw -> script -// coinData.getUnspentTransactions().add(trUnspent); -// } -// } catch (Exception e) { -// e.printStackTrace(); -// Log.e(TAG, "FAIL BLOCKCYPHER_ADDRESS Exception"); -// } -// -// if (serverApiBlockcypher.isRequestsSequenceCompleted()) { -// blockchainRequestsCallbacks.onComplete(!ctx.hasError()); -// } else { -// blockchainRequestsCallbacks.onProgress(); -// } -// } -// -// public void onSuccess(String method, BlockcypherFee blockcypherFee) { -// Log.e(TAG, "Wrong response type for requestBalanceAndUnspentTransactions"); -// ctx.setError("Wrong response type for requestBalanceAndUnspentTransactions"); -// blockchainRequestsCallbacks.onComplete(false); -// } -// -// @Override -// public void onFail(String method, String message) { -// Log.i(TAG, "onFail: " + method + " " + message); -// ctx.setError(message); -// blockchainRequestsCallbacks.onComplete(false); -// } -// }; -// -// serverApiBlockcypher.setResponseListener(blockcypherListener); -// -// serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_ADDRESS, ctx.getCoinData().getWallet(), ""); -// } - - protected Integer calculateEstimatedTransactionSize(String outputAddress, String outAmount) { //todo - правильней было бы использовать constructTransaction try { @@ -992,60 +909,7 @@ public class BtcEngine extends CoinEngine { coinData.maxFee = null; coinData.normalFee = null; - if (useElectrum) { - - final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); - - final ServerApiElectrum.ResponseListener electrumListener = new ServerApiElectrum.ResponseListener() { - @Override - public void onSuccess(ElectrumRequest electrumRequest) { - BigDecimal kbFee; - if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetFee)) { - try { - kbFee = new BigDecimal(electrumRequest.getResultString()); //fee per KB - - if (kbFee.equals(BigDecimal.ZERO)) { - serverApiElectrum.requestData(ctx, ElectrumRequest.getFee()); - } - - BigDecimal minByteFee = kbFee.divide(new BigDecimal(1024)); // per KB -> per byte - BigDecimal normalByteFee = minByteFee.add(new BigDecimal(0.00000010)); - BigDecimal maxByteFee = minByteFee.add(new BigDecimal(0.00000025)); - - BigDecimal minFee = minByteFee.multiply(new BigDecimal(calcSize)).setScale(8, RoundingMode.DOWN); - 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()); - - coinData.minFee = minAmount; - coinData.normalFee = normalAmount; - coinData.maxFee = maxAmount; -// if (coinData.minFee != null && coinData.normalFee != null && coinData.maxFee != null) { - blockchainRequestsCallbacks.onComplete(true); -// } else { -// blockchainRequestsCallbacks.onProgress(); -// } - - } catch (JSONException e) { - e.printStackTrace(); - } - } - } - - @Override - public void onFail(ElectrumRequest electrumRequest) { - ctx.setError(electrumRequest.getError()); - blockchainRequestsCallbacks.onComplete(false); - } - }; - serverApiElectrum.setResponseListener(electrumListener); - - serverApiElectrum.requestData(ctx, ElectrumRequest.getFee()); - } else { - + if (!coinData.isUseBlockcypher()) { final ServerApiCommon serverApiCommon = new ServerApiCommon(); final ServerApiCommon.EstimatedFeeListener estimatedFeeListener = new ServerApiCommon.EstimatedFeeListener() { @@ -1069,7 +933,7 @@ public class BtcEngine extends CoinEngine { return; } - fee = fee.setScale(8, RoundingMode.DOWN); + fee = fee.setScale(getDecimals(), RoundingMode.DOWN); switch (blockCount) { case ServerApiCommon.ESTIMATE_FEE_MINIMAL: @@ -1093,12 +957,19 @@ public class BtcEngine extends CoinEngine { @Override public void onFail(int blockCount, String message) { // TODO - add fail counter to terminate after NNN tries - if (blockchainRequestsCallbacks.allowAdvance()) { - serverApiCommon.requestBtcEstimatedFee(blockCount); - return; +// if (blockchainRequestsCallbacks.allowAdvance()) { +// serverApiCommon.requestBtcEstimatedFee(blockCount); +// return; +// } +// ctx.setError(ctx.getContext().getString(R.string.cannot_calculate_fee_wrong_data_received_from_node)); +// blockchainRequestsCallbacks.onComplete(false); + coinData.setUseBlockcypher(true); + try { + requestFee(blockchainRequestsCallbacks, targetAddress, amount); + } catch (Exception e) { + ctx.setError(e.getMessage()); + blockchainRequestsCallbacks.onComplete(false); } - ctx.setError(ctx.getContext().getString(R.string.cannot_calculate_fee_wrong_data_received_from_node)); - blockchainRequestsCallbacks.onComplete(false); } }; serverApiCommon.setBtcEstimatedFeeListener(estimatedFeeListener); @@ -1107,110 +978,63 @@ public class BtcEngine extends CoinEngine { serverApiCommon.requestBtcEstimatedFee(ServerApiCommon.ESTIMATE_FEE_NORMAL); serverApiCommon.requestBtcEstimatedFee(ServerApiCommon.ESTIMATE_FEE_MINIMAL); - } - } + } else { + final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher(); -// /blockcypher/ -// @Override -// 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; -// -// final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher(); -// -// ServerApiBlockcypher.ResponseListener blockcypherListener = new ServerApiBlockcypher.ResponseListener() { -// @Override -// public void onSuccess(String method, BlockcypherResponse blockcypherResponse) { -// Log.e(TAG, "Wrong response type for requestFee"); -// ctx.setError("Wrong response type for requestFee"); -// blockchainRequestsCallbacks.onComplete(false); -// } -// -// public void onSuccess(String method, BlockcypherFee blockcypherFee) { -// Log.i(TAG, "onSuccess: " + method); -// try { -// BigDecimal minByteFee = new BigDecimal(blockcypherFee.getLow_fee_per_kb()).divide(BigDecimal.valueOf(1024)); -// BigDecimal normalByteFee = new BigDecimal(blockcypherFee.getMedium_fee_per_kb()).divide(BigDecimal.valueOf(1024)); -// BigDecimal maxByteFee = new BigDecimal(blockcypherFee.getHigh_fee_per_kb()).divide(BigDecimal.valueOf(1024)); -// -// CoinEngine.InternalAmount minIntAmount = new CoinEngine.InternalAmount(minByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi"); -// CoinEngine.InternalAmount normalIntAmount = new CoinEngine.InternalAmount(normalByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi"); -// CoinEngine.InternalAmount maxIntAmount = new CoinEngine.InternalAmount(maxByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi"); -// -// coinData.minFee = convertToAmount(minIntAmount); -// coinData.normalFee = convertToAmount(normalIntAmount); -// coinData.maxFee = convertToAmount(maxIntAmount); -// } catch (Exception e) { -// e.printStackTrace(); -// Log.e(TAG, "FAIL BLOCKCYPHER_FEE Exception"); -// } -// -// if (serverApiBlockcypher.isRequestsSequenceCompleted()) { -// blockchainRequestsCallbacks.onComplete(!ctx.hasError()); -// } else { -// blockchainRequestsCallbacks.onProgress(); -// } -// } -// -// @Override -// public void onFail(String method, String message) { -// Log.i(TAG, "onFail: " + method + " " + message); -// ctx.setError(message); -// blockchainRequestsCallbacks.onComplete(false); -// } -// }; -// -// serverApiBlockcypher.setResponseListener(blockcypherListener); -// -// serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_FEE, "", ""); -// } - - @Override - public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) throws Exception { - final String txStr = BTCUtils.toHex(txForSend); - if (useElectrum) { - final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); - - ServerApiElectrum.ResponseListener electrumListener = new ServerApiElectrum.ResponseListener() { + ServerApiBlockcypher.ResponseListener blockcypherListener = new ServerApiBlockcypher.ResponseListener() { @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); - } - } + public void onSuccess(String method, BlockcypherResponse blockcypherResponse) { + Log.e(TAG, "Wrong response type for requestFee"); + ctx.setError("Wrong response type for requestFee"); + blockchainRequestsCallbacks.onComplete(false); + } + + public void onSuccess(String method, BlockcypherFee blockcypherFee) { + Log.i(TAG, "onSuccess: " + method); + try { + BigDecimal minByteFee = new BigDecimal(blockcypherFee.getLow_fee_per_kb()).divide(BigDecimal.valueOf(1024)); + BigDecimal normalByteFee = new BigDecimal(blockcypherFee.getMedium_fee_per_kb()).divide(BigDecimal.valueOf(1024)); + BigDecimal maxByteFee = new BigDecimal(blockcypherFee.getHigh_fee_per_kb()).divide(BigDecimal.valueOf(1024)); + + CoinEngine.InternalAmount minIntAmount = new CoinEngine.InternalAmount(minByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi"); + CoinEngine.InternalAmount normalIntAmount = new CoinEngine.InternalAmount(normalByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi"); + CoinEngine.InternalAmount maxIntAmount = new CoinEngine.InternalAmount(maxByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi"); + + coinData.minFee = convertToAmount(minIntAmount); + coinData.normalFee = convertToAmount(normalIntAmount); + coinData.maxFee = convertToAmount(maxIntAmount); + } catch (Exception e) { + e.printStackTrace(); + Log.e(TAG, "FAIL BLOCKCYPHER_FEE Exception"); + } + + if (serverApiBlockcypher.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + } else { + blockchainRequestsCallbacks.onProgress(); } } @Override - public void onFail(ElectrumRequest electrumRequest) { - ctx.setError(electrumRequest.getError()); + public void onFail(String method, String message) { + Log.i(TAG, "onFail: " + method + " " + message); + ctx.setError(message); blockchainRequestsCallbacks.onComplete(false); } }; - serverApiElectrum.setResponseListener(electrumListener); + + serverApiBlockcypher.setResponseListener(blockcypherListener); + + serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_FEE, "", ""); + } + } - serverApiElectrum.requestData(ctx, ElectrumRequest.broadcast(ctx.getCoinData().getWallet(), txStr)); - } else { - final ServerApiSoChain serverApi = new ServerApiSoChain(); + @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() { @Override @@ -1243,27 +1067,221 @@ public class BtcEngine extends CoinEngine { blockchainRequestsCallbacks.onComplete(false); } }; - serverApi.setSendTxListener(listener); - serverApi.requestSendTransaction(ctx.getBlockchain(), txStr); + serverApiSoChain.setSendTxListener(listener); + serverApiSoChain.requestSendTransaction(ctx.getBlockchain(), txStr); + } else { + final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher(); + + ServerApiBlockcypher.ResponseListener blockcypherListener = new ServerApiBlockcypher.ResponseListener() { + @Override + public void onSuccess(String method, BlockcypherResponse blockcypherResponse) { + String resultString = blockcypherResponse.toString(); + try { + if (resultString.isEmpty()) { + ctx.setError("No response from node"); + blockchainRequestsCallbacks.onComplete(false); + } else { // TODO: Make check for a valid send response + 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.e(TAG, resultString); + } + } + } + + public void onSuccess(String method, BlockcypherFee blockcypherFee) { + Log.e(TAG, "Wrong response type for requestSendTransaction"); + ctx.setError("Wrong response type for requestSendTransaction"); + blockchainRequestsCallbacks.onComplete(false); + } + + @Override + public void onFail(String method, String message) { + Log.i(TAG, "onFail: " + method + " " + message); + ctx.setError(message); + blockchainRequestsCallbacks.onComplete(false); + } + }; + serverApiBlockcypher.setResponseListener(blockcypherListener); + + serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_SEND, "", txStr); } } +} -// /blockcypher/ -// @Override -// public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) { -// final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher(); -// final String txStr = BTCUtils.toHex(txForSend); +//electrum balance +// final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); // -// ServerApiBlockcypher.ResponseListener blockcypherListener = new ServerApiBlockcypher.ResponseListener() { -// @Override -// public void onSuccess(String method, BlockcypherResponse blockcypherResponse) { -// String resultString = blockcypherResponse.toString(); +// 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 { -// if (resultString.isEmpty()) { -// ctx.setError("No response from node"); +// 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.outputN = 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.script = 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())); + +//electrum fee +// final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); +// +// final ServerApiElectrum.ResponseListener electrumListener = new ServerApiElectrum.ResponseListener() { +// @Override +// public void onSuccess(ElectrumRequest electrumRequest) { +// BigDecimal kbFee; +// if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetFee)) { +// try { +// kbFee = new BigDecimal(electrumRequest.getResultString()); //fee per KB +// +// if (kbFee.equals(BigDecimal.ZERO)) { +// serverApiElectrum.requestData(ctx, ElectrumRequest.getFee()); +// } +// +// BigDecimal minByteFee = kbFee.divide(new BigDecimal(1024)); // per KB -> per byte +// BigDecimal normalByteFee = minByteFee.add(new BigDecimal(0.00000010)); +// BigDecimal maxByteFee = minByteFee.add(new BigDecimal(0.00000025)); +// +// BigDecimal minFee = minByteFee.multiply(new BigDecimal(calcSize)).setScale(8, RoundingMode.DOWN); +// 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()); +// +// coinData.minFee = minAmount; +// coinData.normalFee = normalAmount; +// coinData.maxFee = maxAmount; +//// if (coinData.minFee != null && coinData.normalFee != null && coinData.maxFee != null) { +// blockchainRequestsCallbacks.onComplete(true); +//// } else { +//// blockchainRequestsCallbacks.onProgress(); +//// } +// +// } catch (JSONException e) { +// e.printStackTrace(); +// } +// } +// } +// +// @Override +// public void onFail(ElectrumRequest electrumRequest) { +// ctx.setError(electrumRequest.getError()); +// blockchainRequestsCallbacks.onComplete(false); +// } +// }; +// serverApiElectrum.setResponseListener(electrumListener); +// +// serverApiElectrum.requestData(ctx, ElectrumRequest.getFee()); + +//electrum send +// final ServerApiElectrum serverApiElectrum = new ServerApiElectrum(); +// +// ServerApiElectrum.ResponseListener electrumListener = new ServerApiElectrum.ResponseListener() { +// @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 { // TODO: Make check for a valid send response +// } else { // ctx.setError(null); // blockchainRequestsCallbacks.onComplete(true); // } @@ -1274,27 +1292,57 @@ public class BtcEngine extends CoinEngine { // } else { // ctx.setError(e.getClass().getName()); // blockchainRequestsCallbacks.onComplete(false); -// Log.e(TAG, resultString); // } // } // } +// } // -// public void onSuccess(String method, BlockcypherFee blockcypherFee) { -// Log.e(TAG, "Wrong response type for requestSendTransaction"); -// ctx.setError("Wrong response type for requestSendTransaction"); -// blockchainRequestsCallbacks.onComplete(false); -// } +// @Override +// public void onFail(ElectrumRequest electrumRequest) { +// ctx.setError(electrumRequest.getError()); +// blockchainRequestsCallbacks.onComplete(false); +// } +// }; +// serverApiElectrum.setResponseListener(electrumListener); // -// @Override -// public void onFail(String method, String message) { -// Log.i(TAG, "onFail: " + method + " " + message); -// ctx.setError(message); -// blockchainRequestsCallbacks.onComplete(false); -// } -// }; -// serverApiBlockcypher.setResponseListener(blockcypherListener); // -// serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_SEND, "", txStr); -// } +// serverApiElectrum.requestData(ctx, ElectrumRequest.broadcast(ctx.getCoinData().getWallet(), txStr)); -} \ No newline at end of file +//electrum check pending +//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); //TODO Need remove transaction by RAW HEX +// } +// +// } 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)); \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/cardano/CardanoData.java b/app/src/main/java/com/tangem/wallet/cardano/CardanoData.java index 9e46ad937a..e3ba66dc73 100644 --- a/app/src/main/java/com/tangem/wallet/cardano/CardanoData.java +++ b/app/src/main/java/com/tangem/wallet/cardano/CardanoData.java @@ -22,7 +22,7 @@ public class CardanoData extends CoinData { // int gatheredUnspents = 0; if( unspentOutputs ==null ) return ""; // for (int i = 0; i < unspentOutputs.size(); i++) { -// if (unspentOutputs.get(i).Raw != null && unspentOutputs.get(i).Raw.length() > 1) gatheredUnspents++; +// if (unspentOutputs.get(i).script != null && unspentOutputs.get(i).script.length() > 1) gatheredUnspents++; // } return String.valueOf(unspentOutputs.size()) + " unspents";// (" + String.valueOf(gatheredUnspents) + " received)"; } diff --git a/app/src/main/java/com/tangem/wallet/ducatus/DucatusEngine.java b/app/src/main/java/com/tangem/wallet/ducatus/DucatusEngine.java index bc7c58c4d9..a74847447c 100644 --- a/app/src/main/java/com/tangem/wallet/ducatus/DucatusEngine.java +++ b/app/src/main/java/com/tangem/wallet/ducatus/DucatusEngine.java @@ -525,7 +525,7 @@ public class DucatusEngine extends BtcEngine { String txHash = new String(BTCUtils.reverse(CryptoUtil.doubleSha256(BTCUtils.fromHex(raw)))); //TODO: check for (BtcData.UnspentTransaction tx : coinData.getUnspentTransactions()) { if (tx.txID.equals(txHash)) - tx.Raw = raw; + tx.script = raw; } } catch (Exception e) { e.printStackTrace(); @@ -548,8 +548,8 @@ public class DucatusEngine extends BtcEngine { for (InsightResponse utxo : utxoList) { BtcData.UnspentTransaction trUnspent = new BtcData.UnspentTransaction(); trUnspent.txID = utxo.getTxid(); - trUnspent.Amount = utxo.getSatoshis(); - trUnspent.Height = utxo.getHeight(); + trUnspent.amount = utxo.getSatoshis(); + trUnspent.outputN = utxo.getHeight(); coinData.getUnspentTransactions().add(trUnspent); } diff --git a/app/src/main/java/com/tangem/wallet/eos/EosEngine.java b/app/src/main/java/com/tangem/wallet/eos/EosEngine.java index 8dc387cafd..ad2c69dd04 100644 --- a/app/src/main/java/com/tangem/wallet/eos/EosEngine.java +++ b/app/src/main/java/com/tangem/wallet/eos/EosEngine.java @@ -336,8 +336,8 @@ public class EosEngine extends CoinEngine { // return address; // byte[] csum = Ripemd160.from(pkCompressed).bytes(); -// csum = Raw.copy(csum, 0, 4); -// byte[] addy = Raw.concat(pkCompressed, csum); +// csum = script.copy(csum, 0, 4); +// byte[] addy = script.concat(pkCompressed, csum); // StringBuffer bf = new StringBuffer("EOS"); // bf.append(Base58.encode(addy)); // return bf.toString() + " " + address; diff --git a/app/src/main/java/com/tangem/wallet/ltc/LtcEngine.java b/app/src/main/java/com/tangem/wallet/ltc/LtcEngine.java index 3bf36b1450..93d1446479 100644 --- a/app/src/main/java/com/tangem/wallet/ltc/LtcEngine.java +++ b/app/src/main/java/com/tangem/wallet/ltc/LtcEngine.java @@ -2,15 +2,11 @@ package com.tangem.wallet.ltc; 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.wallet.BTCUtils; import com.tangem.wallet.BalanceValidator; import com.tangem.wallet.Base58; import com.tangem.wallet.CoinData; -import com.tangem.wallet.CoinEngine; import com.tangem.wallet.TangemContext; import com.tangem.wallet.Transaction; import com.tangem.wallet.UnspentOutputInfo; @@ -25,8 +21,6 @@ import com.tangem.util.DecimalDigitsInputFilter; import com.tangem.util.DerEncodingUtil; import com.tangem.wallet.R; -import org.json.JSONException; - import java.io.ByteArrayOutputStream; import java.math.BigDecimal; import java.math.BigInteger; @@ -36,7 +30,6 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.util.ArrayList; import java.util.Arrays; -import java.util.List; public class LtcEngine extends BtcEngine { private static final String TAG = LtcEngine.class.getSimpleName(); @@ -402,7 +395,7 @@ public class LtcEngine extends BtcEngine { byte[] pbKey = ctx.getCard().getWalletPublicKey(); 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)); + unspentOutputs.add(new UnspentOutputInfo(BTCUtils.fromHex(utxo.txID), new Transaction.Script(BTCUtils.fromHex(utxo.script)), utxo.amount, utxo.outputN, -1, utxo.txID, null)); } long fullAmount = 0;