sal-modular/sigsocket_client/examples/basic_usage.rs
Sameh Abouel-saad 9f143ded9d 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.
2025-06-04 13:03:15 +03:00

134 lines
4.6 KiB
Rust

//! 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
}