Fixed problem with proof of work

This commit is contained in:
Christian Basler 2016-01-23 23:30:51 +01:00
parent 491a8a0ccb
commit adfb3a920a
6 changed files with 107 additions and 79 deletions

View File

@ -9,7 +9,7 @@ android {
applicationId "ch.dissem.apps.abit" applicationId "ch.dissem.apps.abit"
minSdkVersion 19 minSdkVersion 19
targetSdkVersion 23 targetSdkVersion 23
versionCode 3 versionCode 5
versionName "1.0-beta" versionName "1.0-beta"
} }
signingConfigs { signingConfigs {
@ -29,16 +29,17 @@ android {
} }
} }
ext.jabitVersion = '1.0.0'
dependencies { dependencies {
compile fileTree(dir: 'libs', include: ['*.jar']) compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.1.1' compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:support-v4:23.1.1' compile 'com.android.support:support-v4:23.1.1'
compile 'com.android.support:design:23.1.1' compile 'com.android.support:design:23.1.1'
compile 'ch.dissem.jabit:jabit-core:0.2.1-SNAPSHOT' compile "ch.dissem.jabit:jabit-core:$jabitVersion"
compile 'ch.dissem.jabit:jabit-networking:0.2.1-SNAPSHOT' compile "ch.dissem.jabit:jabit-networking:$jabitVersion"
compile 'ch.dissem.jabit:jabit-cryptography-spongy:0.2.1-SNAPSHOT' compile "ch.dissem.jabit:jabit-cryptography-spongy:$jabitVersion"
compile 'ch.dissem.jabit:jabit-extensions:0.2.1-SNAPSHOT' compile "ch.dissem.jabit:jabit-extensions:$jabitVersion"
compile 'org.slf4j:slf4j-android:1.7.12' compile 'org.slf4j:slf4j-android:1.7.12'

View File

@ -32,23 +32,30 @@ public class ProofOfWorkNotification extends AbstractNotification {
public ProofOfWorkNotification(Context ctx) { public ProofOfWorkNotification(Context ctx) {
super(ctx); super(ctx);
NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx); update(1);
Intent showMessageIntent = new Intent(ctx, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0, showMessageIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setUsesChronometer(true)
.setSmallIcon(R.drawable.ic_notification_proof_of_work)
.setContentTitle(ctx.getString(R.string.proof_of_work_title))
.setContentText(ctx.getString(R.string.proof_of_work_text))
.setContentIntent(pendingIntent);
notification = builder.build();
} }
@Override @Override
protected int getNotificationId() { protected int getNotificationId() {
return ONGOING_NOTIFICATION_ID; return ONGOING_NOTIFICATION_ID;
} }
public ProofOfWorkNotification update(int numberOfItems) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx);
Intent showMessageIntent = new Intent(ctx, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0, showMessageIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setUsesChronometer(true)
.setOngoing(true)
.setSmallIcon(R.drawable.ic_notification_proof_of_work)
.setContentTitle(ctx.getString(R.string.proof_of_work_title))
.setContentText(ctx.getString(R.string.proof_of_work_text, numberOfItems))
.setContentIntent(pendingIntent);
notification = builder.build();
return this;
}
} }

View File

@ -22,10 +22,8 @@ import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.slf4j.Logger; import java.util.LinkedList;
import org.slf4j.LoggerFactory; import java.util.Queue;
import java.lang.ref.WeakReference;
import ch.dissem.apps.abit.notification.ProofOfWorkNotification; import ch.dissem.apps.abit.notification.ProofOfWorkNotification;
import ch.dissem.bitmessage.ports.MultiThreadedPOWEngine; import ch.dissem.bitmessage.ports.MultiThreadedPOWEngine;
@ -38,11 +36,12 @@ import static ch.dissem.apps.abit.notification.ProofOfWorkNotification.ONGOING_N
* killed by the system before the nonce is found. * killed by the system before the nonce is found.
*/ */
public class ProofOfWorkService extends Service { public class ProofOfWorkService extends Service {
public static final Logger LOG = LoggerFactory.getLogger(ProofOfWorkService.class);
// Object to use as a thread-safe lock // Object to use as a thread-safe lock
private static final Object lock = new Object(); private static final Object lock = new Object();
private static ProofOfWorkEngine engine; private static ProofOfWorkEngine engine;
private static boolean calculating;
private static final Queue<PowItem> queue = new LinkedList<>();
private static ProofOfWorkNotification notification;
@Override @Override
public void onCreate() { public void onCreate() {
@ -51,54 +50,74 @@ public class ProofOfWorkService extends Service {
engine = new MultiThreadedPOWEngine(); engine = new MultiThreadedPOWEngine();
} }
} }
notification = new ProofOfWorkNotification(this);
} }
@Nullable @Nullable
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
return new PowBinder(engine, this); return new PowBinder(this);
} }
public static class PowBinder extends Binder { public static class PowBinder extends Binder {
private final ProofOfWorkEngine engine; private final ProofOfWorkService service;
private PowBinder(ProofOfWorkEngine engine, ProofOfWorkService service) { private PowBinder(ProofOfWorkService service) {
this.engine = new EngineWrapper(engine, service); this.service = service;
} }
public ProofOfWorkEngine getEngine() { public void process(PowItem item) {
return engine; synchronized (queue) {
}
}
private static class EngineWrapper implements ProofOfWorkEngine {
private final ProofOfWorkNotification notification;
private final ProofOfWorkEngine engine;
private final WeakReference<ProofOfWorkService> serviceRef;
private EngineWrapper(ProofOfWorkEngine engine, ProofOfWorkService service) {
this.engine = engine;
this.serviceRef = new WeakReference<>(service);
this.notification = new ProofOfWorkNotification(service);
}
@Override
public void calculateNonce(byte[] initialHash, byte[] target, final Callback callback) {
final ProofOfWorkService service = serviceRef.get();
service.startService(new Intent(service, ProofOfWorkService.class)); service.startService(new Intent(service, ProofOfWorkService.class));
service.startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification()); service.startForeground(ONGOING_NOTIFICATION_ID,
engine.calculateNonce(initialHash, target, new ProofOfWorkEngine.Callback() { notification.getNotification());
if (!calculating) {
calculating = true;
service.calculateNonce(item);
} else {
queue.add(item);
notification.update(queue.size()).show();
}
}
}
}
static class PowItem {
private final byte[] initialHash;
private final byte[] targetValue;
private final ProofOfWorkEngine.Callback callback;
PowItem(byte[] initialHash, byte[] targetValue, ProofOfWorkEngine.Callback callback) {
this.initialHash = initialHash;
this.targetValue = targetValue;
this.callback = callback;
}
}
private void calculateNonce(final PowItem item) {
engine.calculateNonce(item.initialHash, item.targetValue, new ProofOfWorkEngine.Callback() {
@Override @Override
public void onNonceCalculated(byte[] initialHash, byte[] nonce) { public void onNonceCalculated(byte[] initialHash, byte[] nonce) {
try { try {
callback.onNonceCalculated(initialHash, nonce); item.callback.onNonceCalculated(initialHash, nonce);
} finally { } finally {
service.stopForeground(true); PowItem item;
service.stopSelf(); synchronized (queue) {
item = queue.poll();
if (item == null) {
calculating = false;
stopForeground(true);
stopSelf();
} else {
notification.update(queue.size()).show();
}
}
if (item != null) {
calculateNonce(item);
}
} }
} }
}); });
}
} }
} }

View File

@ -22,9 +22,11 @@ import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.os.IBinder; import android.os.IBinder;
import java.util.concurrent.Semaphore; import java.util.LinkedList;
import java.util.Queue;
import ch.dissem.apps.abit.service.ProofOfWorkService.PowBinder; import ch.dissem.apps.abit.service.ProofOfWorkService.PowBinder;
import ch.dissem.apps.abit.service.ProofOfWorkService.PowItem;
import ch.dissem.bitmessage.ports.ProofOfWorkEngine; import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
import static android.content.Context.BIND_AUTO_CREATE; import static android.content.Context.BIND_AUTO_CREATE;
@ -32,12 +34,12 @@ import static android.content.Context.BIND_AUTO_CREATE;
/** /**
* Proof of Work engine that uses the Proof of Work service. * Proof of Work engine that uses the Proof of Work service.
*/ */
public class ServicePowEngine implements ProofOfWorkEngine, ProofOfWorkEngine.Callback { public class ServicePowEngine implements ProofOfWorkEngine {
private final Semaphore semaphore = new Semaphore(1, true);
private final Context ctx; private final Context ctx;
private byte[] initialHash, targetValue; private static final Object lock = new Object();
private Callback callback; private Queue<PowItem> queue = new LinkedList<>();
private PowBinder service;
public ServicePowEngine(Context ctx) { public ServicePowEngine(Context ctx) {
this.ctx = ctx; this.ctx = ctx;
@ -46,32 +48,31 @@ public class ServicePowEngine implements ProofOfWorkEngine, ProofOfWorkEngine.Ca
private ServiceConnection connection = new ServiceConnection() { private ServiceConnection connection = new ServiceConnection() {
@Override @Override
public void onServiceConnected(ComponentName name, IBinder service) { public void onServiceConnected(ComponentName name, IBinder service) {
((PowBinder) service).getEngine().calculateNonce(initialHash, targetValue, ServicePowEngine.this); synchronized (lock) {
ServicePowEngine.this.service = (PowBinder) service;
while (!queue.isEmpty()) {
ServicePowEngine.this.service.process(queue.poll());
}
}
} }
@Override @Override
public void onServiceDisconnected(ComponentName name) { public void onServiceDisconnected(ComponentName name) {
semaphore.release(); service = null;
} }
}; };
@Override @Override
public void calculateNonce(byte[] initialHash, byte[] targetValue, Callback callback) { public void calculateNonce(byte[] initialHash, byte[] targetValue, Callback callback) {
try { PowItem item = new PowItem(initialHash, targetValue, callback);
semaphore.acquire(); synchronized (lock) {
} catch (InterruptedException e) { if (service != null) {
throw new RuntimeException(e); service.process(item);
} else {
queue.add(item);
ctx.bindService(new Intent(ctx, ProofOfWorkService.class), connection,
BIND_AUTO_CREATE);
} }
this.initialHash = initialHash;
this.targetValue = targetValue;
this.callback = callback;
ctx.bindService(new Intent(ctx, ProofOfWorkService.class), connection, BIND_AUTO_CREATE);
} }
@Override
public void onNonceCalculated(byte[] initialHash, byte[] bytes) {
callback.onNonceCalculated(initialHash, bytes);
ctx.unbindService(connection);
} }
} }

View File

@ -42,7 +42,7 @@
<string name="connection_info_n">Stream %1$d: %2$d Verbindungen</string> <string name="connection_info_n">Stream %1$d: %2$d Verbindungen</string>
<string name="connection_info_disconnected">Getrennt</string> <string name="connection_info_disconnected">Getrennt</string>
<string name="connection_info_pending">Verbindung wird aufgebaut…</string> <string name="connection_info_pending">Verbindung wird aufgebaut…</string>
<string name="proof_of_work_text">Warnung: dies könnte das Gerät erwärmen bis die Batterie leer ist.</string> <string name="proof_of_work_text">Arbeite am Versenden (%1$d in Warteschlange)</string>
<string name="proof_of_work_title">Proof of Work</string> <string name="proof_of_work_title">Proof of Work</string>
<string name="error_invalid_sync_host">Synchronisation fehlgeschlagen: der vertrauenswürdige Knoten konnte nicht erreicht werden.</string> <string name="error_invalid_sync_host">Synchronisation fehlgeschlagen: der vertrauenswürdige Knoten konnte nicht erreicht werden.</string>
<string name="error_invalid_sync_port">Ungültiger Port in den Synchronisationseinstellungen: %s</string> <string name="error_invalid_sync_port">Ungültiger Port in den Synchronisationseinstellungen: %s</string>

View File

@ -44,7 +44,7 @@
<string name="connection_info_disconnected">Disconnected</string> <string name="connection_info_disconnected">Disconnected</string>
<string name="connection_info_pending">Connecting…</string> <string name="connection_info_pending">Connecting…</string>
<string name="proof_of_work_title">Proof of Work</string> <string name="proof_of_work_title">Proof of Work</string>
<string name="proof_of_work_text">Warning: This might heat your device until the battery\'s dead.</string> <string name="proof_of_work_text">Doing work to send message (%1$d queued)</string>
<string name="error_invalid_sync_port">Invalid port in synchronization settings: %s</string> <string name="error_invalid_sync_port">Invalid port in synchronization settings: %s</string>
<string name="error_invalid_sync_host">Synchronization failed: Trusted node could not be reached.</string> <string name="error_invalid_sync_host">Synchronization failed: Trusted node could not be reached.</string>
<string name="compose_body_hint">Write message</string> <string name="compose_body_hint">Write message</string>