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