Merge remote-tracking branch 'origin/dev' into cicd
All checks were successful
Build and Push to Registry / build-and-push (push) Successful in 2m7s
All checks were successful
Build and Push to Registry / build-and-push (push) Successful in 2m7s
This commit is contained in:
commit
205796d22a
@ -39,6 +39,8 @@ export enum MessageType {
|
||||
DATA_RETRIEVED = 'DATA_RETRIEVED',
|
||||
DECODE_PUBLIC_DATA = 'DECODE_PUBLIC_DATA',
|
||||
PUBLIC_DATA_DECODED = 'PUBLIC_DATA_DECODED',
|
||||
GET_MEMBER_ADDRESSES = 'GET_MEMBER_ADDRESSES',
|
||||
MEMBER_ADDRESSES_RETRIEVED = 'MEMBER_ADDRESSES_RETRIEVED',
|
||||
// Processes
|
||||
CREATE_PROCESS = 'CREATE_PROCESS',
|
||||
PROCESS_CREATED = 'PROCESS_CREATED',
|
||||
@ -53,4 +55,9 @@ export enum MessageType {
|
||||
VALUE_HASHED = 'VALUE_HASHED',
|
||||
GET_MERKLE_PROOF = 'GET_MERKLE_PROOF',
|
||||
MERKLE_PROOF_RETRIEVED = 'MERKLE_PROOF_RETRIEVED',
|
||||
VALIDATE_MERKLE_PROOF = 'VALIDATE_MERKLE_PROOF',
|
||||
MERKLE_PROOF_VALIDATED = 'MERKLE_PROOF_VALIDATED',
|
||||
// Account management
|
||||
ADD_DEVICE = 'ADD_DEVICE',
|
||||
DEVICE_ADDED = 'DEVICE_ADDED',
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import { prepareAndSendPairingTx } from './utils/sp-address.utils';
|
||||
import ModalService from './services/modal.service';
|
||||
import { MessageType } from './models/process.model';
|
||||
import { splitPrivateData, isValid32ByteHex } from './utils/service.utils';
|
||||
import { MerkleProofResult } from 'pkg/sdk_client';
|
||||
|
||||
const routes: { [key: string]: string } = {
|
||||
home: '/src/pages/home/home.html',
|
||||
@ -139,17 +140,22 @@ export async function init(): Promise<void> {
|
||||
(window as any).myService = services;
|
||||
const db = await Database.getInstance();
|
||||
db.registerServiceWorker('/src/service-workers/database.worker.js');
|
||||
let device = await services.getDeviceFromDatabase();
|
||||
const device = await services.getDeviceFromDatabase();
|
||||
console.log('🚀 ~ setTimeout ~ device:', device);
|
||||
|
||||
if (!device) {
|
||||
device = await services.createNewDevice();
|
||||
await services.createNewDevice();
|
||||
} else {
|
||||
services.restoreDevice(device);
|
||||
}
|
||||
|
||||
// If we create a new device, we most probably don't have anything in db, but just in case
|
||||
await services.restoreProcessesFromDB();
|
||||
await services.restoreSecretsFromDB();
|
||||
|
||||
// We connect to all relays now
|
||||
await services.connectAllRelays();
|
||||
|
||||
// We register all the event listeners if we run in an iframe
|
||||
if (window.self !== window.top) {
|
||||
await registerAllListeners();
|
||||
@ -591,9 +597,28 @@ export async function registerAllListeners() {
|
||||
if (!process) {
|
||||
throw new Error('Process not found');
|
||||
}
|
||||
const lastState = services.getLastCommitedState(process);
|
||||
let lastState = services.getLastCommitedState(process);
|
||||
if (!lastState) {
|
||||
throw new Error('Process doesn\'t have a commited state yet');
|
||||
const firstState = process.states[0];
|
||||
const roles = firstState.roles;
|
||||
if (services.rolesContainsUs(roles)) {
|
||||
const approveChangeRes= await services.approveChange(processId, firstState.state_id);
|
||||
await services.handleApiReturn(approveChangeRes);
|
||||
const prdUpdateRes = await services.createPrdUpdate(processId, firstState.state_id);
|
||||
await services.handleApiReturn(prdUpdateRes);
|
||||
} else {
|
||||
if (firstState.validation_tokens.length > 0) {
|
||||
// Try to send it again anyway
|
||||
const res = await services.createPrdUpdate(processId, firstState.state_id);
|
||||
await services.handleApiReturn(res);
|
||||
}
|
||||
}
|
||||
// Wait a couple seconds
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
lastState = services.getLastCommitedState(process);
|
||||
if (!lastState) {
|
||||
throw new Error('Process doesn\'t have a commited state yet');
|
||||
}
|
||||
}
|
||||
const lastStateIndex = services.getLastCommitedStateIndex(process);
|
||||
if (lastStateIndex === null) {
|
||||
@ -675,7 +700,7 @@ export async function registerAllListeners() {
|
||||
throw new Error('Invalid or expired session token');
|
||||
}
|
||||
|
||||
const decodedData = await services.decodeValue(encodedData);
|
||||
const decodedData = services.decodeValue(encodedData);
|
||||
|
||||
window.parent.postMessage(
|
||||
{
|
||||
@ -722,8 +747,6 @@ export async function registerAllListeners() {
|
||||
const handleGetMerkleProof = async (event: MessageEvent) => {
|
||||
if (event.data.type !== MessageType.GET_MERKLE_PROOF) return;
|
||||
|
||||
console.log('handleGetMerkleProof', event.data);
|
||||
|
||||
try {
|
||||
const { accessToken, processState, attributeName } = event.data;
|
||||
|
||||
@ -747,6 +770,41 @@ export async function registerAllListeners() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleValidateMerkleProof = async (event: MessageEvent) => {
|
||||
if (event.data.type !== MessageType.VALIDATE_MERKLE_PROOF) return;
|
||||
|
||||
try {
|
||||
const { accessToken, merkleProof, documentHash } = event.data;
|
||||
|
||||
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
|
||||
throw new Error('Invalid or expired session token');
|
||||
}
|
||||
|
||||
// Try to parse the proof
|
||||
// We will validate it's a MerkleProofResult in the wasm
|
||||
let parsedMerkleProof: MerkleProofResult;
|
||||
try {
|
||||
parsedMerkleProof= JSON.parse(merkleProof);
|
||||
} catch (e) {
|
||||
throw new Error('Provided merkleProof is not a valid json object');
|
||||
}
|
||||
|
||||
const res = services.validateMerkleProof(parsedMerkleProof, documentHash);
|
||||
|
||||
window.parent.postMessage(
|
||||
{
|
||||
type: MessageType.MERKLE_PROOF_VALIDATED,
|
||||
isValid: res,
|
||||
messageId: event.data.messageId
|
||||
},
|
||||
event.origin
|
||||
);
|
||||
} catch (e) {
|
||||
const errorMsg = `Failed to get merkle proof: ${e}`;
|
||||
errorResponse(errorMsg, event.origin, event.data.messageId);
|
||||
}
|
||||
}
|
||||
|
||||
window.removeEventListener('message', handleMessage);
|
||||
window.addEventListener('message', handleMessage);
|
||||
|
||||
@ -795,6 +853,9 @@ export async function registerAllListeners() {
|
||||
case MessageType.GET_MERKLE_PROOF:
|
||||
await handleGetMerkleProof(event);
|
||||
break;
|
||||
case MessageType.VALIDATE_MERKLE_PROOF:
|
||||
await handleValidateMerkleProof(event);
|
||||
break;
|
||||
default:
|
||||
console.warn(`Unhandled message type: ${event.data.type}`);
|
||||
}
|
||||
|
@ -45,6 +45,21 @@ self.addEventListener('message', async (event) => {
|
||||
} catch (error) {
|
||||
event.ports[0].postMessage({ status: 'error', message: error.message });
|
||||
}
|
||||
} else if (data.type === 'BATCH_WRITING') {
|
||||
const { storeName, objects } = data.payload;
|
||||
const db = await openDatabase();
|
||||
const tx = db.transaction(storeName, 'readwrite');
|
||||
const store = tx.objectStore(storeName);
|
||||
|
||||
for (const { key, object } of objects) {
|
||||
if (key) {
|
||||
await store.put(object, key);
|
||||
} else {
|
||||
await store.put(object);
|
||||
}
|
||||
}
|
||||
|
||||
await tx.done;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -147,7 +147,7 @@ export class Database {
|
||||
const activeWorker = this.serviceWorkerRegistration?.active || (await this.waitForServiceWorkerActivation(this.serviceWorkerRegistration!));
|
||||
const service = await Services.getInstance();
|
||||
const payload = await service.getMyProcesses();
|
||||
if (payload!.length != 0) {
|
||||
if (payload && payload.length != 0) {
|
||||
activeWorker?.postMessage({ type: 'SCAN', payload });
|
||||
}
|
||||
}, 5000);
|
||||
@ -323,6 +323,38 @@ export class Database {
|
||||
});
|
||||
}
|
||||
|
||||
public batchWriting(payload: { storeName: string; objects: { key: any; object: any }[] }): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (!this.serviceWorkerRegistration) {
|
||||
this.serviceWorkerRegistration = await navigator.serviceWorker.ready;
|
||||
}
|
||||
|
||||
const activeWorker = await this.waitForServiceWorkerActivation(this.serviceWorkerRegistration);
|
||||
const messageChannel = new MessageChannel();
|
||||
|
||||
messageChannel.port1.onmessage = (event) => {
|
||||
if (event.data.status === 'success') {
|
||||
resolve();
|
||||
} else {
|
||||
const error = event.data.message;
|
||||
reject(new Error(error || 'Unknown error occurred while adding objects'));
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
activeWorker?.postMessage(
|
||||
{
|
||||
type: 'BATCH_WRITING',
|
||||
payload,
|
||||
},
|
||||
[messageChannel.port2],
|
||||
);
|
||||
} catch (error) {
|
||||
reject(new Error(`Failed to send message to service worker: ${error}`));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async getObject(storeName: string, key: string): Promise<any | null> {
|
||||
const db = await this.getDb();
|
||||
const tx = db.transaction(storeName, 'readonly');
|
||||
@ -341,23 +373,25 @@ export class Database {
|
||||
const store = tx.objectStore(storeName);
|
||||
|
||||
try {
|
||||
// Wait for both getAllKeys() and getAll() to resolve
|
||||
const [keys, values] = await Promise.all([
|
||||
new Promise<any[]>((resolve, reject) => {
|
||||
const request = store.getAllKeys();
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
}),
|
||||
new Promise<any[]>((resolve, reject) => {
|
||||
const request = store.getAll();
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
}),
|
||||
]);
|
||||
return new Promise((resolve, reject) => {
|
||||
const result: Record<string, any> = {};
|
||||
const cursor = store.openCursor();
|
||||
|
||||
// Combine keys and values into an object
|
||||
const result: Record<string, any> = Object.fromEntries(keys.map((key, index) => [key, values[index]]));
|
||||
return result;
|
||||
cursor.onsuccess = (event) => {
|
||||
const request = event.target as IDBRequest<IDBCursorWithValue | null>;
|
||||
const cursor = request.result;
|
||||
if (cursor) {
|
||||
result[cursor.key as string] = cursor.value;
|
||||
cursor.continue();
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
};
|
||||
|
||||
cursor.onerror = () => {
|
||||
reject(cursor.error);
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching data from IndexedDB:', error);
|
||||
throw error;
|
||||
|
@ -132,7 +132,7 @@ export default class ModalService {
|
||||
console.log("MEMBERS:", members);
|
||||
// We take all the addresses except our own
|
||||
const service = await Services.getInstance();
|
||||
const localAddress = await service.getDeviceAddress();
|
||||
const localAddress = service.getDeviceAddress();
|
||||
for (const member of members) {
|
||||
if (member.sp_addresses) {
|
||||
for (const address of member.sp_addresses) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { INotification } from '~/models/notification.model';
|
||||
import { IProcess } from '~/models/process.model';
|
||||
import { initWebsocket, sendMessage } from '../websockets';
|
||||
import { ApiReturn, Device, HandshakeMessage, Member, OutPointProcessMap, Process, ProcessState, RoleDefinition, SecretsStore, UserDiff } from '../../pkg/sdk_client';
|
||||
import { ApiReturn, Device, HandshakeMessage, Member, MerkleProofResult, OutPointProcessMap, Process, ProcessState, RoleDefinition, SecretsStore, UserDiff } from '../../pkg/sdk_client';
|
||||
import ModalService from './modal.service';
|
||||
import Database from './database.service';
|
||||
import { navigate } from '../router';
|
||||
@ -23,6 +23,7 @@ export default class Services {
|
||||
private processId: string | null = null;
|
||||
private stateId: string | null = null;
|
||||
private sdkClient: any;
|
||||
private processesCache: Record<string, Process> = {};
|
||||
private myProcesses: Set<string> = new Set();
|
||||
private notifications: any[] | null = null;
|
||||
private subscriptions: { element: Element; event: string; eventHandler: string }[] = [];
|
||||
@ -61,7 +62,6 @@ export default class Services {
|
||||
for (const wsurl of Object.values(BOOTSTRAPURL)) {
|
||||
this.updateRelay(wsurl, '');
|
||||
}
|
||||
await this.connectAllRelays();
|
||||
}
|
||||
|
||||
public setProcessId(processId: string | null) {
|
||||
@ -201,7 +201,7 @@ export default class Services {
|
||||
// Ensure the amount is available before proceeding
|
||||
await this.getTokensFromFaucet();
|
||||
let unconnectedAddresses = [];
|
||||
const myAddress = await this.getDeviceAddress();
|
||||
const myAddress = this.getDeviceAddress();
|
||||
for (const member of members) {
|
||||
const sp_addresses = member.sp_addresses;
|
||||
if (!sp_addresses || sp_addresses.length === 0) continue;
|
||||
@ -386,6 +386,19 @@ export default class Services {
|
||||
members.add(member)
|
||||
}
|
||||
}
|
||||
if (members.size === 0) {
|
||||
// This must be a pairing process
|
||||
// Check if we have a pairedAddresses in the public data
|
||||
const publicData = this.getPublicData(process);
|
||||
if (!publicData || !publicData['pairedAddresses']) {
|
||||
throw new Error('Not a pairing process');
|
||||
}
|
||||
const decodedAddresses = this.decodeValue(publicData['pairedAddresses']);
|
||||
if (decodedAddresses.length === 0) {
|
||||
throw new Error('Not a pairing process');
|
||||
}
|
||||
members.add({ sp_addresses: decodedAddresses });
|
||||
}
|
||||
await this.checkConnections([...members]);
|
||||
const privateSplitData = this.splitData(privateData);
|
||||
const publicSplitData = this.splitData(publicData);
|
||||
@ -660,6 +673,41 @@ export default class Services {
|
||||
await navigate('account');
|
||||
}
|
||||
|
||||
public async updateDevice(): Promise<void> {
|
||||
let myPairingProcessId: string;
|
||||
try {
|
||||
myPairingProcessId = this.getPairingProcessId();
|
||||
} catch (e) {
|
||||
console.error('Failed to get pairing process id');
|
||||
return;
|
||||
}
|
||||
|
||||
const myPairingProcess = await this.getProcess(myPairingProcessId);
|
||||
if (!myPairingProcess) {
|
||||
console.error('Unknown pairing process');
|
||||
return;
|
||||
}
|
||||
const myPairingState = this.getLastCommitedState(myPairingProcess);
|
||||
if (myPairingState) {
|
||||
const encodedSpAddressList = myPairingState.public_data['pairedAddresses'];
|
||||
const spAddressList = this.decodeValue(encodedSpAddressList);
|
||||
if (spAddressList.length === 0) {
|
||||
console.error('Empty pairedAddresses');
|
||||
return;
|
||||
}
|
||||
// We can check if our address is included and simply unpair if it's not
|
||||
if (!spAddressList.includes(this.getDeviceAddress())) {
|
||||
await this.unpairDevice();
|
||||
return;
|
||||
}
|
||||
// We can update the device with the new addresses
|
||||
this.sdkClient.unpair_device();
|
||||
this.sdkClient.pair_device(myPairingProcessId, spAddressList);
|
||||
const newDevice = this.dumpDeviceFromMemory();
|
||||
await this.saveDeviceInDatabase(newDevice);
|
||||
}
|
||||
}
|
||||
|
||||
public async pairDevice() {
|
||||
if (!this.processId) {
|
||||
console.error('No processId set');
|
||||
@ -673,7 +721,19 @@ export default class Services {
|
||||
|
||||
let spAddressList: string[] = [];
|
||||
try {
|
||||
const encodedSpAddressList = process.states[0].public_data['pairedAddresses'];
|
||||
let encodedSpAddressList: number[] = [];
|
||||
if (this.stateId) {
|
||||
const state = process.states.find(state => state.state_id === this.stateId);
|
||||
if (state) {
|
||||
encodedSpAddressList = state.public_data['pairedAddresses'];
|
||||
}
|
||||
} else {
|
||||
// We assume it's the last commited state
|
||||
const lastCommitedState = this.getLastCommitedState(process);
|
||||
if (lastCommitedState) {
|
||||
encodedSpAddressList = lastCommitedState.public_data['pairedAddresses'];
|
||||
}
|
||||
}
|
||||
spAddressList = this.sdkClient.decode_value(encodedSpAddressList);
|
||||
if (!spAddressList || spAddressList.length == 0) {
|
||||
throw new Error('Empty pairedAddresses');
|
||||
@ -693,11 +753,15 @@ export default class Services {
|
||||
return amount;
|
||||
}
|
||||
|
||||
async getDeviceAddress() {
|
||||
return await this.sdkClient.get_address();
|
||||
getDeviceAddress(): string {
|
||||
try {
|
||||
return this.sdkClient.get_address();
|
||||
} catch (e) {
|
||||
throw new Error(`Failed to get device address: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
public dumpDeviceFromMemory(): string {
|
||||
public dumpDeviceFromMemory(): Device {
|
||||
try {
|
||||
return this.sdkClient.dump_device();
|
||||
} catch (e) {
|
||||
@ -722,7 +786,7 @@ export default class Services {
|
||||
}
|
||||
}
|
||||
|
||||
async saveDeviceInDatabase(device: any): Promise<void> {
|
||||
async saveDeviceInDatabase(device: Device): Promise<void> {
|
||||
const db = await Database.getInstance();
|
||||
const walletStore = 'wallet';
|
||||
try {
|
||||
@ -740,14 +804,13 @@ export default class Services {
|
||||
}
|
||||
}
|
||||
|
||||
async getDeviceFromDatabase(): Promise<string | null> {
|
||||
async getDeviceFromDatabase(): Promise<Device | null> {
|
||||
const db = await Database.getInstance();
|
||||
const walletStore = 'wallet';
|
||||
try {
|
||||
const dbRes = await db.getObject(walletStore, '1');
|
||||
if (dbRes) {
|
||||
const wallet = dbRes['device'];
|
||||
return wallet;
|
||||
return dbRes['device'];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -760,8 +823,7 @@ export default class Services {
|
||||
try {
|
||||
const device = await this.getDeviceFromDatabase();
|
||||
if (device) {
|
||||
const parsed: Device = JSON.parse(device);
|
||||
const pairedMember = parsed['paired_member'];
|
||||
const pairedMember = device['paired_member'];
|
||||
return pairedMember.sp_addresses;
|
||||
} else {
|
||||
return null;
|
||||
@ -785,30 +847,22 @@ export default class Services {
|
||||
rolesContainsUs(roles: Record<string, RoleDefinition>): boolean {
|
||||
let us;
|
||||
try {
|
||||
us = this.sdkClient.get_member();
|
||||
us = this.sdkClient.get_pairing_process_id();
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
return this.rolesContainsMember(roles, us.sp_addresses);
|
||||
return this.rolesContainsMember(roles, us);
|
||||
}
|
||||
|
||||
rolesContainsMember(roles: Record<string, RoleDefinition>, member: string[]): boolean {
|
||||
let res = false;
|
||||
for (const [roleName, roleDef] of Object.entries(roles)) {
|
||||
for (const otherMember of roleDef.members) {
|
||||
if (res) { return true }
|
||||
// Get the addresses for the member
|
||||
const otherMemberAddresses: string[] | null = this.getAddressesForMemberId(otherMember);
|
||||
if (!otherMemberAddresses) {
|
||||
// console.error('Failed to get addresses for member', otherMember);
|
||||
continue;
|
||||
}
|
||||
res = this.compareMembers(member, otherMemberAddresses);
|
||||
rolesContainsMember(roles: Record<string, RoleDefinition>, pairingProcessId: string): boolean {
|
||||
for (const roleDef of Object.values(roles)) {
|
||||
if (roleDef.members.includes(pairingProcessId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
return false;
|
||||
}
|
||||
|
||||
async dumpWallet() {
|
||||
@ -834,10 +888,9 @@ export default class Services {
|
||||
return spAddress;
|
||||
}
|
||||
|
||||
restoreDevice(device: string) {
|
||||
public restoreDevice(device: Device) {
|
||||
try {
|
||||
this.sdkClient.restore_device(device);
|
||||
const spAddress = this.sdkClient.get_address();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@ -854,14 +907,33 @@ export default class Services {
|
||||
}
|
||||
}
|
||||
|
||||
public async batchSaveProcessesToDb(processes: Record<string, Process>) {
|
||||
if (Object.keys(processes).length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const db = await Database.getInstance();
|
||||
const storeName = 'processes';
|
||||
try {
|
||||
await db.batchWriting({ storeName, objects: Object.entries(processes).map(([key, value]) => ({ key, object: value })) });
|
||||
this.processesCache = { ...this.processesCache, ...processes };
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public async saveProcessToDb(processId: string, process: Process) {
|
||||
const db = await Database.getInstance();
|
||||
const storeName = 'processes';
|
||||
try {
|
||||
await db.addObject({
|
||||
storeName: 'processes',
|
||||
storeName,
|
||||
object: process,
|
||||
key: processId,
|
||||
});
|
||||
|
||||
// Update the process in the cache
|
||||
this.processesCache[processId] = process;
|
||||
} catch (e) {
|
||||
console.error(`Failed to save process ${processId}: ${e}`);
|
||||
}
|
||||
@ -927,43 +999,36 @@ export default class Services {
|
||||
}
|
||||
|
||||
public async getProcess(processId: string): Promise<Process | null> {
|
||||
const db = await Database.getInstance();
|
||||
return await db.getObject('processes', processId);
|
||||
if (this.processesCache[processId]) {
|
||||
return this.processesCache[processId];
|
||||
} else {
|
||||
const db = await Database.getInstance();
|
||||
const process = await db.getObject('processes', processId);
|
||||
return process;
|
||||
}
|
||||
}
|
||||
|
||||
public async getProcesses(): Promise<Record<string, Process>> {
|
||||
const db = await Database.getInstance();
|
||||
|
||||
const processes: Record<string, Process> = await db.dumpStore('processes');
|
||||
return processes;
|
||||
if (Object.keys(this.processesCache).length > 0) {
|
||||
return this.processesCache;
|
||||
} else {
|
||||
try {
|
||||
const db = await Database.getInstance();
|
||||
this.processesCache = await db.dumpStore('processes');
|
||||
return this.processesCache;
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO rewrite that it's a mess and we don't use it now
|
||||
// public async getChildrenOfProcess(processId: string): Promise<string[]> {
|
||||
// const processes = await this.getProcesses();
|
||||
|
||||
// const res = [];
|
||||
// for (const [hash, process] of Object.entries(processes)) {
|
||||
// const firstState = process.states[0];
|
||||
// const pcdCommitment = firstState['pcd_commitment'];
|
||||
// try {
|
||||
// const parentIdHash = pcdCommitment['parent_id'];
|
||||
// const diff = await this.getDiffByValue(parentIdHash);
|
||||
// if (diff && diff['new_value'] === processId) {
|
||||
// res.push(JSON.stringify(process));
|
||||
// }
|
||||
// } catch (e) {
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return res;
|
||||
// }
|
||||
|
||||
public async restoreProcessesFromBackUp(processes: Record<string, Process>) {
|
||||
const db = await Database.getInstance();
|
||||
for (const [commitedIn, process] of Object.entries(processes)) {
|
||||
await db.addObject({ storeName: 'processes', object: process, key: commitedIn});
|
||||
const storeName = 'processes';
|
||||
try {
|
||||
await db.batchWriting({ storeName, objects: Object.entries(processes).map(([key, value]) => ({ key, object: value })) });
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
await this.restoreProcessesFromDB();
|
||||
@ -976,6 +1041,7 @@ export default class Services {
|
||||
const processes: Record<string, Process> = await db.dumpStore('processes');
|
||||
if (processes && Object.keys(processes).length != 0) {
|
||||
console.log(`Restoring ${Object.keys(processes).length} processes`);
|
||||
this.processesCache = processes;
|
||||
this.sdkClient.set_process_cache(processes);
|
||||
} else {
|
||||
console.log('No processes to restore!');
|
||||
@ -1033,7 +1099,7 @@ export default class Services {
|
||||
}
|
||||
}
|
||||
|
||||
async decodeValue(value: number[]): Promise<any | null> {
|
||||
decodeValue(value: number[]): any | null {
|
||||
try {
|
||||
return this.sdkClient.decode_value(value);
|
||||
} catch (e) {
|
||||
@ -1215,7 +1281,21 @@ export default class Services {
|
||||
|
||||
setTimeout(async () => {
|
||||
const newProcesses: OutPointProcessMap = handshakeMsg.processes_list;
|
||||
if (newProcesses && Object.keys(newProcesses).length !== 0) {
|
||||
if (!newProcesses || Object.keys(newProcesses).length === 0) {
|
||||
console.debug('Received empty processes list from', url);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.processesCache && Object.keys(this.processesCache).length === 0) {
|
||||
// We restored db but cache is empty, meaning we're starting from scratch
|
||||
try {
|
||||
await this.batchSaveProcessesToDb(newProcesses);
|
||||
} catch (e) {
|
||||
console.error('Failed to save processes to db:', e);
|
||||
}
|
||||
} else {
|
||||
// We need to update our processes with what relay provides
|
||||
const toSave: Record<string, Process> = {};
|
||||
for (const [processId, process] of Object.entries(newProcesses)) {
|
||||
const existing = await this.getProcess(processId);
|
||||
if (existing) {
|
||||
@ -1235,6 +1315,25 @@ export default class Services {
|
||||
if (new_states.length != 0) {
|
||||
// We request the new states
|
||||
await this.requestDataFromPeers(processId, new_states, roles);
|
||||
toSave[processId] = process;
|
||||
}
|
||||
|
||||
// Just to be sure check if that's a pairing process
|
||||
const lastCommitedState = this.getLastCommitedState(process);
|
||||
if (lastCommitedState && lastCommitedState.public_data && lastCommitedState.public_data['pairedAddresses']) {
|
||||
// This is a pairing process
|
||||
try {
|
||||
const pairedAddresses = this.decodeValue(lastCommitedState.public_data['pairedAddresses']);
|
||||
// Are we part of it?
|
||||
if (pairedAddresses && pairedAddresses.length > 0 && pairedAddresses.includes(this.getDeviceAddress())) {
|
||||
// We save the process to db
|
||||
await this.saveProcessToDb(processId, process as Process);
|
||||
// We update the device
|
||||
await this.updateDevice();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to check for pairing process:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise we're probably just in the initial loading at page initialization
|
||||
@ -1246,9 +1345,11 @@ export default class Services {
|
||||
} else {
|
||||
// We add it to db
|
||||
console.log(`Saving ${processId} to db`);
|
||||
await this.saveProcessToDb(processId, process as Process);
|
||||
toSave[processId] = process;
|
||||
}
|
||||
}
|
||||
|
||||
await this.batchSaveProcessesToDb(toSave);
|
||||
}
|
||||
}, 500)
|
||||
} catch (e) {
|
||||
@ -1301,9 +1402,12 @@ export default class Services {
|
||||
const content = JSON.parse(response);
|
||||
const error = content.error;
|
||||
const errorMsg = error['GenericError'];
|
||||
if (errorMsg === 'State is identical to the previous state') {
|
||||
return;
|
||||
} else if (errorMsg === 'Not enough valid proofs') { return; }
|
||||
const dontRetry = [
|
||||
'State is identical to the previous state',
|
||||
'Not enough valid proofs',
|
||||
'Not enough members to validate',
|
||||
];
|
||||
if (dontRetry.includes(errorMsg)) { return; }
|
||||
// Wait and retry
|
||||
setTimeout(async () => {
|
||||
await this.sendCommitMessage(JSON.stringify(content));
|
||||
@ -1340,7 +1444,7 @@ export default class Services {
|
||||
const lastCommitedState = this.getLastCommitedState(process);
|
||||
if (lastCommitedState && lastCommitedState.public_data) {
|
||||
const processName = lastCommitedState!.public_data['processName'];
|
||||
if (processName) { return processName }
|
||||
if (processName) { return this.decodeValue(processName) }
|
||||
else { return null }
|
||||
} else {
|
||||
return null;
|
||||
@ -1348,24 +1452,32 @@ export default class Services {
|
||||
}
|
||||
|
||||
public async getMyProcesses(): Promise<string[] | null> {
|
||||
// If we're not paired yet, just skip it
|
||||
try {
|
||||
this.getPairingProcessId();
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const processes = await this.getProcesses();
|
||||
|
||||
const newMyProcesses = new Set<string>(this.myProcesses || []);
|
||||
for (const [processId, process] of Object.entries(processes)) {
|
||||
// We use myProcesses attribute to not reevaluate all processes everytime
|
||||
if (this.myProcesses && this.myProcesses.has(processId)) {
|
||||
if (newMyProcesses.has(processId)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const roles = this.getRoles(process);
|
||||
|
||||
if (roles && this.rolesContainsUs(roles)) {
|
||||
this.myProcesses.add(processId);
|
||||
newMyProcesses.add(processId);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
this.myProcesses = newMyProcesses; // atomic update
|
||||
return Array.from(this.myProcesses);
|
||||
} catch (e) {
|
||||
console.error("Failed to get processes:", e);
|
||||
@ -1418,6 +1530,14 @@ export default class Services {
|
||||
return this.sdkClient.get_merkle_proof(processState, attributeName);
|
||||
}
|
||||
|
||||
public validateMerkleProof(proof: MerkleProofResult, hash: string): boolean {
|
||||
try {
|
||||
return this.sdkClient.validate_merkle_proof(proof, hash);
|
||||
} catch (e) {
|
||||
throw new Error(`Failed to validate merkle proof: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
public getLastCommitedState(process: Process): ProcessState | null {
|
||||
if (process.states.length === 0) return null;
|
||||
const processTip = process.states[process.states.length - 1].commited_in;
|
||||
@ -1440,6 +1560,13 @@ export default class Services {
|
||||
return null;
|
||||
}
|
||||
|
||||
public getUncommitedStates(process: Process): ProcessState[] {
|
||||
if (process.states.length === 0) return [];
|
||||
const processTip = process.states[process.states.length - 1].commited_in;
|
||||
const res = process.states.filter(state => state.commited_in === processTip);
|
||||
return res.filter(state => state.state_id !== EMPTY32BYTES);
|
||||
}
|
||||
|
||||
public getStateFromId(process: Process, stateId: string): ProcessState | null {
|
||||
if (process.states.length === 0) return null;
|
||||
const state = process.states.find(state => state.state_id === stateId);
|
||||
|
Loading…
x
Reference in New Issue
Block a user