Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

Useful error messages on connecting, unlock wallet #45

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 60 additions & 3 deletions app/src/main/java/zapsolutions/zap/HomeActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import androidx.fragment.app.Fragment;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
Expand All @@ -14,8 +15,13 @@
import com.android.volley.toolbox.JsonObjectRequest;
import com.google.android.material.bottomnavigation.BottomNavigationView;

import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.Toast;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
Expand Down Expand Up @@ -50,6 +56,7 @@ public class HomeActivity extends BaseAppCompatActivity implements LifecycleObse
private static final String LOG_TAG = "Main Activity";

private UserGuardian mUG;
private InputMethodManager mInputMethodManager;
private ScheduledExecutorService mExchangeRateScheduler;
private ScheduledExecutorService mLNDInfoScheduler;
private NetworkChangeReceiver mNetworkChangeReceiver;
Expand All @@ -61,6 +68,7 @@ public class HomeActivity extends BaseAppCompatActivity implements LifecycleObse
private boolean mInfoChangeListenerRegistered;
private boolean mWalletLoadedListenerRegistered;
private boolean mMainnetWarningShownOnce;
private boolean mIsFirstUnlockAttempt = true;


@Override
Expand All @@ -69,6 +77,7 @@ protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);

mUG = new UserGuardian(this, this);
mInputMethodManager = (InputMethodManager) this.getSystemService(Context.INPUT_METHOD_SERVICE);

// Register observer to detect if app goes to background
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
Expand Down Expand Up @@ -135,7 +144,7 @@ private void setupExchangeRateSchedule() {
mExchangeRateScheduler.scheduleAtFixedRate
(new Runnable() {
public void run() {
if(!MonetaryUtil.getInstance().getSecondCurrency().isBitcoin()) {
if (!MonetaryUtil.getInstance().getSecondCurrency().isBitcoin()) {
ZapLog.debug(LOG_TAG, "Fiat exchange rate request initiated");
// Adding request to request queue
HttpClient.getInstance().addToRequestQueue(request, "rateRequest");
Expand Down Expand Up @@ -344,10 +353,58 @@ public void onWalletLoadedUpdated(boolean success, String error) {

Wallet.getInstance().subscribeToTransactions();
Wallet.getInstance().subscribeToInvoices();
Wallet.getInstance().subscribeToChannelEvents();
Wallet.getInstance().subscribeToChannelBackup();

// ToDo: subscribeToChannelEvents() causes LND to hang, if it is called short after unlocking.
//Wallet.getInstance().subscribeToChannelEvents();
raphBTC marked this conversation as resolved.
Show resolved Hide resolved
//Wallet.getInstance().subscribeToChannelBackup();

ZapLog.debug(LOG_TAG, "Wallet loaded");
} else {
if (error.equals("locked")) {


// Show unlock dialog
AlertDialog.Builder adb = new AlertDialog.Builder(this);
adb.setTitle(R.string.unlock_wallet);
adb.setCancelable(false);
View viewInflated = LayoutInflater.from(this).inflate(R.layout.dialog_input_password, null, false);

final EditText input = viewInflated.findViewById(R.id.input);
input.setShowSoftInputOnFocus(true);
input.requestFocus();

mInputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);

adb.setView(viewInflated);

adb.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
((WalletFragment) mCurrentFragment).showLoadingForWalletUnlock();
Wallet.getInstance().unlockWallet(input.getText().toString());
mInputMethodManager.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
mIsFirstUnlockAttempt = false;
dialog.dismiss();
}
});
adb.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
InputMethodManager inputMethodManager = (InputMethodManager) HomeActivity.this.getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
((WalletFragment) mCurrentFragment).showErrorAfterNotUnlocked();
mIsFirstUnlockAttempt = true;
dialog.cancel();
}
});

adb.show();
((WalletFragment) mCurrentFragment).showBackgroundForWalletUnlock();

if(!mIsFirstUnlockAttempt) {
Toast.makeText(HomeActivity.this, R.string.error_wrong_password, Toast.LENGTH_LONG).show();
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ private void readSavedConnectionInfo() {
String certificateBase64UrlString = mConnectionInfo[2];
byte[] certificateBytes = BaseEncoding.base64Url().decode(certificateBase64UrlString);

mSSLFactory = CustomSSLSocketFactory.create(certificateBytes);
try {
raphBTC marked this conversation as resolved.
Show resolved Hide resolved
mSSLFactory = CustomSSLSocketFactory.create(certificateBytes);
} catch (RuntimeException e) {
ZapLog.debug(LOG_TAG, "Error on Certificate");
}

generateChannelAndStubs();

Expand Down Expand Up @@ -141,4 +145,8 @@ public LightningGrpc.LightningBlockingStub getBlockingClient() {
return mBlockingClient;
}

public String[] getConnectionInfo() {
return mConnectionInfo;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package zapsolutions.zap.connection.lndConnect;

import com.google.common.io.BaseEncoding;

import java.net.URI;
import java.net.URISyntaxException;

import zapsolutions.zap.connection.CustomSSLSocketFactory;
import zapsolutions.zap.util.ZapLog;

/**
* This class parses a lndconnect which is defined in this project:
* https://github.com/LN-Zap/lndconnect
* <p>
* A lndconnect string consists of the following parts:
* lndconnect://<HOST>:<PORT>?cert=<certificate_encoded_as_base64url>&macaroon=<macaroon_encoded_as_base64url>
* <p>
* Note: The certificate is not mandatory. For cases like BTCPay server where another certificate is used, this can be omitted.
* <p>
* The parser returns an object containing the desired data or an descriptive error.
*/
public class LndConnectStringParser {

private static final String LOG_TAG = "LND connect string parser";

public static final int ERROR_INVALID_CONNECT_STRING = 0;
public static final int ERROR_NO_MACAROON = 1;
public static final int ERROR_INVALID_CERTIFICATE = 2;
public static final int ERROR_INVALID_MACAROON = 3;
public static final int ERROR_INVALID_HOST_OR_PORT = 4;


public LndConnectStringResult parse(String connectString) {

LndConnectStringResult result = new LndConnectStringResult();

// validate not null
if (connectString == null) {
result.setError(ERROR_INVALID_CONNECT_STRING);
return result;
}

// validate scheme
if (!connectString.toLowerCase().startsWith("lndconnect://")) {
result.setError(ERROR_INVALID_CONNECT_STRING);
return result;
}

URI connectURI = null;
try {
connectURI = new URI(connectString);

// validate host and port
if (connectURI.getPort() == -1) {
result.setError(ERROR_INVALID_HOST_OR_PORT);
return result;
}

String cert = null;
String macaroon = null;

// fetch params
if (connectURI.getQuery() != null) {
String[] valuePairs = connectURI.getQuery().split("&");

for (String pair : valuePairs) {
String[] param = pair.split("=");
if (param.length > 1) {
if (param[0].equals("cert")) {
cert = param[1];
}
if (param[0].equals("macaroon")) {
macaroon = param[1];
}
}
}

// validate cert (Certificate is not mandatory for BTCPay server for example, therefore null is valid)
if (cert != null) {
try {
byte[] certificateBytes = BaseEncoding.base64Url().decode(cert);
try {
CustomSSLSocketFactory.create(certificateBytes);
} catch (RuntimeException e) {

ZapLog.debug(LOG_TAG, "certificate creation failed");
result.setError(ERROR_INVALID_CERTIFICATE);
return result;
}
} catch (IllegalArgumentException e) {
ZapLog.debug(LOG_TAG, "cert decoding failed");
result.setError(ERROR_INVALID_CERTIFICATE);
return result;
}
}

// validate macaroon if everything was valid so far
if (macaroon == null) {
ZapLog.debug(LOG_TAG, "lnd connect string does not include a macaroon");
result.setError(ERROR_NO_MACAROON);
return result;
} else {
try {
BaseEncoding.base64Url().decode(macaroon);
} catch (IllegalArgumentException e) {
ZapLog.debug(LOG_TAG, "macaroon decoding failed");

result.setError(ERROR_INVALID_MACAROON);
return result;
}
}

// everything is ok, initiate connection
result.setError(-1);
result.setHost(connectURI.getHost());
result.setPort(connectURI.getPort());
result.setCert(cert);
result.setMacaroon(macaroon);

return result;

} else {
ZapLog.debug(LOG_TAG, "Connect URI has no parameters");
result.setError(ERROR_INVALID_CONNECT_STRING);
return result;
}

} catch (URISyntaxException e) {
ZapLog.debug(LOG_TAG, "URI could not be parsed");
result.setError(ERROR_INVALID_CONNECT_STRING);
return result;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package zapsolutions.zap.connection.lndConnect;

public class LndConnectStringResult {

private int error;
michaelWuensch marked this conversation as resolved.
Show resolved Hide resolved
private String host;
private int port;
private String cert;
private String macaroon;

public int getError() {
return error;
}

public void setError(int error) {
this.error = error;
}

public String getHost() {
return host;
}

public void setHost(String host) {
this.host = host;
}

public int getPort() {
return port;
}

public void setPort(int port) {
this.port = port;
}

public String getCert() {
return cert;
}

public void setCert(String cert) {
this.cert = cert;
}

public String getMacaroon() {
return macaroon;
}

public void setMacaroon(String macaroon) {
this.macaroon = macaroon;
}
}
Loading