Try Live
Add Docs
Rankings
Pricing
Docs
Install
Theme
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Tashi Vertex Rust Library
https://github.com/tashigit/tashi-vertex-rs
Admin
Tashi Vertex is a Rust binding for an embedded Byzantine fault-tolerant consensus engine based on
...
Tokens:
7,077
Snippets:
39
Trust Score:
7.3
Update:
1 month ago
Context
Skills
Chat
Benchmark
78.1
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Tashi Vertex Tashi Vertex is a Rust binding for an embedded Byzantine fault-tolerant (BFT) consensus engine based on the Hashgraph algorithm. It uses a DAG (Directed Acyclic Graph) of cryptographically signed events and virtual voting to achieve consensus finality in under 100 milliseconds without exchanging explicit vote messages. The library provides async-first socket binding and message receiving through Rust futures, with safe FFI wrappers that feature automatic cleanup via `Drop`. The engine tolerates up to `f = ⌊(n-1)/3⌋` Byzantine participants in a network and includes over 15 tunable parameters for configuring heartbeat intervals, latency thresholds, epoch sizing, and threading. Key features include Ed25519 key generation and management with Base58 serialization, configurable peer capabilities (ordering, logic, NAT handling), and built-in support for UDP hole punching to establish direct connections between peers behind NATs. ## KeySecret - Secret Key Generation and Management The `KeySecret` struct represents an Ed25519 secret key used for signing transactions in the Tashi Vertex network. It provides methods for generating new keys, deriving public keys, and serializing to/from Base58-encoded DER format. ```rust use tashi_vertex::KeySecret; fn main() -> tashi_vertex::Result<()> { // Generate a new secret key for signing transactions let secret = KeySecret::generate(); // Derive the corresponding public key let public = secret.public(); // Display keys as Base58-encoded DER strings println!("Secret: {secret}"); println!("Public: {public}"); // Parse a secret key from a Base58-encoded string let secret_str = "3xk8GqN8VbWz..."; // Base58 DER-encoded secret key let parsed_secret: KeySecret = secret_str.parse()?; // Serialize to DER format as a vector let der_bytes = parsed_secret.to_der_vec()?; // Parse from DER bytes let from_der = KeySecret::from_der(&der_bytes)?; Ok(()) } ``` ## KeyPublic - Public Key Verification The `KeyPublic` struct represents an Ed25519 public key used for verifying signatures and identifying peers in the network. It supports Base58-encoded DER serialization and can be derived from a `KeySecret`. ```rust use tashi_vertex::{KeyPublic, KeySecret}; fn main() -> tashi_vertex::Result<()> { // Generate a keypair let secret = KeySecret::generate(); let public = secret.public(); // Parse a public key from Base58 string let public_str = "aSq9DsNNvGhY..."; // Base58 DER-encoded public key let parsed_public: KeyPublic = public_str.parse()?; // Serialize to DER format let der_bytes = parsed_public.to_der_vec()?; // Parse from DER bytes let from_der = KeyPublic::from_der(&der_bytes)?; // Display the public key println!("Public Key: {from_der}"); Ok(()) } ``` ## Context - Runtime Context Initialization The `Context` struct represents the main runtime context for Tashi Vertex. It manages async operations and resources, and must be initialized before using most other components like sockets and engines. ```rust use tashi_vertex::Context; fn main() -> tashi_vertex::Result<()> { // Initialize a new Tashi Vertex context // This must be created before binding sockets or starting engines let context = Context::new()?; println!("Runtime context initialized successfully"); // The context is automatically cleaned up when dropped Ok(()) } ``` ## Socket - Async Network Socket Binding The `Socket` struct represents an async network socket bound to a local address. It uses Rust futures for non-blocking socket binding operations. The address must be a valid IPv4 or IPv6 address with port number (no DNS lookup is performed). ```rust use tashi_vertex::{Context, Socket}; #[tokio::main] async fn main() -> tashi_vertex::Result<()> { // Initialize the runtime context let context = Context::new()?; // Bind a socket to a local address (async operation) let socket = Socket::bind(&context, "127.0.0.1:9000").await?; println!("Socket bound to 127.0.0.1:9000"); // IPv6 binding example // let socket_v6 = Socket::bind(&context, "[::1]:9000").await?; Ok(()) } ``` ## Peers - Network Participant Management The `Peers` struct manages the set of network participants, each identified by their network address and public key. It supports configuring capabilities for each peer such as ordering participation, NAT status, and kick immunity. ```rust use tashi_vertex::{KeySecret, Peers, peers::PeerCapabilities}; fn main() -> tashi_vertex::Result<()> { // Create a new empty peer set let mut peers = Peers::new()?; // Or create with pre-allocated capacity let mut peers = Peers::with_capacity(3)?; // Generate keys for our node let my_secret = KeySecret::generate(); let my_public = my_secret.public(); // Generate or parse peer public keys let peer1_public: tashi_vertex::KeyPublic = "aSq9DsNNvGhY...".parse()?; let peer2_public: tashi_vertex::KeyPublic = "bTr0EtOOvHiZ...".parse()?; // Insert peers with default capabilities peers.insert("192.168.1.10:9001", &peer1_public, Default::default())?; peers.insert("192.168.1.11:9002", &peer2_public, Default::default())?; // Insert ourselves peers.insert("192.168.1.1:9000", &my_public, Default::default())?; // Insert a peer with custom capabilities let capabilities = PeerCapabilities { no_order: false, // Participates in ordering no_logic: false, // Knows application logic public: true, // Has stable public address (not behind NAT) unkickable: true, // Cannot be kicked from session }; peers.insert("203.0.113.50:9003", &peer1_public, capabilities)?; println!("Configured peer set with 4 participants"); Ok(()) } ``` ## Options - Engine Configuration The `Options` struct provides extensive configuration for the consensus engine with over 15 tunable parameters including heartbeat intervals, latency thresholds, epoch sizing, threading limits, and NAT traversal settings. ```rust use tashi_vertex::Options; fn main() { // Create default options let mut options = Options::default(); // Or equivalently: let mut options = Options::new(); // Configure heartbeat interval (default: 500ms) // Empty events are created at this interval to keep the session alive options.set_heartbeat_us(250_000); // 250ms // Configure event reporting options.set_report_gossip_events(true); // Configure when to kick fallen-behind peers (default varies) // Set to -1 to never vote to kick options.set_fallen_behind_kick_s(10); // Kick after 10 seconds behind // Latency thresholds for congestion control options.set_target_ack_latency_ms(400); // Default: 400ms options.set_max_ack_latency_ms(600); // Default: 600ms - reduce throughput options.set_throttle_ack_latency_ms(900); // Default: 900ms - emergency restriction options.set_reset_ack_latency_ms(2000); // Default: 2000ms - reset to initial // Event timing and epoch configuration options.set_base_min_event_interval_us(1000); // Minimum 1ms between events options.set_enable_dynamic_epoch_size(true); // Auto-adjust epoch size // Transaction buffering options.set_transaction_channel_size(32); // Buffer up to 32 transactions options.set_max_unacknowledged_bytes(500 * 1024 * 1024); // 500 MiB unacknowledged // Threading configuration options.set_max_blocking_verify_threads(4); // Signature verification threads // State sharing for fallen-behind peers options.set_enable_state_sharing(true); options.set_epoch_states_to_cache(3); // Cache 3 epoch states // NAT traversal options.set_enable_hole_punching(true); // Read current values println!("Heartbeat: {}us", options.get_heartbeat_us()); println!("Dynamic epochs: {}", options.get_enable_dynamic_epoch_size()); println!("Max verify threads: {}", options.get_max_blocking_verify_threads()); } ``` ## Engine - Consensus Engine Control The `Engine` struct is the core component that drives the consensus protocol. It starts the consensus engine, manages transaction submission, and receives consensus-ordered messages. The engine takes ownership of the socket, options, and peers passed to it. ```rust use tashi_vertex::{Context, Engine, KeySecret, Options, Peers, Socket, Transaction}; #[tokio::main] async fn main() -> tashi_vertex::Result<()> { let secret = KeySecret::generate(); let public = secret.public(); // Configure the peer network let mut peers = Peers::new()?; peers.insert("127.0.0.1:9000", &public, Default::default())?; // Add other peers... // Initialize context and bind socket let context = Context::new()?; let socket = Socket::bind(&context, "127.0.0.1:9000").await?; // Configure engine options let mut options = Options::default(); options.set_report_gossip_events(true); options.set_fallen_behind_kick_s(10); // Start the consensus engine // Note: socket, options, and peers ownership is transferred to the engine let engine = Engine::start(&context, socket, options, &secret, peers)?; println!("Consensus engine started successfully"); // Send a transaction (see Transaction section) let data = b"hello world"; let mut tx = Transaction::allocate(data.len()); tx.copy_from_slice(data); engine.send_transaction(tx)?; Ok(()) } ``` ## Transaction - Data Submission The `Transaction` struct represents an allocated buffer for submitting data to the consensus network. Transactions are allocated with a specific size, filled with data, and then sent through the engine for consensus ordering. ```rust use tashi_vertex::{Context, Engine, KeySecret, Options, Peers, Socket, Transaction}; #[tokio::main] async fn main() -> tashi_vertex::Result<()> { // Setup engine (abbreviated) let secret = KeySecret::generate(); let mut peers = Peers::new()?; peers.insert("127.0.0.1:9000", &secret.public(), Default::default())?; let context = Context::new()?; let socket = Socket::bind(&context, "127.0.0.1:9000").await?; let engine = Engine::start(&context, socket, Options::default(), &secret, peers)?; // Allocate and send a binary transaction let data = b"binary payload data"; let mut tx = Transaction::allocate(data.len()); tx.copy_from_slice(data); engine.send_transaction(tx)?; // Send a string transaction with null terminator let message = "PING"; let mut tx = Transaction::allocate(message.len() + 1); tx[..message.len()].copy_from_slice(message.as_bytes()); tx[message.len()] = 0; // null-terminate engine.send_transaction(tx)?; // Transaction implements Deref/DerefMut to [u8] for flexible manipulation let mut tx = Transaction::allocate(16); tx[0..4].copy_from_slice(&42u32.to_le_bytes()); // Write a u32 tx[4..8].copy_from_slice(&100u32.to_le_bytes()); engine.send_transaction(tx)?; println!("Transactions sent for consensus"); Ok(()) } ``` ## Message, Event, and SyncPoint - Receiving Consensus Results The `Message` enum represents received messages from the consensus engine, either finalized `Event`s containing ordered transactions or `SyncPoint`s for session management. Events provide access to transaction data, timestamps, creator information, and cryptographic hashes. ```rust use tashi_vertex::{Context, Engine, KeySecret, Message, Options, Peers, Socket, Transaction}; use std::str::from_utf8; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { // Setup engine (abbreviated) let secret = KeySecret::generate(); let mut peers = Peers::new()?; peers.insert("127.0.0.1:9000", &secret.public(), Default::default())?; let context = Context::new()?; let socket = Socket::bind(&context, "127.0.0.1:9000").await?; let engine = Engine::start(&context, socket, Options::default(), &secret, peers)?; // Send initial transaction let mut tx = Transaction::allocate(5); tx.copy_from_slice(b"PING\0"); engine.send_transaction(tx)?; // Receive consensus-ordered messages while let Some(message) = engine.recv_message().await? { match message { Message::Event(event) => { // Event metadata let creator = event.creator(); // Public key of creator let created = event.created_at(); // Unix timestamp (nanoseconds) let consensus = event.consensus_at(); // Consensus timestamp let hash = event.hash(); // 32-byte cryptographic hash let count = event.transaction_count(); println!("EVENT from {} at consensus time {}", creator, consensus); println!(" Hash: {:?}", &hash[..8]); // First 8 bytes println!(" Transactions: {}", count); // Access transactions by index if let Some(tx_data) = event.transaction(0) { println!(" First tx ({} bytes): {:?}", tx_data.len(), tx_data); } // Iterate over all transactions for tx in event.transactions() { if let Ok(text) = from_utf8(tx) { println!(" >> {}", text.trim_end_matches('\0')); } else { println!(" >> Binary data: {} bytes", tx.len()); } } // Whitened signature for consensus-driven randomness let whitened = event.whitened_signature(); println!(" Whitened sig ({} bytes): {:?}", whitened.len(), &whitened[..8]); } Message::SyncPoint(sync_point) => { // SyncPoint indicates a session management decision // agreed upon by a super-majority of peers println!("SYNC POINT received - session state updated"); } } } Ok(()) } ``` ## base58 - Base58 Encoding/Decoding Utilities The `base58` module provides utilities for encoding and decoding binary data to/from Base58 strings. This is used internally for key serialization but is also available for general use. ```rust use tashi_vertex::base58; fn main() -> tashi_vertex::Result<()> { // Encode binary data to Base58 let data = b"Hello, World!"; let encoded = base58::encode_to_string(data)?; println!("Encoded: {}", encoded); // Calculate required buffer sizes let encode_size = base58::encode_length(data.len()); let decode_size = base58::decode_length(encoded.len()); // Encode into pre-allocated buffer let mut buffer = vec![0u8; encode_size]; let actual_len = base58::encode(data, &mut buffer)?; let encoded_str = std::str::from_utf8(&buffer[..actual_len]).unwrap(); println!("Encoded (buffer): {}", encoded_str); // Decode Base58 string back to bytes let decoded = base58::decode_to_vec(encoded.as_bytes())?; assert_eq!(&decoded, data); println!("Decoded: {:?}", String::from_utf8_lossy(&decoded)); // Decode into pre-allocated buffer let mut output = vec![0u8; decode_size]; let actual_len = base58::decode(encoded.as_bytes(), &mut output)?; assert_eq!(&output[..actual_len], data); Ok(()) } ``` ## Complete Multi-Node Consensus Example This comprehensive example demonstrates running a complete multi-peer consensus network with transaction exchange, based on the pingback example. ```rust use std::str::from_utf8; use tashi_vertex::{ Context, Engine, KeySecret, KeyPublic, Message, Options, Peers, Socket, Transaction, }; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { // Parse command-line or configuration let bind_address = "127.0.0.1:8001"; let my_secret: KeySecret = "YOUR_BASE58_SECRET_KEY".parse()?; // Peer configuration: public_key@address let peer2_public: KeyPublic = "PEER2_BASE58_PUBLIC_KEY".parse()?; let peer3_public: KeyPublic = "PEER3_BASE58_PUBLIC_KEY".parse()?; // Initialize peer set with all network participants let mut peers = Peers::with_capacity(3)?; peers.insert("127.0.0.1:8002", &peer2_public, Default::default())?; peers.insert("127.0.0.1:8003", &peer3_public, Default::default())?; peers.insert(bind_address, &my_secret.public(), Default::default())?; println!(":: Configured network for 3 peers"); // Initialize runtime context let context = Context::new()?; println!(":: Initialized runtime"); // Bind network socket let socket = Socket::bind(&context, bind_address).await?; println!(":: Bound local socket to {}", bind_address); // Configure engine options let mut options = Options::default(); options.set_report_gossip_events(true); options.set_fallen_behind_kick_s(10); options.set_heartbeat_us(500_000); // 500ms heartbeat // Start consensus engine let engine = Engine::start(&context, socket, options, &my_secret, peers)?; println!(":: Started the consensus engine"); // Send initial PING transaction send_string(&engine, "PING")?; println!(":: Sent PING transaction"); // Process consensus messages while let Some(message) = engine.recv_message().await? { match message { Message::Event(event) => { if event.transaction_count() > 0 { println!(" > Received EVENT"); println!(" - From: {}", event.creator()); println!(" - Created: {}", event.created_at()); println!(" - Consensus: {}", event.consensus_at()); println!(" - Transactions: {}", event.transaction_count()); for tx in event.transactions() { match from_utf8(tx) { Ok(text) => println!(" - >> {}", text.trim_end_matches('\0')), Err(_) => println!(" - >> [binary: {} bytes]", tx.len()), } } } } Message::SyncPoint(_) => { println!(" > Received SYNC POINT"); } } } Ok(()) } /// Helper function to send a null-terminated string transaction fn send_string(engine: &Engine, s: &str) -> tashi_vertex::Result<()> { let mut tx = Transaction::allocate(s.len() + 1); tx[..s.len()].copy_from_slice(s.as_bytes()); tx[s.len()] = 0; engine.send_transaction(tx) } ``` ## Summary Tashi Vertex is ideal for building distributed systems that require fast, deterministic consensus without the overhead of explicit voting protocols. Common use cases include distributed databases requiring total ordering of operations, blockchain and DLT applications needing sub-100ms finality, real-time multiplayer games requiring synchronized state, and financial systems demanding Byzantine fault tolerance. The library's async-first design integrates seamlessly with Tokio and other Rust async runtimes. Integration typically follows the pattern of: generating or loading Ed25519 keypairs, configuring the peer network with addresses and public keys, initializing the context and binding a socket, configuring engine options for the specific use case, starting the engine, and then using the transaction/message loop to submit data and receive consensus-ordered results. The engine's ownership model ensures resources are properly managed, while the `Message` enum provides a clean interface for handling both finalized events and session management sync points.