Implement native and WASM WebSocket client for sigsocket communication
- Added `NativeClient` for non-WASM environments with automatic reconnection and message handling. - Introduced `WasmClient` for WASM environments, supporting WebSocket communication and reconnection logic. - Created protocol definitions for `SignRequest` and `SignResponse` with serialization and deserialization. - Developed integration tests for the client functionality and sign request handling. - Implemented WASM-specific tests to ensure compatibility and functionality in browser environments.
This commit is contained in:
		
							
								
								
									
										133
									
								
								sigsocket_client/examples/basic_usage.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								sigsocket_client/examples/basic_usage.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
			
		||||
//! Basic usage example for sigsocket_client
 | 
			
		||||
//!
 | 
			
		||||
//! This example demonstrates how to:
 | 
			
		||||
//! 1. Create a sigsocket client
 | 
			
		||||
//! 2. Set up a sign request handler
 | 
			
		||||
//! 3. Connect to a sigsocket server
 | 
			
		||||
//! 4. Handle incoming signature requests
 | 
			
		||||
//!
 | 
			
		||||
//! This example only runs on native (non-WASM) targets.
 | 
			
		||||
 | 
			
		||||
#[cfg(not(target_arch = "wasm32"))]
 | 
			
		||||
use sigsocket_client::{SigSocketClient, SignRequest, SignResponse, SignRequestHandler, Result, SigSocketError};
 | 
			
		||||
 | 
			
		||||
#[cfg(not(target_arch = "wasm32"))]
 | 
			
		||||
/// Example sign request handler
 | 
			
		||||
///
 | 
			
		||||
/// In a real application, this would:
 | 
			
		||||
/// - Present the request to the user
 | 
			
		||||
/// - Get user approval
 | 
			
		||||
/// - Use a secure signing method (hardware wallet, etc.)
 | 
			
		||||
/// - Return the signature
 | 
			
		||||
struct ExampleSignHandler;
 | 
			
		||||
 | 
			
		||||
#[cfg(not(target_arch = "wasm32"))]
 | 
			
		||||
impl SignRequestHandler for ExampleSignHandler {
 | 
			
		||||
    fn handle_sign_request(&self, request: &SignRequest) -> Result<Vec<u8>> {
 | 
			
		||||
        println!("📝 Received sign request:");
 | 
			
		||||
        println!("   ID: {}", request.id);
 | 
			
		||||
        println!("   Message (base64): {}", request.message);
 | 
			
		||||
        
 | 
			
		||||
        // Decode the message to show what we're signing
 | 
			
		||||
        match request.message_bytes() {
 | 
			
		||||
            Ok(message_bytes) => {
 | 
			
		||||
                println!("   Message (hex): {}", hex::encode(&message_bytes));
 | 
			
		||||
                println!("   Message (text): {}", String::from_utf8_lossy(&message_bytes));
 | 
			
		||||
            }
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                println!("   ⚠️  Failed to decode message: {}", e);
 | 
			
		||||
                return Err(SigSocketError::Base64(e.to_string()));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // In a real implementation, you would:
 | 
			
		||||
        // 1. Show this to the user
 | 
			
		||||
        // 2. Get user approval
 | 
			
		||||
        // 3. Sign the message using a secure method
 | 
			
		||||
        
 | 
			
		||||
        println!("🤔 Would you like to sign this message? (This is a simulation)");
 | 
			
		||||
        println!("✅ Auto-approving for demo purposes...");
 | 
			
		||||
        
 | 
			
		||||
        // Simulate signing - in reality, this would be a real signature
 | 
			
		||||
        let fake_signature = format!("fake_signature_for_{}", request.id);
 | 
			
		||||
        Ok(fake_signature.into_bytes())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(not(target_arch = "wasm32"))]
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() -> Result<()> {
 | 
			
		||||
    // Initialize logging
 | 
			
		||||
    env_logger::init();
 | 
			
		||||
 | 
			
		||||
    println!("🚀 SigSocket Client Example");
 | 
			
		||||
    println!("============================");
 | 
			
		||||
 | 
			
		||||
    // Example public key (in a real app, this would be your actual public key)
 | 
			
		||||
    let public_key = hex::decode("02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388")
 | 
			
		||||
        .expect("Invalid public key hex");
 | 
			
		||||
 | 
			
		||||
    println!("🔑 Public key: {}", hex::encode(&public_key));
 | 
			
		||||
 | 
			
		||||
    // Create the client
 | 
			
		||||
    let mut client = SigSocketClient::new("ws://localhost:8080/ws", public_key)?;
 | 
			
		||||
    println!("📡 Created client for: {}", client.url());
 | 
			
		||||
 | 
			
		||||
    // Set up the sign request handler
 | 
			
		||||
    client.set_sign_handler(ExampleSignHandler);
 | 
			
		||||
    println!("✅ Sign request handler configured");
 | 
			
		||||
 | 
			
		||||
    // Connect to the server
 | 
			
		||||
    println!("🔌 Connecting to sigsocket server...");
 | 
			
		||||
    match client.connect().await {
 | 
			
		||||
        Ok(()) => {
 | 
			
		||||
            println!("✅ Connected successfully!");
 | 
			
		||||
            println!("📊 Connection state: {:?}", client.state());
 | 
			
		||||
        }
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            println!("❌ Failed to connect: {}", e);
 | 
			
		||||
            println!("💡 Make sure the sigsocket server is running on localhost:8080");
 | 
			
		||||
            return Err(e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Keep the connection alive and handle requests
 | 
			
		||||
    println!("👂 Listening for signature requests...");
 | 
			
		||||
    println!("   (Press Ctrl+C to exit)");
 | 
			
		||||
 | 
			
		||||
    // In a real application, you might want to:
 | 
			
		||||
    // - Handle reconnection
 | 
			
		||||
    // - Provide a UI for user interaction
 | 
			
		||||
    // - Manage multiple concurrent requests
 | 
			
		||||
    // - Store and manage signatures
 | 
			
		||||
 | 
			
		||||
    // For this example, we'll just wait
 | 
			
		||||
    tokio::signal::ctrl_c().await.expect("Failed to listen for ctrl-c");
 | 
			
		||||
 | 
			
		||||
    println!("\n🛑 Shutting down...");
 | 
			
		||||
    client.disconnect().await?;
 | 
			
		||||
    println!("✅ Disconnected cleanly");
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Example of how you might manually send a response (if needed)
 | 
			
		||||
#[cfg(not(target_arch = "wasm32"))]
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
async fn send_manual_response(client: &SigSocketClient) -> Result<()> {
 | 
			
		||||
    let response = SignResponse::new(
 | 
			
		||||
        "example-request-id",
 | 
			
		||||
        "dGVzdCBtZXNzYWdl", // "test message" in base64
 | 
			
		||||
        "ZmFrZV9zaWduYXR1cmU=", // "fake_signature" in base64
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    client.send_sign_response(&response).await?;
 | 
			
		||||
    println!("📤 Sent manual response: {}", response.id);
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WASM main function (does nothing since this example is native-only)
 | 
			
		||||
#[cfg(target_arch = "wasm32")]
 | 
			
		||||
fn main() {
 | 
			
		||||
    // This example is designed for native use only
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user