How to send transactions and receive consensus-ordered events in Tashi Vertex
Tashi Vertex achieves consensus by ordering events that contain transactions. Understanding the relationship between these two concepts is essential for building applications.
use tashi_vertex::Transaction;// Allocate a transaction bufferlet mut tx = Transaction::allocate(data.len());// Copy your data into ittx.copy_from_slice(b"hello world");// Or write directly:tx[0] = 0x01;tx[1] = 0x02;
The Transaction type implements Deref<Target=[u8]> and DerefMut, so you can treat it like a byte slice.
The engine provides backpressure when the transaction buffer is full:
let mut options = Options::default();// Maximum buffered transactions before backpressureoptions.set_transaction_channel_size(32);// Maximum unacknowledged bytes (default: 500 MiB)options.set_max_unacknowledged_bytes(500 * 1024 * 1024);
When limits are reached:
send_transaction() may block briefly
The engine waits for transactions to reach consensus
Buffer space is freed as events are acknowledged
Adjust these parameters based on your transaction rate and network latency to optimize throughput.
let event: Event = /* from consensus */;// Cryptographic hash uniquely identifying this eventlet hash: &[u8; 32] = event.hash();// Public key of the node that created itlet creator: &KeyPublic = event.creator();// When the creator made this event (nanoseconds)let created_at: u64 = event.created_at();// When consensus was reached (nanoseconds)let consensus_at: u64 = event.consensus_at();// Consensus-driven randomnesslet random_seed: &[u8] = event.whitened_signature();
Timestamps are in nanoseconds since the Unix epoch. The consensus_at timestamp is agreed upon by all honest nodes.
A SyncPoint represents session management decisions:
Message::SyncPoint(sync_point) => { // Network has reached a synchronization point // Typically indicates: // - New node joined // - Node left/removed // - Epoch transition handle_sync_point(sync_point);}
Sync points mark boundaries in consensus where network membership may have changed.
When sync points occur
Node joining: A new peer is added to the network
Node leaving: A peer is removed (voluntarily or kicked)
Epoch transition: End of an epoch period
State transfer: When state sharing is enabled
Most applications can safely ignore sync points, but they’re useful for:
Timestamps are based on median of witness timestamps
Byzantine nodes cannot bias the order significantly
Transactions from the same node are guaranteed to be ordered as sent. Transactions from different nodes are ordered by consensus, which may not match the submission order.
Typical pattern for handling consensus-ordered transactions:
use tashi_vertex::{Engine, Message};#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let engine = /* ... initialize engine ... */; // Send some transactions let tx1 = Transaction::allocate(4); tx1.copy_from_slice(b"msg1"); engine.send_transaction(tx1)?; // Receive consensus-ordered events while let Some(message) = engine.recv_message().await? { match message { Message::Event(event) => { for tx in event.transactions() { apply_to_state(tx); } } Message::SyncPoint(_) => { // Optional: handle network changes } } } Ok(())}fn apply_to_state(tx: &[u8]) { // Parse transaction format // Validate transaction // Apply state changes // Emit results/logs}
Keep transaction processing fast. Slow processing can create a bottleneck since events arrive in order and can’t be processed in parallel without careful design.
// Include unique ID to detect duplicatesstruct Transaction { id: [u8; 32], // Unique transaction ID nonce: u64, // Sequence number payload: Vec<u8>, // Actual data}