Skip to content

Commit

Permalink
New Tor implementation and persistent lnd/tor services
Browse files Browse the repository at this point in the history
Co-authored-by: djkazic <[email protected]>
  • Loading branch information
hsjoberg and djkazic authored Jul 25, 2023
1 parent c87ab00 commit e26c5bc
Show file tree
Hide file tree
Showing 29 changed files with 681 additions and 173 deletions.
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +0,0 @@
[submodule "tor"]
path = tor
url = https://github.com/thaliproject/Tor_Onion_Proxy_Library
16 changes: 9 additions & 7 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,17 @@ dependencies {

implementation "com.github.mmin18:realtimeblurview:1.2.1"

// Tor
// implementation "com.msopentech.thali:universal:0.0.3"
// implementation "org.torproject:tor-android-binary:0.4.2.7a"
// implementation "com.msopentech.thali.toronionproxy.android:android:0.0.3@aar"
// implementation "org.slf4j:slf4j-android:1.7.30"
// implementation "net.freehaven.tor.control:jtorctl:0.2" // controlling tor instance via its control port

implementation "com.jakewharton:process-phoenix:2.0.0"
implementation 'org.brotli:dec:0.1.2'

implementation "com.jakewharton:process-phoenix:2.0.0"

implementation 'info.guardianproject:tor-android:0.4.7.8'
implementation 'info.guardianproject:jtorctl:0.4.5.7'

implementation('dev.doubledot.doki:library:0.0.1@aar') {
transitive = true
}
}

apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
Expand Down
2 changes: 2 additions & 0 deletions android/app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@
-keep class lnrpc.** { *; }

-keep class com.facebook.react.turbomodule.** { *; }

-keep class org.torproject.jni.** { *; }
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>

<!-- Blixt: -->
Expand Down
165 changes: 165 additions & 0 deletions android/app/src/main/java/com/blixtwallet/BlixtTor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package com.blixtwallet;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;

import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;

import java.util.Stack;

import androidx.core.app.NotificationManagerCompat;

import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.reactnativecommunity.asyncstorage.AsyncLocalStorageUtil;
import com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier;

import org.torproject.jni.TorService;


public class BlixtTor extends ReactContextBaseJavaModule {
static private final String TAG = "BlixtTor";
static TorService torService;
static String currentTorStatus = TorService.STATUS_OFF;
static Stack<Promise> calleeResolvers = new Stack<>();
static NotificationManagerCompat notificationManager;

static private boolean getPersistentServicesEnabled(Context context) {
ReactDatabaseSupplier dbSupplier = ReactDatabaseSupplier.getInstance(context);
SQLiteDatabase db = dbSupplier.get();
String persistentServicesEnabled = AsyncLocalStorageUtil.getItemImpl(db, "persistentServicesEnabled");
if (persistentServicesEnabled != null) {
return persistentServicesEnabled.equals("true");
}
Log.w(TAG, "Could not find persistentServicesEnabled in asyncStorage");
return false;
}

static private final ServiceConnection torServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
TorService.LocalBinder binder = (TorService.LocalBinder) service;
torService = binder.getService();
Log.i(TAG, "torService.getService()");
boolean persistentServicesEnabled = getPersistentServicesEnabled(torService);
if (persistentServicesEnabled) {
torService.startForeground(0xc0feefee, getNotification(torService));
}
Log.i(TAG, "onServiceConnected");
}

@Override
public void onServiceDisconnected(ComponentName arg0) {
Log.i(TAG, "onServiceDisconnected");
}

public Notification getNotification(Context context) {
Intent notificationIntent = new Intent (context, MainActivity.class);
PendingIntent pendingIntent =
PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(BuildConfig.APPLICATION_ID, "blixt", NotificationManager.IMPORTANCE_NONE);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
notificationManager = NotificationManagerCompat.from(context);
notificationManager.createNotificationChannel(channel);
notificationManager.createNotificationChannel(channel);
}
return new Notification.Builder(context, BuildConfig.APPLICATION_ID)
.setContentTitle("Tor")
.setContentText("Tor is running in the background")
.setSmallIcon(R.drawable.ic_stat_ic_notification)
.setContentIntent(pendingIntent)
.setTicker("Blixt Wallet")
.setOngoing(true)
.build();
}
};

private final BroadcastReceiver torBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Toast.makeText(context, intent.getStringExtra(TorService.EXTRA_STATUS), Toast.LENGTH_SHORT).show();
String status = intent.getStringExtra(TorService.EXTRA_STATUS);
if (intent != null && intent.getAction() != null && intent.getAction().equals("org.torproject.android.intent.action.STOP")) {
torService.stopSelf();
}
currentTorStatus = status;
Log.i(TAG, "onReceive " + status);
if (status.equals(TorService.STATUS_ON)) {
while (calleeResolvers.size() > 0) {
calleeResolvers.pop().resolve(TorService.socksPort);
}
} else if (status.equals(TorService.STATUS_OFF)) {
getReactApplicationContext().unregisterReceiver(torBroadcastReceiver);
}
}
};

public BlixtTor(ReactApplicationContext reactContext) {
super(reactContext);
}

public String getName() {
return "BlixtTor";
}

@ReactMethod
public void startTor(Promise promise) {
Log.i(TAG, "startTor()");
// if (calleeResolver != null) {
// Log.i(TAG, "calleeResolver != null");
// promise.reject(TAG, "Tor already in progress starting");
// return;
// }
if (currentTorStatus.equals(TorService.STATUS_ON)) {
Log.i(TAG, "socksPort = " + TorService.socksPort + ", currentTorStatus.equals(TorService.STATUS_ON) " + currentTorStatus.equals(TorService.STATUS_ON));
promise.resolve(TorService.socksPort);
return;
}
calleeResolvers.add(promise);

boolean persistentServicesEnabled = getPersistentServicesEnabled(getReactApplicationContext());
getReactApplicationContext().registerReceiver(torBroadcastReceiver, new IntentFilter(TorService.ACTION_STATUS));
Intent intent = new Intent(getReactApplicationContext(), TorService.class);

if (persistentServicesEnabled) {
getReactApplicationContext().startForegroundService(intent);
}
getReactApplicationContext().bindService(intent, torServiceConnection, Context.BIND_AUTO_CREATE);
}

@ReactMethod
public void stopTor(Promise promise) {
if (notificationManager != null) {
notificationManager.cancelAll();
}
Log.i(TAG,"Unbinding TorService");
try {
getReactApplicationContext().unbindService(torServiceConnection);
} catch (IllegalArgumentException e) {
Log.w(TAG, "Tried to unbindService on unbound service");
}
promise.resolve(true);
};

@ReactMethod
public void showMsg(Promise promise) {
MainActivity.getActivity().showMsg();
promise.resolve(true);
}
}
22 changes: 22 additions & 0 deletions android/app/src/main/java/com/blixtwallet/BlixtTorPackage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.blixtwallet;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class BlixtTorPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}

@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.asList(new BlixtTor(reactContext));
}
}
16 changes: 16 additions & 0 deletions android/app/src/main/java/com/blixtwallet/BlixtTorUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.blixtwallet;

import com.blixtwallet.BuildConfig;

public class BlixtTorUtils {
public static int getListenPort() {
int listenPort = 9760;
if (BuildConfig.CHAIN.equals("testnet")) {
listenPort += 10;
}
if (BuildConfig.DEBUG) {
listenPort += 100;
}
return listenPort;
}
}
40 changes: 25 additions & 15 deletions android/app/src/main/java/com/blixtwallet/LndMobile.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.blixtwallet;

// import com.blixtwallet.tor.BlixtTorUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
Expand Down Expand Up @@ -63,14 +61,13 @@
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.modules.permissions.PermissionsModule;

import com.reactnativecommunity.asyncstorage.AsyncLocalStorageUtil;
import com.jakewharton.processphoenix.ProcessPhoenix;
import com.oblador.keychain.KeychainModule;
import com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier;

import com.hypertrack.hyperlog.HyperLog;

import org.torproject.jni.TorService;

// TODO break this class up
class LndMobile extends ReactContextBaseJavaModule {
private final String TAG = "LndMobile";
Expand Down Expand Up @@ -242,6 +239,17 @@ public void onServiceDisconnected(ComponentName className) {

private LndMobileServiceConnection lndMobileServiceConnection;

private boolean getPersistentServicesEnabled(Context context) {
ReactDatabaseSupplier dbSupplier = ReactDatabaseSupplier.getInstance(context);
SQLiteDatabase db = dbSupplier.get();
String persistentServicesEnabled = AsyncLocalStorageUtil.getItemImpl(db, "persistentServicesEnabled");
if (persistentServicesEnabled != null) {
return persistentServicesEnabled.equals("true");
}
HyperLog.w(TAG, "Could not find persistentServicesEnabled in asyncStorage");
return false;
}

public LndMobile(ReactApplicationContext reactContext) {
super(reactContext);
}
Expand Down Expand Up @@ -284,9 +292,13 @@ public void initialize(Promise promise) {

lndMobileServiceConnection = new LndMobileServiceConnection(req);
messenger = new Messenger(new IncomingHandler()); // me

Intent intent = new Intent(getReactApplicationContext(), LndMobileService.class);
if (getPersistentServicesEnabled(getReactApplicationContext())) {
getReactApplicationContext().startForegroundService(intent);
}
// else rely on bindService to start LND
getReactApplicationContext().bindService(
new Intent(getReactApplicationContext(), LndMobileService.class),
intent,
lndMobileServiceConnection,
Context.BIND_AUTO_CREATE
);
Expand Down Expand Up @@ -353,13 +365,11 @@ public void startLnd(boolean torEnabled, String args, Promise promise) {

String params = "--lnddir=" + getReactApplicationContext().getFilesDir().getPath();
if (torEnabled) {
// int listenPort = BlixtTorUtils.getListenPort();
// int socksPort = BlixtTorUtils.getSocksPort();
// int controlPort = BlixtTorUtils.getControlPort();
// params += " --tor.active --tor.socks=127.0.0.1:" + socksPort + " --tor.control=127.0.0.1:" + controlPort;
// params += " --tor.v3 --listen=localhost:" + listenPort;
}
else {
int listenPort = BlixtTorUtils.getListenPort();
String controlSocket = "unix://" + getReactApplicationContext().getDir(TorService.class.getSimpleName(), Context.MODE_PRIVATE).getAbsolutePath() + "/data/ControlSocket";
params += " --tor.active --tor.control=" + controlSocket;
params += " --tor.v3 --listen=localhost:" + listenPort;
} else {
// If Tor isn't active, make sure we aren't
// listening at all
params += " --nolisten";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,19 @@
package com.blixtwallet;

import android.util.Log;
import android.content.ComponentName;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;

import androidx.work.Constraints;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.PeriodicWorkRequest;
import androidx.work.NetworkType;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;
import androidx.lifecycle.Observer;
import androidx.annotation.Nullable;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;
import com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier;
import com.reactnativecommunity.asyncstorage.AsyncLocalStorageUtil;

import com.google.common.util.concurrent.ListenableFuture;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import com.hypertrack.hyperlog.HyperLog;
Expand Down
Loading

1 comment on commit e26c5bc

@vercel
Copy link

@vercel vercel bot commented on e26c5bc Jul 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

blixt-wallet – ./

blixt-wallet-git-master-hsjoberg.vercel.app
blixt-wallet-hsjoberg.vercel.app

Please sign in to comment.