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 all 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
4 changes: 4 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

testOptions {
unitTests.returnDefaultValues = true
}
}


Expand Down
67 changes: 62 additions & 5 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 @@ -215,7 +224,7 @@ public void onMoveToForeground() {
ZapLog.debug(LOG_TAG, "Starting to establish connections...");
LndConnection.getInstance().restartBackgroundTasks();

Wallet.getInstance().isLNDReachable();
Wallet.getInstance().checkIfLndIsReachableAndTriggerWalletLoadedInterface();

}
}
Expand Down Expand Up @@ -326,7 +335,7 @@ public void guardianDialogConfirmed(String DialogName) {
}

@Override
public void onWalletLoadedUpdated(boolean success, String error) {
public void onWalletLoadedUpdated(boolean success, int error) {
if (success) {
// We managed to establish a connection to LND.
// Now we can start to fetch all information needed from LND
Expand All @@ -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 == Wallet.WalletLoadedListener.ERROR_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
13 changes: 9 additions & 4 deletions app/src/main/java/zapsolutions/zap/connection/LndConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@

/**
* Singleton to handle the connection to lnd
* <p>
* Please note:
* IP, Certificate and Macaroon are placeholders right now.
*/
public class LndConnection {

Expand Down Expand Up @@ -73,7 +70,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 +142,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,42 @@
package zapsolutions.zap.connection;

public class LndConnectionConfig {

private String host;
private int port;
private String cert;
private String macaroon;


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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
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.connection.LndConnectionConfig;
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;

private int mError = -1;
private String mConnectString;

private LndConnectionConfig mConnectionConfig;

public LndConnectStringParser(String connectString){
mConnectString = connectString;
mConnectionConfig = new LndConnectionConfig();
}

public LndConnectStringParser parse() {

// validate not null
if (mConnectString == null) {
mError = ERROR_INVALID_CONNECT_STRING;
return this;
}

// validate scheme
if (!mConnectString.toLowerCase().startsWith("lndconnect://")) {
mError = ERROR_INVALID_CONNECT_STRING;
return this;
}

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

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

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");
mError = ERROR_INVALID_CERTIFICATE;
return this;
}
} catch (IllegalArgumentException e) {
ZapLog.debug(LOG_TAG, "cert decoding failed");
mError = ERROR_INVALID_CERTIFICATE;
return this;
}
}

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

mError = ERROR_INVALID_MACAROON;
return this;
}
}

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

return this;

} else {
ZapLog.debug(LOG_TAG, "Connect URI has no parameters");
mError = ERROR_INVALID_CONNECT_STRING;
return this;
}

} catch (URISyntaxException e) {
ZapLog.debug(LOG_TAG, "URI could not be parsed");
mError = ERROR_INVALID_CONNECT_STRING;
return this;
}
}

public boolean hasError() {
if (mError > -1) {
return true;
} else {
return false;
}
}

public int getError(){
return mError;
}

public LndConnectionConfig getConnectionConfig() {
return mConnectionConfig;
}
}
Loading