use of org.bitcoinj.core.TransactionInput 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();
}
Aggregations