diff --git a/android/app/build.gradle b/android/app/build.gradle
index d12aa37..a0da5dd 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -39,6 +39,10 @@ dependencies {
implementation "org.mindrot:jbcrypt:0.4"
implementation "at.favre.lib:bcrypt:0.10.2"
implementation 'com.password4j:password4j:1.8.2'
+ implementation 'com.dylibso.chicory:runtime:1.0.0-M1'
+ implementation 'commons-net:commons-net:3.6'
+ implementation 'org.bouncycastle:bcprov-jdk15to18:1.76'
+ implementation 'com.google.guava:guava:32.1.2-jre'
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
diff --git a/android/app/src/main/java/com/github/Qortal/qortalMobile/Crypto.java b/android/app/src/main/java/com/github/Qortal/qortalMobile/Crypto.java
new file mode 100644
index 0000000..803def6
--- /dev/null
+++ b/android/app/src/main/java/com/github/Qortal/qortalMobile/Crypto.java
@@ -0,0 +1,126 @@
+package com.github.Qortal.qortalMobile;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public abstract class Crypto {
+
+ /**
+ * Returns 32-byte SHA-256 digest of message passed in input.
+ *
+ * @param input
+ * variable-length byte[] message
+ * @return byte[32] digest, or null if SHA-256 algorithm can't be accessed
+ */
+ public static byte[] digest(byte[] input) {
+ if (input == null)
+ return null;
+
+ try {
+ // SHA2-256
+ MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
+ return sha256.digest(input);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("SHA-256 message digest not available");
+ }
+ }
+
+ /**
+ * Returns 32-byte SHA-256 digest of message passed in input.
+ *
+ * @param input
+ * variable-length ByteBuffer message
+ * @return byte[32] digest, or null if SHA-256 algorithm can't be accessed
+ */
+ public static byte[] digest(ByteBuffer input) {
+ if (input == null)
+ return null;
+
+ try {
+ // SHA2-256
+ MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
+ sha256.update(input);
+ return sha256.digest();
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("SHA-256 message digest not available");
+ }
+ }
+
+ /**
+ * Returns 32-byte digest of two rounds of SHA-256 on message passed in input.
+ *
+ * @param input
+ * variable-length byte[] message
+ * @return byte[32] digest, or null if SHA-256 algorithm can't be accessed
+ */
+ public static byte[] doubleDigest(byte[] input) {
+ return digest(digest(input));
+ }
+
+ /**
+ * Returns 32-byte SHA-256 digest of file passed in input.
+ *
+ * @param file
+ * file in which to perform digest
+ * @return byte[32] digest, or null if SHA-256 algorithm can't be accessed
+ *
+ * @throws IOException if the file cannot be read
+ */
+ public static byte[] digest(File file) throws IOException {
+ return Crypto.digest(file, 8192);
+ }
+
+ /**
+ * Returns 32-byte SHA-256 digest of file passed in input, in hex format
+ *
+ * @param file
+ * file in which to perform digest
+ * @return String digest as a hexadecimal string, or null if SHA-256 algorithm can't be accessed
+ *
+ * @throws IOException if the file cannot be read
+ */
+ public static String digestHexString(File file, int bufferSize) throws IOException {
+ byte[] digest = Crypto.digest(file, bufferSize);
+
+ // Convert to hex
+ StringBuilder stringBuilder = new StringBuilder();
+ for (byte b : digest) {
+ stringBuilder.append(String.format("%02x", b));
+ }
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Returns 32-byte SHA-256 digest of file passed in input.
+ *
+ * @param file
+ * file in which to perform digest
+ * @param bufferSize
+ * the number of bytes to load into memory
+ * @return byte[32] digest, or null if SHA-256 algorithm can't be accessed
+ *
+ * @throws IOException if the file cannot be read
+ */
+ public static byte[] digest(File file, int bufferSize) throws IOException {
+ try {
+ MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
+ FileInputStream fileInputStream = new FileInputStream(file);
+ byte[] bytes = new byte[bufferSize];
+ int count;
+
+ while ((count = fileInputStream.read(bytes)) != -1) {
+ sha256.update(bytes, 0, count);
+ }
+ fileInputStream.close();
+
+ return sha256.digest();
+
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("SHA-256 message digest not available");
+ }
+ }
+}
diff --git a/android/app/src/main/java/com/github/Qortal/qortalMobile/MainActivity.java b/android/app/src/main/java/com/github/Qortal/qortalMobile/MainActivity.java
index 1351888..5a50ead 100644
--- a/android/app/src/main/java/com/github/Qortal/qortalMobile/MainActivity.java
+++ b/android/app/src/main/java/com/github/Qortal/qortalMobile/MainActivity.java
@@ -2,12 +2,14 @@ package com.github.Qortal.qortalMobile;
import com.getcapacitor.BridgeActivity;
import com.github.Qortal.qortalMobile.NativeBcrypt;
+import com.github.Qortal.qortalMobile.NativePOW;
import android.os.Bundle;
public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
registerPlugin(NativeBcrypt.class);
+ registerPlugin(NativePOW.class);
super.onCreate(savedInstanceState);
diff --git a/android/app/src/main/java/com/github/Qortal/qortalMobile/MemoryPoW.java b/android/app/src/main/java/com/github/Qortal/qortalMobile/MemoryPoW.java
new file mode 100644
index 0000000..686a20e
--- /dev/null
+++ b/android/app/src/main/java/com/github/Qortal/qortalMobile/MemoryPoW.java
@@ -0,0 +1,168 @@
+package com.github.Qortal.qortalMobile;
+
+
+import com.github.Qortal.qortalMobile.NTP;
+import com.github.Qortal.qortalMobile.Crypto;
+
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeoutException;
+
+public class MemoryPoW {
+
+ /**
+ * Compute a MemoryPoW nonce
+ *
+ * @param data
+ * @param workBufferLength
+ * @param difficulty
+ * @return
+ * @throws TimeoutException
+ */
+ public static Integer compute2(byte[] data, int workBufferLength, long difficulty) {
+ try {
+ return MemoryPoW.compute2(data, workBufferLength, difficulty, null);
+
+ } catch (TimeoutException e) {
+ // This won't happen, because above timeout is null
+ return null;
+ }
+ }
+
+ /**
+ * Compute a MemoryPoW nonce, with optional timeout
+ *
+ * @param data
+ * @param workBufferLength
+ * @param difficulty
+ * @param timeout maximum number of milliseconds to compute for before giving up,
or null if no timeout
+ * @return
+ * @throws TimeoutException
+ */
+ public static Integer compute2(byte[] data, int workBufferLength, long difficulty, Long timeout) throws TimeoutException {
+ long startTime = NTP.getTime() != null ? NTP.getTime() : System.currentTimeMillis();
+
+ // Hash data with SHA256
+ byte[] hash = Crypto.digest(data);
+
+ long[] longHash = new long[4];
+ ByteBuffer byteBuffer = ByteBuffer.wrap(hash);
+ longHash[0] = byteBuffer.getLong();
+ longHash[1] = byteBuffer.getLong();
+ longHash[2] = byteBuffer.getLong();
+ longHash[3] = byteBuffer.getLong();
+ byteBuffer = null;
+
+ int longBufferLength = workBufferLength / 8;
+ long[] workBuffer = new long[longBufferLength];
+ long[] state = new long[4];
+
+ long seed = 8682522807148012L;
+ long seedMultiplier = 1181783497276652981L;
+
+ // For each nonce...
+ int nonce = -1;
+ long result = 0;
+ do {
+ ++nonce;
+
+ // If we've been interrupted, exit fast with invalid value
+ if (Thread.currentThread().isInterrupted())
+ return -1;
+
+ if (timeout != null) {
+ long now = NTP.getTime() != null ? NTP.getTime() : System.currentTimeMillis();
+ if (now > startTime + timeout) {
+ throw new TimeoutException("Timeout reached");
+ }
+ }
+
+ seed *= seedMultiplier; // per nonce
+
+ state[0] = longHash[0] ^ seed;
+ state[1] = longHash[1] ^ seed;
+ state[2] = longHash[2] ^ seed;
+ state[3] = longHash[3] ^ seed;
+
+ // Fill work buffer with random
+ for (int i = 0; i < workBuffer.length; ++i)
+ workBuffer[i] = xoshiro256p(state);
+
+ // Random bounce through whole buffer
+ result = workBuffer[0];
+ for (int i = 0; i < 1024; ++i) {
+ int index = (int) (xoshiro256p(state) & Integer.MAX_VALUE) % workBuffer.length;
+ result ^= workBuffer[index];
+ }
+
+ // Return if final value > difficulty
+ } while (Long.numberOfLeadingZeros(result) < difficulty);
+
+ return nonce;
+ }
+
+
+ public static boolean verify2(byte[] data, int workBufferLength, long difficulty, int nonce) {
+ return verify2(data, null, workBufferLength, difficulty, nonce);
+ }
+
+ public static boolean verify2(byte[] data, long[] workBuffer, int workBufferLength, long difficulty, int nonce) {
+ // Hash data with SHA256
+ byte[] hash = Crypto.digest(data);
+
+ long[] longHash = new long[4];
+ ByteBuffer byteBuffer = ByteBuffer.wrap(hash);
+ longHash[0] = byteBuffer.getLong();
+ longHash[1] = byteBuffer.getLong();
+ longHash[2] = byteBuffer.getLong();
+ longHash[3] = byteBuffer.getLong();
+ byteBuffer = null;
+
+ int longBufferLength = workBufferLength / 8;
+
+ if (workBuffer == null)
+ workBuffer = new long[longBufferLength];
+
+ long[] state = new long[4];
+
+ long seed = 8682522807148012L;
+ long seedMultiplier = 1181783497276652981L;
+
+ for (int i = 0; i <= nonce; ++i)
+ seed *= seedMultiplier;
+
+ state[0] = longHash[0] ^ seed;
+ state[1] = longHash[1] ^ seed;
+ state[2] = longHash[2] ^ seed;
+ state[3] = longHash[3] ^ seed;
+
+ // Fill work buffer with random
+ for (int i = 0; i < workBuffer.length; ++i)
+ workBuffer[i] = xoshiro256p(state);
+
+ // Random bounce through whole buffer
+ long result = workBuffer[0];
+ for (int i = 0; i < 1024; ++i) {
+ int index = (int) (xoshiro256p(state) & Integer.MAX_VALUE) % workBuffer.length;
+ result ^= workBuffer[index];
+ }
+
+ return Long.numberOfLeadingZeros(result) >= difficulty;
+ }
+
+ private static final long xoshiro256p(long[] state) {
+ final long result = state[0] + state[3];
+ final long temp = state[1] << 17;
+
+ state[2] ^= state[0];
+ state[3] ^= state[1];
+ state[1] ^= state[2];
+ state[0] ^= state[3];
+
+ state[2] ^= temp;
+ state[3] = (state[3] << 45) | (state[3] >>> (64 - 45)); // rol64(s[3], 45);
+
+ return result;
+ }
+
+}
diff --git a/android/app/src/main/java/com/github/Qortal/qortalMobile/NTP.java b/android/app/src/main/java/com/github/Qortal/qortalMobile/NTP.java
new file mode 100644
index 0000000..f0a21fd
--- /dev/null
+++ b/android/app/src/main/java/com/github/Qortal/qortalMobile/NTP.java
@@ -0,0 +1,241 @@
+package com.github.Qortal.qortalMobile;
+
+import org.apache.commons.net.ntp.NTPUDPClient;
+import org.apache.commons.net.ntp.NtpV3Packet;
+import org.apache.commons.net.ntp.TimeInfo;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.*;
+import java.util.stream.Collectors;
+
+public class NTP implements Runnable {
+
+ private static final String TAG = "NTP";
+
+ private static boolean isStarted = false;
+ private static volatile boolean isStopping = false;
+ private static ExecutorService instanceExecutor;
+ private static NTP instance;
+ private static volatile boolean isOffsetSet = false;
+ private static volatile long offset = 0;
+
+ static class NTPServer {
+ private static final int MIN_POLL = 64;
+
+ public char usage = ' ';
+ public String remote;
+ public String refId;
+ public Integer stratum;
+ public char type = 'u'; // unicast
+ public int poll = MIN_POLL;
+ public byte reach = 0;
+ public Long delay;
+ public Double offset;
+ public Double jitter;
+
+ private Deque offsets = new LinkedList<>();
+ private double totalSquareOffsets = 0.0;
+ private long nextPoll;
+ private Long lastGood;
+
+ public NTPServer(String remote) {
+ this.remote = remote;
+ }
+
+ public boolean doPoll(NTPUDPClient client, final long now) {
+ Thread.currentThread().setName(String.format("NTP: %s", this.remote));
+
+ try {
+ boolean isUpdated = false;
+ try {
+ TimeInfo timeInfo = client.getTime(InetAddress.getByName(remote));
+ timeInfo.computeDetails();
+ NtpV3Packet ntpMessage = timeInfo.getMessage();
+
+ this.refId = ntpMessage.getReferenceIdString();
+ this.stratum = ntpMessage.getStratum();
+ this.poll = Math.max(MIN_POLL, 1 << ntpMessage.getPoll());
+
+ this.delay = timeInfo.getDelay();
+ this.offset = (double) timeInfo.getOffset();
+
+ if (this.offsets.size() == 8) {
+ double oldOffset = this.offsets.removeFirst();
+ this.totalSquareOffsets -= oldOffset * oldOffset;
+ }
+
+ this.offsets.addLast(this.offset);
+ this.totalSquareOffsets += this.offset * this.offset;
+
+ this.jitter = Math.sqrt(this.totalSquareOffsets / this.offsets.size());
+
+ this.reach = (byte) ((this.reach << 1) | 1);
+ this.lastGood = now;
+
+ isUpdated = true;
+ } catch (IOException e) {
+ this.reach <<= 1;
+ Log.e(TAG, "Error polling server: " + remote, e);
+ }
+
+ this.nextPoll = now + this.poll * 1000;
+ return isUpdated;
+ } finally {
+ Thread.currentThread().setName("NTP (dormant)");
+ }
+ }
+
+ public Integer getWhen() {
+ if (this.lastGood == null)
+ return null;
+
+ return (int) ((System.currentTimeMillis() - this.lastGood) / 1000);
+ }
+ }
+
+ private final NTPUDPClient client;
+ private final List ntpServers = new ArrayList<>();
+ private final ExecutorService serverExecutor;
+
+ private NTP(String[] serverNames) {
+ client = new NTPUDPClient();
+ client.setDefaultTimeout(2000);
+
+ for (String serverName : serverNames)
+ ntpServers.add(new NTPServer(serverName));
+
+ serverExecutor = Executors.newCachedThreadPool();
+ }
+
+ public static synchronized void start(String[] serverNames) {
+ if (isStarted)
+ return;
+
+ isStarted = true;
+ instanceExecutor = Executors.newSingleThreadExecutor();
+ instance = new NTP(serverNames);
+ instanceExecutor.execute(instance);
+ Log.d(TAG, "NTP started with servers: " + String.join(", ", serverNames));
+ }
+
+ public static void shutdownNow() {
+ if (instanceExecutor != null)
+ instanceExecutor.shutdownNow();
+ Log.d(TAG, "NTP shutdown.");
+ }
+
+ public static synchronized void setFixedOffset(Long offset) {
+ NTP.offset = offset;
+ isOffsetSet = true;
+ Log.d(TAG, "Fixed offset set: " + offset);
+ }
+
+ public static Long getTime() {
+ if (!isOffsetSet)
+ return null;
+
+ return System.currentTimeMillis() + NTP.offset;
+ }
+
+ public void run() {
+ Thread.currentThread().setName("NTP instance");
+
+ try {
+ while (!isStopping) {
+ Thread.sleep(1000);
+
+ boolean haveUpdates = pollServers();
+ if (!haveUpdates)
+ continue;
+
+ calculateOffset();
+ }
+ } catch (InterruptedException e) {
+ Log.d(TAG, "NTP instance interrupted.");
+ }
+ }
+
+ private boolean pollServers() throws InterruptedException {
+ final long now = System.currentTimeMillis();
+
+ List pendingServers = ntpServers.stream().filter(ntpServer -> now >= ntpServer.nextPoll).collect(Collectors.toList());
+
+ CompletionService ecs = new ExecutorCompletionService<>(serverExecutor);
+ for (NTPServer server : pendingServers)
+ ecs.submit(() -> server.doPoll(client, now));
+
+ boolean haveUpdate = false;
+ for (int i = 0; i < pendingServers.size(); ++i) {
+ if (isStopping)
+ return false;
+
+ try {
+ haveUpdate = ecs.take().get() || haveUpdate;
+ } catch (ExecutionException e) {
+ Log.e(TAG, "Error during server polling", e);
+ }
+ }
+
+ return haveUpdate;
+ }
+
+ private void calculateOffset() {
+ double s0 = 0;
+ double s1 = 0;
+ double s2 = 0;
+
+ for (NTPServer server : ntpServers) {
+ if (server.offset == null) {
+ server.usage = ' ';
+ continue;
+ }
+
+ server.usage = '+';
+ double value = server.offset * (double) server.stratum;
+
+ s0 += 1;
+ s1 += value;
+ s2 += value * value;
+ }
+
+ if (s0 < ntpServers.size() / 3 + 1) {
+ Log.d(TAG, String.format("Not enough replies (%d) to calculate network time", (int) s0));
+ } else {
+ double thresholdStddev = Math.sqrt(((s0 * s2) - (s1 * s1)) / (s0 * (s0 - 1)));
+ double mean = s1 / s0;
+
+ // Now only consider offsets within 1 stddev
+ s0 = 0;
+ s1 = 0;
+ s2 = 0;
+
+ for (NTPServer server : ntpServers) {
+ if (server.offset == null || server.reach == 0)
+ continue;
+
+ if (Math.abs(server.offset * (double) server.stratum - mean) > thresholdStddev)
+ continue;
+
+ server.usage = '*';
+ s0 += 1;
+ s1 += server.offset;
+ s2 += server.offset * server.offset;
+ }
+
+ if (s0 > 1) {
+ double filteredMean = s1 / s0;
+ NTP.offset = (long) filteredMean;
+ isOffsetSet = true;
+ Log.d(TAG, "New NTP offset: " + NTP.offset);
+ } else {
+ Log.d(TAG, "Not enough useful values to calculate network time.");
+ }
+ }
+ }
+}
diff --git a/android/app/src/main/java/com/github/Qortal/qortalMobile/NativePOW.java b/android/app/src/main/java/com/github/Qortal/qortalMobile/NativePOW.java
new file mode 100644
index 0000000..6d39c67
--- /dev/null
+++ b/android/app/src/main/java/com/github/Qortal/qortalMobile/NativePOW.java
@@ -0,0 +1,56 @@
+package com.github.Qortal.qortalMobile;
+
+import com.getcapacitor.Plugin;
+import com.getcapacitor.PluginCall;
+import com.getcapacitor.JSObject;
+import com.getcapacitor.PluginMethod;
+import com.getcapacitor.annotation.CapacitorPlugin;
+import android.util.Log;
+
+import com.github.Qortal.qortalMobile.MemoryPoW;
+
+import java.util.Iterator;
+
+@CapacitorPlugin(name = "NativePOW")
+public class NativePOW extends Plugin {
+
+ @PluginMethod
+ public void computeProofOfWork(PluginCall call) {
+ try {
+ // Extract parameters from the call
+ JSObject chatBytesObject = call.getObject("chatBytes", new JSObject());
+ int difficulty = call.getInt("difficulty", 0);
+
+ // Convert chatBytesObject to a byte array
+ byte[] chatBytes = jsObjectToByteArray(chatBytesObject);
+
+ // Use the MemoryPoW.compute2 method
+ int workBufferLength = 8 * 1024 * 1024; // 8 MiB buffer
+ Integer nonce = MemoryPoW.compute2(chatBytes, workBufferLength, difficulty);
+
+ // Return result to the plugin caller
+ JSObject result = new JSObject();
+ result.put("nonce", nonce);
+
+ call.resolve(result);
+
+ } catch (Exception e) {
+ call.reject("Error computing proof-of-work", e);
+ }
+ }
+
+ private byte[] jsObjectToByteArray(JSObject jsObject) {
+ int length = jsObject.length();
+ byte[] array = new byte[length];
+ Iterator keys = jsObject.keys();
+
+ while (keys.hasNext()) {
+ String key = keys.next();
+ int index = Integer.parseInt(key);
+ int value = jsObject.getInteger(key);
+ array[index] = (byte) value;
+ }
+
+ return array;
+ }
+}
diff --git a/android/app/src/main/res/raw/memorypow.wasm b/android/app/src/main/res/raw/memorypow.wasm
new file mode 100644
index 0000000..073b179
Binary files /dev/null and b/android/app/src/main/res/raw/memorypow.wasm differ
diff --git a/src/background.ts b/src/background.ts
index 792bb5f..1553299 100644
--- a/src/background.ts
+++ b/src/background.ts
@@ -29,6 +29,8 @@ import PhraseWallet from "./utils/generateWallet/phrase-wallet";
import { RequestQueueWithPromise } from "./utils/queue/queue";
import { validateAddress } from "./utils/validateAddress";
import { Sha256 } from "asmcrypto.js";
+import NativePOW from './utils/nativepow'
+
import { TradeBotRespondMultipleRequest } from "./transactions/TradeBotRespondMultipleRequest";
import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from "./constants/resourceTypes";
import {
@@ -98,7 +100,7 @@ import {
import { getData, removeKeysAndLogout, storeData } from "./utils/chromeStorage";
import {BackgroundFetch} from '@transistorsoft/capacitor-background-fetch';
import { LocalNotifications } from '@capacitor/local-notifications';
-import ChatComputePowWorker from './chatComputePow.worker.js?worker';
+// import ChatComputePowWorker from './chatComputePow.worker.js?worker';
const uid = new ShortUniqueId({ length: 9, dictionary: 'number' });
@@ -383,29 +385,13 @@ function playNotificationSound() {
// chrome.runtime.sendMessage({ action: "PLAY_NOTIFICATION_SOUND" });
}
-const worker = new ChatComputePowWorker()
+// const worker = new ChatComputePowWorker()
export async function performPowTask(chatBytes, difficulty) {
- return new Promise((resolve, reject) => {
- worker.onmessage = (e) => {
- if (e.data.error) {
- reject(new Error(e.data.error));
- } else {
- resolve(e.data);
- }
- };
-
- worker.onerror = (err) => {
- reject(err);
- };
-
- // Send the task to the worker
- worker.postMessage({
- chatBytes,
- path: `${import.meta.env.BASE_URL}memory-pow.wasm.full`,
- difficulty,
- });
- });
+ const chatBytesArray = Uint8Array.from(Object.values(chatBytes));
+ const result = await NativePOW.computeProofOfWork({ chatBytes, difficulty });
+ return {nonce: result.nonce, chatBytesArray}
+
}
const handleNotificationDirect = async (directs) => {
diff --git a/src/chatComputePow.worker.js b/src/chatComputePow.worker.js
index afc782b..15178bd 100644
--- a/src/chatComputePow.worker.js
+++ b/src/chatComputePow.worker.js
@@ -1,6 +1,6 @@
import { Sha256 } from 'asmcrypto.js';
import wasmInit from './memory-pow.wasm?init';
-
+import NativePOW from './utils/nativepow'
let compute; // Exported compute function from Wasm
let memory; // WebAssembly.Memory instance
let heap; // Uint8Array view of the memory buffer
@@ -58,24 +58,26 @@ function sbrk(size) {
// Proof-of-Work computation function
async function computePow(chatBytes, difficulty) {
- if (!compute) {
- throw new Error('WebAssembly module not initialized. Call loadWasm first.');
- }
+ // if (!compute) {
+ // throw new Error('WebAssembly module not initialized. Call loadWasm first.');
+ // }
- const chatBytesArray = Uint8Array.from(Object.values(chatBytes));
- const chatBytesHash = new Sha256().process(chatBytesArray).finish().result;
+ // const chatBytesArray = Uint8Array.from(Object.values(chatBytes));
+ // const chatBytesHash = new Sha256().process(chatBytesArray).finish().result;
- // Allocate memory for the hash
- const hashPtr = sbrk(32);
- const hashAry = new Uint8Array(memory.buffer, hashPtr, 32);
- hashAry.set(chatBytesHash);
-
- // Reuse the work buffer if already allocated
- if (!workBufferPtr) {
- workBufferPtr = sbrk(workBufferLength);
- }
-
- const nonce = compute(hashPtr, workBufferPtr, workBufferLength, difficulty);
+ // // Allocate memory for the hash
+ // const hashPtr = sbrk(32);
+ // const hashAry = new Uint8Array(memory.buffer, hashPtr, 32);
+ // hashAry.set(chatBytesHash);
+
+ // // Reuse the work buffer if already allocated
+ // if (!workBufferPtr) {
+ // workBufferPtr = sbrk(workBufferLength);
+ // }
+ console.log('native')
+ const nonce = await NativePOW.computeProofOfWork({ chatBytes, difficulty });
+ console.log('nonce', nonce)
+ (hashPtr, workBufferPtr, workBufferLength, difficulty);
return { nonce, chatBytesArray };
}
@@ -86,9 +88,9 @@ self.addEventListener('message', async (e) => {
try {
// Initialize Wasm if not already done
- if (!compute) {
- await loadWasm();
- }
+ // if (!compute) {
+ // await loadWasm();
+ // }
// Perform the POW computation
const result = await computePow(chatBytes, difficulty);
diff --git a/src/main.tsx b/src/main.tsx
index 0531491..2b89ed3 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -7,6 +7,7 @@ import { ThemeProvider, createTheme } from '@mui/material/styles';
import { CssBaseline } from '@mui/material';
import { MessageQueueProvider } from './MessageQueueContext.tsx';
import { RecoilRoot } from 'recoil';
+import './utils/nativepow.ts'
const theme = createTheme({
palette: {
primary: {
diff --git a/src/utils/nativepow.ts b/src/utils/nativepow.ts
new file mode 100644
index 0000000..1937231
--- /dev/null
+++ b/src/utils/nativepow.ts
@@ -0,0 +1,9 @@
+import { registerPlugin } from '@capacitor/core';
+
+export interface NativePOWPlugin {
+ computeProofOfWork(options: { chatBytes: string; difficulty: number }): Promise<{ nonce: string }>;
+}
+
+const NativePOW = registerPlugin('NativePOW');
+
+export default NativePOW
\ No newline at end of file