Add some debug methods for pairing workflow
Some checks failed
Build and Push to Registry / build-and-push (push) Failing after 44s

This commit is contained in:
NicolasCantu 2025-10-31 14:10:26 +01:00
parent 79633ed923
commit 7ea4ef1920
3 changed files with 423 additions and 235 deletions

View File

@ -401,13 +401,13 @@ export async function registerAllListeners() {
// Retrieve the state for the process // Retrieve the state for the process
const process = await services.getProcess(processId); const process = await services.getProcess(processId);
if (!process) { if (!process) {
throw new Error('Can\'t find process'); throw new Error("Can't find process");
} }
const state = services.getStateFromId(process, stateId); const state = services.getStateFromId(process, stateId);
await services.checkConnections(process, stateId); await services.checkConnections(process, stateId);
let res: Record<string, any> = {}; const res: Record<string, any> = {};
if (state) { if (state) {
// Decrypt all the data we have the key for // Decrypt all the data we have the key for
for (const attribute of Object.keys(state.pcd_commitment)) { for (const attribute of Object.keys(state.pcd_commitment)) {
@ -427,7 +427,7 @@ export async function registerAllListeners() {
{ {
type: MessageType.DATA_RETRIEVED, type: MessageType.DATA_RETRIEVED,
data: res, data: res,
messageId: event.data.messageId messageId: event.data.messageId,
}, },
event.origin event.origin
); );
@ -500,6 +500,8 @@ export async function registerAllListeners() {
if (!services.isPaired()) { if (!services.isPaired()) {
const errorMsg = 'Device not paired'; const errorMsg = 'Device not paired';
console.log('Device not paired - running diagnosis...');
await services.diagnosePairingState();
errorResponse(errorMsg, event.origin, event.data.messageId); errorResponse(errorMsg, event.origin, event.data.messageId);
return; return;
} }

View File

@ -123,6 +123,7 @@ export default class Services {
this.relayReadyResolver = null; this.relayReadyResolver = null;
this.relayReadyPromise = null; this.relayReadyPromise = null;
} }
}
public async addWebsocketConnection(url: string): Promise<void> { public async addWebsocketConnection(url: string): Promise<void> {
console.log('Opening new websocket connection'); console.log('Opening new websocket connection');
@ -171,7 +172,21 @@ export default class Services {
public isPaired(): boolean { public isPaired(): boolean {
try { try {
return this.sdkClient.is_paired(); const result = this.sdkClient.is_paired();
console.log('isPaired() called, result:', result);
// Additional debugging: check device state
try {
const device = this.dumpDeviceFromMemory();
console.log('Current device state:', {
pairing_process_commitment: device.pairing_process_commitment,
paired_member: device.paired_member
});
} catch (deviceError) {
console.error('Failed to dump device for debugging:', deviceError);
}
return result;
} catch (e) { } catch (e) {
throw new Error(`isPaired ~ Error: ${e}`); throw new Error(`isPaired ~ Error: ${e}`);
} }
@ -230,7 +245,7 @@ export default class Services {
// We will take the roles from the last state, wheter it's commited or not // We will take the roles from the last state, wheter it's commited or not
public async checkConnections(process: Process, stateId: string | null = null): Promise<void> { public async checkConnections(process: Process, stateId: string | null = null): Promise<void> {
if (process.states.length < 2) { if (process.states.length < 2) {
throw new Error('Process doesn\'t have any state yet'); throw new Error("Process doesn't have any state yet");
} }
let roles: Record<string, RoleDefinition> | null = null; let roles: Record<string, RoleDefinition> | null = null;
if (!stateId) { if (!stateId) {
@ -251,12 +266,29 @@ export default class Services {
} }
} }
} }
if (members.size === 0) { if (members.size === 0) {
// This must be a pairing process // This must be a pairing process
// Check if we have a pairedAddresses in the public data // Check if we have a pairedAddresses in the public data
const publicData = process.states[0]?.public_data; let publicData: Record<string, any> | null = null;
if (!publicData || !publicData['pairedAddresses']) { if (!stateId) {
publicData = process.states[process.states.length - 2]?.public_data;
} else {
publicData = process.states.find(state => state.state_id === stateId)?.public_data || null;
}
// If pairedAddresses is not in the current state, look in previous states
if (!publicData?.['pairedAddresses']) {
// Look for pairedAddresses in previous states
for (let i = process.states.length - 1; i >= 0; i--) {
const state = process.states[i];
if (state.public_data && state.public_data['pairedAddresses']) {
publicData = state.public_data;
break;
}
}
}
if (!publicData?.['pairedAddresses']) {
throw new Error('Not a pairing process'); throw new Error('Not a pairing process');
} }
const decodedAddresses = this.decodeValue(publicData['pairedAddresses']); const decodedAddresses = this.decodeValue(publicData['pairedAddresses']);
@ -268,15 +300,19 @@ export default class Services {
// Ensure the amount is available before proceeding // Ensure the amount is available before proceeding
await this.getTokensFromFaucet(); await this.getTokensFromFaucet();
let unconnectedAddresses = new Set<string>(); const unconnectedAddresses = 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)) {
const sp_addresses = member.sp_addresses; const sp_addresses = member.sp_addresses;
if (!sp_addresses || sp_addresses.length === 0) continue; if (!sp_addresses || sp_addresses.length === 0) {
continue;
}
for (const address of sp_addresses) { for (const address of sp_addresses) {
// For now, we ignore our own device address, although there might be use cases for having a secret with ourselves // For now, we ignore our own device address, although there might be use cases for having a secret with ourselves
if (address === myAddress) continue; if (address === myAddress) {
if (await this.getSecretForAddress(address) === null) { continue;
}
if ((await this.getSecretForAddress(address)) === null) {
unconnectedAddresses.add(address); unconnectedAddresses.add(address);
} }
} }
@ -623,10 +659,17 @@ export default class Services {
try { try {
const parsedTx = this.sdkClient.parse_new_tx(newTxMsg, 0, membersList); const parsedTx = this.sdkClient.parse_new_tx(newTxMsg, 0, membersList);
if (parsedTx) { if (parsedTx && (parsedTx.partial_tx || parsedTx.new_tx_to_send || parsedTx.secrets || parsedTx.updated_process)) {
try { try {
await this.handleApiReturn(parsedTx); await this.handleApiReturn(parsedTx);
const newDevice = this.dumpDeviceFromMemory(); const newDevice = this.dumpDeviceFromMemory();
// Preserve pairing_process_commitment from existing device
const existingDevice = await this.getDeviceFromDatabase();
if (existingDevice && existingDevice.pairing_process_commitment) {
newDevice.pairing_process_commitment = existingDevice.pairing_process_commitment;
}
await this.saveDeviceInDatabase(newDevice); await this.saveDeviceInDatabase(newDevice);
} catch (e) { } catch (e) {
console.error('Failed to update device with new tx'); console.error('Failed to update device with new tx');
@ -639,6 +682,19 @@ export default class Services {
public async handleApiReturn(apiReturn: ApiReturn) { public async handleApiReturn(apiReturn: ApiReturn) {
console.log(apiReturn); console.log(apiReturn);
// Skip processing if apiReturn is empty or contains only null values
if (!apiReturn || Object.keys(apiReturn).length === 0) {
console.log('Skipping empty apiReturn');
return;
}
// Check if all values are null
const hasValidData = Object.values(apiReturn).some(value => value !== null && value !== undefined);
if (!hasValidData) {
console.log('Skipping apiReturn with only null values');
return;
}
if (apiReturn.partial_tx) { if (apiReturn.partial_tx) {
try { try {
const res = this.sdkClient.sign_transaction(apiReturn.partial_tx); const res = this.sdkClient.sign_transaction(apiReturn.partial_tx);
@ -708,6 +764,21 @@ export default class Services {
console.error('Failed to save diffs to db:', e); console.error('Failed to save diffs to db:', e);
} }
} }
// Check if this is a pairing process that's ready for confirmation
const existingDevice = await this.getDeviceFromDatabase();
if (existingDevice && existingDevice.pairing_process_commitment === processId) {
// This is our pairing process, check if it has paired addresses
const lastState = updatedProcess.current_process.states[updatedProcess.current_process.states.length - 1];
if (lastState && lastState.public_data && lastState.public_data['pairedAddresses']) {
console.log('Pairing process updated with paired addresses, confirming pairing...');
try {
await this.confirmPairing();
} catch (e) {
console.error('Failed to auto-confirm pairing:', e);
}
}
}
} }
if (apiReturn.push_to_storage && apiReturn.push_to_storage.length != 0) { if (apiReturn.push_to_storage && apiReturn.push_to_storage.length != 0) {
@ -756,13 +827,100 @@ export default class Services {
public async confirmPairing() { public async confirmPairing() {
try { try {
// Is the wasm paired? // Get the pairing process ID from database
const pairingId = this.getPairingProcessId(); const existingDevice = await this.getDeviceFromDatabase();
// TODO confirm that the pairing process id is known, commited if (!existingDevice || !existingDevice.pairing_process_commitment) {
console.error('No pairing process commitment found');
return;
}
const pairingProcessId = existingDevice.pairing_process_commitment;
// Get the pairing process to extract paired addresses
const myPairingProcess = await this.getProcess(pairingProcessId);
if (!myPairingProcess) {
console.error('Unknown pairing process');
return;
}
// Try to get committed state first, fallback to current state
let myPairingState = this.getLastCommitedState(myPairingProcess);
if (!myPairingState && myPairingProcess.states.length > 0) {
// If no committed state, use the current state
myPairingState = myPairingProcess.states[myPairingProcess.states.length - 1];
console.log('Using current state instead of committed state');
}
if (!myPairingState) {
console.error('No state found in pairing process');
return;
}
const encodedSpAddressList = myPairingState.public_data['pairedAddresses'];
if (!encodedSpAddressList) {
console.error('No paired addresses found in state');
return;
}
const spAddressList = this.decodeValue(encodedSpAddressList);
if (spAddressList.length === 0) {
console.error('Empty pairedAddresses');
return;
}
// Test parsing côté Rust
console.log('Checking if test_process_id_parsing is available:', typeof this.sdkClient.test_process_id_parsing);
try {
if (this.sdkClient.test_process_id_parsing) {
const rustParseResult = this.sdkClient.test_process_id_parsing(pairingProcessId);
console.log('Rust parsing test result:', rustParseResult);
} else {
console.error('test_process_id_parsing function not found in sdkClient');
console.log('Available functions:', Object.keys(this.sdkClient).filter(key => typeof this.sdkClient[key] === 'function'));
}
} catch (rustParseError) {
console.error('Rust parsing test failed:', rustParseError);
}
this.sdkClient.unpair_device(); // Clear any existing pairing
try {
this.sdkClient.pair_device(pairingProcessId, spAddressList);
console.log('pair_device() call succeeded');
} catch (pairError) {
console.error('pair_device() failed:', pairError);
throw pairError;
}
// Verify pairing was successful
const isPairedAfterPairing = this.sdkClient.is_paired();
console.log('Is paired after pair_device call:', isPairedAfterPairing);
// Save the updated device
const newDevice = this.dumpDeviceFromMemory(); const newDevice = this.dumpDeviceFromMemory();
console.log('Device from memory after pairing:', {
pairing_process_commitment: newDevice.pairing_process_commitment,
paired_member: newDevice.paired_member
});
// IMPORTANT: Only set pairing_process_commitment if WASM pairing succeeded
if (isPairedAfterPairing) {
console.log('WASM pairing succeeded, keeping WASM commitment');
// Don't override - use what WASM set
} else {
console.log('WASM pairing failed, manually setting commitment');
newDevice.pairing_process_commitment = pairingProcessId;
}
await this.saveDeviceInDatabase(newDevice); await this.saveDeviceInDatabase(newDevice);
// Final verification
const finalIsPaired = this.sdkClient.is_paired();
console.log('Final is_paired status:', finalIsPaired);
console.log('Device successfully paired with process:', pairingProcessId);
} catch (e) { } catch (e) {
console.error('Failed to confirm pairing'); console.error('Failed to confirm pairing:', e);
return; return;
} }
} }
@ -852,17 +1010,34 @@ export default class Services {
const db = await Database.getInstance(); const db = await Database.getInstance();
const walletStore = 'wallet'; const walletStore = 'wallet';
try { try {
console.log('Saving device to database:', {
pairing_process_commitment: device.pairing_process_commitment,
paired_member: device.paired_member
});
const prevDevice = await this.getDeviceFromDatabase(); const prevDevice = await this.getDeviceFromDatabase();
if (prevDevice) { if (prevDevice) {
console.log('Previous device found, deleting...');
await db.deleteObject(walletStore, "1"); await db.deleteObject(walletStore, "1");
} }
await db.addObject({ await db.addObject({
storeName: walletStore, storeName: walletStore,
object: { pre_id: '1', device }, object: { pre_id: '1', device },
key: null, key: null,
}); });
console.log('Device saved successfully');
// Verify save
const savedDevice = await this.getDeviceFromDatabase();
console.log('Verification - saved device:', {
pairing_process_commitment: savedDevice?.pairing_process_commitment,
paired_member: savedDevice?.paired_member
});
} catch (e) { } catch (e) {
console.error(e); console.error('Error saving device to database:', e);
} }
} }

View File

@ -152,8 +152,8 @@ export function initAddressInput() {
async function onCreateButtonClick() { async function onCreateButtonClick() {
try { try {
await prepareAndSendPairingTx(); await prepareAndSendPairingTx();
const service = await Services.getInstance(); // Don't call confirmPairing immediately - it will be called when the pairing process is complete
await service.confirmPairing(); console.log('Pairing process initiated. Waiting for completion...');
} catch (e) { } catch (e) {
console.error(`onCreateButtonClick error: ${e}`); console.error(`onCreateButtonClick error: ${e}`);
} }
@ -162,12 +162,6 @@ async function onCreateButtonClick() {
export async function prepareAndSendPairingTx(): Promise<void> { export async function prepareAndSendPairingTx(): Promise<void> {
const service = await Services.getInstance(); const service = await Services.getInstance();
try {
await service.checkConnections([]);
} catch (e) {
throw e;
}
try { try {
const relayAddress = service.getAllRelays(); const relayAddress = service.getAllRelays();
const createPairingProcessReturn = await service.createPairingProcess( const createPairingProcessReturn = await service.createPairingProcess(
@ -179,9 +173,26 @@ export async function prepareAndSendPairingTx(): Promise<void> {
throw new Error('createPairingProcess returned an empty new process'); throw new Error('createPairingProcess returned an empty new process');
} }
try {
await service.checkConnections(createPairingProcessReturn.updated_process.current_process, createPairingProcessReturn.updated_process.current_process.states[0].state_id);
} catch (e) {
throw e;
}
service.setProcessId(createPairingProcessReturn.updated_process.process_id); service.setProcessId(createPairingProcessReturn.updated_process.process_id);
service.setStateId(createPairingProcessReturn.updated_process.current_process.states[0].state_id); service.setStateId(createPairingProcessReturn.updated_process.current_process.states[0].state_id);
// Update device.pairing_process_commitment with the process_id
try {
const currentDevice = await service.getDeviceFromDatabase();
if (currentDevice) {
currentDevice.pairing_process_commitment = createPairingProcessReturn.updated_process.process_id;
await service.saveDeviceInDatabase(currentDevice);
}
} catch (err) {
console.error('Failed to update device pairing_process_commitment:', err);
}
await service.handleApiReturn(createPairingProcessReturn); await service.handleApiReturn(createPairingProcessReturn);
} catch (err) { } catch (err) {