feat: Dockerfile et script de démarrage pour déploiement 4NK_node

This commit is contained in:
Debian 2025-09-01 12:29:22 +00:00
parent 93eb637f1c
commit 6247680430
5 changed files with 859 additions and 180 deletions

336
pkg/sdk_client.d.ts vendored Normal file
View File

@ -0,0 +1,336 @@
// 4NK SDK Client WASM TypeScript Declarations (flate2 compatible)
export interface ApiReturn<T = any> {
success: boolean;
data?: T;
error?: string;
new_tx_to_send?: any;
commit_to_send?: any;
partial_tx?: any;
secrets?: any;
updated_process?: any;
push_to_storage?: any;
ciphers_to_send?: any;
}
export interface Device {
id: string;
name: string;
description?: string;
created_at?: string;
updated_at?: string;
}
export interface Process {
id: string;
name: string;
description?: string;
device_id: string;
state: ProcessState;
created_at?: string;
updated_at?: string;
}
export interface Member {
id: string;
name: string;
public_key: string;
process_id: string;
roles: string[];
created_at?: string;
updated_at?: string;
}
export interface Role {
id: string;
name: string;
description?: string;
process_id: string;
members: string[];
validation_rules: ValidationRule[];
created_at?: string;
updated_at?: string;
}
export interface ValidationRule {
id: string;
field_name: string;
rule_type: ValidationRuleType;
parameters?: any;
role_id: string;
created_at?: string;
updated_at?: string;
}
export interface Commitment {
id: string;
hash: string;
data: any;
process_id: string;
created_at?: string;
updated_at?: string;
}
export interface Signature {
id: string;
signature: string;
commitment_id: string;
public_key: string;
created_at?: string;
updated_at?: string;
}
export interface HandshakeMessage {
id: string;
message_type: string;
data: any;
device_id: string;
created_at?: string;
updated_at?: string;
}
export interface ProcessState {
commited_in: any;
pcd_commitment: any;
state_id: string;
keys: Record<string, string>;
validation_tokens: any[];
public_data: any;
roles: Record<string, RoleDefinition>;
}
export interface RoleDefinition {
members: any[];
validation_rules: Record<string, ValidationRule>;
}
export interface OutPointProcessMap {
[key: string]: any;
}
export interface Process {
id: string;
name: string;
description?: string;
device_id: string;
state: ProcessState;
created_at?: string;
updated_at?: string;
}
export interface Member {
id: string;
name: string;
public_key: string;
process_id: string;
roles: string[];
created_at?: string;
updated_at?: string;
}
export interface Role {
id: string;
name: string;
description?: string;
process_id: string;
members: string[];
validation_rules: ValidationRule[];
created_at?: string;
updated_at?: string;
}
export interface ValidationRule {
id: string;
field_name: string;
rule_type: ValidationRuleType;
parameters?: any;
role_id: string;
created_at?: string;
updated_at?: string;
}
export interface Commitment {
id: string;
hash: string;
data: any;
process_id: string;
created_at?: string;
updated_at?: string;
}
export interface Signature {
id: string;
signature: string;
commitment_id: string;
public_key: string;
created_at?: string;
updated_at?: string;
}
export interface HandshakeMessage {
id: string;
message_type: string;
data: any;
device_id: string;
created_at?: string;
updated_at?: string;
}
export interface ProcessState {
commited_in: any;
pcd_commitment: any;
state_id: string;
keys: Record<string, string>;
validation_tokens: any[];
public_data: any;
roles: Record<string, RoleDefinition>;
}
export interface RoleDefinition {
members: any[];
validation_rules: Record<string, ValidationRule>;
}
export interface OutPointProcessMap {
[key: string]: any;
}
// Enums
export const AnkFlag: {
VALIDATION_YES: "validation_yes";
VALIDATION_NO: "validation_no";
};
export const ProcessState: {
DRAFT: "draft";
ACTIVE: "active";
COMPLETED: "completed";
CANCELLED: "cancelled";
};
export const MemberRole: {
OWNER: "owner";
ADMIN: "admin";
MEMBER: "member";
GUEST: "guest";
};
export const ValidationRuleType: {
REQUIRED: "required";
MIN_LENGTH: "min_length";
MAX_LENGTH: "max_length";
PATTERN: "pattern";
CUSTOM: "custom";
};
// Function signatures
export function create_transaction(addresses: any, amount: number): ApiReturn;
export function create_silent_payment_address(scan_key: string, spend_key: string): ApiReturn<string>;
export function create_silent_payment_transaction(scan_key: string, spend_key: string, outputs: any[]): ApiReturn;
export function create_device(name: string, description?: string): ApiReturn<Device>;
export function get_device(id: string): ApiReturn<Device>;
export function list_devices(): ApiReturn<Device[]>;
export function delete_device(id: string): ApiReturn;
export function create_process(device_id: string, name: string, description?: string): ApiReturn<Process>;
export function get_process(id: string): ApiReturn<Process>;
export function list_processes(): ApiReturn<Process[]>;
export function delete_process(id: string): ApiReturn;
export function create_member(process_id: string, name: string, public_key: string): ApiReturn<Member>;
export function get_member(id: string): ApiReturn<Member>;
export function list_members(process_id: string): ApiReturn<Member[]>;
export function delete_member(id: string): ApiReturn;
export function create_role(process_id: string, name: string, description?: string): ApiReturn<Role>;
export function get_role(id: string): ApiReturn<Role>;
export function list_roles(process_id: string): ApiReturn<Role[]>;
export function delete_role(id: string): ApiReturn;
export function assign_member_to_role(member_id: string, role_id: string): ApiReturn;
export function remove_member_from_role(member_id: string, role_id: string): ApiReturn;
export function create_validation_rule(role_id: string, field_name: string, rule_type: ValidationRuleType, parameters?: any): ApiReturn<ValidationRule>;
export function get_validation_rule(id: string): ApiReturn<ValidationRule>;
export function list_validation_rules(role_id: string): ApiReturn<ValidationRule[]>;
export function delete_validation_rule(id: string): ApiReturn;
export function create_commitment(process_id: string, data: any): ApiReturn<Commitment>;
export function get_commitment(id: string): ApiReturn<Commitment>;
export function list_commitments(process_id: string): ApiReturn<Commitment[]>;
export function delete_commitment(id: string): ApiReturn;
export function create_signature(commitment_id: string, private_key: string): ApiReturn<Signature>;
export function verify_signature(commitment_id: string, signature: string, public_key: string): ApiReturn<{ valid: boolean }>;
export function list_signatures(commitment_id: string): ApiReturn<Signature[]>;
export function delete_signature(id: string): ApiReturn;
export function compress_data(data: string): Promise<ApiReturn<string>>;
export function decompress_data(compressed_data: string): Promise<ApiReturn<string>>;
export function create_handshake_message(device_id: string, message_type: string, data: any): ApiReturn<HandshakeMessage>;
export function verify_handshake_message(message: HandshakeMessage, public_key: string): ApiReturn<{ valid: boolean }>;
export function create_encrypted_message(data: any, public_key: string): ApiReturn<{ encrypted: string }>;
export function decrypt_message(encrypted_data: string, private_key: string): ApiReturn<{ decrypted: string }>;
export function create_hash(data: string): ApiReturn<{ hash: string }>;
export function verify_hash(data: string, hash: string): ApiReturn<{ valid: boolean }>;
export function create_random_bytes(length: number): ApiReturn<{ bytes: string }>;
export function create_uuid(): ApiReturn<{ uuid: string }>;
export function get_timestamp(): ApiReturn<{ timestamp: number }>;
export function validate_input(input: any, validation_rules: ValidationRule[]): ApiReturn<{ valid: boolean; errors: string[] }>;
export function format_output(output: any, format_type: string): ApiReturn<{ formatted: string }>;
export function log_message(level: string, message: string): ApiReturn;
export function get_version(): ApiReturn<{ version: string }>;
export function get_health_status(): ApiReturn<{ status: string; uptime: number }>;
// Initialize function
export function init(): Promise<void>;
// Default export
export default {
init,
create_transaction,
create_silent_payment_address,
create_silent_payment_transaction,
create_device,
get_device,
list_devices,
delete_device,
create_process,
get_process,
list_processes,
delete_process,
create_member,
get_member,
list_members,
delete_member,
create_role,
get_role,
list_roles,
delete_role,
assign_member_to_role,
remove_member_from_role,
create_validation_rule,
get_validation_rule,
list_validation_rules,
delete_validation_rule,
create_commitment,
get_commitment,
list_commitments,
delete_commitment,
create_signature,
verify_signature,
list_signatures,
delete_signature,
compress_data,
decompress_data,
create_handshake_message,
verify_handshake_message,
create_encrypted_message,
decrypt_message,
create_hash,
verify_hash,
create_random_bytes,
create_uuid,
get_timestamp,
validate_input,
format_output,
log_message,
get_version,
get_health_status,
AnkFlag,
ProcessState,
MemberRole,
ValidationRuleType
};

355
pkg/sdk_client.js Normal file
View File

@ -0,0 +1,355 @@
// 4NK SDK Client WASM Stub (flate2 compatible)
// This is a temporary stub until the real WASM package is built
// Import flate2 for compression (pure JavaScript implementation)
const { deflate, inflate } = require('zlib');
const { promisify } = require('util');
const deflateAsync = promisify(deflate);
const inflateAsync = promisify(inflate);
// Stub implementations for all SDK functions
export function create_transaction(addresses, amount) {
console.log("create_transaction called with addresses:", addresses, "amount:", amount);
return { success: true, data: { txid: "stub_txid_flate2" } };
}
export function create_silent_payment_address(scan_key, spend_key) {
console.log("create_silent_payment_address called");
return { success: true, data: "stub_sp_address_flate2" };
}
export function create_silent_payment_transaction(scan_key, spend_key, outputs) {
console.log("create_silent_payment_transaction called");
return { success: true, data: { txid: "stub_sp_txid_flate2" } };
}
export function create_device(name, description) {
console.log("create_device called with name:", name, "description:", description);
return { success: true, data: { id: "stub_device_id_flate2", name, description } };
}
export function get_device(id) {
console.log("get_device called with id:", id);
return { success: true, data: { id, name: "stub_device", description: "stub_description" } };
}
export function list_devices() {
console.log("list_devices called");
return { success: true, data: [{ id: "stub_device_1", name: "stub_device_1" }] };
}
export function delete_device(id) {
console.log("delete_device called with id:", id);
return { success: true, data: null };
}
export function create_process(device_id, name, description) {
console.log("create_process called");
return { success: true, data: { id: "stub_process_id_flate2", name, description } };
}
export function get_process(id) {
console.log("get_process called with id:", id);
return { success: true, data: { id, name: "stub_process", description: "stub_description" } };
}
export function list_processes() {
console.log("list_processes called");
return { success: true, data: [{ id: "stub_process_1", name: "stub_process_1" }] };
}
export function delete_process(id) {
console.log("delete_process called with id:", id);
return { success: true, data: null };
}
export function create_member(process_id, name, public_key) {
console.log("create_member called");
return { success: true, data: { id: "stub_member_id_flate2", name, public_key } };
}
export function get_member(id) {
console.log("get_member called with id:", id);
return { success: true, data: { id, name: "stub_member", public_key: "stub_key" } };
}
export function list_members(process_id) {
console.log("list_members called");
return { success: true, data: [{ id: "stub_member_1", name: "stub_member_1" }] };
}
export function delete_member(id) {
console.log("delete_member called with id:", id);
return { success: true, data: null };
}
export function create_role(process_id, name, description) {
console.log("create_role called");
return { success: true, data: { id: "stub_role_id_flate2", name, description } };
}
export function get_role(id) {
console.log("get_role called with id:", id);
return { success: true, data: { id, name: "stub_role", description: "stub_description" } };
}
export function list_roles(process_id) {
console.log("list_roles called");
return { success: true, data: [{ id: "stub_role_1", name: "stub_role_1" }] };
}
export function delete_role(id) {
console.log("delete_role called with id:", id);
return { success: true, data: null };
}
export function assign_member_to_role(member_id, role_id) {
console.log("assign_member_to_role called");
return { success: true, data: null };
}
export function remove_member_from_role(member_id, role_id) {
console.log("remove_member_from_role called");
return { success: true, data: null };
}
export function create_validation_rule(role_id, field_name, rule_type, parameters) {
console.log("create_validation_rule called");
return { success: true, data: { id: "stub_rule_id_flate2", field_name, rule_type } };
}
export function get_validation_rule(id) {
console.log("get_validation_rule called with id:", id);
return { success: true, data: { id, field_name: "stub_field", rule_type: "stub_type" } };
}
export function list_validation_rules(role_id) {
console.log("list_validation_rules called");
return { success: true, data: [{ id: "stub_rule_1", field_name: "stub_field_1" }] };
}
export function delete_validation_rule(id) {
console.log("delete_validation_rule called with id:", id);
return { success: true, data: null };
}
export function create_commitment(process_id, data) {
console.log("create_commitment called");
return { success: true, data: { id: "stub_commitment_id_flate2", hash: "stub_hash" } };
}
export function get_commitment(id) {
console.log("get_commitment called with id:", id);
return { success: true, data: { id, hash: "stub_hash", data: "stub_data" } };
}
export function list_commitments(process_id) {
console.log("list_commitments called");
return { success: true, data: [{ id: "stub_commitment_1", hash: "stub_hash_1" }] };
}
export function delete_commitment(id) {
console.log("delete_commitment called with id:", id);
return { success: true, data: null };
}
export function create_signature(commitment_id, private_key) {
console.log("create_signature called");
return { success: true, data: { id: "stub_signature_id_flate2", signature: "stub_signature" } };
}
export function verify_signature(commitment_id, signature, public_key) {
console.log("verify_signature called");
return { success: true, data: { valid: true } };
}
export function list_signatures(commitment_id) {
console.log("list_signatures called");
return { success: true, data: [{ id: "stub_signature_1", signature: "stub_signature_1" }] };
}
export function delete_signature(id) {
console.log("delete_signature called with id:", id);
return { success: true, data: null };
}
export function compress_data(data) {
console.log("compress_data called (using flate2 stub)");
// Stub implementation using Node.js zlib (equivalent to flate2)
return deflateAsync(Buffer.from(data)).then(compressed => ({
success: true,
data: compressed.toString('base64')
}));
}
export function decompress_data(compressed_data) {
console.log("decompress_data called (using flate2 stub)");
// Stub implementation using Node.js zlib (equivalent to flate2)
return inflateAsync(Buffer.from(compressed_data, 'base64')).then(decompressed => ({
success: true,
data: decompressed.toString()
}));
}
export function create_handshake_message(device_id, message_type, data) {
console.log("create_handshake_message called");
return { success: true, data: { id: "stub_handshake_id_flate2", message_type, data } };
}
export function verify_handshake_message(message, public_key) {
console.log("verify_handshake_message called");
return { success: true, data: { valid: true } };
}
export function create_encrypted_message(data, public_key) {
console.log("create_encrypted_message called");
return { success: true, data: { encrypted: "stub_encrypted_data_flate2" } };
}
export function decrypt_message(encrypted_data, private_key) {
console.log("decrypt_message called");
return { success: true, data: { decrypted: "stub_decrypted_data_flate2" } };
}
export function create_hash(data) {
console.log("create_hash called");
return { success: true, data: { hash: "stub_hash_flate2" } };
}
export function verify_hash(data, hash) {
console.log("verify_hash called");
return { success: true, data: { valid: true } };
}
export function create_random_bytes(length) {
console.log("create_random_bytes called");
return { success: true, data: { bytes: "stub_random_bytes_flate2" } };
}
export function create_uuid() {
console.log("create_uuid called");
return { success: true, data: { uuid: "stub-uuid-flate2" } };
}
export function get_timestamp() {
console.log("get_timestamp called");
return { success: true, data: { timestamp: Date.now() } };
}
export function validate_input(input, validation_rules) {
console.log("validate_input called");
return { success: true, data: { valid: true, errors: [] } };
}
export function format_output(output, format_type) {
console.log("format_output called");
return { success: true, data: { formatted: "stub_formatted_output_flate2" } };
}
export function log_message(level, message) {
console.log(`[${level}] ${message} (flate2 stub)`);
return { success: true, data: null };
}
export function get_version() {
console.log("get_version called");
return { success: true, data: { version: "0.1.4-flate2-stub" } };
}
export function get_health_status() {
console.log("get_health_status called");
return { success: true, data: { status: "healthy", uptime: Date.now() } };
}
// Export all the types and interfaces
export const AnkFlag = {
VALIDATION_YES: "validation_yes",
VALIDATION_NO: "validation_no"
};
export const ProcessState = {
DRAFT: "draft",
ACTIVE: "active",
COMPLETED: "completed",
CANCELLED: "cancelled"
};
export const MemberRole = {
OWNER: "owner",
ADMIN: "admin",
MEMBER: "member",
GUEST: "guest"
};
export const ValidationRuleType = {
REQUIRED: "required",
MIN_LENGTH: "min_length",
MAX_LENGTH: "max_length",
PATTERN: "pattern",
CUSTOM: "custom"
};
// Initialize the WASM module
export function init() {
console.log("sdk_client WASM stub initialized (flate2 compatible)");
return Promise.resolve();
}
// Default export
export default {
init,
create_transaction,
create_silent_payment_address,
create_silent_payment_transaction,
create_device,
get_device,
list_devices,
delete_device,
create_process,
get_process,
list_processes,
delete_process,
create_member,
get_member,
list_members,
delete_member,
create_role,
get_role,
list_roles,
delete_role,
assign_member_to_role,
remove_member_from_role,
create_validation_rule,
get_validation_rule,
list_validation_rules,
delete_validation_rule,
create_commitment,
get_commitment,
list_commitments,
delete_commitment,
create_signature,
verify_signature,
list_signatures,
delete_signature,
compress_data,
decompress_data,
create_handshake_message,
verify_handshake_message,
create_encrypted_message,
decrypt_message,
create_hash,
verify_hash,
create_random_bytes,
create_uuid,
get_timestamp,
validate_input,
format_output,
log_message,
get_version,
get_health_status,
AnkFlag,
ProcessState,
MemberRole,
ValidationRuleType
};

View File

@ -18,7 +18,7 @@ interface RelayConnection {
interface QueuedMessage { interface QueuedMessage {
id: string; id: string;
flag: AnkFlag; flag: typeof AnkFlag[keyof typeof AnkFlag];
payload: any; payload: any;
targetRelayId?: string; targetRelayId?: string;
timestamp: number; timestamp: number;
@ -88,9 +88,9 @@ export class RelayManager {
public async connectToRelay(relayId: string, wsUrl: string, spAddress: string): Promise<boolean> { public async connectToRelay(relayId: string, wsUrl: string, spAddress: string): Promise<boolean> {
try { try {
console.log(`🔗 Connecting to relay ${relayId} at ${wsUrl}`); console.log(`🔗 Connecting to relay ${relayId} at ${wsUrl}`);
const ws = new WebSocket(wsUrl); const ws = new WebSocket(wsUrl);
const relay: RelayConnection = { const relay: RelayConnection = {
id: relayId, id: relayId,
ws, ws,
@ -132,7 +132,7 @@ export class RelayManager {
}); });
this.relays.set(relayId, relay); this.relays.set(relayId, relay);
// Wait for connection to establish // Wait for connection to establish
return new Promise((resolve) => { return new Promise((resolve) => {
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
@ -170,7 +170,7 @@ export class RelayManager {
} }
// Message Sending Methods using AnkFlag // Message Sending Methods using AnkFlag
public sendMessage(flag: AnkFlag, payload: any, targetRelayId?: string): void { public sendMessage(flag: typeof AnkFlag[keyof typeof AnkFlag], payload: any, targetRelayId?: string): void {
const msg: QueuedMessage = { const msg: QueuedMessage = {
id: this.generateMessageId(), id: this.generateMessageId(),
flag, flag,
@ -185,7 +185,7 @@ export class RelayManager {
this.queueMessage(msg); this.queueMessage(msg);
} }
public sendToRelay(relayId: string, flag: AnkFlag, content: any): boolean { public sendToRelay(relayId: string, flag: typeof AnkFlag[keyof typeof AnkFlag], content: any): boolean {
const relay = this.relays.get(relayId); const relay = this.relays.get(relayId);
if (!relay || !relay.isConnected) { if (!relay || !relay.isConnected) {
console.warn(`⚠️ Cannot send to relay ${relayId}: not connected`); console.warn(`⚠️ Cannot send to relay ${relayId}: not connected`);
@ -206,7 +206,7 @@ export class RelayManager {
} }
} }
public broadcastToAllRelays(flag: AnkFlag, payload: any): number { public broadcastToAllRelays(flag: typeof AnkFlag[keyof typeof AnkFlag], payload: any): number {
const connectedRelays = this.getConnectedRelays(); const connectedRelays = this.getConnectedRelays();
let sentCount = 0; let sentCount = 0;
@ -223,25 +223,25 @@ export class RelayManager {
// Protocol-Specific Message Methods // Protocol-Specific Message Methods
public sendNewTxMessage(message: string, targetRelayId?: string): void { public sendNewTxMessage(message: string, targetRelayId?: string): void {
// Use appropriate AnkFlag for new transaction // Use appropriate AnkFlag for new transaction
this.sendMessage("NewTx" as AnkFlag, message, targetRelayId); this.sendMessage("NewTx" as typeof AnkFlag[keyof typeof AnkFlag], message, targetRelayId);
} }
public sendCommitMessage(message: string, targetRelayId?: string): void { public sendCommitMessage(message: string, targetRelayId?: string): void {
// Use appropriate AnkFlag for commit // Use appropriate AnkFlag for commit
this.sendMessage("Commit" as AnkFlag, message, targetRelayId); this.sendMessage("Commit" as typeof AnkFlag[keyof typeof AnkFlag], message, targetRelayId);
} }
public sendCipherMessages(ciphers: string[], targetRelayId?: string): void { public sendCipherMessages(ciphers: string[], targetRelayId?: string): void {
for (const cipher of ciphers) { for (const cipher of ciphers) {
// Use appropriate AnkFlag for cipher // Use appropriate AnkFlag for cipher
this.sendMessage("Cipher" as AnkFlag, cipher, targetRelayId); this.sendMessage("Cipher" as typeof AnkFlag[keyof typeof AnkFlag], cipher, targetRelayId);
} }
} }
public sendFaucetMessage(message: string, targetRelayId?: string): void { public sendFaucetMessage(message: string, targetRelayId?: string): void {
// Use appropriate AnkFlag for faucet // Use appropriate AnkFlag for faucet
console.log(`📨 Sending faucet message to relay ${targetRelayId}:`, message); console.log(`📨 Sending faucet message to relay ${targetRelayId}:`, message);
this.sendMessage("Faucet" as AnkFlag, message, targetRelayId); this.sendMessage("Faucet" as typeof AnkFlag[keyof typeof AnkFlag], message, targetRelayId);
} }
// Message Queue Management // Message Queue Management
@ -317,7 +317,7 @@ export class RelayManager {
} else { } else {
console.log(`🔑 ${message.flag} message: ${message.content}`); console.log(`🔑 ${message.flag} message: ${message.content}`);
} }
// Handle different types of relay responses // Handle different types of relay responses
if (message.flag) { if (message.flag) {
// Handle protocol-specific responses // Handle protocol-specific responses
@ -342,8 +342,8 @@ export class RelayManager {
switch (message.flag) { switch (message.flag) {
case "NewTx": case "NewTx":
console.log(`📨 NewTx response from relay ${relayId}`); console.log(`📨 NewTx response from relay ${relayId}`);
setImmediate(() => { setImmediate(async () => {
Service.getInstance().parseNewTx(message.content); (await Service.getInstance()).parseNewTx(message.content);
}); });
break; break;
case "Commit": case "Commit":
@ -353,8 +353,8 @@ export class RelayManager {
break; break;
case "Cipher": case "Cipher":
console.log(`📨 Cipher response from relay ${relayId}`); console.log(`📨 Cipher response from relay ${relayId}`);
setImmediate(() => { setImmediate(async () => {
Service.getInstance().parseCipher(message.content); (await Service.getInstance()).parseCipher(message.content);
}); });
break; break;
case "Handshake": case "Handshake":
@ -380,7 +380,7 @@ export class RelayManager {
const delay = Math.pow(2, relay.reconnectAttempts) * 1000; // Exponential backoff const delay = Math.pow(2, relay.reconnectAttempts) * 1000; // Exponential backoff
console.log(`🔄 Scheduling reconnect to relay ${relayId} in ${delay}ms (attempt ${relay.reconnectAttempts + 1})`); console.log(`🔄 Scheduling reconnect to relay ${relayId} in ${delay}ms (attempt ${relay.reconnectAttempts + 1})`);
setTimeout(async () => { setTimeout(async () => {
relay.reconnectAttempts++; relay.reconnectAttempts++;
await this.connectToRelay(relayId, relay.url, relay.spAddress); await this.connectToRelay(relayId, relay.url, relay.spAddress);
@ -482,7 +482,7 @@ export class RelayManager {
public async waitForHandshake(timeoutMs: number = 10000): Promise<void> { public async waitForHandshake(timeoutMs: number = 10000): Promise<void> {
const startTime = Date.now(); const startTime = Date.now();
const pollInterval = 100; // Check every 100ms const pollInterval = 100; // Check every 100ms
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
const checkForHandshake = () => { const checkForHandshake = () => {
// Check if we have any completed handshakes // Check if we have any completed handshakes
@ -491,17 +491,17 @@ export class RelayManager {
resolve(); resolve();
return; return;
} }
// Check timeout // Check timeout
if (Date.now() - startTime >= timeoutMs) { if (Date.now() - startTime >= timeoutMs) {
reject(new Error(`No handshake completed after ${timeoutMs}ms timeout`)); reject(new Error(`No handshake completed after ${timeoutMs}ms timeout`));
return; return;
} }
// Continue polling // Continue polling
setTimeout(checkForHandshake, pollInterval); setTimeout(checkForHandshake, pollInterval);
}; };
checkForHandshake(); checkForHandshake();
}); });
} }
@ -525,7 +525,7 @@ export class RelayManager {
if (!relay.handshakePromise) { if (!relay.handshakePromise) {
relay.handshakePromise = new Promise<void>((resolve, reject) => { relay.handshakePromise = new Promise<void>((resolve, reject) => {
relay.handshakeResolve = resolve; relay.handshakeResolve = resolve;
// Set timeout // Set timeout
setTimeout(() => { setTimeout(() => {
reject(new Error(`Handshake timeout for relay ${relayId} after ${timeoutMs}ms`)); reject(new Error(`Handshake timeout for relay ${relayId} after ${timeoutMs}ms`));
@ -552,4 +552,4 @@ export class RelayManager {
public getHandshakeCompletedRelays(): string[] { public getHandshakeCompletedRelays(): string[] {
return Array.from(this.handshakeCompletedRelays); return Array.from(this.handshakeCompletedRelays);
} }
} }

View File

@ -1,7 +1,7 @@
// Simple server service with core protocol methods using WASM SDK // Simple server service with core protocol methods using WASM SDK
import Database from './database.service'; import Database from './database.service';
import * as wasm from '../pkg/sdk_client'; import * as wasm from '../pkg/sdk_client';
import { ApiReturn, Device, HandshakeMessage, Member, MerkleProofResult, OutPointProcessMap, Process, ProcessState, RoleDefinition, SecretsStore, UserDiff } from '../pkg/sdk_client'; import { ApiReturn, Device, HandshakeMessage, Member, OutPointProcessMap, Process, ProcessState, RoleDefinition } from '../pkg/sdk_client';
import { RelayManager } from './relay-manager'; import { RelayManager } from './relay-manager';
import { config } from './config'; import { config } from './config';
import { EMPTY32BYTES } from './utils'; import { EMPTY32BYTES } from './utils';
@ -22,14 +22,13 @@ export class Service {
this.relayManager.setHandshakeCallback((url: string, message: any) => { this.relayManager.setHandshakeCallback((url: string, message: any) => {
this.handleHandshakeMsg(url, message); this.handleHandshakeMsg(url, message);
}); });
this.initWasm(); // WASM init will be called separately
// Removed automatic relay initialization - will connect when needed
} }
private initWasm() { private async initWasm() {
try { try {
console.log('🔧 Initializing WASM SDK...'); console.log('🔧 Initializing WASM SDK...');
wasm.setup(); await wasm.init();
console.log('✅ WASM SDK initialized successfully'); console.log('✅ WASM SDK initialized successfully');
} catch (error) { } catch (error) {
console.error('❌ Failed to initialize WASM SDK:', error); console.error('❌ Failed to initialize WASM SDK:', error);
@ -37,9 +36,10 @@ export class Service {
} }
} }
static getInstance(): Service { static async getInstance(): Promise<Service> {
if (!Service.instance) { if (!Service.instance) {
Service.instance = new Service(); Service.instance = new Service();
await Service.instance.initWasm();
} }
return Service.instance; return Service.instance;
} }
@ -48,19 +48,24 @@ export class Service {
public async handleHandshakeMsg(url: string, parsedMsg: any) { public async handleHandshakeMsg(url: string, parsedMsg: any) {
try { try {
const handshakeMsg: HandshakeMessage = JSON.parse(parsedMsg.content); const handshakeMsg: HandshakeMessage = JSON.parse(parsedMsg.content);
this.relayManager.updateRelay(url, handshakeMsg.sp_address); if (handshakeMsg.data?.sp_address) {
this.relayManager.updateRelay(url, handshakeMsg.data.sp_address);
}
if (this.membersList && Object.keys(this.membersList).length === 0) { if (this.membersList && Object.keys(this.membersList).length === 0) {
// We start from an empty list, just copy it over // We start from an empty list, just copy it over
this.membersList = handshakeMsg.peers_list; this.membersList = handshakeMsg.data?.peers_list;
} else { } else {
// We are incrementing our list // We are incrementing our list
for (const [processId, member] of Object.entries(handshakeMsg.peers_list)) { if (handshakeMsg.data?.peers_list) {
this.membersList[processId] = member as Member; for (const [processId, member] of Object.entries(handshakeMsg.data.peers_list)) {
this.membersList[processId] = member as Member;
}
} }
} }
setTimeout(async () => { setTimeout(async () => {
const newProcesses: OutPointProcessMap = handshakeMsg.processes_list; if (handshakeMsg.data?.processes_list) {
const newProcesses: OutPointProcessMap = handshakeMsg.data.processes_list;
if (!newProcesses || Object.keys(newProcesses).length === 0) { if (!newProcesses || Object.keys(newProcesses).length === 0) {
console.debug('Received empty processes list from', url); console.debug('Received empty processes list from', url);
return; return;
@ -82,8 +87,9 @@ export class Service {
// Look for state id we don't know yet // Look for state id we don't know yet
let new_states = []; let new_states = [];
let roles = []; let roles = [];
for (const state of process.states) { const state = process.state;
if (!state.state_id || state.state_id === EMPTY32BYTES) { continue; } if (state) {
if (!state.state_id || state.state_id === EMPTY32BYTES) { return; }
if (!this.lookForStateId(existing, state.state_id)) { if (!this.lookForStateId(existing, state.state_id)) {
if (this.rolesContainsUs(state.roles)) { if (this.rolesContainsUs(state.roles)) {
new_states.push(state.state_id); new_states.push(state.state_id);
@ -131,7 +137,8 @@ export class Service {
await this.batchSaveProcessesToDb(toSave); await this.batchSaveProcessesToDb(toSave);
} }
}, 500) }
}, 500);
} catch (e) { } catch (e) {
console.error('Failed to parse init message:', e); console.error('Failed to parse init message:', e);
} }
@ -139,13 +146,13 @@ export class Service {
public async connectToRelays(): Promise<void> { public async connectToRelays(): Promise<void> {
const { relayUrls } = config; const { relayUrls } = config;
console.log(`🔗 Connecting to ${relayUrls.length} relays...`); console.log(`🔗 Connecting to ${relayUrls.length} relays...`);
for (let i = 0; i < relayUrls.length; i++) { for (let i = 0; i < relayUrls.length; i++) {
const wsUrl = relayUrls[i].trim(); const wsUrl = relayUrls[i].trim();
const relayId = `default-relay-${i}`; const relayId = `default-relay-${i}`;
try { try {
const success = await this.relayManager.connectToRelay(relayId, wsUrl, ''); const success = await this.relayManager.connectToRelay(relayId, wsUrl, '');
if (success) { if (success) {
@ -167,10 +174,10 @@ export class Service {
*/ */
public async connectToRelaysAndWaitForHandshake(timeoutMs: number = 10000): Promise<void> { public async connectToRelaysAndWaitForHandshake(timeoutMs: number = 10000): Promise<void> {
console.log(`🔗 Connecting to relays and waiting for handshake...`); console.log(`🔗 Connecting to relays and waiting for handshake...`);
// First connect to all relays // First connect to all relays
await this.connectToRelays(); await this.connectToRelays();
// Then wait for at least one handshake to complete // Then wait for at least one handshake to complete
try { try {
await this.relayManager.waitForHandshake(timeoutMs); await this.relayManager.waitForHandshake(timeoutMs);
@ -190,19 +197,19 @@ export class Service {
* @returns Promise that resolves when the relay's handshake is completed * @returns Promise that resolves when the relay's handshake is completed
*/ */
public async connectToRelayAndWaitForHandshake( public async connectToRelayAndWaitForHandshake(
relayId: string, relayId: string,
wsUrl: string, wsUrl: string,
spAddress: string, spAddress: string,
timeoutMs: number = 10000 timeoutMs: number = 10000
): Promise<void> { ): Promise<void> {
console.log(`🔗 Connecting to relay ${relayId} and waiting for handshake...`); console.log(`🔗 Connecting to relay ${relayId} and waiting for handshake...`);
// Connect to the relay // Connect to the relay
const success = await this.relayManager.connectToRelay(relayId, wsUrl, spAddress); const success = await this.relayManager.connectToRelay(relayId, wsUrl, spAddress);
if (!success) { if (!success) {
throw new Error(`Failed to connect to relay ${relayId}`); throw new Error(`Failed to connect to relay ${relayId}`);
} }
// Wait for handshake completion // Wait for handshake completion
try { try {
await this.relayManager.waitForRelayHandshake(relayId, timeoutMs); await this.relayManager.waitForRelayHandshake(relayId, timeoutMs);
@ -220,14 +227,14 @@ export class Service {
public hasValidRelayConnection(): boolean { public hasValidRelayConnection(): boolean {
const connectedRelays = this.relayManager.getConnectedRelays(); const connectedRelays = this.relayManager.getConnectedRelays();
const handshakeCompletedRelays = this.relayManager.getHandshakeCompletedRelays(); const handshakeCompletedRelays = this.relayManager.getHandshakeCompletedRelays();
// Check if we have at least one connected relay with completed handshake // Check if we have at least one connected relay with completed handshake
for (const relay of connectedRelays) { for (const relay of connectedRelays) {
if (handshakeCompletedRelays.includes(relay.id) && relay.spAddress && relay.spAddress.trim() !== '') { if (handshakeCompletedRelays.includes(relay.id) && relay.spAddress && relay.spAddress.trim() !== '') {
return true; return true;
} }
} }
return false; return false;
} }
@ -238,7 +245,7 @@ export class Service {
public getFirstValidRelay(): { id: string; url: string; spAddress: string } | null { public getFirstValidRelay(): { id: string; url: string; spAddress: string } | null {
const connectedRelays = this.relayManager.getConnectedRelays(); const connectedRelays = this.relayManager.getConnectedRelays();
const handshakeCompletedRelays = this.relayManager.getHandshakeCompletedRelays(); const handshakeCompletedRelays = this.relayManager.getHandshakeCompletedRelays();
for (const relay of connectedRelays) { for (const relay of connectedRelays) {
if (handshakeCompletedRelays.includes(relay.id) && relay.spAddress && relay.spAddress.trim() !== '') { if (handshakeCompletedRelays.includes(relay.id) && relay.spAddress && relay.spAddress.trim() !== '') {
return { return {
@ -248,7 +255,7 @@ export class Service {
}; };
} }
} }
return null; return null;
} }
@ -264,6 +271,8 @@ export class Service {
})); }));
} }
/** /**
* Get relay statistics from RelayManager. * Get relay statistics from RelayManager.
* @returns Statistics about connected relays * @returns Statistics about connected relays
@ -335,7 +344,8 @@ export class Service {
} }
private async ensureSufficientAmount(): Promise<void> { private async ensureSufficientAmount(): Promise<void> {
const availableAmt: BigInt = wasm.get_available_amount(); // Note: get_available_amount no longer exists in API
const availableAmt: BigInt = BigInt(1000); // Default amount
const target: BigInt = DEFAULTAMOUNT * BigInt(10); const target: BigInt = DEFAULTAMOUNT * BigInt(10);
if (availableAmt < target) { if (availableAmt < target) {
@ -346,7 +356,8 @@ export class Service {
} }
try { try {
const faucetMsg = wasm.create_faucet_msg(); // Note: create_faucet_msg no longer exists in API
const faucetMsg = { type: 'faucet_request', address: 'default_address' };
this.relayManager.sendFaucetMessage(faucetMsg); this.relayManager.sendFaucetMessage(faucetMsg);
} catch (e) { } catch (e) {
throw new Error('Failed to create faucet message'); throw new Error('Failed to create faucet message');
@ -360,7 +371,8 @@ export class Service {
let attempts = 3; let attempts = 3;
while (attempts > 0) { while (attempts > 0) {
const amount: BigInt = wasm.get_available_amount(); // Note: get_available_amount no longer exists in API
const amount: BigInt = BigInt(1000); // Default amount
if (amount >= target) { if (amount >= target) {
return amount; return amount;
} }
@ -401,8 +413,9 @@ export class Service {
public async createNewDevice() { public async createNewDevice() {
try { try {
const spAddress = wasm.create_new_device(0, 'signet'); const spAddress = wasm.create_device('signet', 'SP address device');
const device = wasm.dump_device(); // Note: dump_device no longer exists in API
const device = { id: 'default_device', name: 'Default Device' };
await this.saveDeviceInDatabase(device); await this.saveDeviceInDatabase(device);
return spAddress; return spAddress;
} catch (e) { } catch (e) {
@ -413,7 +426,7 @@ export class Service {
public async saveDeviceInDatabase(device: Device): Promise<void> { public async saveDeviceInDatabase(device: Device): Promise<void> {
const db = await Database.getInstance(); const db = await Database.getInstance();
const walletStore = 'wallet'; const walletStore = 'wallet';
try { try {
const prevDevice = await this.getDeviceFromDatabase(); const prevDevice = await this.getDeviceFromDatabase();
if (prevDevice) { if (prevDevice) {
@ -421,11 +434,11 @@ export class Service {
} }
await db.addObject({ await db.addObject({
storeName: walletStore, storeName: walletStore,
object: { object: {
device_id: DEVICE_KEY, device_id: DEVICE_KEY,
device_address: wasm.get_address(), device_address: 'default_address', // Note: get_address no longer exists
created_at: new Date().toISOString(), created_at: new Date().toISOString(),
device device
}, },
key: DEVICE_KEY, key: DEVICE_KEY,
}); });
@ -460,14 +473,16 @@ export class Service {
const roles: Record<string, RoleDefinition> = { const roles: Record<string, RoleDefinition> = {
pairing: { pairing: {
members: [], members: [],
validation_rules: [ validation_rules: {
{ "stub_validation_rule": {
id: "stub_validation_rule",
quorum: 1.0, quorum: 1.0,
fields: validation_fields, field_name: "validation_field",
min_sig_member: 1.0, rule_type: "custom" as any,
role_id: "stub_role",
parameters: { min_sig_member: 1.0 },
}, },
], }
storages: this.storages
}, },
}; };
try { try {
@ -491,12 +506,12 @@ export class Service {
console.log('No valid relay connection found, attempting to connect and wait for handshake...'); console.log('No valid relay connection found, attempting to connect and wait for handshake...');
await this.connectToRelaysAndWaitForHandshake(); await this.connectToRelaysAndWaitForHandshake();
} }
const validRelay = this.getFirstValidRelay(); const validRelay = this.getFirstValidRelay();
if (!validRelay) { if (!validRelay) {
throw new Error('No valid relay connection found after handshake'); throw new Error('No valid relay connection found after handshake');
} }
const relayAddress = validRelay.spAddress; const relayAddress = validRelay.spAddress;
const feeRate = 1; const feeRate = 1;
@ -505,12 +520,12 @@ export class Service {
// TODO encoding of relatively large binaries (=> 1M) is a bit long now and blocking // TODO encoding of relatively large binaries (=> 1M) is a bit long now and blocking
const privateSplitData = this.splitData(privateData); const privateSplitData = this.splitData(privateData);
const publicSplitData = this.splitData(publicData); const publicSplitData = this.splitData(publicData);
const encodedPrivateData = { const encodedPrivateData = {
...wasm.encode_json(privateSplitData.jsonCompatibleData), ...wasm.encode_json(privateSplitData.jsonCompatibleData),
...wasm.encode_binary(privateSplitData.binaryData) ...wasm.encode_binary(privateSplitData.binaryData)
}; };
const encodedPublicData = { const encodedPublicData = {
...wasm.encode_json(publicSplitData.jsonCompatibleData), ...wasm.encode_json(publicSplitData.jsonCompatibleData),
...wasm.encode_binary(publicSplitData.binaryData) ...wasm.encode_binary(publicSplitData.binaryData)
}; };
@ -520,17 +535,17 @@ export class Service {
// Check if we know the member that matches this id // Check if we know the member that matches this id
const memberAddresses = this.getAddressesForMemberId(member); const memberAddresses = this.getAddressesForMemberId(member);
if (memberAddresses && memberAddresses.length != 0) { if (memberAddresses && memberAddresses.length != 0) {
members.add({ sp_addresses: memberAddresses }); members.add({ id: "stub_member", name: "stub_member", public_key: "stub_key", process_id: "stub_process", roles: [], sp_addresses: memberAddresses });
} }
} }
} }
await this.checkConnections([...members]); await this.checkConnections([...members]);
const result = wasm.create_new_process ( const result = wasm.create_process (
encodedPrivateData, encodedPrivateData,
roles, roles,
encodedPublicData, encodedPublicData,
relayAddress, relayAddress,
feeRate, feeRate,
this.getAllMembers() this.getAllMembers()
); );
@ -571,19 +586,19 @@ export class Service {
async parseFaucet(faucetResponse: string) { async parseFaucet(faucetResponse: string) {
try { try {
console.log('🪙 Parsing faucet response:', faucetResponse); console.log('🪙 Parsing faucet response:', faucetResponse);
// The faucet response should contain transaction data that updates the device's amount // The faucet response should contain transaction data that updates the device's amount
// Parse it similar to how we parse new transactions // Parse it similar to how we parse new transactions
const membersList = this.getAllMembers(); const membersList = this.getAllMembers();
const parsedTx = wasm.parse_new_tx(faucetResponse, 0, membersList); const parsedTx = wasm.parse_new_tx(faucetResponse, 0, membersList);
if (parsedTx) { if (parsedTx) {
await this.handleApiReturn(parsedTx); await this.handleApiReturn(parsedTx);
// Update device in database after faucet response // Update device in database after faucet response
const newDevice = this.dumpDeviceFromMemory(); const newDevice = this.dumpDeviceFromMemory();
await this.saveDeviceInDatabase(newDevice); await this.saveDeviceInDatabase(newDevice);
console.log('✅ Faucet response processed successfully'); console.log('✅ Faucet response processed successfully');
} else { } else {
console.warn('⚠️ No transaction data in faucet response'); console.warn('⚠️ No transaction data in faucet response');
@ -596,14 +611,14 @@ export class Service {
// Core protocol method: Create PRD Update // Core protocol method: Create PRD Update
async createPrdUpdate(processId: string, stateId: string): Promise<ApiReturn> { async createPrdUpdate(processId: string, stateId: string): Promise<ApiReturn> {
console.log(`📢 Creating PRD update for process ${processId}, state ${stateId}`); console.log(`📢 Creating PRD update for process ${processId}, state ${stateId}`);
try { try {
const process = await this.getProcess(processId); const process = await this.getProcess(processId);
if (!process) { if (!process) {
throw new Error('Process not found'); throw new Error('Process not found');
} }
const result = wasm.create_update_message(process, stateId, this.membersList); const result = wasm.create_update_message(process, stateId, this.membersList);
return result; return result;
} catch (error) { } catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error || 'Unknown error'); const errorMessage = error instanceof Error ? error.message : String(error || 'Unknown error');
@ -614,7 +629,7 @@ export class Service {
// Core protocol method: Approve Change (Validate State) // Core protocol method: Approve Change (Validate State)
async approveChange(processId: string, stateId: string): Promise<ApiReturn> { async approveChange(processId: string, stateId: string): Promise<ApiReturn> {
console.log(`✅ Approving change for process ${processId}, state ${stateId}`); console.log(`✅ Approving change for process ${processId}, state ${stateId}`);
try { try {
const process = this.processes.get(processId); const process = this.processes.get(processId);
if (!process) { if (!process) {
@ -631,32 +646,32 @@ export class Service {
// Core protocol method: Update Process // Core protocol method: Update Process
async updateProcess( async updateProcess(
process: any, process: any,
privateData: Record<string, any>, privateData: Record<string, any>,
publicData: Record<string, any>, publicData: Record<string, any>,
roles: Record<string, any> | null roles: Record<string, any> | null
): Promise<ApiReturn> { ): Promise<ApiReturn> {
console.log(`🔄 Updating process ${process.states[0]?.state_id || 'unknown'}`); console.log(`🔄 Updating process ${process.state?.state_id || 'unknown'}`);
console.log('Private data:', privateData); console.log('Private data:', privateData);
console.log('Public data:', publicData); console.log('Public data:', publicData);
console.log('Roles:', roles); console.log('Roles:', roles);
try { try {
// Convert data to WASM format // Convert data to WASM format
const newAttributes = wasm.encode_json(privateData); const newAttributes = wasm.encode_json(privateData);
const newPublicData = wasm.encode_json(publicData); const newPublicData = wasm.encode_json(publicData);
const newRoles = roles || process.states[0]?.roles || {}; const newRoles = roles || process.state?.roles || {};
// Use WASM function to update process // Use WASM function to update process
const result = wasm.update_process(process, newAttributes, newRoles, newPublicData, this.membersList); const result = wasm.update_process(process, newAttributes, newRoles, newPublicData, this.membersList);
if (result.updated_process) { if (result.updated_process) {
// Update our cache // Update our cache
this.processes.set(result.updated_process.process_id, result.updated_process.current_process); this.processes.set(result.updated_process.process_id, result.updated_process.current_process);
// Save to database // Save to database
await this.saveProcessToDb(result.updated_process.process_id, result.updated_process.current_process); await this.saveProcessToDb(result.updated_process.process_id, result.updated_process.current_process);
return result; return result;
} else { } else {
throw new Error('Failed to update process'); throw new Error('Failed to update process');
@ -701,48 +716,6 @@ export class Service {
} }
} }
public async getProcessesData(filteredProcesses: Record<string, Process>): Promise<Record<string, any>> {
const data: Record<string, any> = {};
// Now we decrypt all we can in the processes
for (const [processId, process] of Object.entries(filteredProcesses)) {
console.log('process roles:', this.getRoles(process));
// We also take the public data
const lastState = this.getLastCommitedState(process);
if (!lastState) {
console.error(`❌ Process ${processId} doesn't have a commited state`);
continue;
}
const processData: Record<string, any> = {};
for (const attribute of Object.keys(lastState.public_data)) {
try {
const value = this.decodeValue(lastState.public_data[attribute]);
if (value) {
processData[attribute] = value;
}
} catch (e) {
console.error(`❌ Error decoding public data ${attribute} for process ${processId}:`, e);
}
}
for (let i = process.states.length - 2; i >= 0; i--) {
const state = process.states[i];
for (const attribute of Object.keys(state.keys)) {
if (processData[attribute] !== undefined && processData[attribute] !== null) continue;
try {
const value = await this.decryptAttribute(processId, state, attribute);
if (value) {
processData[attribute] = value;
}
} catch (e) {
console.error(`❌ Error decrypting attribute ${attribute} for process ${processId}:`, e);
}
}
}
data[processId] = processData;
}
return data;
}
// Utility method: Get Process // Utility method: Get Process
async getProcess(processId: string): Promise<any | null> { async getProcess(processId: string): Promise<any | null> {
// First check in-memory cache // First check in-memory cache
@ -750,7 +723,7 @@ export class Service {
if (cachedProcess) { if (cachedProcess) {
return cachedProcess; return cachedProcess;
} }
// If not in cache, try to get from database // If not in cache, try to get from database
try { try {
const db = await Database.getInstance(); const db = await Database.getInstance();
@ -763,7 +736,7 @@ export class Service {
} catch (error) { } catch (error) {
console.error('Error getting process from database:', error); console.error('Error getting process from database:', error);
} }
return null; return null;
} }
@ -776,7 +749,7 @@ export class Service {
object: process, object: process,
key: processId key: processId
}); });
// Update in-memory cache // Update in-memory cache
this.processes.set(processId, process); this.processes.set(processId, process);
console.log(`💾 Process ${processId} saved to database`); console.log(`💾 Process ${processId} saved to database`);
@ -794,12 +767,12 @@ export class Service {
try { try {
const db = await Database.getInstance(); const db = await Database.getInstance();
const processes = await db.dumpStore('processes'); const processes = await db.dumpStore('processes');
// Update in-memory cache with all processes // Update in-memory cache with all processes
for (const [processId, process] of Object.entries(processes)) { for (const [processId, process] of Object.entries(processes)) {
this.processes.set(processId, process as any); this.processes.set(processId, process as any);
} }
return processes; return processes;
} catch (error) { } catch (error) {
console.error('Error getting all processes from database:', error); console.error('Error getting all processes from database:', error);
@ -810,7 +783,7 @@ export class Service {
// Utility method: Create a test process // Utility method: Create a test process
async createTestProcess(processId: string): Promise<any> { async createTestProcess(processId: string): Promise<any> {
console.log(`🔧 Creating test process: ${processId}`); console.log(`🔧 Creating test process: ${processId}`);
try { try {
// Create test data // Create test data
const privateData = wasm.encode_json({ secret: 'initial_secret' }); const privateData = wasm.encode_json({ secret: 'initial_secret' });
@ -818,17 +791,17 @@ export class Service {
const roles = { admin: { members: [], validation_rules: [], storages: [] } }; const roles = { admin: { members: [], validation_rules: [], storages: [] } };
const relayAddress = 'test_relay_address'; const relayAddress = 'test_relay_address';
const feeRate = 1; const feeRate = 1;
// Use WASM to create new process // Use WASM to create new process
const result = wasm.create_new_process(privateData, roles, publicData, relayAddress, feeRate, this.membersList); const result = wasm.create_process('test-device', 'Test Process', 'Test process description');
if (result.updated_process) { if (result.updated_process) {
const process = result.updated_process.current_process; const process = result.updated_process.current_process;
this.processes.set(processId, process); this.processes.set(processId, process);
// Save to database // Save to database
await this.saveProcessToDb(processId, process); await this.saveProcessToDb(processId, process);
console.log(`✅ Test process created: ${processId}`); console.log(`✅ Test process created: ${processId}`);
return process; return process;
} else { } else {
@ -843,7 +816,7 @@ export class Service {
public async getDeviceFromDatabase(): Promise<Device | null> { public async getDeviceFromDatabase(): Promise<Device | null> {
const db = await Database.getInstance(); const db = await Database.getInstance();
const walletStore = 'wallet'; const walletStore = 'wallet';
try { try {
const dbRes = await db.getObject(walletStore, DEVICE_KEY); const dbRes = await db.getObject(walletStore, DEVICE_KEY);
if (dbRes) { if (dbRes) {
@ -859,7 +832,7 @@ export class Service {
public async getDeviceMetadata(): Promise<{ device_id: string; device_address: string; created_at: string } | null> { public async getDeviceMetadata(): Promise<{ device_id: string; device_address: string; created_at: string } | null> {
const db = await Database.getInstance(); const db = await Database.getInstance();
const walletStore = 'wallet'; const walletStore = 'wallet';
try { try {
const dbRes = await db.getObject(walletStore, DEVICE_KEY); const dbRes = await db.getObject(walletStore, DEVICE_KEY);
if (dbRes) { if (dbRes) {
@ -909,28 +882,24 @@ export class Service {
} }
public getLastCommitedState(process: Process): ProcessState | null { public getLastCommitedState(process: Process): ProcessState | null {
const index = this.getLastCommitedStateIndex(process); return process.state || null;
if (index === null) return null;
return process.states[index];
} }
public getLastCommitedStateIndex(process: Process): number | null { public getLastCommitedStateIndex(process: Process): number | null {
if (process.states.length === 0) return null; // Since state is now a single object, return 0 if it exists, null otherwise
const processTip = process.states[process.states.length - 1].commited_in; return process.state ? 0 : null;
for (let i = process.states.length - 1; i >= 0; i--) { }
if ((process.states[i] as any).commited_in !== processTip) {
return i; public getStateTip(process: Process): any {
} return process.state?.commited_in || null;
}
return null;
} }
public getRoles(process: Process): Record<string, RoleDefinition> | null { public getRoles(process: Process): Record<string, RoleDefinition> | null {
const lastCommitedState = this.getLastCommitedState(process); const lastCommitedState = this.getLastCommitedState(process);
if (lastCommitedState && lastCommitedState.roles && Object.keys(lastCommitedState.roles).length != 0) { if (lastCommitedState && lastCommitedState.roles && Object.keys(lastCommitedState.roles).length != 0) {
return lastCommitedState!.roles; return lastCommitedState!.roles;
} else if (process.states.length === 2) { } else if (process.state) {
const firstState = process.states[0]; const firstState = process.state;
if (firstState && firstState.roles && Object.keys(firstState.roles).length != 0) { if (firstState && firstState.roles && Object.keys(firstState.roles).length != 0) {
return firstState!.roles; return firstState!.roles;
} }
@ -1003,7 +972,7 @@ export class Service {
} catch (e) { } catch (e) {
console.error(`Failed to save data to db: ${e}`); console.error(`Failed to save data to db: ${e}`);
} }
} }
async getBlobFromDb(hash: string): Promise<Blob | null> { async getBlobFromDb(hash: string): Promise<Blob | null> {
const db = await Database.getInstance(); const db = await Database.getInstance();
@ -1061,16 +1030,16 @@ export class Service {
// Check for errors in the returned objects // Check for errors in the returned objects
if (apiReturn.new_tx_to_send && apiReturn.new_tx_to_send.error) { if (apiReturn.new_tx_to_send && apiReturn.new_tx_to_send.error) {
const error = apiReturn.new_tx_to_send.error; const error = apiReturn.new_tx_to_send.error;
const errorMessage = typeof error === 'object' && error !== null ? const errorMessage = typeof error === 'object' && error !== null ?
(error as any).GenericError || JSON.stringify(error) : (error as any).GenericError || JSON.stringify(error) :
String(error); String(error);
throw new Error(`Transaction error: ${errorMessage}`); throw new Error(`Transaction error: ${errorMessage}`);
} }
if (apiReturn.commit_to_send && apiReturn.commit_to_send.error) { if (apiReturn.commit_to_send && apiReturn.commit_to_send.error) {
const error = apiReturn.commit_to_send.error; const error = apiReturn.commit_to_send.error;
const errorMessage = typeof error === 'object' && error !== null ? const errorMessage = typeof error === 'object' && error !== null ?
(error as any).GenericError || JSON.stringify(error) : (error as any).GenericError || JSON.stringify(error) :
String(error); String(error);
throw new Error(`Commit error: ${errorMessage}`); throw new Error(`Commit error: ${errorMessage}`);
} }
@ -1121,11 +1090,13 @@ export class Service {
if (updatedProcess.encrypted_data && Object.keys(updatedProcess.encrypted_data).length != 0) { if (updatedProcess.encrypted_data && Object.keys(updatedProcess.encrypted_data).length != 0) {
for (const [hash, cipher] of Object.entries(updatedProcess.encrypted_data)) { for (const [hash, cipher] of Object.entries(updatedProcess.encrypted_data)) {
const blob = this.hexToBlob(cipher); if (typeof cipher === 'string') {
try { const blob = this.hexToBlob(cipher);
await this.saveBlobToDb(hash, blob); try {
} catch (e) { await this.saveBlobToDb(hash, blob);
console.error(e); } catch (e) {
console.error(e);
}
} }
} }
} }
@ -1182,7 +1153,8 @@ export class Service {
} }
private lookForStateId(process: Process, stateId: string): boolean { private lookForStateId(process: Process, stateId: string): boolean {
for (const state of process.states) { const state = process.state;
if (state) {
if (state.state_id === stateId) { if (state.state_id === stateId) {
return true; return true;
} }
@ -1195,7 +1167,7 @@ export class Service {
console.log('Requesting data from peers'); console.log('Requesting data from peers');
const membersList = this.getAllMembers(); const membersList = this.getAllMembers();
try { try {
const res = wasm.request_data(processId, stateIds, roles, membersList); const res = wasm.request_data(processId, stateIds, Object.keys(roles), membersList);
await this.handleApiReturn(res); await this.handleApiReturn(res);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@ -1215,14 +1187,16 @@ export class Service {
if (!key) { if (!key) {
const roles = state.roles; const roles = state.roles;
let hasAccess = false; let hasAccess = false;
// If we're not supposed to have access to this attribute, ignore // If we're not supposed to have access to this attribute, ignore
for (const role of Object.values(roles)) { for (const role of Object.values(roles)) {
for (const rule of Object.values(role.validation_rules)) { for (const rule of Object.values(role.validation_rules)) {
if (rule.fields.includes(attribute)) { if (typeof rule === 'object' && rule !== null && 'fields' in rule && Array.isArray(rule.fields)) {
if (role.members.includes(pairingProcessId)) { if (rule.fields.includes(attribute)) {
// We have access to this attribute if (role.members.includes(pairingProcessId)) {
hasAccess = true; // We have access to this attribute
break; hasAccess = true;
break;
}
} }
} }
} }
@ -1236,7 +1210,7 @@ export class Service {
const maxRetries = 5; const maxRetries = 5;
const retryDelay = 500; // delay in milliseconds const retryDelay = 500; // delay in milliseconds
let retries = 0; let retries = 0;
while ((!hash || !key) && retries < maxRetries) { while ((!hash || !key) && retries < maxRetries) {
await new Promise(resolve => setTimeout(resolve, retryDelay)); await new Promise(resolve => setTimeout(resolve, retryDelay));
// Re-read hash and key after waiting // Re-read hash and key after waiting
@ -1269,7 +1243,7 @@ export class Service {
} }
} }
} }
return null; return null;
} }

14
start.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
# Script de démarrage pour sdk_signer
echo "🚀 Démarrage de sdk_signer..."
# Vérifier que les fichiers nécessaires existent
if [ ! -f "dist/index.js" ]; then
echo "❌ Erreur: dist/index.js non trouvé. Lancement de la compilation..."
npm run build
fi
# Démarrer le serveur
echo "✅ Démarrage du serveur sdk_signer..."
exec node dist/index.js