sequence-algo-sdk 0.4.0

Sequence Markets Algo SDK — write HFT trading algos in Rust, compile to WASM, deploy to Sequence
Documentation
//! Mesh algo messaging — send/receive between algo instances by label.
//!
//! Each instance is assigned a label (e.g., "maker", "hedger") via Sequence.toml.
//! Call `send("hedger", payload)` to message a peer. Messages arrive via
//! `Algo::on_message(from_label, payload, ...)`.
//!
//! Under the hood, labels map to u64 mesh_ids. The runtime writes the mapping
//! table at `MESH_TABLE_WASM_OFFSET` in WASM memory.

/// Maximum message payload size in bytes.
pub const MAX_MESSAGE_SIZE: usize = 256;

/// Maximum label length in bytes (null-padded).
pub const MAX_LABEL_LEN: usize = 32;

/// Maximum number of mesh peers.
pub const MAX_MESH_PEERS: usize = 16;

/// Fixed WASM memory offset for the mesh routing table.
/// Written by the runtime, read by the SDK.
pub const MESH_TABLE_WASM_OFFSET: u32 = 0x24200;

// ─── Mesh table layout (repr(C), written by runtime at MESH_TABLE_WASM_OFFSET) ──

/// A single peer entry in the mesh table.
#[derive(Clone, Copy)]
#[repr(C)]
pub struct MeshPeerEntry {
    /// Null-padded label (e.g., b"hedger\0\0\0\0\0\0\0\0\0\0")
    pub label: [u8; MAX_LABEL_LEN],
    /// Internal mesh_id (used by __seq_send, opaque to the user)
    pub mesh_id: u64,
}

/// The mesh routing table — written to WASM memory by the runtime.
#[derive(Clone, Copy)]
#[repr(C)]
pub struct MeshTable {
    /// This instance's label
    pub self_label: [u8; MAX_LABEL_LEN],
    /// This instance's mesh_id
    pub self_mesh_id: u64,
    /// Number of active peers
    pub peer_count: u8,
    pub _pad: [u8; 7],
    /// Peer entries (label + mesh_id)
    pub peers: [MeshPeerEntry; MAX_MESH_PEERS],
}

impl MeshPeerEntry {
    pub const EMPTY: Self = Self {
        label: [0; MAX_LABEL_LEN],
        mesh_id: 0,
    };

    /// Get the label as a &str (trims null bytes).
    pub fn label_str(&self) -> &str {
        let end = self.label.iter().position(|&b| b == 0).unwrap_or(MAX_LABEL_LEN);
        core::str::from_utf8(&self.label[..end]).unwrap_or("")
    }
}

impl MeshTable {
    pub const EMPTY: Self = Self {
        self_label: [0; MAX_LABEL_LEN],
        self_mesh_id: 0,
        peer_count: 0,
        _pad: [0; 7],
        peers: [MeshPeerEntry::EMPTY; MAX_MESH_PEERS],
    };

    /// Get own label as a &str.
    pub fn self_label_str(&self) -> &str {
        let end = self.self_label.iter().position(|&b| b == 0).unwrap_or(MAX_LABEL_LEN);
        core::str::from_utf8(&self.self_label[..end]).unwrap_or("")
    }

    /// Look up a peer's mesh_id by label. Returns 0 if not found.
    pub fn resolve(&self, label: &str) -> u64 {
        let label_bytes = label.as_bytes();
        for i in 0..self.peer_count as usize {
            let entry = &self.peers[i];
            let end = entry.label.iter().position(|&b| b == 0).unwrap_or(MAX_LABEL_LEN);
            if end == label_bytes.len() && &entry.label[..end] == label_bytes {
                return entry.mesh_id;
            }
        }
        0
    }

    /// Look up a peer's label by mesh_id. Returns "" if not found.
    pub fn label_for(&self, mesh_id: u64) -> &str {
        for i in 0..self.peer_count as usize {
            if self.peers[i].mesh_id == mesh_id {
                return self.peers[i].label_str();
            }
        }
        ""
    }
}

// ─── Read the mesh table from WASM memory ────────────────────────────────────

/// Get a reference to the mesh table in WASM memory (read-only).
#[cfg(target_arch = "wasm32")]
#[inline]
fn mesh_table() -> &'static MeshTable {
    unsafe { &*(MESH_TABLE_WASM_OFFSET as *const MeshTable) }
}

// ─── Public API ──────────────────────────────────────────────────────────────

/// Send a message to a peer by label.
///
/// ```rust,ignore
/// messaging::send("hedger", b"HEDGE_NOW");
/// messaging::send("scanner", b"OPPORTUNITY");
/// ```
///
/// The label is resolved to a mesh_id via the mesh table (written by runtime).
/// If the label isn't found, the message is silently dropped.
///
/// # Panics
/// Panics if `payload.len() > MAX_MESSAGE_SIZE`.
#[cfg(target_arch = "wasm32")]
pub fn send(to: &str, payload: &[u8]) {
    assert!(
        payload.len() <= MAX_MESSAGE_SIZE,
        "mesh message payload exceeds {} bytes",
        MAX_MESSAGE_SIZE
    );
    let mesh_id = mesh_table().resolve(to);
    if mesh_id != 0 {
        unsafe {
            __seq_send(mesh_id, payload.as_ptr() as u32, payload.len() as u32);
        }
    }
}

#[cfg(target_arch = "wasm32")]
extern "C" {
    fn __seq_send(to: u64, ptr: u32, len: u32);
}

/// Non-WASM stub — no-op (for native testing).
#[cfg(not(target_arch = "wasm32"))]
pub fn send(_to: &str, payload: &[u8]) {
    assert!(
        payload.len() <= MAX_MESSAGE_SIZE,
        "mesh message payload exceeds {} bytes",
        MAX_MESSAGE_SIZE
    );
}

#[cfg(test)]
mod tests {
    use super::*;

    fn make_label(s: &str) -> [u8; MAX_LABEL_LEN] {
        let mut l = [0u8; MAX_LABEL_LEN];
        let b = s.as_bytes();
        l[..b.len().min(MAX_LABEL_LEN)].copy_from_slice(&b[..b.len().min(MAX_LABEL_LEN)]);
        l
    }

    fn make_table(self_label: &str, self_mesh_id: u64, peers: &[(&str, u64)]) -> MeshTable {
        let mut t = MeshTable::EMPTY;
        t.self_label = make_label(self_label);
        t.self_mesh_id = self_mesh_id;
        t.peer_count = peers.len() as u8;
        for (i, &(label, id)) in peers.iter().enumerate() {
            t.peers[i] = MeshPeerEntry {
                label: make_label(label),
                mesh_id: id,
            };
        }
        t
    }

    #[test]
    fn resolve_known_label() {
        let t = make_table("self", 1, &[("maker", 10), ("hedger", 20)]);
        assert_eq!(t.resolve("maker"), 10);
        assert_eq!(t.resolve("hedger"), 20);
    }

    #[test]
    fn resolve_unknown_returns_zero() {
        let t = make_table("self", 1, &[("maker", 10), ("hedger", 20)]);
        assert_eq!(t.resolve("scanner"), 0);
    }

    #[test]
    fn resolve_empty_table() {
        let t = make_table("self", 1, &[]);
        assert_eq!(t.resolve("maker"), 0);
        assert_eq!(t.resolve("anything"), 0);
    }

    #[test]
    fn label_for_known_id() {
        let t = make_table("self", 1, &[("maker", 10), ("hedger", 20)]);
        assert_eq!(t.label_for(10), "maker");
        assert_eq!(t.label_for(20), "hedger");
    }

    #[test]
    fn label_for_unknown_returns_empty() {
        let t = make_table("self", 1, &[("maker", 10)]);
        assert_eq!(t.label_for(999), "");
    }

    #[test]
    fn label_str_full_buffer() {
        let entry = MeshPeerEntry {
            label: [b'A'; MAX_LABEL_LEN],
            mesh_id: 42,
        };
        let s = entry.label_str();
        assert_eq!(s.len(), MAX_LABEL_LEN);
        assert_eq!(s, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
    }

    #[test]
    fn label_str_empty() {
        let entry = MeshPeerEntry::EMPTY;
        assert_eq!(entry.label_str(), "");
    }

    #[test]
    fn label_str_normal() {
        let entry = MeshPeerEntry {
            label: make_label("hedger"),
            mesh_id: 5,
        };
        assert_eq!(entry.label_str(), "hedger");
    }

    #[test]
    fn self_label_str_works() {
        let t = make_table("primary", 99, &[]);
        assert_eq!(t.self_label_str(), "primary");
    }

    #[test]
    fn round_trip() {
        let t = make_table("self", 1, &[("maker", 10), ("hedger", 20), ("scanner", 30)]);
        // Resolve label to mesh_id, then back to label
        let id = t.resolve("hedger");
        assert_eq!(id, 20);
        let label = t.label_for(id);
        assert_eq!(label, "hedger");
    }
}