Add gsio-client and gsio-wallet crates with initial implementations

- Introduced `gsio-client` crate for interacting with GSIO nodes, including ledger entry management and node discovery.
- Introduced `gsio-wallet` crate for key management, transaction creation, and wallet functionality.
- Updated workspace configuration to include new crates.
This commit is contained in:
geoffsee
2025-06-15 13:34:12 -04:00
parent 2f2ddc7f6d
commit 4c0848e2f3
10 changed files with 1146 additions and 49 deletions

View File

@@ -0,0 +1,18 @@
[package]
name = "gsio-client"
version = "0.1.0"
publish = false
edition = "2024"
license = "MIT"
[dependencies]
tokio = { version = "1.45.1", features = ["rt-multi-thread", "macros", "time", "net"] }
tracing = { version = "0.1.41" }
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uuid = { version = "1.7.0", features = ["v4", "serde"] }
chrono = { version = "0.4.35", features = ["serde"] }
reqwest = { version = "0.11", features = ["json"] }
thiserror = "1.0"
futures = "0.3.31"

View File

@@ -0,0 +1,41 @@
use gsio_client::{GsioClient, GsioClientError};
use serde_json::json;
use tracing_subscriber::FmtSubscriber;
#[tokio::main]
async fn main() -> Result<(), GsioClientError> {
// Initialize tracing
tracing::subscriber::set_global_default(FmtSubscriber::default())
.expect("Failed to set tracing subscriber");
// Create a new client
let client = GsioClient::new("http://localhost:3000")?;
println!("Created GSIO client");
// Add an entry to the ledger
let entry_data = json!({
"message": "Hello, GSIO!",
"timestamp": chrono::Utc::now().to_rfc3339(),
});
let entry = client.add_ledger_entry(entry_data).await?;
println!("Added ledger entry: {:?}", entry);
// Get all entries in the ledger
let entries = client.get_ledger().await?;
println!("Ledger entries:");
for entry in entries {
println!(" - {}: {}", entry.id, entry.data);
}
// Get all known nodes
let nodes = client.get_known_nodes().await?;
println!("Known nodes:");
for node in nodes {
println!(" - {}", node);
}
println!("Example completed successfully");
Ok(())
}

View File

@@ -0,0 +1,138 @@
//! GSIO Client Library
//!
//! This library provides a client for interacting with GSIO nodes.
//! It allows connecting to nodes, adding entries to the ledger,
//! and retrieving ledger data.
use reqwest::{Client as HttpClient, Error as ReqwestError};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use std::time::Duration;
use thiserror::Error;
use tracing::{error, info};
/// Error type for GSIO client operations
#[derive(Error, Debug)]
pub enum GsioClientError {
#[error("HTTP error: {0}")]
HttpError(#[from] ReqwestError),
#[error("JSON serialization error: {0}")]
SerializationError(#[from] serde_json::Error),
#[error("Connection error: {0}")]
ConnectionError(String),
#[error("Server error: {0}")]
ServerError(String),
}
/// A ledger entry
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LedgerEntry {
pub id: String,
pub timestamp: String,
pub data: JsonValue,
pub node_id: String,
pub hash: String,
}
/// GSIO Client for interacting with GSIO nodes
pub struct GsioClient {
client: HttpClient,
node_url: String,
}
impl GsioClient {
/// Create a new GSIO client
pub fn new(node_url: &str) -> Result<Self, GsioClientError> {
let client = HttpClient::builder()
.timeout(Duration::from_secs(30))
.build()
.map_err(|e| GsioClientError::ConnectionError(e.to_string()))?;
Ok(Self {
client,
node_url: node_url.to_string(),
})
}
/// Add an entry to the ledger
pub async fn add_ledger_entry(&self, data: JsonValue) -> Result<LedgerEntry, GsioClientError> {
info!("Adding ledger entry: {:?}", data);
let url = format!("{}/api/ledger", self.node_url);
let response = self.client.post(&url)
.json(&data)
.send()
.await?;
if !response.status().is_success() {
let error_text = response.text().await?;
return Err(GsioClientError::ServerError(format!("Server returned error: {}", error_text)));
}
let entry: LedgerEntry = response.json().await?;
Ok(entry)
}
/// Get all entries in the ledger
pub async fn get_ledger(&self) -> Result<Vec<LedgerEntry>, GsioClientError> {
info!("Getting ledger entries");
let url = format!("{}/api/ledger", self.node_url);
let response = self.client.get(&url)
.send()
.await?;
if !response.status().is_success() {
let error_text = response.text().await?;
return Err(GsioClientError::ServerError(format!("Server returned error: {}", error_text)));
}
let entries: Vec<LedgerEntry> = response.json().await?;
Ok(entries)
}
/// Get all known nodes in the network
pub async fn get_known_nodes(&self) -> Result<Vec<String>, GsioClientError> {
info!("Getting known nodes");
let url = format!("{}/api/nodes", self.node_url);
let response = self.client.get(&url)
.send()
.await?;
if !response.status().is_success() {
let error_text = response.text().await?;
return Err(GsioClientError::ServerError(format!("Server returned error: {}", error_text)));
}
let data: JsonValue = response.json().await?;
let nodes = data.get("nodes")
.ok_or_else(|| GsioClientError::ServerError("Invalid response format".to_string()))?;
let nodes: Vec<String> = serde_json::from_value(nodes.clone())?;
Ok(nodes)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_client_creation() {
let client = GsioClient::new("http://localhost:3000").unwrap();
assert_eq!(client.node_url, "http://localhost:3000");
}
// More tests would be added here in a real implementation
}