diff --git a/app/src/main/cpp/monerujo.cpp b/app/src/main/cpp/monerujo.cpp index 70232ec..980a049 100644 --- a/app/src/main/cpp/monerujo.cpp +++ b/app/src/main/cpp/monerujo.cpp @@ -1083,14 +1083,17 @@ Java_net_mynero_wallet_model_Wallet_getCoinsJ(JNIEnv *env, jobject instance) { jobject newCoinsInfo(JNIEnv *env, Monero::CoinsInfo *info) { jmethodID c = env->GetMethodID(class_CoinsInfo, "", - "(JZLjava/lang/String;J)V"); + "(JZLjava/lang/String;JLjava/lang/String;)V"); jstring _key_image = env->NewStringUTF(info->keyImage().c_str()); + jstring _hash = env->NewStringUTF(info->hash().c_str()); jobject result = env->NewObject(class_CoinsInfo, c, static_cast (info->globalOutputIndex()), info->spent(), _key_image, - static_cast (info->amount())); + static_cast (info->amount()), + _hash); env->DeleteLocalRef(_key_image); + env->DeleteLocalRef(_hash); return result; } diff --git a/app/src/main/java/net/mynero/wallet/fragment/dialog/ReceiveBottomSheetDialog.java b/app/src/main/java/net/mynero/wallet/fragment/dialog/ReceiveBottomSheetDialog.java index 5be6a71..1fbec63 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/dialog/ReceiveBottomSheetDialog.java +++ b/app/src/main/java/net/mynero/wallet/fragment/dialog/ReceiveBottomSheetDialog.java @@ -52,14 +52,6 @@ public class ReceiveBottomSheetDialog extends BottomSheetDialogFragment { addressTextView.setText(addr.getAddress()); addressImageView.setImageBitmap(generate(addr.getAddress(), 256, 256)); copyAddressImageButton.setOnClickListener(view1 -> Helper.clipBoardCopy(getContext(), "address", addr.getAddress())); - - List coins = WalletManager.getInstance().getWallet().getCoins().getAll(); - System.out.println("COINS::"); - for(CoinsInfo coinsInfo : coins) { - if(!coinsInfo.isSpent()) { - System.out.println(coinsInfo.getGlobalOutputIndex()+": "+coinsInfo.getKeyImage()); - } - } } public Bitmap generate(String text, int width, int height) { diff --git a/app/src/main/java/net/mynero/wallet/fragment/dialog/SendBottomSheetDialog.java b/app/src/main/java/net/mynero/wallet/fragment/dialog/SendBottomSheetDialog.java index dab0ffc..59b6933 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/dialog/SendBottomSheetDialog.java +++ b/app/src/main/java/net/mynero/wallet/fragment/dialog/SendBottomSheetDialog.java @@ -254,15 +254,25 @@ public class SendBottomSheetDialog extends BottomSheetDialogFragment { private void createTx(String address, String amount, boolean sendAll, PendingTransaction.Priority feePriority) { AsyncTask.execute(() -> { - PendingTransaction pendingTx = TxService.getInstance().createTx(address, amount, sendAll, feePriority, selectedUtxos); - if (pendingTx != null && pendingTx.getStatus() == PendingTransaction.Status.Status_Ok) { - _pendingTransaction.postValue(pendingTx); - } else { + try { + PendingTransaction pendingTx = TxService.getInstance().createTx(address, amount, sendAll, feePriority, selectedUtxos); + if (pendingTx != null && pendingTx.getStatus() == PendingTransaction.Status.Status_Ok) { + _pendingTransaction.postValue(pendingTx); + } else { + Activity activity = getActivity(); + if (activity != null) { + activity.runOnUiThread(() -> { + createButton.setEnabled(true); + Toast.makeText(getActivity(), getString(R.string.error_creating_tx), Toast.LENGTH_SHORT).show(); + }); + } + } + } catch (Exception e) { Activity activity = getActivity(); if (activity != null) { activity.runOnUiThread(() -> { createButton.setEnabled(true); - Toast.makeText(getActivity(), getString(R.string.error_creating_tx), Toast.LENGTH_SHORT).show(); + Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT).show(); }); } } diff --git a/app/src/main/java/net/mynero/wallet/fragment/utxos/UtxosFragment.java b/app/src/main/java/net/mynero/wallet/fragment/utxos/UtxosFragment.java index b4c0341..73f4f85 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/utxos/UtxosFragment.java +++ b/app/src/main/java/net/mynero/wallet/fragment/utxos/UtxosFragment.java @@ -78,10 +78,8 @@ public class UtxosFragment extends Fragment implements CoinsInfoAdapter.CoinsInf public void onUtxoSelected(CoinsInfo coinsInfo) { boolean selected = selectedUtxos.contains(coinsInfo.getKeyImage()); if(selected) { - System.out.println("Deselecting: " + coinsInfo.getKeyImage()); selectedUtxos.remove(coinsInfo.getKeyImage()); } else { - System.out.println("Selecting: " + coinsInfo.getKeyImage()); selectedUtxos.add(coinsInfo.getKeyImage()); } diff --git a/app/src/main/java/net/mynero/wallet/model/CoinsInfo.java b/app/src/main/java/net/mynero/wallet/model/CoinsInfo.java index 8ef386b..31a528e 100644 --- a/app/src/main/java/net/mynero/wallet/model/CoinsInfo.java +++ b/app/src/main/java/net/mynero/wallet/model/CoinsInfo.java @@ -33,12 +33,14 @@ public class CoinsInfo implements Parcelable { boolean spent; String keyImage; long amount; + String hash; - public CoinsInfo(long globalOutputIndex, boolean spent, String keyImage, long amount) { + public CoinsInfo(long globalOutputIndex, boolean spent, String keyImage, long amount, String hash) { this.globalOutputIndex = globalOutputIndex; this.spent = spent; this.keyImage = keyImage; this.amount = amount; + this.hash = hash; } protected CoinsInfo(Parcel in) { @@ -69,6 +71,10 @@ public class CoinsInfo implements Parcelable { return keyImage; } + public String getHash() { + return hash; + } + public long getAmount() { return amount; } diff --git a/app/src/main/java/net/mynero/wallet/service/MoneroHandlerThread.java b/app/src/main/java/net/mynero/wallet/service/MoneroHandlerThread.java index 6d1a29a..a69ff7f 100644 --- a/app/src/main/java/net/mynero/wallet/service/MoneroHandlerThread.java +++ b/app/src/main/java/net/mynero/wallet/service/MoneroHandlerThread.java @@ -17,11 +17,10 @@ package net.mynero.wallet.service; -import static net.mynero.wallet.model.Wallet.SWEEP_ALL; - import net.mynero.wallet.data.DefaultNodes; import net.mynero.wallet.data.Node; import net.mynero.wallet.data.TxData; +import net.mynero.wallet.model.CoinsInfo; import net.mynero.wallet.model.PendingTransaction; import net.mynero.wallet.model.Wallet; import net.mynero.wallet.model.WalletListener; @@ -122,17 +121,33 @@ public class MoneroHandlerThread extends Thread implements WalletListener { listener.onRefresh(); } - public PendingTransaction createTx(String address, String amountStr, boolean sendAll, PendingTransaction.Priority feePriority, ArrayList selectedUtxos) { - long amount = sendAll ? SWEEP_ALL : Wallet.getAmountFromString(amountStr); + public PendingTransaction createTx(String address, String amountStr, boolean sendAll, PendingTransaction.Priority feePriority, ArrayList selectedUtxos) throws Exception { + long amount = sendAll ? Wallet.SWEEP_ALL : Wallet.getAmountFromString(amountStr); ArrayList preferredInputs; if(selectedUtxos.isEmpty()) { preferredInputs = UTXOService.getInstance().selectUtxos(amount, sendAll); } else { preferredInputs = selectedUtxos; + checkSelectedAmounts(selectedUtxos, amount, sendAll); } return wallet.createTransaction(new TxData(address, amount, 0, feePriority, preferredInputs)); } + private void checkSelectedAmounts(ArrayList selectedUtxos, long amount, boolean sendAll) throws Exception { + if(!sendAll) { + long amountSelected = 0; + for(CoinsInfo coinsInfo : UTXOService.getInstance().getUtxos()) { + if(selectedUtxos.contains(coinsInfo.getKeyImage())) { + amountSelected += coinsInfo.getAmount(); + } + } + + if(amountSelected <= amount) { + throw new Exception("insufficient wallet balance"); + } + } + } + public boolean sendTx(PendingTransaction pendingTx) { return pendingTx.commit("", true); } diff --git a/app/src/main/java/net/mynero/wallet/service/TxService.java b/app/src/main/java/net/mynero/wallet/service/TxService.java index 881e8f5..e0e726d 100644 --- a/app/src/main/java/net/mynero/wallet/service/TxService.java +++ b/app/src/main/java/net/mynero/wallet/service/TxService.java @@ -16,7 +16,7 @@ public class TxService extends ServiceBase { return instance; } - public PendingTransaction createTx(String address, String amount, boolean sendAll, PendingTransaction.Priority feePriority, ArrayList selectedUtxos) { + public PendingTransaction createTx(String address, String amount, boolean sendAll, PendingTransaction.Priority feePriority, ArrayList selectedUtxos) throws Exception { return this.getThread().createTx(address, amount, sendAll, feePriority, selectedUtxos); } diff --git a/app/src/main/java/net/mynero/wallet/service/UTXOService.java b/app/src/main/java/net/mynero/wallet/service/UTXOService.java index a2585bc..da4b06b 100644 --- a/app/src/main/java/net/mynero/wallet/service/UTXOService.java +++ b/app/src/main/java/net/mynero/wallet/service/UTXOService.java @@ -33,28 +33,32 @@ public class UTXOService extends ServiceBase { return WalletManager.getInstance().getWallet().getCoins().getAll(); } - public ArrayList selectUtxos(long amount, boolean sendAll) { + public ArrayList selectUtxos(long amount, boolean sendAll) throws Exception { ArrayList selectedUtxos = new ArrayList<>(); + ArrayList seenTxs = new ArrayList<>(); List utxos = getUtxos(); - if(sendAll) { - for(CoinsInfo coinsInfo : utxos) { - selectedUtxos.add(coinsInfo.getKeyImage()); - } - } else { - long amountSelected = 0; - Collections.shuffle(utxos); - for (CoinsInfo coinsInfo : utxos) { - if (amount == Wallet.SWEEP_ALL) { + long amountSelected = 0; + Collections.shuffle(utxos); + for (CoinsInfo coinsInfo : utxos) { + if(!coinsInfo.isSpent()) { + if (sendAll) { selectedUtxos.add(coinsInfo.getKeyImage()); + amountSelected = Wallet.SWEEP_ALL; } else { - if (amountSelected <= amount) { + if (amountSelected <= amount && !seenTxs.contains(coinsInfo.getHash())) { selectedUtxos.add(coinsInfo.getKeyImage()); + // we don't want to spend multiple utxos from the same transaction, so we prevent that from happening here. + seenTxs.add(coinsInfo.getHash()); amountSelected += coinsInfo.getAmount(); } } } } + if (amountSelected <= amount && !sendAll) { + throw new Exception("insufficient wallet balance"); + } + return selectedUtxos; } }