Search in sources :

Example 16 with UTXO

use of com.samourai.wallet.send.UTXO in project samourai-wallet-android by Samourai-Wallet.

the class RicochetMeta method getHop0UTXO.

private Pair<List<UTXO>, BigInteger> getHop0UTXO(long spendAmount, int nbHops, long feePerHop) {
    List<UTXO> utxos = APIFactory.getInstance(context).getUtxos(true);
    final List<UTXO> selectedUTXO = new ArrayList<UTXO>();
    long totalValueSelected = 0L;
    long totalSpendAmount = 0L;
    int selected = 0;
    // sort in ascending order by value
    Collections.sort(utxos, new UTXO.UTXOComparator());
    Collections.reverse(utxos);
    for (UTXO u : utxos) {
        selectedUTXO.add(u);
        totalValueSelected += u.getValue();
        selected += u.getOutpoints().size();
        // Log.d("RicochetMeta", "selected:" + u.getValue());
        totalSpendAmount = spendAmount + samouraiFeeAmount.longValue() + (feePerHop * nbHops) + SamouraiWallet.bDust.longValue() + FeeUtil.getInstance().estimatedFee(selected, 3).longValue();
        // Log.d("RicochetMeta", "totalValueSelected:" + totalValueSelected);
        if (totalValueSelected >= totalSpendAmount) {
            // Log.d("RicochetMeta", "breaking");
            break;
        }
    }
    if (selectedUTXO.size() < 1) {
        return Pair.of(null, null);
    } else {
        return Pair.of(selectedUTXO, FeeUtil.getInstance().estimatedFee(selected, 3));
    }
}
Also used : UTXO(com.samourai.wallet.send.UTXO) ArrayList(java.util.ArrayList) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint)

Example 17 with UTXO

use of com.samourai.wallet.send.UTXO in project samourai-wallet-android by Samourai-Wallet.

the class SendActivity method markUTXOAsUnspendable.

private void markUTXOAsUnspendable(String hexTx) {
    HashMap<String, Long> utxos = new HashMap<String, Long>();
    for (UTXO utxo : APIFactory.getInstance(SendActivity.this).getUtxos(true)) {
        for (MyTransactionOutPoint outpoint : utxo.getOutpoints()) {
            utxos.put(outpoint.getTxHash().toString() + "-" + outpoint.getTxOutputN(), outpoint.getValue().longValue());
        }
    }
    Transaction tx = new Transaction(SamouraiWallet.getInstance().getCurrentNetworkParams(), Hex.decode(hexTx));
    for (TransactionInput input : tx.getInputs()) {
        BlockedUTXO.getInstance().add(input.getOutpoint().getHash().toString(), (int) input.getOutpoint().getIndex(), utxos.get(input.getOutpoint().getHash().toString() + "-" + (int) input.getOutpoint().getIndex()));
    }
}
Also used : UTXO(com.samourai.wallet.send.UTXO) BlockedUTXO(com.samourai.wallet.send.BlockedUTXO) Transaction(org.bitcoinj.core.Transaction) HashMap(java.util.HashMap) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) TransactionInput(org.bitcoinj.core.TransactionInput)

Example 18 with UTXO

use of com.samourai.wallet.send.UTXO in project samourai-wallet-android by Samourai-Wallet.

the class SendActivity method onCreate.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_send);
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    SendActivity.this.getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
    if (SamouraiWallet.getInstance().getShowTotalBalance()) {
        if (SamouraiWallet.getInstance().getCurrentSelectedAccount() == 2) {
            selectedAccount = 1;
        } else {
            selectedAccount = 0;
        }
    } else {
        selectedAccount = 0;
    }
    tvMaxPrompt = (TextView) findViewById(R.id.max_prompt);
    tvMax = (TextView) findViewById(R.id.max);
    try {
        balance = APIFactory.getInstance(SendActivity.this).getXpubAmounts().get(HD_WalletFactory.getInstance(SendActivity.this).get().getAccount(selectedAccount).xpubstr());
    } catch (IOException ioe) {
        balance = 0L;
    } catch (MnemonicException.MnemonicLengthException mle) {
        balance = 0L;
    } catch (java.lang.NullPointerException npe) {
        balance = 0L;
    }
    final String strAmount;
    NumberFormat nf = NumberFormat.getInstance(Locale.US);
    nf.setMaximumFractionDigits(8);
    nf.setMinimumFractionDigits(1);
    nf.setMinimumIntegerDigits(1);
    strAmount = nf.format(balance / 1e8);
    tvMax.setText(strAmount + " " + getDisplayUnits());
    tvMaxPrompt.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            edAmountBTC.setText(strAmount);
            return false;
        }
    });
    tvMax.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            edAmountBTC.setText(strAmount);
            return false;
        }
    });
    DecimalFormat format = (DecimalFormat) DecimalFormat.getInstance(Locale.US);
    DecimalFormatSymbols symbols = format.getDecimalFormatSymbols();
    defaultSeparator = Character.toString(symbols.getDecimalSeparator());
    strFiat = PrefsUtil.getInstance(SendActivity.this).getValue(PrefsUtil.CURRENT_FIAT, "USD");
    btc_fx = ExchangeRateFactory.getInstance(SendActivity.this).getAvgPrice(strFiat);
    tvFiatSymbol = (TextView) findViewById(R.id.fiatSymbol);
    tvFiatSymbol.setText(getDisplayUnits() + "-" + strFiat);
    edAddress = (EditText) findViewById(R.id.destination);
    textWatcherAddress = new TextWatcher() {

        public void afterTextChanged(Editable s) {
            validateSpend();
        }

        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            ;
        }

        public void onTextChanged(CharSequence s, int start, int before, int count) {
            ;
        }
    };
    edAddress.addTextChangedListener(textWatcherAddress);
    edAddress.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // final int DRAWABLE_LEFT = 0;
            // final int DRAWABLE_TOP = 1;
            final int DRAWABLE_RIGHT = 2;
            if (event.getAction() == MotionEvent.ACTION_UP && event.getRawX() >= (edAddress.getRight() - edAddress.getCompoundDrawables()[DRAWABLE_RIGHT].getBounds().width())) {
                final List<String> entries = new ArrayList<String>();
                entries.addAll(BIP47Meta.getInstance().getSortedByLabels(false));
                final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(SendActivity.this, android.R.layout.select_dialog_singlechoice);
                for (int i = 0; i < entries.size(); i++) {
                    arrayAdapter.add(BIP47Meta.getInstance().getDisplayLabel(entries.get(i)));
                }
                AlertDialog.Builder dlg = new AlertDialog.Builder(SendActivity.this);
                dlg.setIcon(R.drawable.ic_launcher);
                dlg.setTitle(R.string.app_name);
                dlg.setAdapter(arrayAdapter, new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // Toast.makeText(SendActivity.this, BIP47Meta.getInstance().getDisplayLabel(entries.get(which)), Toast.LENGTH_SHORT).show();
                        // Toast.makeText(SendActivity.this, entries.get(which), Toast.LENGTH_SHORT).show();
                        processPCode(entries.get(which), null);
                    }
                });
                dlg.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
                dlg.show();
                return true;
            }
            return false;
        }
    });
    edAmountBTC = (EditText) findViewById(R.id.amountBTC);
    edAmountFiat = (EditText) findViewById(R.id.amountFiat);
    textWatcherBTC = new TextWatcher() {

        public void afterTextChanged(Editable s) {
            edAmountBTC.removeTextChangedListener(this);
            edAmountFiat.removeTextChangedListener(textWatcherFiat);
            int max_len = 8;
            NumberFormat btcFormat = NumberFormat.getInstance(Locale.US);
            btcFormat.setMaximumFractionDigits(max_len + 1);
            btcFormat.setMinimumFractionDigits(0);
            double d = 0.0;
            try {
                d = NumberFormat.getInstance(Locale.US).parse(s.toString()).doubleValue();
                String s1 = btcFormat.format(d);
                if (s1.indexOf(defaultSeparator) != -1) {
                    String dec = s1.substring(s1.indexOf(defaultSeparator));
                    if (dec.length() > 0) {
                        dec = dec.substring(1);
                        if (dec.length() > max_len) {
                            edAmountBTC.setText(s1.substring(0, s1.length() - 1));
                            edAmountBTC.setSelection(edAmountBTC.getText().length());
                            s = edAmountBTC.getEditableText();
                        }
                    }
                }
            } catch (NumberFormatException nfe) {
                ;
            } catch (ParseException pe) {
                ;
            }
            if (d > 21000000.0) {
                edAmountFiat.setText("0.00");
                edAmountFiat.setSelection(edAmountFiat.getText().length());
                edAmountBTC.setText("0");
                edAmountBTC.setSelection(edAmountBTC.getText().length());
                Toast.makeText(SendActivity.this, R.string.invalid_amount, Toast.LENGTH_SHORT).show();
            } else {
                edAmountFiat.setText(MonetaryUtil.getInstance().getFiatFormat(strFiat).format(d * btc_fx));
                edAmountFiat.setSelection(edAmountFiat.getText().length());
            }
            edAmountFiat.addTextChangedListener(textWatcherFiat);
            edAmountBTC.addTextChangedListener(this);
            validateSpend();
        }

        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            ;
        }

        public void onTextChanged(CharSequence s, int start, int before, int count) {
            ;
        }
    };
    edAmountBTC.addTextChangedListener(textWatcherBTC);
    textWatcherFiat = new TextWatcher() {

        public void afterTextChanged(Editable s) {
            edAmountFiat.removeTextChangedListener(this);
            edAmountBTC.removeTextChangedListener(textWatcherBTC);
            int max_len = 2;
            NumberFormat fiatFormat = NumberFormat.getInstance(Locale.US);
            fiatFormat.setMaximumFractionDigits(max_len + 1);
            fiatFormat.setMinimumFractionDigits(0);
            double d = 0.0;
            try {
                d = NumberFormat.getInstance(Locale.US).parse(s.toString()).doubleValue();
                String s1 = fiatFormat.format(d);
                if (s1.indexOf(defaultSeparator) != -1) {
                    String dec = s1.substring(s1.indexOf(defaultSeparator));
                    if (dec.length() > 0) {
                        dec = dec.substring(1);
                        if (dec.length() > max_len) {
                            edAmountFiat.setText(s1.substring(0, s1.length() - 1));
                            edAmountFiat.setSelection(edAmountFiat.getText().length());
                        }
                    }
                }
            } catch (NumberFormatException nfe) {
                ;
            } catch (ParseException pe) {
                ;
            }
            if ((d / btc_fx) > 21000000.0) {
                edAmountFiat.setText("0.00");
                edAmountFiat.setSelection(edAmountFiat.getText().length());
                edAmountBTC.setText("0");
                edAmountBTC.setSelection(edAmountBTC.getText().length());
                Toast.makeText(SendActivity.this, R.string.invalid_amount, Toast.LENGTH_SHORT).show();
            } else {
                edAmountBTC.setText(MonetaryUtil.getInstance().getBTCFormat().format(d / btc_fx));
                edAmountBTC.setSelection(edAmountBTC.getText().length());
            }
            edAmountBTC.addTextChangedListener(textWatcherBTC);
            edAmountFiat.addTextChangedListener(this);
            validateSpend();
        }

        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            ;
        }

        public void onTextChanged(CharSequence s, int start, int before, int count) {
            ;
        }
    };
    edAmountFiat.addTextChangedListener(textWatcherFiat);
    SPEND_TYPE = PrefsUtil.getInstance(SendActivity.this).getValue(PrefsUtil.USE_BIP126, true) ? SPEND_BIP126 : SPEND_SIMPLE;
    if (SPEND_TYPE > SPEND_BIP126) {
        SPEND_TYPE = SPEND_BIP126;
        PrefsUtil.getInstance(SendActivity.this).setValue(PrefsUtil.SPEND_TYPE, SPEND_BIP126);
    }
    swRicochet = (Switch) findViewById(R.id.ricochet);
    swRicochet.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (isChecked) {
                SPEND_TYPE = SPEND_RICOCHET;
            } else {
                SPEND_TYPE = PrefsUtil.getInstance(SendActivity.this).getValue(PrefsUtil.SPEND_TYPE, SPEND_BIP126);
            }
        }
    });
    btLowFee = (Button) findViewById(R.id.low_fee);
    btAutoFee = (Button) findViewById(R.id.auto_fee);
    btPriorityFee = (Button) findViewById(R.id.priority_fee);
    btCustomFee = (Button) findViewById(R.id.custom_fee);
    tvFeePrompt = (TextView) findViewById(R.id.current_fee_prompt);
    FEE_TYPE = PrefsUtil.getInstance(SendActivity.this).getValue(PrefsUtil.CURRENT_FEE_TYPE, FEE_NORMAL);
    long lo = FeeUtil.getInstance().getLowFee().getDefaultPerKB().longValue() / 1000L;
    long mi = FeeUtil.getInstance().getNormalFee().getDefaultPerKB().longValue() / 1000L;
    long hi = FeeUtil.getInstance().getHighFee().getDefaultPerKB().longValue() / 1000L;
    if (lo == mi && mi == hi) {
        lo = (long) ((double) mi * 0.85);
        hi = (long) ((double) mi * 1.15);
        SuggestedFee lo_sf = new SuggestedFee();
        lo_sf.setDefaultPerKB(BigInteger.valueOf(lo * 1000L));
        FeeUtil.getInstance().setLowFee(lo_sf);
        SuggestedFee hi_sf = new SuggestedFee();
        hi_sf.setDefaultPerKB(BigInteger.valueOf(hi * 1000L));
        FeeUtil.getInstance().setHighFee(hi_sf);
    } else if (lo == mi || mi == hi) {
        mi = (lo + hi) / 2L;
        SuggestedFee mi_sf = new SuggestedFee();
        mi_sf.setDefaultPerKB(BigInteger.valueOf(mi * 1000L));
        FeeUtil.getInstance().setNormalFee(mi_sf);
    } else {
        ;
    }
    switch(FEE_TYPE) {
        case FEE_LOW:
            FeeUtil.getInstance().setSuggestedFee(FeeUtil.getInstance().getLowFee());
            btLowFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.blue));
            btAutoFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btPriorityFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btCustomFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btLowFee.setTypeface(null, Typeface.BOLD);
            btAutoFee.setTypeface(null, Typeface.NORMAL);
            btPriorityFee.setTypeface(null, Typeface.NORMAL);
            btCustomFee.setTypeface(null, Typeface.NORMAL);
            tvFeePrompt.setText(getText(R.string.fee_low_priority) + " " + getText(R.string.blocks_to_cf));
            break;
        case FEE_PRIORITY:
            FeeUtil.getInstance().setSuggestedFee(FeeUtil.getInstance().getHighFee());
            btLowFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btAutoFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btPriorityFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.blue));
            btCustomFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btLowFee.setTypeface(null, Typeface.NORMAL);
            btAutoFee.setTypeface(null, Typeface.NORMAL);
            btPriorityFee.setTypeface(null, Typeface.BOLD);
            btCustomFee.setTypeface(null, Typeface.NORMAL);
            tvFeePrompt.setText(getText(R.string.fee_high_priority) + " " + getText(R.string.blocks_to_cf));
            break;
        default:
            FeeUtil.getInstance().setSuggestedFee(FeeUtil.getInstance().getNormalFee());
            btLowFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btAutoFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.blue));
            btPriorityFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btCustomFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btLowFee.setTypeface(null, Typeface.NORMAL);
            btAutoFee.setTypeface(null, Typeface.BOLD);
            btPriorityFee.setTypeface(null, Typeface.NORMAL);
            btCustomFee.setTypeface(null, Typeface.NORMAL);
            tvFeePrompt.setText(getText(R.string.fee_mid_priority) + " " + getText(R.string.blocks_to_cf));
            break;
    }
    btLowFee.setText((FeeUtil.getInstance().getLowFee().getDefaultPerKB().longValue() / 1000L) + "\n" + getString(R.string.sat_b));
    btPriorityFee.setText((FeeUtil.getInstance().getHighFee().getDefaultPerKB().longValue() / 1000L) + "\n" + getString(R.string.sat_b));
    btAutoFee.setText((FeeUtil.getInstance().getNormalFee().getDefaultPerKB().longValue() / 1000L) + "\n" + getString(R.string.sat_b));
    btLowFee.setOnClickListener(new View.OnClickListener() {

        public void onClick(View v) {
            FeeUtil.getInstance().setSuggestedFee(FeeUtil.getInstance().getLowFee());
            PrefsUtil.getInstance(SendActivity.this).setValue(PrefsUtil.CURRENT_FEE_TYPE, FEE_LOW);
            btLowFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.blue));
            btAutoFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btPriorityFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btCustomFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btLowFee.setTypeface(null, Typeface.BOLD);
            btAutoFee.setTypeface(null, Typeface.NORMAL);
            btPriorityFee.setTypeface(null, Typeface.NORMAL);
            btCustomFee.setTypeface(null, Typeface.NORMAL);
            btCustomFee.setText(R.string.custom_fee);
            tvFeePrompt.setText(getText(R.string.fee_low_priority) + " " + getText(R.string.blocks_to_cf));
        }
    });
    btAutoFee.setOnClickListener(new View.OnClickListener() {

        public void onClick(View v) {
            FeeUtil.getInstance().setSuggestedFee(FeeUtil.getInstance().getNormalFee());
            PrefsUtil.getInstance(SendActivity.this).setValue(PrefsUtil.CURRENT_FEE_TYPE, FEE_NORMAL);
            btLowFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btAutoFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.blue));
            btPriorityFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btCustomFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btLowFee.setTypeface(null, Typeface.NORMAL);
            btAutoFee.setTypeface(null, Typeface.BOLD);
            btPriorityFee.setTypeface(null, Typeface.NORMAL);
            btCustomFee.setTypeface(null, Typeface.NORMAL);
            btCustomFee.setText(R.string.custom_fee);
            tvFeePrompt.setText(getText(R.string.fee_mid_priority) + " " + getText(R.string.blocks_to_cf));
        }
    });
    btPriorityFee.setOnClickListener(new View.OnClickListener() {

        public void onClick(View v) {
            FeeUtil.getInstance().setSuggestedFee(FeeUtil.getInstance().getHighFee());
            PrefsUtil.getInstance(SendActivity.this).setValue(PrefsUtil.CURRENT_FEE_TYPE, FEE_PRIORITY);
            btLowFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btAutoFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btPriorityFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.blue));
            btCustomFee.setBackgroundColor(SendActivity.this.getResources().getColor(R.color.darkgrey));
            btLowFee.setTypeface(null, Typeface.NORMAL);
            btAutoFee.setTypeface(null, Typeface.NORMAL);
            btPriorityFee.setTypeface(null, Typeface.BOLD);
            btCustomFee.setTypeface(null, Typeface.NORMAL);
            btCustomFee.setText(R.string.custom_fee);
            tvFeePrompt.setText(getText(R.string.fee_high_priority) + " " + getText(R.string.blocks_to_cf));
        }
    });
    btCustomFee.setOnClickListener(new View.OnClickListener() {

        public void onClick(View v) {
            doCustomFee();
        }
    });
    btSend = (Button) findViewById(R.id.send);
    btSend.setOnClickListener(new View.OnClickListener() {

        public void onClick(View v) {
            btSend.setClickable(false);
            btSend.setActivated(false);
            double btc_amount = 0.0;
            try {
                btc_amount = NumberFormat.getInstance(Locale.US).parse(edAmountBTC.getText().toString().trim()).doubleValue();
            // Log.i("SendFragment", "amount entered:" + btc_amount);
            } catch (NumberFormatException nfe) {
                btc_amount = 0.0;
            } catch (ParseException pe) {
                btc_amount = 0.0;
            }
            double dAmount = btc_amount;
            long amount = (long) (Math.round(dAmount * 1e8));
            ;
            // Log.i("SendActivity", "amount:" + amount);
            final String address = strDestinationBTCAddress == null ? edAddress.getText().toString() : strDestinationBTCAddress;
            final int accountIdx = selectedAccount;
            final boolean isSegwitChange = (FormatsUtil.getInstance().isValidBech32(address) || Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), address).isP2SHAddress()) || PrefsUtil.getInstance(SendActivity.this).getValue(PrefsUtil.USE_LIKE_TYPED_CHANGE, true) == false;
            final HashMap<String, BigInteger> receivers = new HashMap<String, BigInteger>();
            receivers.put(address, BigInteger.valueOf(amount));
            // store current change index to restore value in case of sending fail
            int change_index = 0;
            if (isSegwitChange) {
                change_index = BIP49Util.getInstance(SendActivity.this).getWallet().getAccount(0).getChange().getAddrIdx();
            } else {
                try {
                    change_index = HD_WalletFactory.getInstance(SendActivity.this).get().getAccount(0).getChange().getAddrIdx();
                // Log.d("SendActivity", "storing change index:" + change_index);
                } catch (IOException ioe) {
                    ;
                } catch (MnemonicException.MnemonicLengthException mle) {
                    ;
                }
            }
            // get all UTXO
            List<UTXO> utxos = null;
            // if possible, get UTXO by input 'type': p2pkh or p2sh-p2wpkh, else get all UTXO
            long neededAmount = 0L;
            if (FormatsUtil.getInstance().isValidBech32(address) || Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), address).isP2SHAddress()) {
                neededAmount += FeeUtil.getInstance().estimatedFeeSegwit(0, UTXOFactory.getInstance().getCountP2SH_P2WPKH(), 4).longValue();
            // Log.d("SendActivity", "segwit:" + neededAmount);
            } else {
                neededAmount += FeeUtil.getInstance().estimatedFeeSegwit(UTXOFactory.getInstance().getCountP2PKH(), 0, 4).longValue();
            // Log.d("SendActivity", "p2pkh:" + neededAmount);
            }
            neededAmount += amount;
            neededAmount += SamouraiWallet.bDust.longValue();
            if ((FormatsUtil.getInstance().isValidBech32(address) || Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), address).isP2SHAddress()) && (UTXOFactory.getInstance().getP2SH_P2WPKH().size() > 0 && UTXOFactory.getInstance().getTotalP2SH_P2WPKH() > neededAmount)) {
                utxos = new ArrayList<UTXO>(UTXOFactory.getInstance().getP2SH_P2WPKH().values());
            // Log.d("SendActivity", "segwit utxos:" + utxos.size());
            } else if ((UTXOFactory.getInstance().getP2PKH().size() > 0) && (UTXOFactory.getInstance().getTotalP2PKH() > neededAmount)) {
                utxos = new ArrayList<UTXO>(UTXOFactory.getInstance().getP2PKH().values());
            // Log.d("SendActivity", "p2pkh utxos:" + utxos.size());
            } else {
                utxos = APIFactory.getInstance(SendActivity.this).getUtxos(true);
            // Log.d("SendActivity", "all filtered utxos:" + utxos.size());
            }
            final List<UTXO> selectedUTXO = new ArrayList<UTXO>();
            long totalValueSelected = 0L;
            long change = 0L;
            BigInteger fee = null;
            // insufficient funds
            if (amount > balance) {
                Toast.makeText(SendActivity.this, R.string.insufficient_funds, Toast.LENGTH_SHORT).show();
            } else // entire balance (can only be simple spend)
            if (amount == balance) {
                // make sure we are using simple spend
                SPEND_TYPE = SPEND_SIMPLE;
                // Log.d("SendActivity", "amount == balance");
                // take all utxos, deduct fee
                selectedUTXO.addAll(utxos);
                for (UTXO u : selectedUTXO) {
                    totalValueSelected += u.getValue();
                }
            // Log.d("SendActivity", "balance:" + balance);
            // Log.d("SendActivity", "total value selected:" + totalValueSelected);
            } else {
                ;
            }
            org.apache.commons.lang3.tuple.Pair<ArrayList<MyTransactionOutPoint>, ArrayList<TransactionOutput>> pair = null;
            if (SPEND_TYPE == SPEND_RICOCHET) {
                boolean samouraiFeeViaBIP47 = false;
                if (BIP47Meta.getInstance().getOutgoingStatus(BIP47Meta.strSamouraiDonationPCode) == BIP47Meta.STATUS_SENT_CFM) {
                    samouraiFeeViaBIP47 = true;
                }
                final JSONObject jObj = RicochetMeta.getInstance(SendActivity.this).script(amount, FeeUtil.getInstance().getSuggestedFee().getDefaultPerKB().longValue(), address, 4, strPCode, samouraiFeeViaBIP47);
                if (jObj != null) {
                    try {
                        long totalAmount = jObj.getLong("total_spend");
                        if (totalAmount > balance) {
                            Toast.makeText(SendActivity.this, R.string.insufficient_funds, Toast.LENGTH_SHORT).show();
                            return;
                        }
                        String msg = getText(R.string.ricochet_spend1) + " " + address + " " + getText(R.string.ricochet_spend2) + " " + Coin.valueOf(totalAmount).toPlainString() + " " + getText(R.string.ricochet_spend3);
                        AlertDialog.Builder dlg = new AlertDialog.Builder(SendActivity.this).setTitle(R.string.app_name).setMessage(msg).setCancelable(false).setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

                            public void onClick(DialogInterface dialog, int whichButton) {
                                RicochetMeta.getInstance(SendActivity.this).add(jObj);
                                dialog.dismiss();
                                Intent intent = new Intent(SendActivity.this, RicochetActivity.class);
                                startActivityForResult(intent, RICOCHET);
                            }
                        }).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {

                            public void onClick(DialogInterface dialog, int whichButton) {
                                dialog.dismiss();
                            }
                        });
                        if (!isFinishing()) {
                            dlg.show();
                        }
                        return;
                    } catch (JSONException je) {
                        return;
                    }
                }
                return;
            } else // if BIP126 try both hetero/alt, if fails change type to SPEND_SIMPLE
            if (SPEND_TYPE == SPEND_BIP126) {
                List<UTXO> _utxos = utxos;
                // Collections.shuffle(_utxos);
                // sort in descending order by value
                Collections.sort(_utxos, new UTXO.UTXOComparator());
                // hetero
                pair = SendFactory.getInstance(SendActivity.this).heterogeneous(_utxos, BigInteger.valueOf(amount), address);
                if (pair == null) {
                    // Collections.sort(_utxos, new UTXO.UTXOComparator());
                    // alt
                    pair = SendFactory.getInstance(SendActivity.this).altHeterogeneous(_utxos, BigInteger.valueOf(amount), address);
                }
                if (pair == null) {
                    // can't do BIP126, revert to SPEND_SIMPLE
                    SPEND_TYPE = SPEND_SIMPLE;
                }
            } else {
                ;
            }
            // simple spend (less than balance)
            if (SPEND_TYPE == SPEND_SIMPLE) {
                List<UTXO> _utxos = utxos;
                // sort in ascending order by value
                Collections.sort(_utxos, new UTXO.UTXOComparator());
                Collections.reverse(_utxos);
                // get smallest 1 UTXO > than spend + fee + dust
                for (UTXO u : _utxos) {
                    Pair<Integer, Integer> outpointTypes = FeeUtil.getInstance().getOutpointCount(u.getOutpoints());
                    if (u.getValue() >= (amount + SamouraiWallet.bDust.longValue() + FeeUtil.getInstance().estimatedFeeSegwit(outpointTypes.getLeft(), outpointTypes.getRight(), 2).longValue())) {
                        selectedUTXO.add(u);
                        totalValueSelected += u.getValue();
                        // Log.d("SendActivity", "nb inputs:" + u.getOutpoints().size());
                        break;
                    }
                }
                if (selectedUTXO.size() == 0) {
                    // sort in descending order by value
                    Collections.sort(_utxos, new UTXO.UTXOComparator());
                    int selected = 0;
                    int p2pkh = 0;
                    int p2wpkh = 0;
                    // get largest UTXOs > than spend + fee + dust
                    for (UTXO u : _utxos) {
                        selectedUTXO.add(u);
                        totalValueSelected += u.getValue();
                        selected += u.getOutpoints().size();
                        // Log.d("SendActivity", "value selected:" + u.getValue());
                        // Log.d("SendActivity", "total value selected/threshold:" + totalValueSelected + "/" + (amount + SamouraiWallet.bDust.longValue() + FeeUtil.getInstance().estimatedFee(selected, 2).longValue()));
                        Pair<Integer, Integer> outpointTypes = FeeUtil.getInstance().getOutpointCount(u.getOutpoints());
                        p2pkh += outpointTypes.getLeft();
                        p2wpkh += outpointTypes.getRight();
                        if (totalValueSelected >= (amount + SamouraiWallet.bDust.longValue() + FeeUtil.getInstance().estimatedFeeSegwit(p2pkh, p2wpkh, 2).longValue())) {
                            // Log.d("SendActivity", "nb inputs:" + selected);
                            break;
                        }
                    }
                }
            } else if (pair != null) {
                selectedUTXO.clear();
                receivers.clear();
                long inputAmount = 0L;
                long outputAmount = 0L;
                for (MyTransactionOutPoint outpoint : pair.getLeft()) {
                    UTXO u = new UTXO();
                    List<MyTransactionOutPoint> outs = new ArrayList<MyTransactionOutPoint>();
                    outs.add(outpoint);
                    u.setOutpoints(outs);
                    totalValueSelected += u.getValue();
                    selectedUTXO.add(u);
                    inputAmount += u.getValue();
                }
                for (TransactionOutput output : pair.getRight()) {
                    try {
                        Script script = new Script(output.getScriptBytes());
                        receivers.put(script.getToAddress(SamouraiWallet.getInstance().getCurrentNetworkParams()).toString(), BigInteger.valueOf(output.getValue().longValue()));
                        outputAmount += output.getValue().longValue();
                    } catch (Exception e) {
                        Toast.makeText(SendActivity.this, R.string.error_bip126_output, Toast.LENGTH_SHORT).show();
                        return;
                    }
                }
                change = outputAmount - amount;
                fee = BigInteger.valueOf(inputAmount - outputAmount);
            } else {
                Toast.makeText(SendActivity.this, R.string.cannot_select_utxo, Toast.LENGTH_SHORT).show();
                return;
            }
            // do spend here
            if (selectedUTXO.size() > 0) {
                // estimate fee for simple spend, already done if BIP126
                if (SPEND_TYPE == SPEND_SIMPLE) {
                    List<MyTransactionOutPoint> outpoints = new ArrayList<MyTransactionOutPoint>();
                    for (UTXO utxo : selectedUTXO) {
                        outpoints.addAll(utxo.getOutpoints());
                    }
                    Pair<Integer, Integer> outpointTypes = FeeUtil.getInstance().getOutpointCount(outpoints);
                    fee = FeeUtil.getInstance().estimatedFeeSegwit(outpointTypes.getLeft(), outpointTypes.getRight(), 2);
                }
                // Log.d("SendActivity", "spend type:" + SPEND_TYPE);
                // Log.d("SendActivity", "amount:" + amount);
                // Log.d("SendActivity", "total value selected:" + totalValueSelected);
                // Log.d("SendActivity", "fee:" + fee.longValue());
                // Log.d("SendActivity", "nb inputs:" + selectedUTXO.size());
                change = totalValueSelected - (amount + fee.longValue());
                // Log.d("SendActivity", "change:" + change);
                boolean changeIsDust = false;
                if (change < SamouraiWallet.bDust.longValue() && SPEND_TYPE == SPEND_SIMPLE) {
                    change = 0L;
                    fee = fee.add(BigInteger.valueOf(change));
                    amount = totalValueSelected - fee.longValue();
                    // Log.d("SendActivity", "fee:" + fee.longValue());
                    // Log.d("SendActivity", "change:" + change);
                    // Log.d("SendActivity", "amount:" + amount);
                    receivers.put(address, BigInteger.valueOf(amount));
                    changeIsDust = true;
                }
                final long _change = change;
                final BigInteger _fee = fee;
                final int _change_index = change_index;
                String dest = null;
                if (strPCode != null && strPCode.length() > 0) {
                    dest = BIP47Meta.getInstance().getDisplayLabel(strPCode);
                } else {
                    dest = address;
                }
                final String strPrivacyWarning;
                if (SendAddressUtil.getInstance().get(address) == 1) {
                    strPrivacyWarning = getString(R.string.send_privacy_warning) + "\n\n";
                } else {
                    strPrivacyWarning = "";
                }
                String strChangeIsDust = null;
                if (changeIsDust) {
                    strChangeIsDust = getString(R.string.change_is_dust) + "\n\n";
                } else {
                    strChangeIsDust = "";
                }
                String message = strChangeIsDust + strPrivacyWarning + "Send " + Coin.valueOf(amount).toPlainString() + " to " + dest + " (fee:" + Coin.valueOf(_fee.longValue()).toPlainString() + ")?\n";
                final long _amount = amount;
                AlertDialog.Builder builder = new AlertDialog.Builder(SendActivity.this);
                builder.setTitle(R.string.app_name);
                builder.setMessage(message);
                final CheckBox cbShowAgain;
                if (strPrivacyWarning.length() > 0) {
                    cbShowAgain = new CheckBox(SendActivity.this);
                    cbShowAgain.setText(R.string.do_not_repeat_sent_to);
                    cbShowAgain.setChecked(false);
                    builder.setView(cbShowAgain);
                } else {
                    cbShowAgain = null;
                }
                builder.setCancelable(false);
                builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

                    public void onClick(final DialogInterface dialog, int whichButton) {
                        final ProgressDialog progress = new ProgressDialog(SendActivity.this);
                        progress.setCancelable(false);
                        progress.setTitle(R.string.app_name);
                        progress.setMessage(getString(R.string.please_wait_sending));
                        progress.show();
                        final List<MyTransactionOutPoint> outPoints = new ArrayList<MyTransactionOutPoint>();
                        for (UTXO u : selectedUTXO) {
                            outPoints.addAll(u.getOutpoints());
                        }
                        // add change
                        if (_change > 0L) {
                            if (SPEND_TYPE == SPEND_SIMPLE) {
                                if (isSegwitChange) {
                                    String change_address = BIP49Util.getInstance(SendActivity.this).getAddressAt(AddressFactory.CHANGE_CHAIN, BIP49Util.getInstance(SendActivity.this).getWallet().getAccount(0).getChange().getAddrIdx()).getAddressAsString();
                                    receivers.put(change_address, BigInteger.valueOf(_change));
                                } else {
                                    try {
                                        String change_address = HD_WalletFactory.getInstance(SendActivity.this).get().getAccount(0).getChange().getAddressAt(HD_WalletFactory.getInstance(SendActivity.this).get().getAccount(0).getChange().getAddrIdx()).getAddressString();
                                        receivers.put(change_address, BigInteger.valueOf(_change));
                                    } catch (IOException ioe) {
                                        Toast.makeText(SendActivity.this, R.string.error_change_output, Toast.LENGTH_SHORT).show();
                                        return;
                                    } catch (MnemonicException.MnemonicLengthException mle) {
                                        Toast.makeText(SendActivity.this, R.string.error_change_output, Toast.LENGTH_SHORT).show();
                                        return;
                                    }
                                }
                            } else if (SPEND_TYPE == SPEND_BIP126) {
                            // do nothing, change addresses included
                            } else {
                                ;
                            }
                        }
                        // make tx
                        Transaction tx = SendFactory.getInstance(SendActivity.this).makeTransaction(0, outPoints, receivers);
                        final RBFSpend rbf;
                        if (PrefsUtil.getInstance(SendActivity.this).getValue(PrefsUtil.RBF_OPT_IN, false) == true) {
                            rbf = new RBFSpend();
                            for (TransactionInput input : tx.getInputs()) {
                                boolean _isBIP49 = false;
                                String _addr = null;
                                Address _address = input.getConnectedOutput().getAddressFromP2PKHScript(SamouraiWallet.getInstance().getCurrentNetworkParams());
                                if (_address != null) {
                                    _addr = _address.toString();
                                }
                                if (_addr == null) {
                                    _addr = input.getConnectedOutput().getAddressFromP2SH(SamouraiWallet.getInstance().getCurrentNetworkParams()).toString();
                                    _isBIP49 = true;
                                }
                                String path = APIFactory.getInstance(SendActivity.this).getUnspentPaths().get(_addr);
                                if (path != null) {
                                    if (_isBIP49) {
                                        rbf.addKey(input.getOutpoint().toString(), path + "/49");
                                    } else {
                                        rbf.addKey(input.getOutpoint().toString(), path);
                                    }
                                } else {
                                    String pcode = BIP47Meta.getInstance().getPCode4Addr(_addr);
                                    int idx = BIP47Meta.getInstance().getIdx4Addr(_addr);
                                    rbf.addKey(input.getOutpoint().toString(), pcode + "/" + idx);
                                }
                            }
                        } else {
                            rbf = null;
                        }
                        if (tx != null) {
                            tx = SendFactory.getInstance(SendActivity.this).signTransaction(tx);
                            final Transaction _tx = tx;
                            final String hexTx = new String(Hex.encode(tx.bitcoinSerialize()));
                            // Log.d("SendActivity", hexTx);
                            final String strTxHash = tx.getHashAsString();
                            if (PrefsUtil.getInstance(SendActivity.this).getValue(PrefsUtil.BROADCAST_TX, true) == false) {
                                if (progress != null && progress.isShowing()) {
                                    progress.dismiss();
                                }
                                doShowTx(hexTx, strTxHash);
                                return;
                            }
                            new Thread(new Runnable() {

                                @Override
                                public void run() {
                                    Looper.prepare();
                                    boolean isOK = false;
                                    String response = null;
                                    try {
                                        if (PrefsUtil.getInstance(SendActivity.this).getValue(PrefsUtil.USE_TRUSTED_NODE, false) == true) {
                                            if (TrustedNodeUtil.getInstance().isSet()) {
                                                response = PushTx.getInstance(SendActivity.this).trustedNode(hexTx);
                                                JSONObject jsonObject = new org.json.JSONObject(response);
                                                if (jsonObject.has("result")) {
                                                    if (jsonObject.getString("result").matches("^[A-Za-z0-9]{64}$")) {
                                                        isOK = true;
                                                    } else {
                                                        Toast.makeText(SendActivity.this, R.string.trusted_node_tx_error, Toast.LENGTH_SHORT).show();
                                                    }
                                                }
                                            } else {
                                                Toast.makeText(SendActivity.this, R.string.trusted_node_not_valid, Toast.LENGTH_SHORT).show();
                                            }
                                        } else {
                                            response = PushTx.getInstance(SendActivity.this).samourai(hexTx);
                                            if (response != null) {
                                                JSONObject jsonObject = new org.json.JSONObject(response);
                                                if (jsonObject.has("status")) {
                                                    if (jsonObject.getString("status").equals("ok")) {
                                                        isOK = true;
                                                    }
                                                }
                                            } else {
                                                Toast.makeText(SendActivity.this, R.string.pushtx_returns_null, Toast.LENGTH_SHORT).show();
                                            }
                                        }
                                        if (isOK) {
                                            if (PrefsUtil.getInstance(SendActivity.this).getValue(PrefsUtil.USE_TRUSTED_NODE, false) == false) {
                                                Toast.makeText(SendActivity.this, R.string.tx_sent, Toast.LENGTH_SHORT).show();
                                            } else {
                                                Toast.makeText(SendActivity.this, R.string.trusted_node_tx_sent, Toast.LENGTH_SHORT).show();
                                            }
                                            if (_change > 0L && SPEND_TYPE == SPEND_SIMPLE) {
                                                if (isSegwitChange) {
                                                    BIP49Util.getInstance(SendActivity.this).getWallet().getAccount(0).getChange().incAddrIdx();
                                                } else {
                                                    try {
                                                        HD_WalletFactory.getInstance(SendActivity.this).get().getAccount(0).getChange().incAddrIdx();
                                                    } catch (IOException ioe) {
                                                        ;
                                                    } catch (MnemonicException.MnemonicLengthException mle) {
                                                        ;
                                                    }
                                                }
                                            }
                                            if (PrefsUtil.getInstance(SendActivity.this).getValue(PrefsUtil.RBF_OPT_IN, false) == true) {
                                                for (TransactionOutput out : _tx.getOutputs()) {
                                                    try {
                                                        if (!isSegwitChange && !address.equals(out.getAddressFromP2PKHScript(SamouraiWallet.getInstance().getCurrentNetworkParams()).toString())) {
                                                            rbf.addChangeAddr(out.getAddressFromP2PKHScript(SamouraiWallet.getInstance().getCurrentNetworkParams()).toString());
                                                        } else if (isSegwitChange && !address.equals(out.getAddressFromP2SH(SamouraiWallet.getInstance().getCurrentNetworkParams()).toString())) {
                                                            rbf.addChangeAddr(out.getAddressFromP2SH(SamouraiWallet.getInstance().getCurrentNetworkParams()).toString());
                                                        } else {
                                                            ;
                                                        }
                                                    } catch (NullPointerException npe) {
                                                        // test for bech32, skip for now as it's not a change address
                                                        ;
                                                    }
                                                }
                                                rbf.setHash(strTxHash);
                                                rbf.setSerializedTx(hexTx);
                                                RBFUtil.getInstance().add(rbf);
                                            }
                                            // increment counter if BIP47 spend
                                            if (strPCode != null && strPCode.length() > 0) {
                                                BIP47Meta.getInstance().getPCode4AddrLookup().put(address, strPCode);
                                                BIP47Meta.getInstance().inc(strPCode);
                                                SimpleDateFormat sd = new SimpleDateFormat("dd MMM");
                                                String strTS = sd.format(currentTimeMillis());
                                                String event = strTS + " " + SendActivity.this.getString(R.string.sent) + " " + MonetaryUtil.getInstance().getBTCFormat().format((double) _amount / 1e8) + " BTC";
                                                BIP47Meta.getInstance().setLatestEvent(strPCode, event);
                                                strPCode = null;
                                            }
                                            if (strPrivacyWarning.length() > 0 && cbShowAgain != null) {
                                                SendAddressUtil.getInstance().add(address, cbShowAgain.isChecked() ? false : true);
                                            } else if (SendAddressUtil.getInstance().get(address) == 0) {
                                                SendAddressUtil.getInstance().add(address, false);
                                            } else {
                                                SendAddressUtil.getInstance().add(address, true);
                                            }
                                            Intent intent = new Intent("com.samourai.wallet.BalanceFragment.REFRESH");
                                            intent.putExtra("notifTx", false);
                                            intent.putExtra("fetch", true);
                                            LocalBroadcastManager.getInstance(SendActivity.this).sendBroadcast(intent);
                                            View view = SendActivity.this.getCurrentFocus();
                                            if (view != null) {
                                                InputMethodManager imm = (InputMethodManager) SendActivity.this.getSystemService(Context.INPUT_METHOD_SERVICE);
                                                imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
                                            }
                                            if (bViaMenu) {
                                                SendActivity.this.finish();
                                            } else {
                                                Intent _intent = new Intent(SendActivity.this, BalanceActivity.class);
                                                _intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
                                                startActivity(_intent);
                                            }
                                        } else {
                                            Toast.makeText(SendActivity.this, R.string.tx_failed, Toast.LENGTH_SHORT).show();
                                            // reset change index upon tx fail
                                            if (isSegwitChange) {
                                                BIP49Util.getInstance(SendActivity.this).getWallet().getAccount(0).getChange().setAddrIdx(_change_index);
                                            } else {
                                                HD_WalletFactory.getInstance(SendActivity.this).get().getAccount(0).getChange().setAddrIdx(_change_index);
                                            }
                                        }
                                    } catch (JSONException je) {
                                        Toast.makeText(SendActivity.this, "pushTx:" + je.getMessage(), Toast.LENGTH_SHORT).show();
                                    } catch (MnemonicException.MnemonicLengthException mle) {
                                        Toast.makeText(SendActivity.this, "pushTx:" + mle.getMessage(), Toast.LENGTH_SHORT).show();
                                    } catch (DecoderException de) {
                                        Toast.makeText(SendActivity.this, "pushTx:" + de.getMessage(), Toast.LENGTH_SHORT).show();
                                    } catch (IOException ioe) {
                                        Toast.makeText(SendActivity.this, "pushTx:" + ioe.getMessage(), Toast.LENGTH_SHORT).show();
                                    } finally {
                                        SendActivity.this.runOnUiThread(new Runnable() {

                                            @Override
                                            public void run() {
                                                btSend.setActivated(true);
                                                btSend.setClickable(true);
                                                progress.dismiss();
                                                dialog.dismiss();
                                            }
                                        });
                                    }
                                    Looper.loop();
                                }
                            }).start();
                        } else {
                            // Log.d("SendActivity", "tx error");
                            Toast.makeText(SendActivity.this, "tx error", Toast.LENGTH_SHORT).show();
                        }
                    }
                });
                builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {

                    public void onClick(final DialogInterface dialog, int whichButton) {
                        try {
                            // reset change index upon 'NO'
                            if (isSegwitChange) {
                                BIP49Util.getInstance(SendActivity.this).getWallet().getAccount(0).getChange().setAddrIdx(_change_index);
                            } else {
                                HD_WalletFactory.getInstance(SendActivity.this).get().getAccount(0).getChange().setAddrIdx(_change_index);
                            }
                        } catch (Exception e) {
                            // Log.d("SendActivity", e.getMessage());
                            Toast.makeText(SendActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
                        } finally {
                            SendActivity.this.runOnUiThread(new Runnable() {

                                @Override
                                public void run() {
                                    btSend.setActivated(true);
                                    btSend.setClickable(true);
                                    dialog.dismiss();
                                }
                            });
                        }
                    }
                });
                AlertDialog alert = builder.create();
                alert.show();
            }
        }
    });
    Bundle extras = getIntent().getExtras();
    if (extras != null) {
        bViaMenu = extras.getBoolean("via_menu", false);
        String strUri = extras.getString("uri");
        strPCode = extras.getString("pcode");
        if (strUri != null && strUri.length() > 0) {
            processScan(strUri);
        }
        if (strPCode != null && strPCode.length() > 0) {
            processPCode(strPCode, null);
        }
    }
    validateSpend();
}
Also used : AlertDialog(android.app.AlertDialog) PaymentAddress(com.samourai.wallet.bip47.rpc.PaymentAddress) Address(org.bitcoinj.core.Address) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) RicochetActivity(com.samourai.wallet.ricochet.RicochetActivity) ProgressDialog(android.app.ProgressDialog) ArrayList(java.util.ArrayList) List(java.util.List) Script(org.bitcoinj.script.Script) SuggestedFee(com.samourai.wallet.send.SuggestedFee) JSONObject(org.json.JSONObject) Transaction(org.bitcoinj.core.Transaction) CheckBox(android.widget.CheckBox) BigInteger(java.math.BigInteger) TransactionOutput(org.bitcoinj.core.TransactionOutput) DialogInterface(android.content.DialogInterface) DecimalFormat(java.text.DecimalFormat) InputMethodManager(android.view.inputmethod.InputMethodManager) TransactionInput(org.bitcoinj.core.TransactionInput) MnemonicException(org.bitcoinj.crypto.MnemonicException) TextWatcher(android.text.TextWatcher) Editable(android.text.Editable) RBFSpend(com.samourai.wallet.send.RBFSpend) Pair(org.apache.commons.lang3.tuple.Pair) DecimalFormatSymbols(java.text.DecimalFormatSymbols) Bundle(android.os.Bundle) JSONException(org.json.JSONException) Intent(android.content.Intent) IOException(java.io.IOException) ImageView(android.widget.ImageView) View(android.view.View) TextView(android.widget.TextView) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) Point(android.graphics.Point) JSONException(org.json.JSONException) IOException(java.io.IOException) WriterException(com.google.zxing.WriterException) ParseException(java.text.ParseException) DecoderException(org.bouncycastle.util.encoders.DecoderException) FileNotFoundException(java.io.FileNotFoundException) MnemonicException(org.bitcoinj.crypto.MnemonicException) MotionEvent(android.view.MotionEvent) UTXO(com.samourai.wallet.send.UTXO) BlockedUTXO(com.samourai.wallet.send.BlockedUTXO) DecoderException(org.bouncycastle.util.encoders.DecoderException) ParseException(java.text.ParseException) SimpleDateFormat(java.text.SimpleDateFormat) ArrayAdapter(android.widget.ArrayAdapter) CompoundButton(android.widget.CompoundButton) NumberFormat(java.text.NumberFormat)

Example 19 with UTXO

use of com.samourai.wallet.send.UTXO in project samourai-wallet-android by Samourai-Wallet.

the class UTXOActivity method update.

private void update(boolean broadcast) {
    data.clear();
    doNotSpend.clear();
    for (UTXO utxo : APIFactory.getInstance(UTXOActivity.this).getUtxos(false)) {
        for (MyTransactionOutPoint outpoint : utxo.getOutpoints()) {
            DisplayData displayData = new DisplayData();
            displayData.addr = outpoint.getAddress();
            displayData.amount = outpoint.getValue().longValue();
            displayData.hash = outpoint.getTxHash().toString();
            displayData.idx = outpoint.getTxOutputN();
            if (BlockedUTXO.getInstance().contains(outpoint.getTxHash().toString(), outpoint.getTxOutputN())) {
                doNotSpend.add(displayData);
                Log.d("UTXOActivity", "marked as do not spend");
            } else {
                data.add(displayData);
                Log.d("UTXOActivity", "unmarked");
            }
        }
    }
    data.addAll(doNotSpend);
    if (adapter != null) {
        adapter.notifyDataSetInvalidated();
        Log.d("UTXOActivity", "nb:" + data.size());
    }
    if (broadcast) {
        Intent intent = new Intent("com.samourai.wallet.BalanceFragment.REFRESH");
        intent.putExtra("notifTx", false);
        intent.putExtra("fetch", true);
        LocalBroadcastManager.getInstance(UTXOActivity.this).sendBroadcast(intent);
    }
}
Also used : UTXO(com.samourai.wallet.send.UTXO) BlockedUTXO(com.samourai.wallet.send.BlockedUTXO) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) Intent(android.content.Intent)

Example 20 with UTXO

use of com.samourai.wallet.send.UTXO in project samourai-wallet-android by Samourai-Wallet.

the class ReplayProtectionActivity method replayProtectionThread.

private void replayProtectionThread() {
    final Handler handler = new Handler();
    new Thread(new Runnable() {

        @Override
        public void run() {
            Looper.prepare();
            String rbfHash = null;
            if (PrefsUtil.getInstance(ReplayProtectionActivity.this).getValue(PrefsUtil.BCC_REPLAY1, "").length() == 0) {
                long balance = 0L;
                List<MyTransactionOutPoint> outpoints = new ArrayList<MyTransactionOutPoint>();
                List<UTXO> utxos = APIFactory.getInstance(ReplayProtectionActivity.this).getUtxos(true);
                for (UTXO utxo : utxos) {
                    balance += utxo.getValue();
                    outpoints.addAll(utxo.getOutpoints());
                }
                Log.d("ReplayProtectionA", "outpoints selected:" + outpoints.size());
                Log.d("ReplayProtectionA", "balance:" + balance);
                SuggestedFee rbfFee = FeeUtil.getInstance().getNormalFee();
                BigInteger biFee0 = FeeUtil.getInstance().estimatedFee(outpoints.size(), 1, BigInteger.valueOf(3L * 1000L));
                Log.d("ReplayProtectionA", "biFee0:" + biFee0.longValue());
                BigInteger biFee1 = FeeUtil.getInstance().estimatedFee(outpoints.size(), 1, rbfFee.getDefaultPerKB());
                Log.d("ReplayProtectionA", "biFee1:" + biFee1.longValue());
                long amount0 = balance - biFee0.longValue();
                Log.d("ReplayProtectionA", "amount0:" + amount0);
                long amount1 = balance - biFee1.longValue();
                Log.d("ReplayProtectionA", "amount1:" + amount1);
                String ownReceiveAddr = AddressFactory.getInstance(ReplayProtectionActivity.this).get(AddressFactory.RECEIVE_CHAIN).getAddressString();
                Log.d("ReplayProtectionA", "receive address:" + ownReceiveAddr);
                HashMap<String, BigInteger> receivers = new HashMap<String, BigInteger>();
                boolean currentRBF = PrefsUtil.getInstance(ReplayProtectionActivity.this).getValue(PrefsUtil.RBF_OPT_IN, false);
                PrefsUtil.getInstance(ReplayProtectionActivity.this).setValue(PrefsUtil.RBF_OPT_IN, true);
                receivers.put(ownReceiveAddr, BigInteger.valueOf(amount0));
                Transaction tx0 = SendFactory.getInstance(ReplayProtectionActivity.this).makeTransaction(0, outpoints, receivers);
                tx0 = SendFactory.getInstance(ReplayProtectionActivity.this).signTransaction(tx0);
                final String hexTx0 = new String(Hex.encode(tx0.bitcoinSerialize()));
                Log.d("ReplayProtectionA", "hexTx0:" + hexTx0);
                final String strTxHash0 = tx0.getHashAsString();
                Log.d("ReplayProtectionA", "txHash0:" + strTxHash0);
                receivers.clear();
                receivers.put(ownReceiveAddr, BigInteger.valueOf(amount1));
                Transaction tx1 = SendFactory.getInstance(ReplayProtectionActivity.this).makeTransaction(0, outpoints, receivers);
                tx1 = SendFactory.getInstance(ReplayProtectionActivity.this).signTransaction(tx1);
                String hexTx1 = new String(Hex.encode(tx1.bitcoinSerialize()));
                Log.d("ReplayProtectionA", "hexTx1:" + hexTx1);
                final String strTxHash1 = tx1.getHashAsString();
                Log.d("ReplayProtectionA", "txHash1:" + strTxHash1);
                PrefsUtil.getInstance(ReplayProtectionActivity.this).setValue(PrefsUtil.RBF_OPT_IN, currentRBF);
                boolean tx0pushedBTC = PushTx.getInstance(ReplayProtectionActivity.this).pushTx(hexTx0);
                Log.d("ReplayProtectionA", "tx0 pushed BTC:" + tx0pushedBTC);
                if (tx0pushedBTC) {
                    handler.post(new Runnable() {

                        public void run() {
                            tvBitcoin2.setText(getText(R.string.replay_broadcast_to_network));
                        }
                    });
                }
                boolean tx0pushedBCC = HardForkUtil.getInstance(ReplayProtectionActivity.this).bccPushTx(hexTx0);
                Log.d("ReplayProtectionA", "tx0 pushed BCC:" + tx0pushedBCC);
                /*
                    if(tx0pushedBCC)    {
                        handler.post(new Runnable() {
                            public void run() {
                                tvShitcoin2.setText(getText(R.string.replay_broadcast_to_network));
                            }
                        });
                    }
                    */
                handler.post(new Runnable() {

                    public void run() {
                        tvShitcoin2.setText(getText(R.string.replay_broadcast_to_network));
                    }
                });
                try {
                    Thread.sleep(2L * 1000L);
                } catch (InterruptedException ie) {
                    return;
                }
                JSONObject txObj0 = APIFactory.getInstance(ReplayProtectionActivity.this).getTxInfo(strTxHash0);
                Log.d("ReplayProtectionA", "tx0 status:" + txObj0.toString());
                boolean tx0Status = btcTxSeen(txObj0);
                Log.d("ReplayProtectionA", "tx0 status:" + tx0Status);
                if (tx0Status) {
                    handler.post(new Runnable() {

                        public void run() {
                            tvBitcoin2.setText(getText(R.string.replay_seen_on_network));
                            tvBitcoin3.setText(strTxHash0.substring(0, 30) + "...");
                        }
                    });
                }
                String bccTxOut = HardForkUtil.getInstance(ReplayProtectionActivity.this).bccTxOut(strTxHash0, 0);
                Log.d("ReplayProtectionA", "bcc tx out:" + bccTxOut);
                boolean bccTxStatus = bccTxConfirmed(bccTxOut);
                Log.d("ReplayProtectionA", "bcc confirmed:" + bccTxStatus);
                if (bccTxStatus) {
                    handler.post(new Runnable() {

                        public void run() {
                            tvShitcoin2.setText(getText(R.string.replay_seen_on_network));
                            tvShitcoin3.setText(strTxHash0.substring(0, 30) + "...");
                        }
                    });
                }
                boolean tx1pushedBTC = PushTx.getInstance(ReplayProtectionActivity.this).pushTx(hexTx1);
                Log.d("ReplayProtectionA", "tx1 pushed BTC:" + tx1pushedBTC);
                if (tx1pushedBTC) {
                    handler.post(new Runnable() {

                        public void run() {
                            tvBitcoin2.setText(getText(R.string.replay_split_tx_broadcast));
                            tvBitcoin3.setText("");
                        }
                    });
                }
                try {
                    Thread.sleep(2L * 1000L);
                } catch (InterruptedException ie) {
                    return;
                }
                JSONObject txObj1 = APIFactory.getInstance(ReplayProtectionActivity.this).getTxInfo(strTxHash1);
                Log.d("ReplayProtectionA", "tx1 status:" + txObj1.toString());
                boolean tx1Status = btcTxSeen(txObj1);
                Log.d("ReplayProtectionA", "tx1 status:" + tx1Status);
                if (tx1Status) {
                    if (PrefsUtil.getInstance(ReplayProtectionActivity.this).getValue(PrefsUtil.BCC_REPLAY1, "").length() == 0) {
                        PrefsUtil.getInstance(ReplayProtectionActivity.this).setValue(PrefsUtil.BCC_REPLAY0, strTxHash0);
                        PrefsUtil.getInstance(ReplayProtectionActivity.this).setValue(PrefsUtil.BCC_REPLAY1, strTxHash1);
                    }
                    handler.post(new Runnable() {

                        public void run() {
                            tvBitcoin2.setText(getText(R.string.replay_seen_on_network) + ", " + getText(R.string.replay_confirmations) + "0/6");
                            tvBitcoin3.setText(strTxHash1.substring(0, 30) + "...");
                        }
                    });
                }
                if (tx0Status && tx1Status) {
                    handler.post(new Runnable() {

                        public void run() {
                            Toast.makeText(ReplayProtectionActivity.this, R.string.replay_in_progress, Toast.LENGTH_SHORT).show();
                        }
                    });
                }
                rbfHash = strTxHash1;
            } else {
                rbfHash = PrefsUtil.getInstance(ReplayProtectionActivity.this).getValue(PrefsUtil.BCC_REPLAY1, "");
            }
            boolean tx1Confirmed = false;
            JSONObject txObj1 = null;
            final String _strTxHash1 = rbfHash;
            if (_strTxHash1 != null && _strTxHash1.length() > 0) {
                txObj1 = APIFactory.getInstance(ReplayProtectionActivity.this).getTxInfo(_strTxHash1);
                Log.d("ReplayProtectionA", "tx1 status:" + txObj1.toString());
                tx1Confirmed = btcTxConfirmed(txObj1);
                Log.d("ReplayProtectionA", "tx1 confirmed:" + tx1Confirmed);
            }
            if (tx1Confirmed) {
                final int blockHeight = btcTxHeight(txObj1);
                if (blockHeight != -1) {
                    final int latestBlockHeight = (int) APIFactory.getInstance(ReplayProtectionActivity.this).getLatestBlockHeight();
                    handler.post(new Runnable() {

                        public void run() {
                            int cf = (latestBlockHeight - blockHeight) + 1;
                            if (cf >= 6) {
                                ((TextView) layoutAlert.findViewById(R.id.right)).setText(getText(R.string.replay_protected));
                                layoutAlert.setBackgroundColor(COLOR_GREEN);
                                layoutShitcoin.setVisibility(View.INVISIBLE);
                                tvBitcoin2.setText(getText(R.string.replay_confirmed));
                                tvBitcoin3.setText(_strTxHash1.substring(0, 30) + "...");
                            } else {
                                tvBitcoin2.setText(getText(R.string.replay_waiting_for_confirmations) + " " + cf + "/6");
                                tvBitcoin3.setText(_strTxHash1.substring(0, 30) + "...");
                            }
                        }
                    });
                }
            } else {
                handler.post(new Runnable() {

                    public void run() {
                        tvBitcoin2.setText(getText(R.string.replay_seen_on_network) + ", " + getText(R.string.replay_confirmations) + "0/6");
                        Toast.makeText(ReplayProtectionActivity.this, R.string.replay_awaiting_confirmation, Toast.LENGTH_SHORT).show();
                    }
                });
            }
            Looper.loop();
        }
    }).start();
}
Also used : SuggestedFee(com.samourai.wallet.send.SuggestedFee) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Handler(android.os.Handler) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) UTXO(com.samourai.wallet.send.UTXO) Transaction(org.bitcoinj.core.Transaction) JSONObject(org.json.JSONObject) BigInteger(java.math.BigInteger)

Aggregations

UTXO (com.samourai.wallet.send.UTXO)35 MyTransactionOutPoint (com.samourai.wallet.send.MyTransactionOutPoint)33 ArrayList (java.util.ArrayList)23 HashMap (java.util.HashMap)19 Transaction (org.bitcoinj.core.Transaction)17 IOException (java.io.IOException)14 TransactionOutPoint (org.bitcoinj.core.TransactionOutPoint)14 BlockedUTXO (com.samourai.wallet.send.BlockedUTXO)13 BigInteger (java.math.BigInteger)13 MnemonicException (org.bitcoinj.crypto.MnemonicException)13 JSONException (org.json.JSONException)13 JSONObject (org.json.JSONObject)13 ECKey (org.bitcoinj.core.ECKey)12 TransactionInput (org.bitcoinj.core.TransactionInput)10 Intent (android.content.Intent)9 SegwitAddress (com.samourai.wallet.segwit.SegwitAddress)9 Script (org.bitcoinj.script.Script)9 AlertDialog (android.app.AlertDialog)8 DecryptionException (com.samourai.wallet.crypto.DecryptionException)8 DialogInterface (android.content.DialogInterface)6