use of com.samourai.wallet.send.UTXO in project samourai-wallet-android by Samourai-Wallet.
the class UTXODetailsActivity method onCreate.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_utxodetails);
setSupportActionBar(findViewById(R.id.toolbar_utxo_activity));
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
addressTextView = findViewById(R.id.utxo_details_address);
amountTextView = findViewById(R.id.utxo_details_amount);
statusTextView = findViewById(R.id.utxo_details_spendable_status);
hashTextView = findViewById(R.id.utxo_details_hash);
addNote = findViewById(R.id.add_note_button);
notesTextView = findViewById(R.id.utxo_details_note);
deleteButton = findViewById(R.id.delete_note);
paynymLayout = findViewById(R.id.utxo_details_paynym_container);
paynymLayout.setVisibility(View.GONE);
df.setMinimumIntegerDigits(1);
df.setMinimumFractionDigits(8);
df.setMaximumFractionDigits(8);
if (getIntent().getExtras() != null && getIntent().getExtras().containsKey("hashIdx")) {
hashIdx = getIntent().getExtras().getString("hashIdx");
} else {
finish();
}
if (getIntent().getExtras() != null && getIntent().getExtras().containsKey("_account")) {
account = getIntent().getExtras().getInt("_account");
} else {
finish();
}
List<UTXO> utxos = new ArrayList<>();
if (account == WhirlpoolMeta.getInstance(getApplicationContext()).getWhirlpoolPostmix()) {
utxos.addAll(APIFactory.getInstance(getApplicationContext()).getUtxosPostMix(false));
} else {
utxos.addAll(APIFactory.getInstance(getApplicationContext()).getUtxos(false));
}
utxos.addAll(APIFactory.getInstance(getApplicationContext()).getUtxos(false));
for (UTXO utxo : utxos) {
for (MyTransactionOutPoint outpoint : utxo.getOutpoints()) {
if (outpoint.getTxHash() != null) {
String hashWithIdx = outpoint.getTxHash().toString().concat("-").concat(String.valueOf(outpoint.getTxOutputN()));
if (hashWithIdx.equals(hashIdx)) {
idx = outpoint.getTxOutputN();
amount = outpoint.getValue().longValue();
hash = outpoint.getTxHash().toString();
addr = outpoint.getAddress();
utxoCoin = new UTXOCoin(outpoint, utxo);
if (BlockedUTXO.getInstance().contains(outpoint.getTxHash().toString(), outpoint.getTxOutputN())) {
utxoCoin.doNotSpend = true;
}
setUTXOState();
}
}
}
}
deleteButton.setOnClickListener(view -> {
if (UTXOUtil.getInstance().getNote(hash) != null) {
UTXOUtil.getInstance().removeNote(hash);
}
setNoteState();
saveWalletState();
});
addNote.setOnClickListener(view -> {
View dialogView = getLayoutInflater().inflate(R.layout.bottom_sheet_note, null);
BottomSheetDialog dialog = new BottomSheetDialog(this, R.style.bottom_sheet_note);
dialog.setContentView(dialogView);
dialog.show();
Button submitButton = dialog.findViewById(R.id.submit_note);
if (UTXOUtil.getInstance().getNote(hash) != null) {
((EditText) dialog.findViewById(R.id.utxo_details_note)).setText(UTXOUtil.getInstance().getNote(hash));
submitButton.setText("Save");
} else {
submitButton.setText("Add");
}
dialog.findViewById(R.id.submit_note).setOnClickListener((View view1) -> {
dialog.dismiss();
addNote(((EditText) dialog.findViewById(R.id.utxo_details_note)).getText().toString());
});
});
setNoteState();
addressTextView.setOnClickListener((event) -> new AlertDialog.Builder(UTXODetailsActivity.this).setTitle(R.string.app_name).setMessage(R.string.receive_address_to_clipboard).setCancelable(false).setPositiveButton(R.string.yes, (dialog, whichButton) -> {
android.content.ClipboardManager clipboard = (android.content.ClipboardManager) UTXODetailsActivity.this.getSystemService(android.content.Context.CLIPBOARD_SERVICE);
android.content.ClipData clip = null;
clip = android.content.ClipData.newPlainText("address", addr);
clipboard.setPrimaryClip(clip);
Toast.makeText(UTXODetailsActivity.this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
}).setNegativeButton(R.string.no, (dialog, whichButton) -> {
}).show());
hashTextView.setOnClickListener(view -> {
new android.app.AlertDialog.Builder(this).setTitle(R.string.app_name).setMessage(R.string.txid_to_clipboard).setCancelable(false).setPositiveButton(R.string.yes, (dialog, whichButton) -> {
android.content.ClipboardManager clipboard = (android.content.ClipboardManager) UTXODetailsActivity.this.getSystemService(android.content.Context.CLIPBOARD_SERVICE);
android.content.ClipData clip;
clip = android.content.ClipData.newPlainText("tx id", hash);
if (clipboard != null) {
clipboard.setPrimaryClip(clip);
}
Toast.makeText(UTXODetailsActivity.this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
}).setNegativeButton(R.string.no, (dialog, whichButton) -> {
}).show();
});
}
use of com.samourai.wallet.send.UTXO 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.send.UTXO in project samourai-wallet-android by Samourai-Wallet.
the class RBFTask method doInBackground.
@Override
protected String doInBackground(final String... params) {
Looper.prepare();
Log.d("RBF", "hash:" + params[0]);
rbf = RBFUtil.getInstance().get(params[0]);
Log.d("RBF", "rbf:" + rbf.toJSON().toString());
final Transaction tx = new Transaction(SamouraiWallet.getInstance().getCurrentNetworkParams(), Hex.decode(rbf.getSerializedTx()));
Log.d("RBF", "tx serialized:" + rbf.getSerializedTx());
Log.d("RBF", "tx inputs:" + tx.getInputs().size());
Log.d("RBF", "tx outputs:" + tx.getOutputs().size());
JSONObject txObj = APIFactory.getInstance(activity).getTxInfo(params[0]);
if (tx != null && txObj.has("inputs") && txObj.has("outputs")) {
try {
JSONArray inputs = txObj.getJSONArray("inputs");
JSONArray outputs = txObj.getJSONArray("outputs");
int p2pkh = 0;
int p2sh_p2wpkh = 0;
int p2wpkh = 0;
for (int i = 0; i < inputs.length(); i++) {
if (inputs.getJSONObject(i).has("outpoint") && inputs.getJSONObject(i).getJSONObject("outpoint").has("scriptpubkey")) {
String scriptpubkey = inputs.getJSONObject(i).getJSONObject("outpoint").getString("scriptpubkey");
Script script = new Script(Hex.decode(scriptpubkey));
String address = null;
if (Bech32Util.getInstance().isBech32Script(scriptpubkey)) {
try {
address = Bech32Util.getInstance().getAddressFromScript(scriptpubkey);
} catch (Exception e) {
;
}
} else {
address = script.getToAddress(SamouraiWallet.getInstance().getCurrentNetworkParams()).toString();
}
if (FormatsUtil.getInstance().isValidBech32(address)) {
p2wpkh++;
} else if (Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), address).isP2SHAddress()) {
p2sh_p2wpkh++;
} else {
p2pkh++;
}
}
}
SuggestedFee suggestedFee = FeeUtil.getInstance().getSuggestedFee();
FeeUtil.getInstance().setSuggestedFee(FeeUtil.getInstance().getHighFee());
BigInteger estimatedFee = FeeUtil.getInstance().estimatedFeeSegwit(p2pkh, p2sh_p2wpkh, p2wpkh, outputs.length());
long total_inputs = 0L;
long total_outputs = 0L;
long fee = 0L;
long total_change = 0L;
List<String> selfAddresses = new ArrayList<String>();
for (int i = 0; i < inputs.length(); i++) {
JSONObject obj = inputs.getJSONObject(i);
if (obj.has("outpoint")) {
JSONObject objPrev = obj.getJSONObject("outpoint");
if (objPrev.has("value")) {
total_inputs += objPrev.getLong("value");
String key = objPrev.getString("txid") + ":" + objPrev.getLong("vout");
input_values.put(key, objPrev.getLong("value"));
}
}
}
for (int i = 0; i < outputs.length(); i++) {
JSONObject obj = outputs.getJSONObject(i);
if (obj.has("value")) {
total_outputs += obj.getLong("value");
String _addr = null;
if (obj.has("address")) {
_addr = obj.getString("address");
}
selfAddresses.add(_addr);
if (_addr != null && rbf.getChangeAddrs().contains(_addr.toString())) {
total_change += obj.getLong("value");
}
}
}
boolean feeWarning = false;
fee = total_inputs - total_outputs;
if (fee > estimatedFee.longValue()) {
feeWarning = true;
}
long remainingFee = (estimatedFee.longValue() > fee) ? estimatedFee.longValue() - fee : 0L;
Log.d("RBF", "total inputs:" + total_inputs);
Log.d("RBF", "total outputs:" + total_outputs);
Log.d("RBF", "total change:" + total_change);
Log.d("RBF", "fee:" + fee);
Log.d("RBF", "estimated fee:" + estimatedFee.longValue());
Log.d("RBF", "fee warning:" + feeWarning);
Log.d("RBF", "remaining fee:" + remainingFee);
List<TransactionOutput> txOutputs = new ArrayList<TransactionOutput>();
txOutputs.addAll(tx.getOutputs());
long remainder = remainingFee;
if (total_change > remainder) {
for (TransactionOutput output : txOutputs) {
Script script = output.getScriptPubKey();
String scriptPubKey = Hex.toHexString(script.getProgram());
Address _p2sh = output.getAddressFromP2SH(SamouraiWallet.getInstance().getCurrentNetworkParams());
Address _p2pkh = output.getAddressFromP2PKHScript(SamouraiWallet.getInstance().getCurrentNetworkParams());
try {
if ((Bech32Util.getInstance().isBech32Script(scriptPubKey) && rbf.getChangeAddrs().contains(Bech32Util.getInstance().getAddressFromScript(scriptPubKey))) || (_p2sh != null && rbf.getChangeAddrs().contains(_p2sh.toString())) || (_p2pkh != null && rbf.getChangeAddrs().contains(_p2pkh.toString()))) {
if (output.getValue().longValue() >= (remainder + SamouraiWallet.bDust.longValue())) {
output.setValue(Coin.valueOf(output.getValue().longValue() - remainder));
remainder = 0L;
break;
} else {
remainder -= output.getValue().longValue();
// output will be discarded later
output.setValue(Coin.valueOf(0L));
}
}
} catch (Exception e) {
;
}
}
}
//
// original inputs are not modified
//
List<MyTransactionInput> _inputs = new ArrayList<MyTransactionInput>();
List<TransactionInput> txInputs = tx.getInputs();
for (TransactionInput input : txInputs) {
MyTransactionInput _input = new MyTransactionInput(SamouraiWallet.getInstance().getCurrentNetworkParams(), null, new byte[0], input.getOutpoint(), input.getOutpoint().getHash().toString(), (int) input.getOutpoint().getIndex());
_input.setSequenceNumber(SamouraiWallet.RBF_SEQUENCE_VAL.longValue());
_inputs.add(_input);
Log.d("RBF", "add outpoint:" + _input.getOutpoint().toString());
}
Triple<Integer, Integer, Integer> outpointTypes = null;
if (remainder > 0L) {
List<UTXO> selectedUTXO = new ArrayList<UTXO>();
long selectedAmount = 0L;
int selected = 0;
long _remainingFee = remainder;
Collections.sort(utxos, new UTXO.UTXOComparator());
for (UTXO _utxo : utxos) {
Log.d("RBF", "utxo value:" + _utxo.getValue());
//
// do not select utxo that are change outputs in current rbf tx
//
boolean isChange = false;
boolean isSelf = false;
for (MyTransactionOutPoint outpoint : _utxo.getOutpoints()) {
if (rbf.containsChangeAddr(outpoint.getAddress())) {
Log.d("RBF", "is change:" + outpoint.getAddress());
Log.d("RBF", "is change:" + outpoint.getValue().longValue());
isChange = true;
break;
}
if (selfAddresses.contains(outpoint.getAddress())) {
Log.d("RBF", "is self:" + outpoint.getAddress());
Log.d("RBF", "is self:" + outpoint.getValue().longValue());
isSelf = true;
break;
}
}
if (isChange || isSelf) {
continue;
}
selectedUTXO.add(_utxo);
selected += _utxo.getOutpoints().size();
Log.d("RBF", "selected utxo:" + selected);
selectedAmount += _utxo.getValue();
Log.d("RBF", "selected utxo value:" + _utxo.getValue());
outpointTypes = FeeUtil.getInstance().getOutpointCount(new Vector(_utxo.getOutpoints()));
p2pkh += outpointTypes.getLeft();
p2sh_p2wpkh += outpointTypes.getMiddle();
p2wpkh += outpointTypes.getRight();
_remainingFee = FeeUtil.getInstance().estimatedFeeSegwit(p2pkh, p2sh_p2wpkh, p2wpkh, outputs.length() == 1 ? 2 : outputs.length()).longValue();
Log.d("RBF", "_remaining fee:" + _remainingFee);
if (selectedAmount >= (_remainingFee + SamouraiWallet.bDust.longValue())) {
break;
}
}
long extraChange = 0L;
if (selectedAmount < (_remainingFee + SamouraiWallet.bDust.longValue())) {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, R.string.insufficient_funds, Toast.LENGTH_SHORT).show();
}
});
return "KO";
} else {
extraChange = selectedAmount - _remainingFee;
Log.d("RBF", "extra change:" + extraChange);
}
boolean addedChangeOutput = false;
// parent tx didn't have change output
if (outputs.length() == 1 && extraChange > 0L) {
try {
boolean isSegwitChange = (FormatsUtil.getInstance().isValidBech32(outputs.getJSONObject(0).getString("address")) || Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), outputs.getJSONObject(0).getString("address")).isP2SHAddress()) || PrefsUtil.getInstance(activity).getValue(PrefsUtil.USE_LIKE_TYPED_CHANGE, true) == false;
String change_address = null;
if (isSegwitChange) {
int changeIdx = BIP49Util.getInstance(activity).getWallet().getAccount(0).getChange().getAddrIdx();
change_address = BIP49Util.getInstance(activity).getAddressAt(AddressFactory.CHANGE_CHAIN, changeIdx).getAddressAsString();
} else {
int changeIdx = HD_WalletFactory.getInstance(activity).get().getAccount(0).getChange().getAddrIdx();
change_address = HD_WalletFactory.getInstance(activity).get().getAccount(0).getChange().getAddressAt(changeIdx).getAddressString();
}
Script toOutputScript = ScriptBuilder.createOutputScript(Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), change_address));
TransactionOutput output = new TransactionOutput(SamouraiWallet.getInstance().getCurrentNetworkParams(), null, Coin.valueOf(extraChange), toOutputScript.getProgram());
txOutputs.add(output);
addedChangeOutput = true;
} catch (MnemonicException.MnemonicLengthException | IOException e) {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, e.getMessage(), Toast.LENGTH_SHORT).show();
Toast.makeText(activity, R.string.cannot_create_change_output, Toast.LENGTH_SHORT).show();
}
});
return "KO";
}
} else // parent tx had change output
{
for (TransactionOutput output : txOutputs) {
Script script = output.getScriptPubKey();
String scriptPubKey = Hex.toHexString(script.getProgram());
String _addr = null;
if (Bech32Util.getInstance().isBech32Script(scriptPubKey)) {
try {
_addr = Bech32Util.getInstance().getAddressFromScript(scriptPubKey);
} catch (Exception e) {
;
}
}
if (_addr == null) {
Address _address = output.getAddressFromP2PKHScript(SamouraiWallet.getInstance().getCurrentNetworkParams());
if (_address == null) {
_address = output.getAddressFromP2SH(SamouraiWallet.getInstance().getCurrentNetworkParams());
}
_addr = _address.toString();
}
Log.d("RBF", "checking for change:" + _addr);
if (rbf.containsChangeAddr(_addr)) {
Log.d("RBF", "before extra:" + output.getValue().longValue());
output.setValue(Coin.valueOf(extraChange + output.getValue().longValue()));
Log.d("RBF", "after extra:" + output.getValue().longValue());
addedChangeOutput = true;
break;
}
}
}
// sanity check
if (extraChange > 0L && !addedChangeOutput) {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, R.string.cannot_create_change_output, Toast.LENGTH_SHORT).show();
}
});
return "KO";
}
//
// update keyBag w/ any new paths
//
final HashMap<String, String> keyBag = rbf.getKeyBag();
for (UTXO _utxo : selectedUTXO) {
for (MyTransactionOutPoint outpoint : _utxo.getOutpoints()) {
MyTransactionInput _input = new MyTransactionInput(SamouraiWallet.getInstance().getCurrentNetworkParams(), null, new byte[0], outpoint, outpoint.getTxHash().toString(), outpoint.getTxOutputN());
_input.setSequenceNumber(SamouraiWallet.RBF_SEQUENCE_VAL.longValue());
_inputs.add(_input);
Log.d("RBF", "add selected outpoint:" + _input.getOutpoint().toString());
String path = APIFactory.getInstance(activity).getUnspentPaths().get(outpoint.getAddress());
if (path != null) {
if (FormatsUtil.getInstance().isValidBech32(outpoint.getAddress())) {
rbf.addKey(outpoint.toString(), path + "/84");
} else if (Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), outpoint.getAddress()) != null && Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), outpoint.getAddress()).isP2SHAddress()) {
rbf.addKey(outpoint.toString(), path + "/49");
} else {
rbf.addKey(outpoint.toString(), path);
}
Log.d("RBF", "outpoint address:" + outpoint.getAddress());
} else {
String pcode = BIP47Meta.getInstance().getPCode4Addr(outpoint.getAddress());
int idx = BIP47Meta.getInstance().getIdx4Addr(outpoint.getAddress());
rbf.addKey(outpoint.toString(), pcode + "/" + idx);
}
}
}
rbf.setKeyBag(keyBag);
}
//
// BIP69 sort of outputs/inputs
//
final Transaction _tx = new Transaction(SamouraiWallet.getInstance().getCurrentNetworkParams());
List<TransactionOutput> _txOutputs = new ArrayList<TransactionOutput>();
_txOutputs.addAll(txOutputs);
Collections.sort(_txOutputs, new BIP69OutputComparator());
for (TransactionOutput to : _txOutputs) {
// zero value outputs discarded here
if (to.getValue().longValue() > 0L) {
_tx.addOutput(to);
}
}
List<MyTransactionInput> __inputs = new ArrayList<MyTransactionInput>();
__inputs.addAll(_inputs);
Collections.sort(__inputs, new SendFactory.BIP69InputComparator());
for (TransactionInput input : __inputs) {
_tx.addInput(input);
}
FeeUtil.getInstance().setSuggestedFee(suggestedFee);
String message = "";
if (feeWarning) {
message += activity.getString(R.string.fee_bump_not_necessary);
message += "\n\n";
}
message += activity.getString(R.string.bump_fee) + " " + Coin.valueOf(remainingFee).toPlainString() + " BTC";
AlertDialog.Builder dlg = new AlertDialog.Builder(activity).setTitle(R.string.app_name).setMessage(message).setCancelable(false).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Transaction __tx = signTx(_tx);
final String hexTx = new String(Hex.encode(__tx.bitcoinSerialize()));
Log.d("RBF", "hex tx:" + hexTx);
final String strTxHash = __tx.getHashAsString();
Log.d("RBF", "tx hash:" + strTxHash);
if (__tx != null) {
boolean isOK = false;
try {
isOK = PushTx.getInstance(activity).pushTx(hexTx);
if (isOK) {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, R.string.rbf_spent, Toast.LENGTH_SHORT).show();
// includes updated 'keyBag'
RBFSpend _rbf = rbf;
_rbf.setSerializedTx(hexTx);
_rbf.setHash(strTxHash);
_rbf.setPrevHash(params[0]);
RBFUtil.getInstance().add(_rbf);
Intent _intent = new Intent(activity, MainActivity2.class);
_intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
activity.startActivity(_intent);
}
});
} else {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, R.string.tx_failed, Toast.LENGTH_SHORT).show();
}
});
}
} catch (final DecoderException de) {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, "pushTx:" + de.getMessage(), Toast.LENGTH_SHORT).show();
}
});
} finally {
;
}
}
}
}).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
}
});
if (!activity.isFinishing()) {
dlg.show();
}
} catch (final JSONException je) {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, "rbf:" + je.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
} else {
Toast.makeText(activity, R.string.cpfp_cannot_retrieve_tx, Toast.LENGTH_SHORT).show();
}
Looper.loop();
return "OK";
}
use of com.samourai.wallet.send.UTXO in project samourai-wallet-android by Samourai-Wallet.
the class CPFPTask method getUTXO.
private UTXO getUTXO(String address) {
UTXO ret = null;
int idx = -1;
for (int i = 0; i < utxos.size(); i++) {
UTXO utxo = utxos.get(i);
Log.d("activity", "utxo address:" + utxo.getOutpoints().get(0).getAddress());
if (utxo.getOutpoints().get(0).getAddress().equals(address)) {
ret = utxo;
idx = i;
break;
}
}
if (ret != null) {
utxos.remove(idx);
return ret;
}
return null;
}
use of com.samourai.wallet.send.UTXO in project samourai-wallet-android by Samourai-Wallet.
the class CPFPTask method doInBackground.
@Override
protected String doInBackground(String... params) {
Looper.prepare();
Log.d("activity", "hash:" + params[0]);
JSONObject txObj = APIFactory.getInstance(activity).getTxInfo(params[0]);
if (txObj.has("inputs") && txObj.has("outputs")) {
final SuggestedFee suggestedFee = FeeUtil.getInstance().getSuggestedFee();
try {
JSONArray inputs = txObj.getJSONArray("inputs");
JSONArray outputs = txObj.getJSONArray("outputs");
int p2pkh = 0;
int p2sh_p2wpkh = 0;
int p2wpkh = 0;
for (int i = 0; i < inputs.length(); i++) {
if (inputs.getJSONObject(i).has("outpoint") && inputs.getJSONObject(i).getJSONObject("outpoint").has("scriptpubkey")) {
String scriptpubkey = inputs.getJSONObject(i).getJSONObject("outpoint").getString("scriptpubkey");
Script script = new Script(Hex.decode(scriptpubkey));
String address = null;
if (Bech32Util.getInstance().isBech32Script(scriptpubkey)) {
try {
address = Bech32Util.getInstance().getAddressFromScript(scriptpubkey);
} catch (Exception e) {
;
}
} else {
address = script.getToAddress(SamouraiWallet.getInstance().getCurrentNetworkParams()).toString();
}
if (FormatsUtil.getInstance().isValidBech32(address)) {
p2wpkh++;
} else if (Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), address).isP2SHAddress()) {
p2sh_p2wpkh++;
} else {
p2pkh++;
}
}
}
FeeUtil.getInstance().setSuggestedFee(FeeUtil.getInstance().getHighFee());
BigInteger estimatedFee = FeeUtil.getInstance().estimatedFeeSegwit(p2pkh, p2sh_p2wpkh, p2wpkh, outputs.length());
long total_inputs = 0L;
long total_outputs = 0L;
long fee = 0L;
UTXO utxo = null;
for (int i = 0; i < inputs.length(); i++) {
JSONObject obj = inputs.getJSONObject(i);
if (obj.has("outpoint")) {
JSONObject objPrev = obj.getJSONObject("outpoint");
if (objPrev.has("value")) {
total_inputs += objPrev.getLong("value");
}
}
}
for (int i = 0; i < outputs.length(); i++) {
JSONObject obj = outputs.getJSONObject(i);
if (obj.has("value")) {
total_outputs += obj.getLong("value");
String addr = obj.getString("address");
Log.d("activity", "checking address:" + addr);
if (utxo == null) {
utxo = getUTXO(addr);
} else {
break;
}
}
}
boolean feeWarning = false;
fee = total_inputs - total_outputs;
if (fee > estimatedFee.longValue()) {
feeWarning = true;
}
Log.d("activity", "total inputs:" + total_inputs);
Log.d("activity", "total outputs:" + total_outputs);
Log.d("activity", "fee:" + fee);
Log.d("activity", "estimated fee:" + estimatedFee.longValue());
Log.d("activity", "fee warning:" + feeWarning);
if (utxo != null) {
Log.d("activity", "utxo found");
List<UTXO> selectedUTXO = new ArrayList<UTXO>();
selectedUTXO.add(utxo);
int selected = utxo.getOutpoints().size();
long remainingFee = (estimatedFee.longValue() > fee) ? estimatedFee.longValue() - fee : 0L;
Log.d("activity", "remaining fee:" + remainingFee);
int receiveIdx = AddressFactory.getInstance(activity).getHighestTxReceiveIdx(0);
Log.d("activity", "receive index:" + receiveIdx);
final String addr;
if (PrefsUtil.getInstance(activity).getValue(PrefsUtil.USE_LIKE_TYPED_CHANGE, true) == true) {
addr = utxo.getOutpoints().get(0).getAddress();
} else {
addr = outputs.getJSONObject(0).getString("address");
}
final String ownReceiveAddr;
if (FormatsUtil.getInstance().isValidBech32(addr)) {
ownReceiveAddr = AddressFactory.getInstance(activity).getBIP84(AddressFactory.RECEIVE_CHAIN).getBech32AsString();
} else if (Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), addr).isP2SHAddress()) {
ownReceiveAddr = AddressFactory.getInstance(activity).getBIP49(AddressFactory.RECEIVE_CHAIN).getAddressAsString();
} else {
ownReceiveAddr = AddressFactory.getInstance(activity).get(AddressFactory.RECEIVE_CHAIN).getAddressString();
}
Log.d("activity", "receive address:" + ownReceiveAddr);
long totalAmount = utxo.getValue();
Log.d("activity", "amount before fee:" + totalAmount);
Triple<Integer, Integer, Integer> outpointTypes = FeeUtil.getInstance().getOutpointCount(new Vector(utxo.getOutpoints()));
BigInteger cpfpFee = FeeUtil.getInstance().estimatedFeeSegwit(outpointTypes.getLeft(), outpointTypes.getMiddle(), outpointTypes.getRight(), 1);
Log.d("activity", "cpfp fee:" + cpfpFee.longValue());
p2pkh = outpointTypes.getLeft();
p2sh_p2wpkh = outpointTypes.getMiddle();
p2wpkh = outpointTypes.getRight();
if (totalAmount < (cpfpFee.longValue() + remainingFee)) {
Log.d("activity", "selecting additional utxo");
Collections.sort(utxos, new UTXO.UTXOComparator());
for (UTXO _utxo : utxos) {
totalAmount += _utxo.getValue();
selectedUTXO.add(_utxo);
selected += _utxo.getOutpoints().size();
outpointTypes = FeeUtil.getInstance().getOutpointCount(new Vector(utxo.getOutpoints()));
p2pkh += outpointTypes.getLeft();
p2sh_p2wpkh += outpointTypes.getMiddle();
p2wpkh += outpointTypes.getRight();
cpfpFee = FeeUtil.getInstance().estimatedFeeSegwit(p2pkh, p2sh_p2wpkh, p2wpkh, 1);
if (totalAmount > (cpfpFee.longValue() + remainingFee + SamouraiWallet.bDust.longValue())) {
break;
}
}
if (totalAmount < (cpfpFee.longValue() + remainingFee + SamouraiWallet.bDust.longValue())) {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, R.string.insufficient_funds, Toast.LENGTH_SHORT).show();
}
});
FeeUtil.getInstance().setSuggestedFee(suggestedFee);
return "KO";
}
}
cpfpFee = cpfpFee.add(BigInteger.valueOf(remainingFee));
Log.d("activity", "cpfp fee:" + cpfpFee.longValue());
final List<MyTransactionOutPoint> outPoints = new ArrayList<MyTransactionOutPoint>();
for (UTXO u : selectedUTXO) {
outPoints.addAll(u.getOutpoints());
}
long _totalAmount = 0L;
for (MyTransactionOutPoint outpoint : outPoints) {
_totalAmount += outpoint.getValue().longValue();
}
Log.d("activity", "checked total amount:" + _totalAmount);
assert (_totalAmount == totalAmount);
long amount = totalAmount - cpfpFee.longValue();
Log.d("activity", "amount after fee:" + amount);
if (amount < SamouraiWallet.bDust.longValue()) {
Log.d("activity", "dust output");
Toast.makeText(activity, R.string.cannot_output_dust, Toast.LENGTH_SHORT).show();
}
final HashMap<String, BigInteger> receivers = new HashMap<String, BigInteger>();
receivers.put(ownReceiveAddr, BigInteger.valueOf(amount));
String message = "";
if (feeWarning) {
message += activity.getString(R.string.fee_bump_not_necessary);
message += "\n\n";
}
message += activity.getString(R.string.bump_fee) + " " + Coin.valueOf(remainingFee).toPlainString() + " BTC";
AlertDialog.Builder dlg = new AlertDialog.Builder(activity).setTitle(R.string.app_name).setMessage(message).setCancelable(false).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
if (AppUtil.getInstance(activity.getApplicationContext()).isServiceRunning(WebSocketService.class)) {
activity.stopService(new Intent(activity.getApplicationContext(), WebSocketService.class));
}
activity.startService(new Intent(activity.getApplicationContext(), WebSocketService.class));
Transaction tx = SendFactory.getInstance(activity).makeTransaction(0, outPoints, receivers);
if (tx != null) {
tx = SendFactory.getInstance(activity).signTransaction(tx, 0);
final String hexTx = new String(Hex.encode(tx.bitcoinSerialize()));
Log.d("activity", hexTx);
final String strTxHash = tx.getHashAsString();
Log.d("activity", strTxHash);
boolean isOK = false;
try {
isOK = PushTx.getInstance(activity).pushTx(hexTx);
if (isOK) {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, R.string.cpfp_spent, Toast.LENGTH_SHORT).show();
FeeUtil.getInstance().setSuggestedFee(suggestedFee);
Intent _intent = new Intent(activity, MainActivity2.class);
_intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
activity.startActivity(_intent);
}
});
} else {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, R.string.tx_failed, Toast.LENGTH_SHORT).show();
}
});
// reset receive index upon tx fail
if (FormatsUtil.getInstance().isValidBech32(addr)) {
int prevIdx = BIP84Util.getInstance(activity).getWallet().getAccount(0).getReceive().getAddrIdx() - 1;
BIP84Util.getInstance(activity).getWallet().getAccount(0).getReceive().setAddrIdx(prevIdx);
} else if (Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), addr).isP2SHAddress()) {
int prevIdx = BIP49Util.getInstance(activity).getWallet().getAccount(0).getReceive().getAddrIdx() - 1;
BIP49Util.getInstance(activity).getWallet().getAccount(0).getReceive().setAddrIdx(prevIdx);
} else {
int prevIdx = HD_WalletFactory.getInstance(activity).get().getAccount(0).getReceive().getAddrIdx() - 1;
HD_WalletFactory.getInstance(activity).get().getAccount(0).getReceive().setAddrIdx(prevIdx);
}
}
} catch (MnemonicException.MnemonicLengthException | DecoderException | IOException e) {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, "pushTx:" + e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
} finally {
;
}
}
}
}).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
try {
if (Bech32Util.getInstance().isBech32Script(addr)) {
int prevIdx = BIP84Util.getInstance(activity).getWallet().getAccount(0).getReceive().getAddrIdx() - 1;
BIP84Util.getInstance(activity).getWallet().getAccount(0).getReceive().setAddrIdx(prevIdx);
} else if (Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), addr).isP2SHAddress()) {
int prevIdx = BIP49Util.getInstance(activity).getWallet().getAccount(0).getReceive().getAddrIdx() - 1;
BIP49Util.getInstance(activity).getWallet().getAccount(0).getReceive().setAddrIdx(prevIdx);
} else {
int prevIdx = HD_WalletFactory.getInstance(activity).get().getAccount(0).getReceive().getAddrIdx() - 1;
HD_WalletFactory.getInstance(activity).get().getAccount(0).getReceive().setAddrIdx(prevIdx);
}
} catch (MnemonicException.MnemonicLengthException | DecoderException | IOException e) {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
} finally {
dialog.dismiss();
}
}
});
if (!activity.isFinishing()) {
dlg.show();
}
} else {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, R.string.cannot_create_cpfp, Toast.LENGTH_SHORT).show();
}
});
}
} catch (final JSONException je) {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, "cpfp:" + je.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
FeeUtil.getInstance().setSuggestedFee(suggestedFee);
} else {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity, R.string.cpfp_cannot_retrieve_tx, Toast.LENGTH_SHORT).show();
}
});
}
Looper.loop();
return "OK";
}
Aggregations