Compare commits

...

2 Commits

Author SHA1 Message Date
Sosthene
e0e186f4f4 Refactor pairing logic
Account for the fact that we need the pairing id earlier now
2025-09-05 07:57:57 +02:00
Sosthene
bfca596e8b Adapt storage to new api 2025-09-05 07:57:50 +02:00
3 changed files with 81 additions and 82 deletions

View File

@ -251,15 +251,20 @@ export async function registerAllListeners() {
}
console.log('🚀 Starting pairing process');
await prepareAndSendPairingTx();
const myAddress = services.getDeviceAddress();
const createPairingProcessReturn = await services.createPairingProcess('', [myAddress]);
const pairingId = createPairingProcessReturn.updated_process?.process_id;
const stateId = createPairingProcessReturn.updated_process?.current_process?.states[0]?.state_id as string;
services.pairDevice(pairingId, [myAddress]);
await services.handleApiReturn(createPairingProcessReturn);
const createPrdUpdateReturn = await services.createPrdUpdate(pairingId, stateId);
await services.handleApiReturn(createPrdUpdateReturn);
const approveChangeReturn = await services.approveChange(pairingId, stateId);
await services.handleApiReturn(approveChangeReturn);
await services.confirmPairing();
const pairingId = services.getPairingProcessId();
if (!pairingId) {
throw new Error('Failed to get pairing process id');
}
// Send success response
const successMsg = {
type: MessageType.PAIRING_CREATED,

View File

@ -702,33 +702,16 @@ export default class Services {
}
public async confirmPairing() {
if (!this.processId || !this.stateId) {
console.error('Missing process and/or state ID');
return;
}
let createPrdUpdateReturn;
try {
createPrdUpdateReturn = await this.createPrdUpdate(this.processId, this.stateId);
} catch (e) {
throw new Error(`createPrdUpdate failed: ${e}`);
}
await this.handleApiReturn(createPrdUpdateReturn);
let approveChangeReturn;
try {
approveChangeReturn = await this.approveChange(this.processId, this.stateId);
} catch (e) {
throw new Error(`approveChange failed: ${e}`);
}
await this.handleApiReturn(approveChangeReturn);
await this.pairDevice();
this.processId = null;
this.stateId = null;
// Is the wasm paired?
const pairingId = this.getPairingProcessId();
// TODO confirm that the pairing process id is known, commited
const newDevice = this.dumpDeviceFromMemory();
await this.saveDeviceInDatabase(newDevice);
await navigate('account');
} catch (e) {
console.error('Failed to confirm pairing');
return;
}
}
public async updateDevice(): Promise<void> {
@ -766,41 +749,9 @@ export default class Services {
}
}
public async pairDevice() {
if (!this.processId) {
console.error('No processId set');
return;
}
const process = await this.getProcess(this.processId);
if (!process) {
console.error('Unknown process');
return;
}
let spAddressList: string[] = [];
public pairDevice(processId: string, spAddressList: string[]): void {
try {
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');
}
} catch (e) {
throw new Error(`Failed to get pairedAddresses from process: ${e}`);
}
try {
this.sdkClient.pair_device(this.processId, spAddressList);
this.sdkClient.pair_device(processId, spAddressList);
} catch (e) {
throw new Error(`Failed to pair device: ${e}`);
}
@ -1076,7 +1027,7 @@ export default class Services {
}
}
public async fetchValueFromStorage(hash: string): Promise<any | null> {
public async fetchValueFromStorage(hash: string): Promise<ArrayBuffer | null> {
const storages = [STORAGEURL];
return await retrieveData(storages, hash);

View File

@ -3,15 +3,26 @@ import axios, { AxiosResponse } from 'axios';
export async function storeData(servers: string[], key: string, value: Blob, ttl: number | null): Promise<AxiosResponse | null> {
for (const server of servers) {
try {
// Append key and ttl as query parameters
const url = new URL(`${server}/store`);
url.searchParams.append('key', key);
// Handle relative paths (for development proxy) vs absolute URLs (for production)
let url: string;
if (server.startsWith('/')) {
// Relative path - construct manually for proxy
url = `${server}/store?key=${encodeURIComponent(key)}`;
if (ttl !== null) {
url.searchParams.append('ttl', ttl.toString());
url += `&ttl=${ttl}`;
}
} else {
// Absolute URL - use URL constructor
const urlObj = new URL(`${server}/store`);
urlObj.searchParams.append('key', key);
if (ttl !== null) {
urlObj.searchParams.append('ttl', ttl.toString());
}
url = urlObj.toString();
}
// Send the encrypted ArrayBuffer as the raw request body.
const response = await axios.post(url.toString(), value, {
const response = await axios.post(url, value, {
headers: {
'Content-Type': 'application/octet-stream'
},
@ -35,21 +46,48 @@ export async function storeData(servers: string[], key: string, value: Blob, ttl
export async function retrieveData(servers: string[], key: string): Promise<ArrayBuffer | null> {
for (const server of servers) {
try {
// Handle relative paths (for development proxy) vs absolute URLs (for production)
const url = server.startsWith('/')
? `${server}/retrieve/${key}` // Relative path - use as-is for proxy
: new URL(`${server}/retrieve/${key}`).toString(); // Absolute URL - construct properly
console.log('Retrieving data', key,' from:', url);
// When fetching the data from the server:
const response = await axios.get(`${server}/retrieve/${key}`, {
const response = await axios.get(url, {
responseType: 'arraybuffer'
});
if (response.status !== 200) {
console.error('Received response status', response.status);
if (response.status === 200) {
// Validate that we received an ArrayBuffer
if (response.data instanceof ArrayBuffer) {
return response.data;
} else {
console.error('Server returned non-ArrayBuffer data:', typeof response.data);
continue;
}
} else {
console.error(`Server ${server} returned status ${response.status}`);
continue;
}
// console.log('Retrieved data:', response.data);
return response.data;
} catch (error) {
console.error('Error retrieving data:', error);
if (axios.isAxiosError(error)) {
if (error.response?.status === 404) {
console.log(`Data not found on server ${server} for key ${key}`);
continue; // Try next server
} else if (error.response?.status) {
console.error(`Server ${server} error ${error.response.status}:`, error.response.statusText);
continue;
} else {
console.error(`Network error connecting to ${server}:`, error.message);
continue;
}
} else {
console.error(`Unexpected error retrieving data from ${server}:`, error);
continue;
}
}
return null
}
return null;
}
interface TestResponse {
@ -62,7 +100,12 @@ export async function testData(servers: string[], key: string): Promise<Record<s
for (const server of servers) {
res[server] = null;
try {
const response = await axios.get(`${server}/test/${key}`);
// Handle relative paths (for development proxy) vs absolute URLs (for production)
const url = server.startsWith('/')
? `${server}/test/${key}` // Relative path - use as-is for proxy
: new URL(`${server}/test/${key}`).toString(); // Absolute URL - construct properly
const response = await axios.get(url);
if (response.status !== 200) {
console.error(`${server}: Test response status: ${response.status}`);
continue;