use of org.bitcoinj.core.Address in project bitcoin-wallet by bitcoin-wallet.
the class SendCoinsFragment method validateReceivingAddress.
private void validateReceivingAddress() {
try {
final String addressStr = receivingAddressView.getText().toString().trim();
if (!addressStr.isEmpty()) {
final Address address = Address.fromString(Constants.NETWORK_PARAMETERS, addressStr);
final String label = addressBookDao.resolveLabel(address.toString());
viewModel.validatedAddress = new AddressAndLabel(Constants.NETWORK_PARAMETERS, address.toString(), label);
receivingAddressView.setText(null);
log.info("Locked to valid address: {}", viewModel.validatedAddress);
}
} catch (final AddressFormatException x) {
// swallow
}
}
use of org.bitcoinj.core.Address in project bitcoin-wallet by bitcoin-wallet.
the class SendCoinsFragment method sendPayment.
private void sendPayment(final SendRequest sendRequest, final Coin finalAmount) {
final Wallet wallet = walletActivityViewModel.wallet.getValue();
new SendCoinsOfflineTask(wallet, backgroundHandler) {
@Override
protected void onSuccess(final Transaction transaction) {
viewModel.sentTransaction.setValue(transaction);
setState(SendCoinsViewModel.State.SENDING);
final Address refundAddress = viewModel.paymentIntent.standard == Standard.BIP70 ? wallet.freshAddress(KeyPurpose.REFUND) : null;
final Payment payment = PaymentProtocol.createPaymentMessage(Collections.singletonList(transaction), finalAmount, refundAddress, null, viewModel.paymentIntent.payeeData);
if (directPaymentEnableView.isChecked())
directPay(payment);
final ListenableFuture<Transaction> future = walletActivityViewModel.broadcastTransaction(transaction);
future.addListener(() -> {
// Auto-close the dialog after a short delay
if (config.getSendCoinsAutoclose())
handler.postDelayed(() -> activity.finish(), Constants.AUTOCLOSE_DELAY_MS);
}, Threading.THREAD_POOL);
final ComponentName callingActivity = activity.getCallingActivity();
if (callingActivity != null) {
log.info("returning result to calling activity: {}", callingActivity.flattenToString());
final Intent result = new Intent();
BitcoinIntegration.transactionHashToResult(result, transaction.getTxId().toString());
if (viewModel.paymentIntent.standard == Standard.BIP70)
BitcoinIntegration.paymentToResult(result, payment.toByteArray());
activity.setResult(Activity.RESULT_OK, result);
}
}
private void directPay(final Payment payment) {
final DirectPaymentTask.ResultCallback callback = new DirectPaymentTask.ResultCallback() {
@Override
public void onResult(final boolean ack) {
viewModel.directPaymentAck = ack;
if (viewModel.state == SendCoinsViewModel.State.SENDING)
setState(SendCoinsViewModel.State.SENT);
updateView();
}
@Override
public void onFail(final int messageResId, final Object... messageArgs) {
final DialogBuilder dialog = DialogBuilder.warn(activity, R.string.send_coins_fragment_direct_payment_failed_title, viewModel.paymentIntent.paymentUrl + "\n" + getString(messageResId, messageArgs) + "\n\n" + getString(R.string.send_coins_fragment_direct_payment_failed_msg));
dialog.setPositiveButton(R.string.button_retry, (d, which) -> directPay(payment));
dialog.setNegativeButton(R.string.button_dismiss, null);
dialog.show();
}
};
if (viewModel.paymentIntent.isHttpPaymentUrl()) {
new DirectPaymentTask.HttpPaymentTask(backgroundHandler, callback, viewModel.paymentIntent.paymentUrl, application.httpUserAgent()).send(payment);
} else if (viewModel.paymentIntent.isBluetoothPaymentUrl() && bluetoothAdapter != null && bluetoothAdapter.isEnabled()) {
new DirectPaymentTask.BluetoothPaymentTask(backgroundHandler, callback, bluetoothAdapter, Bluetooth.getBluetoothMac(viewModel.paymentIntent.paymentUrl)).send(payment);
}
}
@Override
protected void onInsufficientMoney(final Coin missing) {
setState(SendCoinsViewModel.State.INPUT);
final Coin estimated = wallet.getBalance(BalanceType.ESTIMATED);
final Coin available = wallet.getBalance(BalanceType.AVAILABLE);
final Coin pending = estimated.subtract(available);
final MonetaryFormat btcFormat = config.getFormat();
final StringBuilder msg = new StringBuilder();
msg.append(getString(R.string.send_coins_fragment_insufficient_money_msg1, btcFormat.format(missing)));
if (pending.signum() > 0)
msg.append("\n\n").append(getString(R.string.send_coins_fragment_pending, btcFormat.format(pending)));
if (viewModel.paymentIntent.mayEditAmount())
msg.append("\n\n").append(getString(R.string.send_coins_fragment_insufficient_money_msg2));
final DialogBuilder dialog = DialogBuilder.warn(activity, R.string.send_coins_fragment_insufficient_money_title, msg);
if (viewModel.paymentIntent.mayEditAmount()) {
dialog.setPositiveButton(R.string.send_coins_options_empty, (d, which) -> handleEmpty());
dialog.setNegativeButton(R.string.button_cancel, null);
} else {
dialog.setNeutralButton(R.string.button_dismiss, null);
}
dialog.show();
}
@Override
protected void onInvalidEncryptionKey() {
setState(SendCoinsViewModel.State.INPUT);
privateKeyBadPasswordView.setVisibility(View.VISIBLE);
privateKeyPasswordView.requestFocus();
}
@Override
protected void onEmptyWalletFailed() {
setState(SendCoinsViewModel.State.INPUT);
final DialogBuilder dialog = DialogBuilder.warn(activity, R.string.send_coins_fragment_empty_wallet_failed_title, R.string.send_coins_fragment_hint_empty_wallet_failed);
dialog.setNeutralButton(R.string.button_dismiss, null);
dialog.show();
}
@Override
protected void onFailure(Exception exception) {
setState(SendCoinsViewModel.State.FAILED);
final DialogBuilder dialog = DialogBuilder.warn(activity, R.string.send_coins_error_msg, exception.toString());
dialog.setNeutralButton(R.string.button_dismiss, null);
dialog.show();
}
}.sendCoinsOffline(// send asynchronously
sendRequest);
}
use of org.bitcoinj.core.Address in project bitcoin-wallet by bitcoin-wallet.
the class RequestWalletBalanceTask method requestWalletBalance.
public void requestWalletBalance(final AssetManager assets, final ECKey key) {
backgroundHandler.post(new Runnable() {
@Override
public void run() {
org.bitcoinj.core.Context.propagate(Constants.CONTEXT);
final Address legacyAddress = LegacyAddress.fromKey(Constants.NETWORK_PARAMETERS, key);
final Script[] outputScripts;
final String addressesStr;
if (key.isCompressed()) {
final Address segwitAddress = SegwitAddress.fromKey(Constants.NETWORK_PARAMETERS, key);
outputScripts = new Script[] { ScriptBuilder.createP2PKHOutputScript(legacyAddress.getHash()), ScriptBuilder.createP2WPKHOutputScript(segwitAddress.getHash()) };
addressesStr = legacyAddress.toString() + "," + segwitAddress.toString();
} else {
outputScripts = new Script[] { ScriptBuilder.createP2PKHOutputScript(legacyAddress.getHash()) };
addressesStr = legacyAddress.toString();
}
final List<ElectrumServer> servers = loadElectrumServers(Assets.open(assets, Constants.Files.ELECTRUM_SERVERS_ASSET));
final List<Callable<Set<UTXO>>> tasks = new ArrayList<>(servers.size());
for (final ElectrumServer server : servers) {
tasks.add(() -> {
log.info("{} - trying to request wallet balance for {}", server.socketAddress, addressesStr);
try (final Socket socket = connect(server)) {
final BufferedSink sink = Okio.buffer(Okio.sink(socket));
sink.timeout().timeout(5000, TimeUnit.MILLISECONDS);
final BufferedSource source = Okio.buffer(Okio.source(socket));
source.timeout().timeout(5000, TimeUnit.MILLISECONDS);
final Moshi moshi = new Moshi.Builder().build();
final JsonAdapter<ElectrumRequest> requestAdapter = moshi.adapter(ElectrumRequest.class);
final JsonAdapter<ListunspentResponse> listunspentResponseAdapter = moshi.adapter(ListunspentResponse.class);
final JsonAdapter<TransactionResponse> transactionResponseAdapter = moshi.adapter(TransactionResponse.class);
final Set<UTXO> utxos = new HashSet<>();
for (final Script outputScript : outputScripts) {
requestAdapter.toJson(sink, new ElectrumRequest(outputScript.getScriptType().ordinal(), "blockchain.scripthash.listunspent", new String[] { Constants.HEX.encode(Sha256Hash.of(outputScript.getProgram()).getReversedBytes()) }));
sink.writeUtf8("\n").flush();
final ListunspentResponse listunspentResponse = listunspentResponseAdapter.fromJson(source);
final int expectedResponseId = outputScript.getScriptType().ordinal();
if (listunspentResponse.id != expectedResponseId) {
log.warn("{} - id mismatch listunspentResponse:{} vs request:{}", server.socketAddress, listunspentResponse.id, expectedResponseId);
return null;
}
if (listunspentResponse.error != null) {
log.info("{} - server error {}: {}", server.socketAddress, listunspentResponse.error.code, listunspentResponse.error.message);
return null;
}
if (listunspentResponse.result == null) {
log.info("{} - missing result", server.socketAddress);
return null;
}
for (final ListunspentResponse.Utxo responseUtxo : listunspentResponse.result) {
final Sha256Hash utxoHash = Sha256Hash.wrap(responseUtxo.tx_hash);
final int utxoIndex = responseUtxo.tx_pos;
// the value cannot be trusted; will be validated below
final Coin utxoValue = Coin.valueOf(responseUtxo.value);
final UTXO utxo = new UTXO(utxoHash, utxoIndex, utxoValue, responseUtxo.height, false, outputScript);
// validation of value and some sanity checks
requestAdapter.toJson(sink, new ElectrumRequest("blockchain.transaction.get", new String[] { Constants.HEX.encode(utxo.getHash().getBytes()) }));
sink.writeUtf8("\n").flush();
final TransactionResponse transactionResponse = transactionResponseAdapter.fromJson(source);
if (transactionResponse.error != null) {
log.info("{} - server error {}: {}", server.socketAddress, transactionResponse.error.code, transactionResponse.error.message);
return null;
}
if (transactionResponse.result == null) {
log.info("{} - missing result", server.socketAddress);
return null;
}
final Transaction tx = new Transaction(Constants.NETWORK_PARAMETERS, Constants.HEX.decode(transactionResponse.result));
if (!tx.getTxId().equals(utxo.getHash()))
log.warn("{} - lied about txid", server.socketAddress);
else if (!tx.getOutput(utxo.getIndex()).getValue().equals(utxo.getValue()))
log.warn("{} - lied about amount", server.socketAddress);
else if (!tx.getOutput(utxo.getIndex()).getScriptPubKey().equals(outputScript))
log.warn("{} - lied about output script", server.socketAddress);
else
// use valid UTXO
utxos.add(utxo);
}
}
log.info("{} - got {} UTXOs {}", server.socketAddress, utxos.size(), utxos);
return utxos;
} catch (final ConnectException | SSLPeerUnverifiedException | JsonDataException x) {
log.warn("{} - {}", server.socketAddress, x.getMessage());
return null;
} catch (final IOException x) {
log.info(server.socketAddress.toString(), x);
return null;
} catch (final RuntimeException x) {
log.error(server.socketAddress.toString(), x);
throw x;
}
});
}
final ExecutorService threadPool = Executors.newFixedThreadPool(servers.size(), new ContextPropagatingThreadFactory("request"));
final List<Future<Set<UTXO>>> futures;
try {
futures = threadPool.invokeAll(tasks, 10, TimeUnit.SECONDS);
} catch (final InterruptedException x) {
throw new RuntimeException(x);
} finally {
threadPool.shutdown();
}
final Multiset<UTXO> countedUtxos = HashMultiset.create();
int numSuccess = 0, numFail = 0, numTimeOuts = 0;
for (Future<Set<UTXO>> future : futures) {
if (!future.isCancelled()) {
try {
final Set<UTXO> utxos = future.get();
if (utxos != null) {
countedUtxos.addAll(utxos);
numSuccess++;
} else {
numFail++;
}
} catch (InterruptedException | ExecutionException x) {
throw new RuntimeException(x);
}
} else {
numTimeOuts++;
}
}
final int trustThreshold = servers.size() / 2;
for (final Iterator<Multiset.Entry<UTXO>> i = countedUtxos.entrySet().iterator(); i.hasNext(); ) {
final Multiset.Entry<UTXO> entry = i.next();
if (entry.getCount() < trustThreshold)
i.remove();
}
final Set<UTXO> utxos = countedUtxos.elementSet();
log.info("{} successes, {} fails, {} time-outs, {} UTXOs {}", numSuccess, numFail, numTimeOuts, utxos.size(), utxos);
if (numSuccess < trustThreshold)
onFail(R.string.sweep_wallet_fragment_request_wallet_balance_failed_connection);
else if (utxos.isEmpty())
onFail(R.string.sweep_wallet_fragment_request_wallet_balance_empty);
else
onResult(utxos);
}
private Socket connect(final ElectrumServer server) throws IOException {
final Socket socket;
if (server.type == ElectrumServer.Type.TLS) {
final SocketFactory sf = sslTrustAllCertificates();
socket = sf.createSocket(server.socketAddress.getHostName(), server.socketAddress.getPort());
final SSLSession sslSession = ((SSLSocket) socket).getSession();
final Certificate certificate = sslSession.getPeerCertificates()[0];
final String certificateFingerprint = sslCertificateFingerprint(certificate);
if (server.certificateFingerprint == null) {
// signed by CA
if (!HttpsURLConnection.getDefaultHostnameVerifier().verify(server.socketAddress.getHostName(), sslSession))
throw new SSLPeerUnverifiedException("Expected " + server.socketAddress.getHostName() + ", got " + sslSession.getPeerPrincipal());
} else {
// self-signed
if (!certificateFingerprint.equals(server.certificateFingerprint))
throw new SSLPeerUnverifiedException("Expected " + server.certificateFingerprint + " for " + server.socketAddress.getHostName() + ", got " + certificateFingerprint);
}
} else if (server.type == ElectrumServer.Type.TCP) {
socket = new Socket();
socket.connect(server.socketAddress, 5000);
} else {
throw new IllegalStateException("Cannot handle: " + server.type);
}
return socket;
}
});
}
use of org.bitcoinj.core.Address in project bitcoin-wallet by bitcoin-wallet.
the class WalletTransactionsFragment method onClickTransactionContextMenuItem.
@Override
public boolean onClickTransactionContextMenuItem(final MenuItem item, final Sha256Hash transactionId) {
final Wallet wallet = viewModel.wallet.getValue();
final Transaction tx = wallet.getTransaction(transactionId);
final int itemId = item.getItemId();
if (itemId == R.id.wallet_transactions_context_edit_address) {
final boolean txSent = tx.getValue(wallet).signum() < 0;
final Address txAddress = txSent ? WalletUtils.getToAddressOfSent(tx, wallet) : WalletUtils.getWalletAddressOfReceived(tx, wallet);
viewModel.showEditAddressBookEntryDialog.setValue(new Event<>(txAddress));
return true;
} else if (itemId == R.id.wallet_transactions_context_show_qr) {
final byte[] txSerialized = tx.unsafeBitcoinSerialize();
final Bitmap qrCodeBitmap = Qr.bitmap(Qr.encodeCompressBinary(txSerialized));
viewModel.showBitmapDialog.setValue(new Event<>(qrCodeBitmap));
return true;
} else if (itemId == R.id.wallet_transactions_context_raise_fee) {
RaiseFeeDialogFragment.show(fragmentManager, transactionId);
return true;
} else if (itemId == R.id.wallet_transactions_context_report_issue) {
viewModel.showReportIssueDialog.setValue(new Event<>(transactionId));
return true;
} else if (itemId == R.id.wallet_transactions_context_browse) {
final Uri blockExplorerUri = config.getBlockExplorer();
log.info("Viewing transaction {} on {}", transactionId, blockExplorerUri);
activity.startExternalDocument(Uri.withAppendedPath(blockExplorerUri, "tx/" + transactionId.toString()));
return true;
} else {
return false;
}
}
use of org.bitcoinj.core.Address in project bitcoin-wallet by bitcoin-wallet.
the class WalletTransactionsFragment method onCreate.
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.fragmentManager = getChildFragmentManager();
setHasOptionsMenu(true);
activityViewModel = new ViewModelProvider(activity).get(WalletActivityViewModel.class);
viewModel = new ViewModelProvider(this).get(WalletTransactionsViewModel.class);
viewModel.direction.observe(this, direction -> activity.invalidateOptionsMenu());
viewModel.transactions.observe(this, transactions -> {
if (transactions.isEmpty()) {
viewGroup.setDisplayedChild(0);
final WalletTransactionsViewModel.Direction direction = viewModel.direction.getValue();
final WarningType warning = viewModel.warning.getValue();
final SpannableStringBuilder emptyText = new SpannableStringBuilder(getString(direction == WalletTransactionsViewModel.Direction.SENT ? R.string.wallet_transactions_fragment_empty_text_sent : R.string.wallet_transactions_fragment_empty_text_received));
emptyText.setSpan(new StyleSpan(Typeface.BOLD), 0, emptyText.length(), SpannableStringBuilder.SPAN_POINT_MARK);
if (direction != WalletTransactionsViewModel.Direction.SENT)
emptyText.append("\n\n").append(getString(R.string.wallet_transactions_fragment_empty_text_howto));
if (warning == WarningType.BACKUP) {
final int start = emptyText.length();
emptyText.append("\n\n").append(getString(R.string.wallet_transactions_fragment_empty_remind_backup));
emptyText.setSpan(new StyleSpan(Typeface.BOLD), start, emptyText.length(), SpannableStringBuilder.SPAN_POINT_MARK);
}
emptyView.setText(emptyText);
} else {
viewGroup.setDisplayedChild(1);
}
});
viewModel.selectedTransaction.observe(this, transactionId -> {
adapter.setSelectedTransaction(transactionId);
final int position = adapter.positionOf(transactionId);
if (position != RecyclerView.NO_POSITION)
recyclerView.smoothScrollToPosition(position);
});
viewModel.list.observe(this, listItems -> {
adapter.submitList(listItems);
activityViewModel.transactionsLoadingFinished();
});
viewModel.showBitmapDialog.observe(this, new Event.Observer<Bitmap>() {
@Override
protected void onEvent(final Bitmap bitmap) {
BitmapFragment.show(fragmentManager, bitmap);
}
});
viewModel.showEditAddressBookEntryDialog.observe(this, new Event.Observer<Address>() {
@Override
protected void onEvent(final Address address) {
EditAddressBookEntryFragment.edit(fragmentManager, address);
}
});
viewModel.showReportIssueDialog.observe(this, new Event.Observer<Sha256Hash>() {
@Override
protected void onEvent(final Sha256Hash transactionHash) {
ReportIssueDialogFragment.show(fragmentManager, R.string.report_issue_dialog_title_transaction, R.string.report_issue_dialog_message_issue, Constants.REPORT_SUBJECT_ISSUE, transactionHash);
}
});
adapter = new TransactionsAdapter(activity, this, this);
}
Aggregations