diff --git a/src/service-workers/network.sw.ts b/src/service-workers/network.sw.ts
index 68211c3..434686d 100644
--- a/src/service-workers/network.sw.ts
+++ b/src/service-workers/network.sw.ts
@@ -3,8 +3,6 @@
///
-declare const self: ServiceWorkerGlobalScope;
-
type AnkFlag = 'Handshake' | 'NewTx' | 'Cipher' | 'Commit' | 'Faucet' | 'Ping';
interface ServiceWorkerMessage {
@@ -24,14 +22,14 @@ let heartbeatInterval: any = null;
// SERVICE WORKER LIFECYCLE
// ==========================================
-self.addEventListener('install', (event: ExtendableEvent) => {
+(self as unknown as ServiceWorkerGlobalScope).addEventListener('install', (event: ExtendableEvent) => {
console.log('[NetworkSW] Installing...');
- event.waitUntil(self.skipWaiting());
+ event.waitUntil((self as unknown as ServiceWorkerGlobalScope).skipWaiting());
});
-self.addEventListener('activate', (event: ExtendableEvent) => {
+(self as unknown as ServiceWorkerGlobalScope).addEventListener('activate', (event: ExtendableEvent) => {
console.log('[NetworkSW] Activating...');
- event.waitUntil(self.clients.claim());
+ event.waitUntil((self as unknown as ServiceWorkerGlobalScope).clients.claim());
startHeartbeat();
});
@@ -39,7 +37,7 @@ self.addEventListener('activate', (event: ExtendableEvent) => {
// MESSAGE HANDLING
// ==========================================
-self.addEventListener('message', async (event: ExtendableMessageEvent) => {
+(self as unknown as ServiceWorkerGlobalScope).addEventListener('message', async (event: ExtendableMessageEvent) => {
const { type, payload, id } = event.data;
console.log(`[NetworkSW] Received message: ${type} (id: ${id})`);
@@ -227,9 +225,9 @@ function respondToClient(client: Client | null, message: any) {
console.error('[NetworkSW] Error sending message to client:', error);
// Fallback: try to get the client by ID or use broadcast
if (client.id) {
- self.clients.get(client.id).then((c) => {
+ (self as unknown as ServiceWorkerGlobalScope).clients.get(client.id).then((c: Client | undefined) => {
if (c) c.postMessage(message);
- }).catch((err) => {
+ }).catch((err: any) => {
console.error('[NetworkSW] Failed to get client by ID:', err);
});
}
@@ -237,8 +235,8 @@ function respondToClient(client: Client | null, message: any) {
}
async function broadcastToClients(message: any) {
- const clients = await self.clients.matchAll({ includeUncontrolled: true });
- clients.forEach((client) => {
+ const clients = await (self as unknown as ServiceWorkerGlobalScope).clients.matchAll({ includeUncontrolled: true });
+ clients.forEach((client: Client) => {
client.postMessage(message);
});
}
diff --git a/src/services/core/sdk.service.ts b/src/services/core/sdk.service.ts
deleted file mode 100644
index 0a0949f..0000000
--- a/src/services/core/sdk.service.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { ApiReturn, Device } from '../../../pkg/sdk_client';
-
-export class SdkService {
- private client: any;
-
- async init() {
- this.client = await import('../../../pkg/sdk_client');
- this.client.setup();
- }
-
- public getClient(): any {
- if (!this.client) throw new Error('SDK not initialized');
- return this.client;
- }
-
- // Méthodes utilitaires directes du SDK
- public encodeJson(data: any): any {
- return this.client.encode_json(data);
- }
- public encodeBinary(data: any): any {
- return this.client.encode_binary(data);
- }
- public decodeValue(value: number[]): any {
- return this.client.decode_value(value);
- }
-}
diff --git a/src/services/domain/crypto.service.ts b/src/services/crypto.service.ts
similarity index 59%
rename from src/services/domain/crypto.service.ts
rename to src/services/crypto.service.ts
index c04a459..29714bc 100644
--- a/src/services/domain/crypto.service.ts
+++ b/src/services/crypto.service.ts
@@ -1,8 +1,14 @@
-import { MerkleProofResult, ProcessState } from '../../../pkg/sdk_client';
-import { SdkService } from '../core/sdk.service';
+import { MerkleProofResult, ProcessState } from '../../pkg/sdk_client';
+
+// Type for WasmService proxy (passed from Core Worker)
+type WasmServiceProxy = {
+ hashValue(fileBlob: { type: string; data: Uint8Array }, commitedIn: string, label: string): Promise;
+ getMerkleProof(processState: any, attributeName: string): Promise;
+ validateMerkleProof(proof: any, hash: string): Promise;
+};
export class CryptoService {
- constructor(private sdk: SdkService) {}
+ constructor(private wasm: WasmServiceProxy) {}
public hexToBlob(hexString: string): Blob {
const uint8Array = this.hexToUInt8Array(hexString);
@@ -26,16 +32,16 @@ export class CryptoService {
.join('');
}
- public getHashForFile(commitedIn: string, label: string, fileBlob: { type: string; data: Uint8Array }): string {
- return this.sdk.getClient().hash_value(fileBlob, commitedIn, label);
+ public async getHashForFile(commitedIn: string, label: string, fileBlob: { type: string; data: Uint8Array }): Promise {
+ return await this.wasm.hashValue(fileBlob, commitedIn, label);
}
- public getMerkleProofForFile(processState: ProcessState, attributeName: string): MerkleProofResult {
- return this.sdk.getClient().get_merkle_proof(processState, attributeName);
+ public async getMerkleProofForFile(processState: ProcessState, attributeName: string): Promise {
+ return await this.wasm.getMerkleProof(processState, attributeName);
}
- public validateMerkleProof(proof: MerkleProofResult, hash: string): boolean {
- return this.sdk.getClient().validate_merkle_proof(proof, hash);
+ public async validateMerkleProof(proof: MerkleProofResult, hash: string): Promise {
+ return await this.wasm.validateMerkleProof(proof, hash);
}
public splitData(obj: Record) {
diff --git a/src/services/domain/wallet.service.ts b/src/services/domain/wallet.service.ts
deleted file mode 100644
index b7e26b6..0000000
--- a/src/services/domain/wallet.service.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-import { Device } from '../../../pkg/sdk_client';
-import { SdkService } from '../core/sdk.service';
-import Database from '../database.service';
-
-export class WalletService {
- constructor(private sdk: SdkService, private db: Database) {}
-
- public isPaired(): boolean {
- try {
- return this.sdk.getClient().is_paired();
- } catch (e) {
- return false;
- }
- }
-
- public getAmount(): BigInt {
- return this.sdk.getClient().get_available_amount();
- }
-
- public getDeviceAddress(): string {
- return this.sdk.getClient().get_address();
- }
-
- public getPairingProcessId(): string {
- return this.sdk.getClient().get_pairing_process_id();
- }
-
- public async createNewDevice(chainTip: number): Promise {
- const spAddress = await this.sdk.getClient().create_new_device(0, 'signet');
- const device = this.dumpDeviceFromMemory();
- if (device.sp_wallet.birthday === 0) {
- device.sp_wallet.birthday = chainTip;
- device.sp_wallet.last_scan = chainTip;
- this.sdk.getClient().restore_device(device);
- }
- await this.saveDeviceInDatabase(device);
- return spAddress;
- }
-
- public dumpDeviceFromMemory(): Device {
- return this.sdk.getClient().dump_device();
- }
-
- public dumpNeuteredDevice(): Device | null {
- try {
- return this.sdk.getClient().dump_neutered_device();
- } catch (e) {
- return null;
- }
- }
-
- public async dumpWallet(): Promise {
- return await this.sdk.getClient().dump_wallet();
- }
-
- public async getMemberFromDevice(): Promise {
- try {
- const device = await this.getDeviceFromDatabase();
- if (device) {
- const pairedMember = device['paired_member'];
- return pairedMember.sp_addresses;
- } else {
- return null;
- }
- } catch (e) {
- throw new Error(`[WalletService] Échec: ${e}`);
- }
- }
-
- public async saveDeviceInDatabase(device: Device): Promise {
- await this.db.saveDevice(device);
- }
-
- public async getDeviceFromDatabase(): Promise {
- const db = await Database.getInstance();
- const res = await db.getObject('wallet', '1');
- return res ? res['device'] : null;
- }
-
- public restoreDevice(device: Device) {
- this.sdk.getClient().restore_device(device);
- }
-
- public pairDevice(processId: string, spAddressList: string[]): void {
- this.sdk.getClient().pair_device(processId, spAddressList);
- }
-
- public async unpairDevice(): Promise {
- this.sdk.getClient().unpair_device();
- const newDevice = this.dumpDeviceFromMemory();
- await this.saveDeviceInDatabase(newDevice);
- }
-}
diff --git a/src/services/core/network.service.ts b/src/services/network.service.ts
similarity index 95%
rename from src/services/core/network.service.ts
rename to src/services/network.service.ts
index 17cd1ad..85d3e6f 100644
--- a/src/services/core/network.service.ts
+++ b/src/services/network.service.ts
@@ -1,4 +1,4 @@
-import Services from "../service";
+import Services from "./service";
interface ServiceWorkerMessage {
type: string;
@@ -160,7 +160,16 @@ export class NetworkService {
}
// Wait for the Service Worker to be ready (installed and activated)
- await this.serviceWorkerRegistration.ready;
+ if (this.serviceWorkerRegistration.installing) {
+ const installing = this.serviceWorkerRegistration.installing;
+ await new Promise((resolve) => {
+ installing.addEventListener('statechange', () => {
+ if (installing.state === 'installed') {
+ resolve();
+ }
+ });
+ });
+ }
// Also ensure it's active
if (this.serviceWorkerRegistration.active) {
@@ -234,18 +243,18 @@ export class NetworkService {
};
// Listen on both the serviceWorker and controller
- navigator.serviceWorker.addEventListener("message", messageHandler);
+ navigator.serviceWorker.addEventListener("message", messageHandler as EventListener);
// Also listen on the controller if it exists
if (navigator.serviceWorker.controller) {
- navigator.serviceWorker.controller.addEventListener("message", messageHandler);
+ navigator.serviceWorker.controller.addEventListener("message", messageHandler as EventListener);
}
// Listen for controller changes
navigator.serviceWorker.addEventListener("controllerchange", () => {
console.log("[NetworkService] Service Worker controller changed");
if (navigator.serviceWorker.controller) {
- navigator.serviceWorker.controller.addEventListener("message", messageHandler);
+ navigator.serviceWorker.controller.addEventListener("message", messageHandler as EventListener);
}
});
}
diff --git a/src/services/domain/process.service.ts b/src/services/process.service.ts
similarity index 79%
rename from src/services/domain/process.service.ts
rename to src/services/process.service.ts
index 7f97b76..dadd67e 100644
--- a/src/services/domain/process.service.ts
+++ b/src/services/process.service.ts
@@ -1,6 +1,12 @@
-import { Process, ProcessState, RoleDefinition } from '../../../pkg/sdk_client';
-import { SdkService } from '../core/sdk.service';
-import Database from '../database.service';
+import { Process, ProcessState, RoleDefinition } from '../../pkg/sdk_client';
+
+// Type for Database proxy (passed from Core Worker)
+type DatabaseServiceProxy = {
+ getProcess(processId: string): Promise;
+ getAllProcesses(): Promise>;
+ saveProcess(processId: string, process: Process): Promise;
+ saveProcessesBatch(processes: Record): Promise;
+};
const EMPTY32BYTES = String('').padStart(64, '0');
@@ -8,7 +14,7 @@ export class ProcessService {
private processesCache: Record = {};
private myProcesses: Set = new Set();
- constructor(private sdk: SdkService, private db: Database) {}
+ constructor(private db: any) {}
public async getProcess(processId: string): Promise {
if (this.processesCache[processId]) return this.processesCache[processId];
@@ -39,17 +45,17 @@ export class ProcessService {
public getLastCommitedState(process: Process): ProcessState | null {
if (process.states.length === 0) return null;
const processTip = process.states[process.states.length - 1].commited_in;
- return process.states.findLast((state) => state.commited_in !== processTip) || null;
+ return process.states.findLast((state: ProcessState) => state.commited_in !== processTip) || null;
}
public getUncommitedStates(process: Process): ProcessState[] {
if (process.states.length === 0) return [];
const processTip = process.states[process.states.length - 1].commited_in;
- return process.states.filter((state) => state.commited_in === processTip).filter((state) => state.state_id !== EMPTY32BYTES);
+ return process.states.filter((state: ProcessState) => state.commited_in === processTip).filter((state: ProcessState) => state.state_id !== EMPTY32BYTES);
}
public getStateFromId(process: Process, stateId: string): ProcessState | null {
- return process.states.find((state) => state.state_id === stateId) || null;
+ return process.states.find((state: ProcessState) => state.state_id === stateId) || null;
}
public getRoles(process: Process): Record | null {
diff --git a/src/services/service.ts b/src/services/service.ts
index f0dc928..dd0a3f3 100755
--- a/src/services/service.ts
+++ b/src/services/service.ts
@@ -11,9 +11,10 @@ import {
} from "../../pkg/sdk_client";
import { BackUp } from "../types/index";
import { APP_CONFIG } from "../config/constants";
-import { NetworkService } from "./core/network.service";
+import { NetworkService } from "./network.service";
import type { CoreBackend } from "../workers/core.worker";
import Database from "./database.service";
+import WasmService from "./wasm.service";
export default class Services {
private static instance: Services;
@@ -53,13 +54,20 @@ export default class Services {
public async init(): Promise {
console.log("[Services] 🚀 Démarrage Proxy...");
- // 1. Initialiser le Core Worker
+ // 1. Initialiser les Services dans le Main Thread (Hub)
+ const db = await Database.getInstance();
+ const wasmService = await WasmService.getInstance();
+
+ // 2. Passer les Services au Core Worker via Comlink proxy (BEFORE init)
+ await this.coreWorker.setServices(
+ Comlink.proxy(wasmService),
+ Comlink.proxy(db)
+ );
+
+ // 3. Initialiser le Core Worker (after services are set)
await this.coreWorker.init();
- // 2. Initialiser la Database (Main Thread)
- await Database.getInstance();
-
- // 3. Configurer les Callbacks
+ // 4. Configurer les Callbacks
await this.coreWorker.setCallbacks(
Comlink.proxy(this.handleWorkerNotification.bind(this)),
Comlink.proxy(this.handleWorkerNetworkSend.bind(this)),
@@ -67,11 +75,11 @@ export default class Services {
Comlink.proxy(this.handleWorkerRelayRequest.bind(this))
);
- // 4. Initialiser le Réseau
+ // 5. Initialiser le Réseau
await this.networkService.initRelays();
console.log(
- "[Services] ✅ Proxy connecté au CoreWorker et NetworkService."
+ "[Services] ✅ Proxy connecté au CoreWorker, WASM Service, Database Service et NetworkService."
);
}
@@ -176,9 +184,6 @@ export default class Services {
public async dumpDeviceFromMemory() {
return await this.coreWorker.dumpDeviceFromMemory();
}
- public async dumpNeuteredDevice() {
- return await this.coreWorker.dumpNeuteredDevice();
- }
public async getPairingProcessId() {
return await this.coreWorker.getPairingProcessId();
}
@@ -339,9 +344,6 @@ export default class Services {
public async resetDevice() {
await this.coreWorker.resetDevice();
}
- public async handleApiReturn(res: ApiReturn) {
- await this.coreWorker.handleApiReturn(res);
- }
public async saveDiffsToDb(diffs: UserDiff[]) {
await this.coreWorker.saveDiffsToDb(diffs);
}
@@ -349,6 +351,10 @@ export default class Services {
await this.coreWorker.handleCommitError(res);
}
+ public async handleApiReturn(res: ApiReturn) {
+ await this.coreWorker.handleApiReturn(res);
+ }
+
public async rolesContainsUs(roles: any) {
return await this.coreWorker.rolesContainsUs(roles);
}
diff --git a/src/services/wallet.service.ts b/src/services/wallet.service.ts
new file mode 100644
index 0000000..9ff1a65
--- /dev/null
+++ b/src/services/wallet.service.ts
@@ -0,0 +1,134 @@
+import { Device } from '../../pkg/sdk_client';
+import Database from './database.service';
+
+// Type for WasmService proxy (passed from Core Worker)
+type WasmServiceProxy = {
+ isPaired(): Promise;
+ getAvailableAmount(): Promise;
+ getAddress(): Promise;
+ getPairingProcessId(): Promise;
+ createNewDevice(birthday: number, network: string): Promise;
+ dumpDevice(): Promise;
+ dumpNeuteredDevice(): Promise;
+ dumpWallet(): Promise;
+ restoreDevice(device: any): Promise;
+ pairDevice(processId: string, spAddresses: string[]): Promise;
+ unpairDevice(): Promise;
+};
+
+type DatabaseServiceProxy = {
+ getStoreList(): Promise<{ [key: string]: string }>;
+ addObject(payload: { storeName: string; object: any; key: any }): Promise;
+ batchWriting(payload: { storeName: string; objects: { key: any; object: any }[] }): Promise;
+ getObject(storeName: string, key: string): Promise;
+ dumpStore(storeName: string): Promise>;
+ deleteObject(storeName: string, key: string): Promise;
+ clearStore(storeName: string): Promise;
+ requestStoreByIndex(storeName: string, indexName: string, request: string): Promise;
+ clearMultipleStores(storeNames: string[]): Promise;
+ saveDevice(device: any): Promise;
+ getDevice(): Promise;
+ saveProcess(processId: string, process: any): Promise;
+ saveProcessesBatch(processes: Record): Promise;
+ getProcess(processId: string): Promise;
+ getAllProcesses(): Promise>;
+ saveBlob(hash: string, data: Blob): Promise;
+ getBlob(hash: string): Promise;
+ saveDiffs(diffs: any[]): Promise;
+ getDiff(hash: string): Promise;
+ getAllDiffs(): Promise>;
+ getSharedSecret(address: string): Promise;
+ saveSecretsBatch(unconfirmedSecrets: any[], sharedSecrets: { key: string; value: any }[]): Promise;
+ getAllSecrets(): Promise<{ shared_secrets: Record; unconfirmed_secrets: any[] }>;
+};
+
+
+export class WalletService {
+ constructor(private wasm: WasmServiceProxy, private db: DatabaseServiceProxy) {}
+
+ public async isPaired(): Promise {
+ try {
+ return await this.wasm.isPaired();
+ } catch (e) {
+ return false;
+ }
+ }
+
+ public async getAmount(): Promise {
+ return await this.wasm.getAvailableAmount();
+ }
+
+ public async getDeviceAddress(): Promise {
+ return await this.wasm.getAddress();
+ }
+
+ public async getPairingProcessId(): Promise {
+ return await this.wasm.getPairingProcessId();
+ }
+
+ public async createNewDevice(chainTip: number): Promise {
+ const spAddress = await this.wasm.createNewDevice(0, 'signet');
+ const device = await this.dumpDeviceFromMemory();
+ if (device.sp_wallet.birthday === 0) {
+ device.sp_wallet.birthday = chainTip;
+ device.sp_wallet.last_scan = chainTip;
+ await this.wasm.restoreDevice(device);
+ }
+ await this.saveDeviceInDatabase(device);
+ return spAddress;
+ }
+
+ public async dumpDeviceFromMemory(): Promise {
+ return await this.wasm.dumpDevice();
+ }
+
+ public async dumpNeuteredDevice(): Promise {
+ try {
+ return await this.wasm.dumpNeuteredDevice();
+ } catch (e) {
+ return null;
+ }
+ }
+
+ public async dumpWallet(): Promise {
+ return await this.wasm.dumpWallet();
+ }
+
+ public async getMemberFromDevice(): Promise {
+ try {
+ const device = await this.getDeviceFromDatabase();
+ if (device) {
+ const pairedMember = device['paired_member'];
+ return pairedMember.sp_addresses;
+ } else {
+ return null;
+ }
+ } catch (e) {
+ throw new Error(`[WalletService] Échec: ${e}`);
+ }
+ }
+
+ public async saveDeviceInDatabase(device: Device): Promise {
+ await this.db.saveDevice(device);
+ }
+
+ public async getDeviceFromDatabase(): Promise {
+ const db = await Database.getInstance();
+ const res = await db.getObject('wallet', '1');
+ return res ? res['device'] : null;
+ }
+
+ public async restoreDevice(device: Device): Promise {
+ await this.wasm.restoreDevice(device);
+ }
+
+ public async pairDevice(processId: string, spAddressList: string[]): Promise {
+ await this.wasm.pairDevice(processId, spAddressList);
+ }
+
+ public async unpairDevice(): Promise {
+ await this.wasm.unpairDevice();
+ const newDevice = await this.dumpDeviceFromMemory();
+ await this.saveDeviceInDatabase(newDevice);
+ }
+}
diff --git a/src/workers/core.worker.ts b/src/workers/core.worker.ts
index 0405eb2..b083483 100644
--- a/src/workers/core.worker.ts
+++ b/src/workers/core.worker.ts
@@ -19,18 +19,93 @@ import { APP_CONFIG } from "../config/constants";
import { splitPrivateData } from "../utils/service.utils";
// Services internes au worker
-import { SdkService } from "../services/core/sdk.service";
-import { WalletService } from "../services/domain/wallet.service";
-import { ProcessService } from "../services/domain/process.service";
-import { CryptoService } from "../services/domain/crypto.service";
+import { WalletService } from "../services/wallet.service";
+import { ProcessService } from "../services/process.service";
+import { CryptoService } from "../services/crypto.service";
+
+// Types for services passed from main thread
+type WasmServiceProxy = {
+ callMethod(method: string, ...args: any[]): Promise;
+ parseCipher(msg: string, membersList: any, processes: any): Promise;
+ parseNewTx(msg: string, blockHeight: number, membersList: any): Promise;
+ createTransaction(addresses: string[], feeRate: number): Promise;
+ signTransaction(partialTx: any): Promise;
+ getTxid(transaction: string): Promise;
+ getOpReturn(transaction: string): Promise;
+ getPrevouts(transaction: string): Promise;
+ createProcess(privateData: any, publicData: any, roles: any, membersList: any): Promise;
+ createNewProcess(privateData: any, roles: any, publicData: any, relayAddress: string, feeRate: number, membersList: any): Promise;
+ updateProcess(process: any, privateData: any, publicData: any, roles: any, membersList: any): Promise;
+ createUpdateMessage(process: any, stateId: string, membersList: any): Promise;
+ createPrdResponse(process: any, stateId: string, membersList: any): Promise;
+ validateState(process: any, stateId: string, membersList: any): Promise;
+ refuseState(process: any, stateId: string): Promise;
+ requestData(processId: string, stateIds: string[], roles: any, membersList: any): Promise;
+ processCommitNewState(process: any, newStateId: string, newTip: string): Promise;
+ encodeJson(data: any): Promise;
+ encodeBinary(data: any): Promise;
+ decodeValue(value: number[]): Promise;
+ createFaucetMessage(): Promise;
+ isChildRole(parent: any, child: any): Promise;
+ // Wallet methods
+ isPaired(): Promise;
+ getAvailableAmount(): Promise;
+ getAddress(): Promise;
+ getPairingProcessId(): Promise;
+ createNewDevice(birthday: number, network: string): Promise;
+ dumpDevice(): Promise;
+ dumpNeuteredDevice(): Promise;
+ dumpWallet(): Promise;
+ restoreDevice(device: any): Promise;
+ pairDevice(processId: string, spAddresses: string[]): Promise;
+ unpairDevice(): Promise;
+ resetDevice(): Promise;
+ // Crypto methods
+ hashValue(fileBlob: { type: string; data: Uint8Array }, commitedIn: string, label: string): Promise;
+ getMerkleProof(processState: any, attributeName: string): Promise;
+ validateMerkleProof(proof: any, hash: string): Promise;
+ decryptData(key: Uint8Array, data: Uint8Array): Promise;
+ // Secrets management
+ setSharedSecrets(secretsJson: string): Promise;
+ // Blockchain scanning
+ scanBlocks(tipHeight: number, blindbitUrl: string): Promise;
+};
+
+type DatabaseServiceProxy = {
+ getStoreList(): Promise<{ [key: string]: string }>;
+ addObject(payload: { storeName: string; object: any; key: any }): Promise;
+ batchWriting(payload: { storeName: string; objects: { key: any; object: any }[] }): Promise;
+ getObject(storeName: string, key: string): Promise;
+ dumpStore(storeName: string): Promise>;
+ deleteObject(storeName: string, key: string): Promise;
+ clearStore(storeName: string): Promise;
+ requestStoreByIndex(storeName: string, indexName: string, request: string): Promise;
+ clearMultipleStores(storeNames: string[]): Promise;
+ saveDevice(device: any): Promise;
+ getDevice(): Promise;
+ saveProcess(processId: string, process: any): Promise;
+ saveProcessesBatch(processes: Record): Promise;
+ getProcess(processId: string): Promise;
+ getAllProcesses(): Promise>;
+ saveBlob(hash: string, data: Blob): Promise;
+ getBlob(hash: string): Promise;
+ saveDiffs(diffs: any[]): Promise;
+ getDiff(hash: string): Promise;
+ getAllDiffs(): Promise>;
+ getSharedSecret(address: string): Promise;
+ saveSecretsBatch(unconfirmedSecrets: any[], sharedSecrets: { key: string; value: any }[]): Promise;
+ getAllSecrets(): Promise<{ shared_secrets: Record; unconfirmed_secrets: any[] }>;
+};
export class CoreBackend {
- // Services
- private sdkService: SdkService;
+ // Services (passed from main thread via Comlink)
+ private wasmService!: WasmServiceProxy;
+ private db!: DatabaseServiceProxy;
+
+ // Domain services
private walletService!: WalletService;
private processService!: ProcessService;
- private cryptoService: CryptoService;
- private db!: Database;
+ private cryptoService!: CryptoService;
// État (State)
private processId: string | null = null;
@@ -54,28 +129,39 @@ export class CoreBackend {
private relayGetter: (() => Promise) | null = null;
constructor() {
- this.sdkService = new SdkService();
- this.cryptoService = new CryptoService(this.sdkService);
- // Initialisation temporaire
- this.walletService = new WalletService(this.sdkService, null as any);
- this.processService = new ProcessService(this.sdkService, null as any);
+ // Services will be set via setServices() from main thread
}
public async init(): Promise {
if (this.isInitialized) return;
console.log("[CoreWorker] ⚙️ Initialisation du Backend...");
- await this.sdkService.init(); // Charge le WASM
- this.db = await Database.getInstance(); // Lance le Database Worker
+
+ // Services must be set before init
+ if (!this.wasmService || !this.db) {
+ throw new Error("Services must be set via setServices() before init()");
+ }
- this.walletService = new WalletService(this.sdkService, this.db);
- this.processService = new ProcessService(this.sdkService, this.db);
+ // Initialize domain services with WASM and Database proxies
+ this.cryptoService = new CryptoService(this.wasmService);
+ this.walletService = new WalletService(this.wasmService, this.db);
+ // @ts-ignore - ProcessService accepts DatabaseServiceProxy via Comlink but TypeScript sees Database type
+ this.processService = new ProcessService(this.db);
this.notifications = this.getNotifications();
this.isInitialized = true;
console.log("[CoreWorker] ✅ Backend prêt.");
}
+ // --- CONFIGURATION DES SERVICES (depuis Main Thread) ---
+ public setServices(
+ wasmService: WasmServiceProxy,
+ db: DatabaseServiceProxy
+ ) {
+ this.wasmService = wasmService;
+ this.db = db;
+ }
+
// --- CONFIGURATION DES CALLBACKS ---
public setCallbacks(
notifier: (event: string, data?: any) => void,
@@ -120,32 +206,32 @@ export class CoreBackend {
// ==========================================
// WALLET PROXY
// ==========================================
- public isPaired() {
- return this.walletService.isPaired();
+ public async isPaired() {
+ return await this.walletService.isPaired();
}
- public getAmount() {
- return this.walletService.getAmount();
+ public async getAmount() {
+ return await this.walletService.getAmount();
}
- public getDeviceAddress() {
- return this.walletService.getDeviceAddress();
+ public async getDeviceAddress() {
+ return await this.walletService.getDeviceAddress();
}
- public dumpDeviceFromMemory() {
- return this.walletService.dumpDeviceFromMemory();
+ public async dumpDeviceFromMemory() {
+ return await this.walletService.dumpDeviceFromMemory();
}
- public dumpNeuteredDevice() {
- return this.walletService.dumpNeuteredDevice();
+ public async dumpNeuteredDevice() {
+ return await this.walletService.dumpNeuteredDevice();
}
- public getPairingProcessId() {
- return this.walletService.getPairingProcessId();
+ public async getPairingProcessId() {
+ return await this.walletService.getPairingProcessId();
}
public async getDeviceFromDatabase() {
return this.walletService.getDeviceFromDatabase();
}
- public restoreDevice(d: Device) {
- this.walletService.restoreDevice(d);
+ public async restoreDevice(d: Device) {
+ await this.walletService.restoreDevice(d);
}
- public pairDevice(pid: string, list: string[]) {
- this.walletService.pairDevice(pid, list);
+ public async pairDevice(pid: string, list: string[]) {
+ await this.walletService.pairDevice(pid, list);
}
public async unpairDevice() {
await this.walletService.unpairDevice();
@@ -199,8 +285,8 @@ export class CoreBackend {
// ==========================================
// CRYPTO HELPERS
// ==========================================
- public decodeValue(val: number[]) {
- return this.sdkService.decodeValue(val);
+ public async decodeValue(val: number[]) {
+ return await this.wasmService.decodeValue(val);
}
public hexToBlob(hex: string) {
return this.cryptoService.hexToBlob(hex);
@@ -211,14 +297,14 @@ export class CoreBackend {
public async blobToHex(blob: Blob) {
return this.cryptoService.blobToHex(blob);
}
- public getHashForFile(c: string, l: string, f: any) {
- return this.cryptoService.getHashForFile(c, l, f);
+ public async getHashForFile(c: string, l: string, f: any) {
+ return await this.cryptoService.getHashForFile(c, l, f);
}
- public getMerkleProofForFile(s: ProcessState, a: string) {
- return this.cryptoService.getMerkleProofForFile(s, a);
+ public async getMerkleProofForFile(s: ProcessState, a: string) {
+ return await this.cryptoService.getMerkleProofForFile(s, a);
}
- public validateMerkleProof(p: MerkleProofResult, h: string) {
- return this.cryptoService.validateMerkleProof(p, h);
+ public async validateMerkleProof(p: MerkleProofResult, h: string) {
+ return await this.cryptoService.validateMerkleProof(p, h);
}
private splitData(obj: Record) {
return this.cryptoService.splitData(obj);
@@ -257,16 +343,13 @@ export class CoreBackend {
// ==========================================
// UTILITAIRES DIVERS
// ==========================================
- public createFaucetMessage() {
- return this.sdkService.getClient().create_faucet_msg();
+ public async createFaucetMessage() {
+ return await this.wasmService.createFaucetMessage();
}
- public isChildRole(parent: any, child: any): boolean {
+ public async isChildRole(parent: any, child: any): Promise {
try {
- this.sdkService
- .getClient()
- .is_child_role(JSON.stringify(parent), JSON.stringify(child));
- return true;
+ return await this.wasmService.isChildRole(parent, child);
} catch (e) {
console.error(e);
return false;
@@ -285,7 +368,7 @@ export class CoreBackend {
}
this.currentBlockHeight = handshakeMsg.chain_tip;
- if (!this.isPaired()) {
+ if (!(await this.isPaired())) {
console.log(`[CoreWorker] ⏳ Non pairé. Attente appairage...`);
}
@@ -315,16 +398,14 @@ export class CoreBackend {
device.sp_wallet.birthday = this.currentBlockHeight;
device.sp_wallet.last_scan = this.currentBlockHeight;
await this.walletService.saveDeviceInDatabase(device);
- this.walletService.restoreDevice(device);
+ await this.walletService.restoreDevice(device);
} else if (device.sp_wallet.last_scan < this.currentBlockHeight) {
console.log(
`[CoreWorker] Scan requis de ${device.sp_wallet.last_scan} à ${this.currentBlockHeight}`
);
try {
- await this.sdkService
- .getClient()
- .scan_blocks(this.currentBlockHeight, APP_CONFIG.URLS.BLINDBIT);
- const updatedDevice = this.walletService.dumpDeviceFromMemory();
+ await this.wasmService.scanBlocks(this.currentBlockHeight, APP_CONFIG.URLS.BLINDBIT);
+ const updatedDevice = await this.walletService.dumpDeviceFromMemory();
await this.walletService.saveDeviceInDatabase(updatedDevice);
} catch (e) {
console.error("Scan error", e);
@@ -371,7 +452,7 @@ export class CoreBackend {
existing.states.push(existingLast);
toSave[processId] = existing;
hasChanged = true;
- if (this.rolesContainsUs(state.roles)) {
+ if (await this.rolesContainsUs(state.roles)) {
newStates.push(state.state_id);
newRoles.push(state.roles);
}
@@ -387,7 +468,7 @@ export class CoreBackend {
(!existingState.keys ||
Object.keys(existingState.keys).length === 0)
) {
- if (this.rolesContainsUs(state.roles)) {
+ if (await this.rolesContainsUs(state.roles)) {
newStates.push(state.state_id);
newRoles.push(state.roles);
// Ici on ne marque pas forcément hasChanged pour la DB, mais on demande les clés
@@ -429,7 +510,10 @@ export class CoreBackend {
// ==========================================
public async getMyProcesses(): Promise {
try {
- const pid = this.getPairingProcessId();
+ if (!(await this.isPaired())) {
+ return null;
+ }
+ const pid = await this.getPairingProcessId();
return await this.processService.getMyProcesses(pid);
} catch (e) {
return null;
@@ -470,7 +554,7 @@ export class CoreBackend {
}
}
if (publicData && publicData["pairedAddresses"]) {
- const decoded = this.decodeValue(publicData["pairedAddresses"]);
+ const decoded = await this.decodeValue(publicData["pairedAddresses"]);
if (decoded) members.add({ sp_addresses: decoded });
}
}
@@ -478,7 +562,7 @@ export class CoreBackend {
if (members.size === 0) return;
const unconnected = new Set();
- const myAddress = this.getDeviceAddress();
+ const myAddress = await this.getDeviceAddress();
for (const member of Array.from(members)) {
if (!member.sp_addresses) continue;
for (const address of member.sp_addresses) {
@@ -502,13 +586,11 @@ export class CoreBackend {
if (addresses.length === 0) return null;
const feeRate = APP_CONFIG.FEE_RATE;
try {
- return this.sdkService.getClient().create_transaction(addresses, feeRate);
+ return await this.wasmService.createTransaction(addresses, feeRate);
} catch (error: any) {
if (String(error).includes("Insufficient funds")) {
await this.getTokensFromFaucet();
- return this.sdkService
- .getClient()
- .create_transaction(addresses, feeRate);
+ return await this.wasmService.createTransaction(addresses, feeRate);
} else {
throw error;
}
@@ -517,15 +599,15 @@ export class CoreBackend {
private async getTokensFromFaucet(): Promise {
console.log("[CoreWorker] 🚰 Demande Faucet...");
- const availableAmt = this.getAmount();
+ const availableAmt = await this.getAmount();
const target: BigInt = APP_CONFIG.DEFAULT_AMOUNT * BigInt(10);
if (availableAmt < target) {
- const msg = this.sdkService.getClient().create_faucet_msg();
+ const msg = await this.wasmService.createFaucetMessage();
if (this.networkSender) this.networkSender("Faucet", msg);
let attempts = 3;
while (attempts > 0) {
- if (this.getAmount() >= target) return;
+ if ((await this.getAmount()) >= target) return;
attempts--;
await new Promise((r) =>
setTimeout(r, APP_CONFIG.TIMEOUTS.RETRY_DELAY)
@@ -539,8 +621,8 @@ export class CoreBackend {
userName: string,
pairWith: string[]
): Promise {
- if (this.isPaired()) throw new Error("Déjà appairé");
- const myAddress = this.getDeviceAddress();
+ if (await this.isPaired()) throw new Error("Déjà appairé");
+ const myAddress = await this.getDeviceAddress();
pairWith.push(myAddress);
const privateData = { description: "pairing", counter: 0 };
const publicData = {
@@ -610,9 +692,7 @@ export class CoreBackend {
fee: number,
members: any
): Promise {
- const res = this.sdkService
- .getClient()
- .create_new_process(priv, roles, pub, relay, fee, members);
+ const res = await this.wasmService.createNewProcess(priv, roles, pub, relay, fee, members);
if (res.updated_process) {
await this.ensureConnections(res.updated_process.current_process);
}
@@ -633,7 +713,7 @@ export class CoreBackend {
if (!lastState) {
const first = process.states[0];
- if (this.rolesContainsUs(first.roles)) {
+ if (await this.rolesContainsUs(first.roles)) {
const appRes = await this.approveChange(processId, first.state_id);
await this.handleApiReturn(appRes);
const prdRes = await this.createPrdUpdate(processId, first.state_id);
@@ -678,15 +758,13 @@ export class CoreBackend {
const { encodedPrivateData, encodedPublicData } =
await this.prepareProcessData(privateData, publicData);
- const res = this.sdkService
- .getClient()
- .update_process(
- currentProcess,
- encodedPrivateData,
- finalRoles,
- encodedPublicData,
- this.membersList
- );
+ const res = await this.wasmService.updateProcess(
+ currentProcess,
+ encodedPrivateData,
+ encodedPublicData,
+ finalRoles,
+ this.membersList
+ );
if (res.updated_process)
await this.ensureConnections(res.updated_process.current_process);
return res;
@@ -697,12 +775,12 @@ export class CoreBackend {
const p2 = this.splitData(pub);
return {
encodedPrivateData: {
- ...this.sdkService.getClient().encode_json(p1.jsonCompatibleData),
- ...this.sdkService.getClient().encode_binary(p1.binaryData),
+ ...(await this.wasmService.encodeJson(p1.jsonCompatibleData)),
+ ...(await this.wasmService.encodeBinary(p1.binaryData)),
},
encodedPublicData: {
- ...this.sdkService.getClient().encode_json(p2.jsonCompatibleData),
- ...this.sdkService.getClient().encode_binary(p2.binaryData),
+ ...(await this.wasmService.encodeJson(p2.jsonCompatibleData)),
+ ...(await this.wasmService.encodeBinary(p2.binaryData)),
},
};
}
@@ -713,38 +791,30 @@ export class CoreBackend {
public async createPrdUpdate(pid: string, sid: string) {
const p = await this.getProcess(pid);
await this.ensureConnections(p!);
- return this.sdkService
- .getClient()
- .create_update_message(p, sid, this.membersList);
+ return await this.wasmService.createUpdateMessage(p, sid, this.membersList);
}
public async createPrdResponse(pid: string, sid: string) {
const p = await this.getProcess(pid);
- return this.sdkService
- .getClient()
- .create_response_prd(p, sid, this.membersList);
+ return await this.wasmService.createPrdResponse(p, sid, this.membersList);
}
public async approveChange(pid: string, sid: string) {
const p = await this.getProcess(pid);
- const res = this.sdkService
- .getClient()
- .validate_state(p, sid, this.membersList);
+ const res = await this.wasmService.validateState(p, sid, this.membersList);
if (res.updated_process)
await this.ensureConnections(res.updated_process.current_process);
return res;
}
public async rejectChange(pid: string, sid: string) {
const p = await this.getProcess(pid);
- return this.sdkService.getClient().refuse_state(p, sid);
+ return await this.wasmService.refuseState(p, sid);
}
public async requestDataFromPeers(pid: string, sids: string[], roles: any) {
- const res = this.sdkService
- .getClient()
- .request_data(pid, sids, roles, this.membersList);
+ const res = await this.wasmService.requestData(pid, sids, roles, this.membersList);
await this.handleApiReturn(res);
}
public async resetDevice() {
- this.sdkService.getClient().reset_device();
+ await this.wasmService.resetDevice();
await this.db.clearMultipleStores([
"wallet",
"shared_secrets",
@@ -782,8 +852,8 @@ export class CoreBackend {
private async handlePartialTx(partialTx: any): Promise {
try {
- return this.sdkService.getClient().sign_transaction(partialTx)
- .new_tx_to_send;
+ const result = await this.wasmService.signTransaction(partialTx);
+ return result.new_tx_to_send;
} catch (e) {
return null;
}
@@ -871,11 +941,19 @@ export class CoreBackend {
}
}
- public rolesContainsUs(roles: any) {
- return this.processService.rolesContainsMember(
- roles,
- this.getPairingProcessId()
- );
+ public async rolesContainsUs(roles: any) {
+ try {
+ if (!(await this.isPaired())) {
+ return false;
+ }
+ return this.processService.rolesContainsMember(
+ roles,
+ await this.getPairingProcessId()
+ );
+ } catch (e) {
+ console.error("RolesContainsUs Error:", e);
+ return false;
+ }
}
public async getSecretForAddress(address: string): Promise {
@@ -946,9 +1024,7 @@ export class CoreBackend {
// ==========================================
async parseCipher(msg: string) {
try {
- const res = this.sdkService
- .getClient()
- .parse_cipher(msg, this.membersList, await this.getProcesses());
+ const res = await this.wasmService.parseCipher(msg, this.membersList, await this.getProcesses());
await this.handleApiReturn(res);
} catch (e) {
console.error("Cipher Error", e);
@@ -959,27 +1035,19 @@ export class CoreBackend {
const parsed = JSON.parse(msg);
if (parsed.error) return;
- const prevouts = this.sdkService
- .getClient()
- .get_prevouts(parsed.transaction);
+ const prevouts = await this.wasmService.getPrevouts(parsed.transaction);
for (const p of Object.values(await this.getProcesses())) {
const tip = p.states[p.states.length - 1].commited_in;
if (prevouts.includes(tip)) {
- const newTip = this.sdkService.getClient().get_txid(parsed.transaction);
- const newStateId = this.sdkService
- .getClient()
- .get_opreturn(parsed.transaction);
- this.sdkService
- .getClient()
- .process_commit_new_state(p, newStateId, newTip);
+ const newTip = await this.wasmService.getTxid(parsed.transaction);
+ const newStateId = await this.wasmService.getOpReturn(parsed.transaction);
+ await this.wasmService.processCommitNewState(p, newStateId, newTip);
break;
}
}
try {
- const res = this.sdkService
- .getClient()
- .parse_new_tx(msg, 0, this.membersList);
+ const res = await this.wasmService.parseNewTx(msg, 0, this.membersList);
if (
res &&
(res.partial_tx ||
@@ -988,7 +1056,7 @@ export class CoreBackend {
res.updated_process)
) {
await this.handleApiReturn(res);
- const d = this.dumpDeviceFromMemory();
+ const d = await this.dumpDeviceFromMemory();
const old = await this.getDeviceFromDatabase();
if (old && old.pairing_process_commitment)
d.pairing_process_commitment = old.pairing_process_commitment;
@@ -1003,7 +1071,7 @@ export class CoreBackend {
public async importJSON(backup: BackUp) {
await this.resetDevice();
await this.walletService.saveDeviceInDatabase(backup.device);
- this.walletService.restoreDevice(backup.device);
+ await this.walletService.restoreDevice(backup.device);
await this.processService.batchSaveProcesses(backup.processes);
await this.restoreSecretsFromBackUp(backup.secrets);
}
@@ -1019,9 +1087,7 @@ export class CoreBackend {
}
public async restoreSecretsFromDB() {
const secretsStore = await this.db.getAllSecrets();
- this.sdkService
- .getClient()
- .set_shared_secrets(JSON.stringify(secretsStore));
+ await this.wasmService.setSharedSecrets(JSON.stringify(secretsStore));
console.log("[CoreWorker] 🔐 Secrets restaurés depuis la DB");
}
public async createBackUp() {
@@ -1047,9 +1113,13 @@ export class CoreBackend {
);
try {
+ if (!(await this.isPaired())) {
+ console.warn(`⚠️ Device is not paired. Cannot decrypt attribute.`);
+ return null;
+ }
let hash: string | null | undefined = state.pcd_commitment[attribute];
let key: string | null | undefined = state.keys[attribute];
- const pairingProcessId = this.getPairingProcessId();
+ const pairingProcessId = await this.getPairingProcessId();
if (!hash) {
console.warn(`⚠️ L'attribut n'existe pas (pas de hash).`);
@@ -1080,12 +1150,10 @@ export class CoreBackend {
const cipher = new Uint8Array(buf);
const keyUIntArray = this.hexToUInt8Array(key);
- const clear = this.sdkService
- .getClient()
- .decrypt_data(keyUIntArray, cipher);
+ const clear = await this.wasmService.decryptData(keyUIntArray, cipher);
if (!clear) throw new Error("decrypt_data returned null");
- const decoded = this.sdkService.getClient().decode_value(clear);
+ const decoded = await this.wasmService.decodeValue(Array.from(clear));
console.log(`✅ Attribut '${attribute}' déchiffré avec succès.`);
return decoded;
} catch (e) {
diff --git a/src/workers/wasm.worker.ts b/src/workers/wasm.worker.ts
new file mode 100644
index 0000000..1ed36a3
--- /dev/null
+++ b/src/workers/wasm.worker.ts
@@ -0,0 +1,112 @@
+/**
+ * WASM Worker - Handles all WASM operations in background
+ * Single source of truth for WASM module lifecycle and method calls
+ */
+
+///
+
+interface WasmWorkerMessage {
+ id: string;
+ type: 'WASM_METHOD' | 'INIT';
+ method?: string;
+ args?: any[];
+}
+
+interface WasmWorkerResponse {
+ id: string;
+ type: 'SUCCESS' | 'ERROR';
+ result?: any;
+ error?: string;
+}
+
+let wasmClient: any = null;
+let isInitialized = false;
+
+// ============================================
+// WASM INITIALIZATION
+// ============================================
+
+async function initWasm(): Promise {
+ if (isInitialized && wasmClient) {
+ return;
+ }
+
+ console.log('[WasmWorker] 🔄 Initializing WASM module...');
+ wasmClient = await import('../../pkg/sdk_client');
+ wasmClient.setup();
+ isInitialized = true;
+ console.log('[WasmWorker] ✅ WASM module initialized');
+}
+
+// ============================================
+// WASM METHOD CALL HANDLER
+// ============================================
+
+async function callWasmMethod(method: string, args: any[]): Promise {
+ if (!wasmClient) {
+ throw new Error('WASM client not initialized');
+ }
+
+ if (typeof wasmClient[method] !== 'function') {
+ throw new Error(`WASM method '${method}' does not exist`);
+ }
+
+ try {
+ return await wasmClient[method](...args);
+ } catch (error) {
+ console.error(`[WasmWorker] Error calling WASM method '${method}':`, error);
+ throw error;
+ }
+}
+
+// ============================================
+// MESSAGE HANDLER
+// ============================================
+
+self.addEventListener('message', async (event: MessageEvent) => {
+ const { id, type, method, args = [] } = event.data;
+
+ try {
+ let result: any;
+
+ switch (type) {
+ case 'INIT':
+ await initWasm();
+ result = { success: true };
+ break;
+
+ case 'WASM_METHOD':
+ if (!method) {
+ throw new Error('Method name is required');
+ }
+ if (!isInitialized) {
+ await initWasm();
+ }
+ result = await callWasmMethod(method, args);
+ break;
+
+ default:
+ throw new Error(`Unknown message type: ${type}`);
+ }
+
+ self.postMessage({
+ id,
+ type: 'SUCCESS',
+ result,
+ } as WasmWorkerResponse);
+ } catch (error) {
+ self.postMessage({
+ id,
+ type: 'ERROR',
+ error: error instanceof Error ? error.message : String(error),
+ } as WasmWorkerResponse);
+ }
+});
+
+// ============================================
+// INITIALIZATION
+// ============================================
+
+console.log('[WasmWorker] 🔄 Worker script loaded');
+
+