use of com.samourai.wallet.utxos.models.UTXOCoin in project samourai-wallet-android by Samourai-Wallet.
the class UTXOSActivity method applyFilters.
private void applyFilters() {
// ArrayList will store UTXOs that or filtered based on spending status
ArrayList<UTXOCoin> filteredStatus = new ArrayList<>();
for (UTXOCoin model : unFilteredUTXOS) {
if (statusUnSpendable) {
if (model.doNotSpend) {
if (!filteredStatus.contains(model)) {
filteredStatus.add(model);
}
}
}
if (statusSpendable) {
if (!model.doNotSpend) {
if (!filteredStatus.contains(model)) {
filteredStatus.add(model);
}
}
}
}
// ArrayList will store UTXOs that or filtered based on Address types
// types includes SEGWIT_NATIVE,SEGWIT_COMPAT,LEGACY
List<UTXOCoin> filteredAddress = new ArrayList<>(filteredStatus);
// counters to check spendables and unspendables
// sections will be added based on this counters
int unspendables = 0;
int spendables = 0;
for (UTXOCoin model : filteredStatus) {
if (model.doNotSpend) {
unspendables = unspendables + 1;
} else {
spendables = spendables + 1;
}
UTXOUtil.AddressTypes type = UTXOUtil.getAddressType(model.address);
switch(type) {
case LEGACY:
if (!addressFilterLegacy) {
filteredAddress.remove(model);
}
break;
case SEGWIT_COMPAT:
{
if (!addressFilterSegwitCompat) {
filteredAddress.remove(model);
}
break;
}
case SEGWIT_NATIVE:
{
if (!addressFilterSegwitNat) {
filteredAddress.remove(model);
}
break;
}
}
}
if (utxoSortOrder) {
// Ascending order sorting based on the UTXO amount
Collections.sort(filteredAddress, (model, t1) -> Long.compare(model.amount, t1.amount));
} else {
// Descending order sorting based on the UTXO amount
Collections.sort(filteredAddress, (model, t1) -> Long.compare(t1.amount, model.amount));
}
// Sectioned dataset for RecyclerView adapter
// here array will split based on spending status
List<UTXOCoin> sectioned = new ArrayList<>();
if (spendables > 0) {
UTXOCoinSegment active = new UTXOCoinSegment(null, null);
active.id = 0;
active.isActive = true;
sectioned.add(active);
}
for (UTXOCoin models : filteredAddress) {
if (!models.doNotSpend) {
models.id = filteredAddress.indexOf(models) + 1;
sectioned.add(models);
}
}
if (unspendables > 0) {
UTXOCoinSegment doNotSpend = new UTXOCoinSegment(null, null);
doNotSpend.id = filteredAddress.size() + 1;
doNotSpend.isActive = false;
doNotSpend.hash = "not_active";
sectioned.add(doNotSpend);
}
for (UTXOCoin models : filteredAddress) {
if (models.doNotSpend) {
models.id = filteredAddress.indexOf(models) + 1;
sectioned.add(models);
}
}
this.adapter.updateList(sectioned);
}
use of com.samourai.wallet.utxos.models.UTXOCoin in project samourai-wallet-android by Samourai-Wallet.
the class UTXOSActivity method getUTXOs.
private Observable<Map<String, Object>> getUTXOs() {
return Observable.fromCallable(() -> {
long totalP2WPKH = 0L;
long totalBlocked = 0L;
long totalP2PKH = 0L;
long totalP2SH_P2WPKH = 0L;
noteAmounts.clear();
tagAmounts.clear();
Map<String, Object> dataSet = new HashMap<>();
List<UTXO> utxos = null;
if (account == WhirlpoolMeta.getInstance(getApplicationContext()).getWhirlpoolPostmix()) {
utxos = APIFactory.getInstance(getApplicationContext()).getUtxosPostMix(false);
} else {
// utxos = APIFactory.getInstance(getApplicationContext()).getUtxosWithLocalCache(false, true);
utxos = APIFactory.getInstance(getApplicationContext()).getUtxos(false);
}
long amount = 0L;
for (UTXO utxo : utxos) {
for (MyTransactionOutPoint out : utxo.getOutpoints()) {
debug("UTXOSActivity", "utxo:" + out.getAddress() + "," + out.getValue());
debug("UTXOSActivity", "utxo:" + utxo.getPath());
amount += out.getValue().longValue();
}
}
ArrayList<UTXOCoin> items = new ArrayList<>();
for (UTXO utxo : utxos) {
for (MyTransactionOutPoint outpoint : utxo.getOutpoints()) {
UTXOCoin displayData = new UTXOCoin(outpoint, utxo);
if (BlockedUTXO.getInstance().contains(outpoint.getTxHash().toString(), outpoint.getTxOutputN())) {
displayData.doNotSpend = true;
totalBlocked += displayData.amount;
} else if (BlockedUTXO.getInstance().containsPostMix(outpoint.getTxHash().toString(), outpoint.getTxOutputN())) {
displayData.doNotSpend = true;
// Log.d("UTXOActivity", "marked as do not spend");
totalBlocked += displayData.amount;
} else {
// Log.d("UTXOActivity", "unmarked");
if (FormatsUtil.getInstance().isValidBech32(displayData.address)) {
totalP2WPKH += displayData.amount;
} else if (Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), displayData.address).isP2SHAddress()) {
totalP2SH_P2WPKH += displayData.amount;
} else {
totalP2PKH += displayData.amount;
}
}
if (UTXOUtil.getInstance().get(outpoint.getTxHash().toString(), outpoint.getTxOutputN()) != null) {
List<String> tags = UTXOUtil.getInstance().get(outpoint.getTxHash().toString(), outpoint.getTxOutputN());
for (String tag : tags) {
if (tagAmounts.containsKey(tag.toLowerCase())) {
long val = tagAmounts.get(tag.toLowerCase());
val += displayData.amount;
tagAmounts.put(tag.toLowerCase(), val);
} else {
tagAmounts.put(tag.toLowerCase(), displayData.amount);
}
}
}
if (UTXOUtil.getInstance().getNote(outpoint.getTxHash().toString()) != null) {
String note = UTXOUtil.getInstance().getNote(outpoint.getTxHash().toString());
if (noteAmounts.containsKey(note.toLowerCase())) {
long val = noteAmounts.get(note.toLowerCase());
val += displayData.amount;
noteAmounts.put(note.toLowerCase(), val);
} else {
noteAmounts.put(note.toLowerCase(), displayData.amount);
}
}
items.add(displayData);
}
}
dataSet.put("totalP2WPKH", totalP2WPKH);
dataSet.put("totalBlocked", totalBlocked);
dataSet.put("totalP2SH_P2WPKH", totalP2SH_P2WPKH);
dataSet.put("totalP2PKH", totalP2PKH);
dataSet.put("utxos", items);
return dataSet;
});
}
use of com.samourai.wallet.utxos.models.UTXOCoin in project samourai-wallet-android by Samourai-Wallet.
the class UTXOSActivity method getPreSelected.
public String getPreSelected() {
ArrayList<UTXOCoin> utxos = new ArrayList<>();
for (UTXOCoin utxo : this.filteredUTXOs) {
if (utxo.isSelected) {
utxos.add(utxo);
}
}
String id = null;
if (utxos.size() > 0) {
id = UUID.randomUUID().toString();
PreSelectUtil.getInstance().add(id, utxos);
}
return id;
}
use of com.samourai.wallet.utxos.models.UTXOCoin in project samourai-wallet-android by Samourai-Wallet.
the class SendActivity method setBalance.
private void setBalance() {
if (preselectedUTXOs != null && preselectedUTXOs.size() > 0) {
long amount = 0;
for (UTXOCoin utxo : preselectedUTXOs) {
amount += utxo.amount;
}
balance = amount;
} else {
try {
if (account == WhirlpoolMeta.getInstance(SendActivity.this).getWhirlpoolPostmix()) {
balance = APIFactory.getInstance(SendActivity.this).getXpubPostMixBalance();
} else {
Long tempBalance = APIFactory.getInstance(SendActivity.this).getXpubAmounts().get(HD_WalletFactory.getInstance(SendActivity.this).get().getAccount(0).xpubstr());
if (tempBalance != 0L) {
balance = tempBalance;
}
}
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (MnemonicException.MnemonicLengthException mle) {
mle.printStackTrace();
} catch (java.lang.NullPointerException npe) {
npe.printStackTrace();
}
}
final String strAmount;
NumberFormat nf = NumberFormat.getInstance(Locale.US);
nf.setMaximumFractionDigits(8);
nf.setMinimumFractionDigits(1);
nf.setMinimumIntegerDigits(1);
strAmount = nf.format(balance / 1e8);
if (account == 0) {
tvMaxAmount.setOnClickListener(view -> {
btcEditText.setText(strAmount);
});
}
tvMaxAmount.setOnLongClickListener(view -> {
setBalance();
return true;
});
tvMaxAmount.setText(strAmount + " " + getDisplayUnits());
if (balance == 0L && !APIFactory.getInstance(getApplicationContext()).walletInit) {
// some time, user may navigate to this activity even before wallet initialization completes
// so we will set a delay to reload balance info
Disposable disposable = Completable.timer(700, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()).subscribe(this::setBalance);
compositeDisposables.add(disposable);
if (!shownWalletLoadingMessage) {
Snackbar.make(tvMaxAmount.getRootView(), "Please wait... your wallet is still loading ", Snackbar.LENGTH_LONG).show();
shownWalletLoadingMessage = true;
}
}
}
use of com.samourai.wallet.utxos.models.UTXOCoin in project samourai-wallet-android by Samourai-Wallet.
the class SendActivity method prepareSpend.
private boolean prepareSpend() {
restoreChangeIndexes();
double btc_amount = 0.0;
try {
btc_amount = NumberFormat.getInstance(Locale.US).parse(btcEditText.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;
amount = (long) (Math.round(dAmount * 1e8));
if (selectedCahootsType == SelectCahootsType.type.STOWAWAY) {
setButtonForStowaway(true);
return true;
} else {
setButtonForStowaway(false);
}
address = strDestinationBTCAddress == null ? toAddressEditText.getText().toString().trim() : strDestinationBTCAddress;
if (account == WhirlpoolMeta.getInstance(SendActivity.this).getWhirlpoolPostmix()) {
changeType = 84;
} else if (PrefsUtil.getInstance(SendActivity.this).getValue(PrefsUtil.USE_LIKE_TYPED_CHANGE, true) == false) {
changeType = 84;
} else if (FormatsUtil.getInstance().isValidBech32(address) || Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), address).isP2SHAddress()) {
changeType = FormatsUtil.getInstance().isValidBech32(address) ? 84 : 49;
} else {
changeType = 44;
}
receivers = new HashMap<String, BigInteger>();
receivers.put(address, BigInteger.valueOf(amount));
if (account == WhirlpoolMeta.getInstance(SendActivity.this).getWhirlpoolPostmix()) {
change_index = idxBIP84PostMixInternal;
} else if (changeType == 84) {
change_index = idxBIP84Internal;
} else if (changeType == 49) {
change_index = idxBIP49Internal;
} else {
change_index = idxBIP44Internal;
}
// if possible, get UTXO by input 'type': p2pkh, p2sh-p2wpkh or p2wpkh, else get all UTXO
long neededAmount = 0L;
if (FormatsUtil.getInstance().isValidBech32(address) || account == WhirlpoolMeta.getInstance(SendActivity.this).getWhirlpoolPostmix()) {
neededAmount += FeeUtil.getInstance().estimatedFeeSegwit(0, 0, UTXOFactory.getInstance().getCountP2WPKH(), 4).longValue();
// Log.d("SendActivity", "segwit:" + neededAmount);
} else if (Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), address).isP2SHAddress()) {
neededAmount += FeeUtil.getInstance().estimatedFeeSegwit(0, UTXOFactory.getInstance().getCountP2SH_P2WPKH(), 0, 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();
// get all UTXO
List<UTXO> utxos = new ArrayList<>();
if (preselectedUTXOs != null && preselectedUTXOs.size() > 0) {
// sort in descending order by value
for (UTXOCoin utxoCoin : preselectedUTXOs) {
UTXO u = new UTXO();
List<MyTransactionOutPoint> outs = new ArrayList<MyTransactionOutPoint>();
outs.add(utxoCoin.getOutPoint());
u.setOutpoints(outs);
utxos.add(u);
}
} else {
utxos = SpendUtil.getUTXOS(SendActivity.this, address, neededAmount, account);
}
List<UTXO> utxosP2WPKH = new ArrayList<UTXO>(UTXOFactory.getInstance().getAllP2WPKH().values());
List<UTXO> utxosP2SH_P2WPKH = new ArrayList<UTXO>(UTXOFactory.getInstance().getAllP2SH_P2WPKH().values());
List<UTXO> utxosP2PKH = new ArrayList<UTXO>(UTXOFactory.getInstance().getAllP2PKH().values());
if ((preselectedUTXOs == null || preselectedUTXOs.size() == 0) && account == WhirlpoolMeta.getInstance(SendActivity.this).getWhirlpoolPostmix()) {
utxos = new ArrayList<UTXO>(UTXOFactory.getInstance().getAllPostMix().values());
utxosP2WPKH = new ArrayList<UTXO>(UTXOFactory.getInstance().getAllPostMix().values());
utxosP2PKH.clear();
utxosP2SH_P2WPKH.clear();
}
selectedUTXO = new ArrayList<UTXO>();
long totalValueSelected = 0L;
long change = 0L;
BigInteger fee = null;
boolean canDoBoltzmann = true;
// insufficient funds
if (amount > balance) {
Toast.makeText(SendActivity.this, R.string.insufficient_funds, Toast.LENGTH_SHORT).show();
}
if (preselectedUTXOs != null) {
canDoBoltzmann = false;
SPEND_TYPE = SPEND_SIMPLE;
} else // entire balance (can only be simple spend)
if (amount == balance) {
// make sure we are using simple spend
SPEND_TYPE = SPEND_SIMPLE;
canDoBoltzmann = false;
// 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;
}
ricochetJsonObj = RicochetMeta.getInstance(SendActivity.this).script(amount, FeeUtil.getInstance().getSuggestedFee().getDefaultPerKB().longValue(), address, 4, strPCode, samouraiFeeViaBIP47, ricochetStaggeredDelivery.isChecked());
if (ricochetJsonObj != null) {
try {
long totalAmount = ricochetJsonObj.getLong("total_spend");
if (totalAmount > balance) {
Toast.makeText(SendActivity.this, R.string.insufficient_funds, Toast.LENGTH_SHORT).show();
ricochetHopsSwitch.setChecked(false);
return false;
}
long hop0Fee = ricochetJsonObj.getJSONArray("hops").getJSONObject(0).getLong("fee");
long perHopFee = ricochetJsonObj.getJSONArray("hops").getJSONObject(0).getLong("fee_per_hop");
long ricochetFee = hop0Fee + (RicochetMeta.defaultNbHops * perHopFee);
if (selectedCahootsType == SelectCahootsType.type.NONE) {
tvTotalFee.setText(Coin.valueOf(ricochetFee).toPlainString().concat(" BTC"));
} else {
tvTotalFee.setText("__");
}
ricochetMessage = getText(R.string.ricochet_spend1) + " " + address + " " + getText(R.string.ricochet_spend2) + " " + Coin.valueOf(totalAmount).toPlainString() + " " + getText(R.string.ricochet_spend3);
btnSend.setText("send ".concat(String.format(Locale.ENGLISH, "%.8f", getBtcValue((double) totalAmount)).concat(" BTC")));
return true;
} catch (JSONException je) {
return false;
}
}
return true;
} else if (SPEND_TYPE == SPEND_BOLTZMANN) {
Log.d("SendActivity", "needed amount:" + neededAmount);
List<UTXO> _utxos1 = null;
List<UTXO> _utxos2 = null;
long valueP2WPKH = UTXOFactory.getInstance().getTotalP2WPKH();
long valueP2SH_P2WPKH = UTXOFactory.getInstance().getTotalP2SH_P2WPKH();
long valueP2PKH = UTXOFactory.getInstance().getTotalP2PKH();
if (account == WhirlpoolMeta.getInstance(SendActivity.this).getWhirlpoolPostmix()) {
valueP2WPKH = UTXOFactory.getInstance().getTotalPostMix();
valueP2SH_P2WPKH = 0L;
valueP2PKH = 0L;
utxosP2SH_P2WPKH.clear();
utxosP2PKH.clear();
}
Log.d("SendActivity", "value P2WPKH:" + valueP2WPKH);
Log.d("SendActivity", "value P2SH_P2WPKH:" + valueP2SH_P2WPKH);
Log.d("SendActivity", "value P2PKH:" + valueP2PKH);
boolean selectedP2WPKH = false;
boolean selectedP2SH_P2WPKH = false;
boolean selectedP2PKH = false;
if ((valueP2WPKH > (neededAmount * 2)) && account == WhirlpoolMeta.getInstance(SendActivity.this).getWhirlpoolPostmix()) {
Log.d("SendActivity", "set 1 P2WPKH 2x");
_utxos1 = utxosP2WPKH;
selectedP2WPKH = true;
} else if ((valueP2WPKH > (neededAmount * 2)) && FormatsUtil.getInstance().isValidBech32(address)) {
Log.d("SendActivity", "set 1 P2WPKH 2x");
_utxos1 = utxosP2WPKH;
selectedP2WPKH = true;
} else if (!FormatsUtil.getInstance().isValidBech32(address) && (valueP2SH_P2WPKH > (neededAmount * 2)) && Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), address).isP2SHAddress()) {
Log.d("SendActivity", "set 1 P2SH_P2WPKH 2x");
_utxos1 = utxosP2SH_P2WPKH;
selectedP2SH_P2WPKH = true;
} else if (!FormatsUtil.getInstance().isValidBech32(address) && (valueP2PKH > (neededAmount * 2)) && !Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), address).isP2SHAddress()) {
Log.d("SendActivity", "set 1 P2PKH 2x");
_utxos1 = utxosP2PKH;
selectedP2PKH = true;
} else if (valueP2WPKH > (neededAmount * 2)) {
Log.d("SendActivity", "set 1 P2WPKH 2x");
_utxos1 = utxosP2WPKH;
selectedP2WPKH = true;
} else if (valueP2SH_P2WPKH > (neededAmount * 2)) {
Log.d("SendActivity", "set 1 P2SH_P2WPKH 2x");
_utxos1 = utxosP2SH_P2WPKH;
selectedP2SH_P2WPKH = true;
} else if (valueP2PKH > (neededAmount * 2)) {
Log.d("SendActivity", "set 1 P2PKH 2x");
_utxos1 = utxosP2PKH;
selectedP2PKH = true;
} else {
;
}
if (_utxos1 == null || _utxos1.size() == 0) {
if (valueP2SH_P2WPKH > neededAmount) {
Log.d("SendActivity", "set 1 P2SH_P2WPKH");
_utxos1 = utxosP2SH_P2WPKH;
selectedP2SH_P2WPKH = true;
} else if (valueP2WPKH > neededAmount) {
Log.d("SendActivity", "set 1 P2WPKH");
_utxos1 = utxosP2WPKH;
selectedP2WPKH = true;
} else if (valueP2PKH > neededAmount) {
Log.d("SendActivity", "set 1 P2PKH");
_utxos1 = utxosP2PKH;
selectedP2PKH = true;
} else {
;
}
}
if (_utxos1 != null && _utxos1.size() > 0) {
if (!selectedP2SH_P2WPKH && valueP2SH_P2WPKH > neededAmount) {
Log.d("SendActivity", "set 2 P2SH_P2WPKH");
_utxos2 = utxosP2SH_P2WPKH;
selectedP2SH_P2WPKH = true;
}
if (!selectedP2SH_P2WPKH && !selectedP2WPKH && valueP2WPKH > neededAmount) {
Log.d("SendActivity", "set 2 P2WPKH");
_utxos2 = utxosP2WPKH;
selectedP2WPKH = true;
}
if (!selectedP2SH_P2WPKH && !selectedP2WPKH && !selectedP2PKH && valueP2PKH > neededAmount) {
Log.d("SendActivity", "set 2 P2PKH");
_utxos2 = utxosP2PKH;
selectedP2PKH = true;
} else {
;
}
}
if ((_utxos1 == null || _utxos1.size() == 0) && (_utxos2 == null || _utxos2.size() == 0)) {
// can't do boltzmann, revert to SPEND_SIMPLE
canDoBoltzmann = false;
SPEND_TYPE = SPEND_SIMPLE;
} else {
Log.d("SendActivity", "boltzmann spend");
Collections.shuffle(_utxos1);
if (_utxos2 != null && _utxos2.size() > 0) {
Collections.shuffle(_utxos2);
}
// boltzmann spend (STONEWALL)
pair = SendFactory.getInstance(SendActivity.this).boltzmann(_utxos1, _utxos2, BigInteger.valueOf(amount), address, account);
if (pair == null) {
// can't do boltzmann, revert to SPEND_SIMPLE
canDoBoltzmann = false;
restoreChangeIndexes();
SPEND_TYPE = SPEND_SIMPLE;
} else {
canDoBoltzmann = true;
}
}
} else {
;
}
if (SPEND_TYPE == SPEND_SIMPLE && amount == balance && preselectedUTXOs == null) {
// do nothing, utxo selection handles above
;
} 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) {
Triple<Integer, Integer, Integer> outpointTypes = FeeUtil.getInstance().getOutpointCount(new Vector(u.getOutpoints()));
if (u.getValue() >= (amount + SamouraiWallet.bDust.longValue() + FeeUtil.getInstance().estimatedFeeSegwit(outpointTypes.getLeft(), outpointTypes.getMiddle(), outpointTypes.getRight(), 2).longValue())) {
selectedUTXO.add(u);
totalValueSelected += u.getValue();
Log.d("SendActivity", "spend type:" + SPEND_TYPE);
Log.d("SendActivity", "single output");
Log.d("SendActivity", "amount:" + amount);
Log.d("SendActivity", "value selected:" + u.getValue());
Log.d("SendActivity", "total value selected:" + totalValueSelected);
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 p2sh_p2wpkh = 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()));
Triple<Integer, Integer, Integer> outpointTypes = FeeUtil.getInstance().getOutpointCount(new Vector<MyTransactionOutPoint>(u.getOutpoints()));
p2pkh += outpointTypes.getLeft();
p2sh_p2wpkh += outpointTypes.getMiddle();
p2wpkh += outpointTypes.getRight();
if (totalValueSelected >= (amount + SamouraiWallet.bDust.longValue() + FeeUtil.getInstance().estimatedFeeSegwit(p2pkh, p2sh_p2wpkh, p2wpkh, 2).longValue())) {
Log.d("SendActivity", "spend type:" + SPEND_TYPE);
Log.d("SendActivity", "multiple outputs");
Log.d("SendActivity", "amount:" + amount);
Log.d("SendActivity", "total value selected:" + totalValueSelected);
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());
if (Bech32Util.getInstance().isP2WPKHScript(Hex.toHexString(output.getScriptBytes()))) {
receivers.put(Bech32Util.getInstance().getAddressFromScript(script), BigInteger.valueOf(output.getValue().longValue()));
} else {
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 false;
}
}
fee = BigInteger.valueOf(inputAmount - outputAmount);
} else {
Toast.makeText(SendActivity.this, R.string.cannot_select_utxo, Toast.LENGTH_SHORT).show();
return false;
}
// do spend here
if (selectedUTXO.size() > 0) {
// estimate fee for simple spend, already done if boltzmann
if (SPEND_TYPE == SPEND_SIMPLE) {
List<MyTransactionOutPoint> outpoints = new ArrayList<MyTransactionOutPoint>();
for (UTXO utxo : selectedUTXO) {
outpoints.addAll(utxo.getOutpoints());
}
Triple<Integer, Integer, Integer> outpointTypes = FeeUtil.getInstance().getOutpointCount(new Vector(outpoints));
if (amount == balance) {
fee = FeeUtil.getInstance().estimatedFeeSegwit(outpointTypes.getLeft(), outpointTypes.getMiddle(), outpointTypes.getRight(), 1);
amount -= fee.longValue();
receivers.clear();
receivers.put(address, BigInteger.valueOf(amount));
//
// fee sanity check
//
restoreChangeIndexes();
Transaction tx = SendFactory.getInstance(SendActivity.this).makeTransaction(account, outpoints, receivers);
tx = SendFactory.getInstance(SendActivity.this).signTransaction(tx, account);
byte[] serialized = tx.bitcoinSerialize();
Log.d("SendActivity", "size:" + serialized.length);
Log.d("SendActivity", "vsize:" + tx.getVirtualTransactionSize());
Log.d("SendActivity", "fee:" + fee.longValue());
if ((tx.hasWitness() && (fee.longValue() < tx.getVirtualTransactionSize())) || (!tx.hasWitness() && (fee.longValue() < serialized.length))) {
Toast.makeText(SendActivity.this, R.string.insufficient_fee, Toast.LENGTH_SHORT).show();
return false;
}
//
//
//
} else {
fee = FeeUtil.getInstance().estimatedFeeSegwit(outpointTypes.getLeft(), outpointTypes.getMiddle(), 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());
if (change > 0L && change < SamouraiWallet.bDust.longValue() && SPEND_TYPE == SPEND_SIMPLE) {
AlertDialog.Builder dlg = new AlertDialog.Builder(SendActivity.this).setTitle(R.string.app_name).setMessage(R.string.change_is_dust).setCancelable(false).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
}
});
if (!isFinishing()) {
dlg.show();
}
return false;
}
_change = change;
final BigInteger _fee = fee;
String dest = null;
if (strPCode != null && strPCode.length() > 0) {
dest = BIP47Meta.getInstance().getDisplayLabel(strPCode);
} else {
dest = address;
}
strCannotDoBoltzmann = "";
if (SendAddressUtil.getInstance().get(address) == 1) {
strPrivacyWarning = getString(R.string.send_privacy_warning) + "\n\n";
} else {
strPrivacyWarning = "";
}
if (!canDoBoltzmann) {
restoreChangeIndexes();
sendTransactionDetailsView.getStoneWallSwitch().setOnClickListener(null);
sendTransactionDetailsView.getStoneWallSwitch().setEnabled(false);
sendTransactionDetailsView.enableStonewall(false);
sendTransactionDetailsView.setEntropyBarStoneWallX1(null);
sendTransactionDetailsView.getStoneWallSwitch().setOnCheckedChangeListener(onCheckedChangeListener);
if (account == WhirlpoolMeta.getInstance(SendActivity.this).getWhirlpoolPostmix()) {
strCannotDoBoltzmann = getString(R.string.boltzmann_cannot) + "\n\n";
}
}
message = strCannotDoBoltzmann + strPrivacyWarning + "Send " + Coin.valueOf(amount).toPlainString() + " to " + dest + " (fee:" + Coin.valueOf(_fee.longValue()).toPlainString() + ")?\n";
if (selectedCahootsType == SelectCahootsType.type.NONE) {
tvTotalFee.setText(Coin.valueOf(_fee.longValue()).toPlainString().concat(" BTC"));
} else {
tvTotalFee.setText("__");
}
double value = Double.parseDouble(String.valueOf(_fee.add(BigInteger.valueOf(amount))));
btnSend.setText("send ".concat(String.format(Locale.ENGLISH, "%.8f", getBtcValue(value))).concat(" BTC"));
switch(selectedCahootsType) {
case STONEWALLX2_MANUAL:
{
sendTransactionDetailsView.showStonewallX2Layout("Manual", 1000);
btnSend.setBackgroundResource(R.drawable.button_blue);
btnSend.setText(getString(R.string.begin_stonewallx2));
break;
}
case STONEWALLX2_SAMOURAI:
{
sendTransactionDetailsView.showStonewallX2Layout("Samourai Wallet", 1000);
break;
}
case STOWAWAY:
{
// mixingPartner.setText("Samourai Wallet");
sendTransactionDetailsView.showStowawayLayout(address, null, 1000);
btnSend.setBackgroundResource(R.drawable.button_blue);
btnSend.setText(getString(R.string.begin_stowaway));
break;
}
case NONE:
{
sendTransactionDetailsView.showStonewallx1Layout(null);
// for ricochet entropy will be 0 always
if (SPEND_TYPE == SPEND_RICOCHET) {
break;
}
if (receivers.size() <= 1) {
sendTransactionDetailsView.setEntropyBarStoneWallX1ZeroBits();
break;
}
if (receivers.size() > 8) {
sendTransactionDetailsView.setEntropyBarStoneWallX1(null);
break;
}
CalculateEntropy(selectedUTXO, receivers).subscribeOn(Schedulers.computation()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<TxProcessorResult>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(TxProcessorResult entropyResult) {
sendTransactionDetailsView.setEntropyBarStoneWallX1(entropyResult);
}
@Override
public void onError(Throwable e) {
sendTransactionDetailsView.setEntropyBarStoneWallX1(null);
e.printStackTrace();
}
@Override
public void onComplete() {
}
});
break;
}
default:
{
btnSend.setBackgroundResource(R.drawable.button_green);
btnSend.setText("send ".concat(String.format(Locale.ENGLISH, "%.8f", getBtcValue((double) amount))).concat(" BTC"));
}
}
return true;
}
return false;
}
Aggregations