From 6c895d5b2c670679a0d3574b38d52aa5d12ca12b Mon Sep 17 00:00:00 2001
From: "agentfarmx[bot]" <198411105+agentfarmx[bot]@users.noreply.github.com>
Date: Wed, 12 Mar 2025 15:52:50 +0000
Subject: [PATCH 1/2] feat: implement DataStructureStore with capacity
management
The commit adds a new DataStructureStore implementation with methods to
initialize and manage data structure capacity. It introduces a MAX_DATA_STRUCTURES
constant and implements various Solana operations and data structure
operations for graphs, OHLCV, and hypergraphs.
---
lessvm-solana/src/vm/core.rs | 332 +++++++++++++++++++++++-
lessvm-solana/src/vm/data_structures.rs | 1 +
lessvm-solana/src/vm/memory.rs | 4 +-
3 files changed, 331 insertions(+), 6 deletions(-)
diff --git a/lessvm-solana/src/vm/core.rs b/lessvm-solana/src/vm/core.rs
index 461f11f..f2870d5 100644
--- a/lessvm-solana/src/vm/core.rs
+++ b/lessvm-solana/src/vm/core.rs
@@ -51,6 +51,9 @@ impl ReentrancyGuard {
}
}
+/// Maximum number of data structures that can be created
+const MAX_DATA_STRUCTURES: usize = 16;
+
#[repr(C, align(64))]
struct DataStructureStore {
btrees: Vec>,
@@ -60,6 +63,48 @@ struct DataStructureStore {
hypergraphs: Vec >,
}
+impl DataStructureStore {
+ fn new() -> Self {
+ Self {
+ btrees: Vec::with_capacity(MAX_DATA_STRUCTURES),
+ tries: Vec::with_capacity(MAX_DATA_STRUCTURES),
+ graphs: Vec::with_capacity(MAX_DATA_STRUCTURES),
+ ohlcvs: Vec::with_capacity(MAX_DATA_STRUCTURES),
+ hypergraphs: Vec::with_capacity(MAX_DATA_STRUCTURES),
+ }
+ }
+
+ fn ensure_capacity(&mut self, ds_type: DataStructureType, id: usize) {
+ match ds_type {
+ DataStructureType::BTreeMap => {
+ if id >= self.btrees.len() {
+ self.btrees.resize_with(id + 1, || None);
+ }
+ },
+ DataStructureType::Trie => {
+ if id >= self.tries.len() {
+ self.tries.resize_with(id + 1, || None);
+ }
+ },
+ DataStructureType::Graph => {
+ if id >= self.graphs.len() {
+ self.graphs.resize_with(id + 1, || None);
+ }
+ },
+ DataStructureType::OHLCV => {
+ if id >= self.ohlcvs.len() {
+ self.ohlcvs.resize_with(id + 1, || None);
+ }
+ },
+ DataStructureType::Hypergraph => {
+ if id >= self.hypergraphs.len() {
+ self.hypergraphs.resize_with(id + 1, || None);
+ }
+ },
+ }
+ }
+}
+
#[repr(C, align(64))]
pub struct VM<'a> {
pc: usize,
@@ -139,16 +184,17 @@ impl<'a> VM<'a> {
return Err(VMError::StackUnderflow);
}
- // Get two vectors from the stack (4 values each)
- let values1 = _mm256_loadu_si256(self.stack.as_simd_ptr()? as *const __m256i);
+ // Get the second vector from the stack (4 values)
+ let values2 = _mm256_loadu_si256(self.stack.as_simd_ptr()? as *const __m256i);
- // Pop the first 4 values and get the next 4 values
+ // Pop the first 4 values to get to the first vector
self.stack.pop()?;
self.stack.pop()?;
self.stack.pop()?;
self.stack.pop()?;
- let values2 = _mm256_loadu_si256(self.stack.as_simd_ptr()? as *const __m256i);
+ // Get the first vector from the stack (4 values)
+ let values1 = _mm256_loadu_si256(self.stack.as_simd_ptr()? as *const __m256i);
// Add the two vectors
let result = _mm256_add_epi64(values1, values2);
@@ -754,6 +800,284 @@ impl<'a> VM<'a> {
OpCode::Halt => {
break;
},
+ // Solana Operations that were missing
+ OpCode::GetBalance => {
+ let account_idx = self.stack.pop()?.0 as usize;
+ if account_idx >= self.accounts.accounts.len() {
+ return Err(VMError::InvalidAccount.into());
+ }
+ let account = &self.accounts.accounts[account_idx];
+ self.stack.push(Value(account.lamports()))?;
+ },
+ OpCode::GetOwner => {
+ let account_idx = self.stack.pop()?.0 as usize;
+ if account_idx >= self.accounts.accounts.len() {
+ return Err(VMError::InvalidAccount.into());
+ }
+ let account = &self.accounts.accounts[account_idx];
+ // Convert Pubkey to u64 for stack storage (using first 8 bytes)
+ let owner_bytes = account.owner.to_bytes();
+ let mut value_bytes = [0u8; 8];
+ value_bytes.copy_from_slice(&owner_bytes[0..8]);
+ self.stack.push(Value(u64::from_le_bytes(value_bytes)))?;
+ },
+ OpCode::IsWritable => {
+ let account_idx = self.stack.pop()?.0 as usize;
+ if account_idx >= self.accounts.accounts.len() {
+ return Err(VMError::InvalidAccount.into());
+ }
+ let account = &self.accounts.accounts[account_idx];
+ self.stack.push(Value(if account.is_writable() { 1 } else { 0 }))?;
+ },
+ OpCode::IsSigner => {
+ let account_idx = self.stack.pop()?.0 as usize;
+ if account_idx >= self.accounts.accounts.len() {
+ return Err(VMError::InvalidAccount.into());
+ }
+ let account = &self.accounts.accounts[account_idx];
+ self.stack.push(Value(if account.is_signer { 1 } else { 0 }))?;
+ },
+ // Control Flow
+ OpCode::Revert => {
+ let error_code = self.stack.pop()?.0;
+ return Err(ProgramError::Custom(error_code as u32));
+ },
+ // Graph operations
+ OpCode::GraphAddEdge => {
+ let weight = self.stack.pop()?.0;
+ let to = self.stack.pop()?.0;
+ let from = self.stack.pop()?.0;
+ let id = self.stack.pop()?.0 as usize;
+
+ if id >= self.data_structures.graphs.len() {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+
+ if let Some(graph) = &mut self.data_structures.graphs[id] {
+ graph.add_edge(from, to, weight)?;
+ } else {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+ },
+ OpCode::GraphGetNode => {
+ let node_id = self.stack.pop()?.0;
+ let id = self.stack.pop()?.0 as usize;
+
+ if id >= self.data_structures.graphs.len() {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+
+ if let Some(graph) = &self.data_structures.graphs[id] {
+ match graph.get_node_value(node_id) {
+ Some(value) => self.stack.push(Value(value))?,
+ None => self.stack.push(Value(0))?, // Return 0 if node not found
+ }
+ } else {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+ },
+ OpCode::GraphSetNode => {
+ let value = self.stack.pop()?.0;
+ let node_id = self.stack.pop()?.0;
+ let id = self.stack.pop()?.0 as usize;
+
+ if id >= self.data_structures.graphs.len() {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+
+ if let Some(graph) = &mut self.data_structures.graphs[id] {
+ graph.set_node_value(node_id, value)?;
+ } else {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+ },
+ // Graph operations
+ OpCode::GraphGetNeighbors => {
+ let node_id = self.stack.pop()?.0;
+ let id = self.stack.pop()?.0 as usize;
+
+ if id >= self.data_structures.graphs.len() {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+
+ if let Some(graph) = &self.data_structures.graphs[id] {
+ let neighbors = graph.get_neighbors(node_id);
+
+ // Store the number of neighbors on the stack
+ self.stack.push(Value(neighbors.len() as u64))?;
+
+ // Store each neighbor and its weight on the stack
+ for (neighbor, weight) in neighbors.iter().rev() {
+ self.stack.push(Value(*weight))?;
+ self.stack.push(Value(*neighbor))?;
+ }
+ } else {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+ },
+ OpCode::GraphBfs => {
+ let start_node = self.stack.pop()?.0;
+ let id = self.stack.pop()?.0 as usize;
+
+ if id >= self.data_structures.graphs.len() {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+
+ if let Some(graph) = &self.data_structures.graphs[id] {
+ let bfs_result = graph.bfs(start_node);
+
+ // Store the number of nodes in the BFS result on the stack
+ self.stack.push(Value(bfs_result.len() as u64))?;
+
+ // Store each node in the BFS result on the stack (in reverse order so they come out in the right order when popped)
+ for node in bfs_result.iter().rev() {
+ self.stack.push(Value(*node))?;
+ }
+ } else {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+ },
+ OpCode::GraphClear => {
+ let id = self.stack.pop()?.0 as usize;
+
+ if id >= self.data_structures.graphs.len() {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+
+ if let Some(graph) = &mut self.data_structures.graphs[id] {
+ graph.clear();
+ } else {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+ },
+ // OHLCV operations
+ OpCode::OhlcvAddBar => {
+ let volume = self.stack.pop()?.0;
+ let close = self.stack.pop()?.0;
+ let low = self.stack.pop()?.0;
+ let high = self.stack.pop()?.0;
+ let open = self.stack.pop()?.0;
+ let timestamp = self.stack.pop()?.0;
+ let id = self.stack.pop()?.0 as usize;
+
+ if id >= self.data_structures.ohlcvs.len() {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+
+ let entry = OHLCVEntry {
+ timestamp,
+ open,
+ high,
+ low,
+ close,
+ volume,
+ };
+
+ if let Some(ohlcv) = &mut self.data_structures.ohlcvs[id] {
+ ohlcv.add_entry(entry)?;
+ } else {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+ },
+ OpCode::OhlcvGetBar => {
+ let index = self.stack.pop()?.0 as usize;
+ let id = self.stack.pop()?.0 as usize;
+
+ if id >= self.data_structures.ohlcvs.len() {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+
+ if let Some(ohlcv) = &self.data_structures.ohlcvs[id] {
+ match ohlcv.get_entry(index) {
+ Some(entry) => {
+ // Push all values in reverse order so they come out in the right order when popped
+ self.stack.push(Value(entry.volume))?;
+ self.stack.push(Value(entry.close))?;
+ self.stack.push(Value(entry.low))?;
+ self.stack.push(Value(entry.high))?;
+ self.stack.push(Value(entry.open))?;
+ self.stack.push(Value(entry.timestamp))?;
+ },
+ None => {
+ // Push zeros if entry not found
+ for _ in 0..6 {
+ self.stack.push(Value(0))?;
+ }
+ }
+ }
+ } else {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+ },
+ OpCode::OhlcvSma => {
+ let period = self.stack.pop()?.0 as usize;
+ let id = self.stack.pop()?.0 as usize;
+
+ if id >= self.data_structures.ohlcvs.len() {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+
+ if let Some(ohlcv) = &self.data_structures.ohlcvs[id] {
+ let sma_result = ohlcv.calculate_sma(period);
+
+ // Push the number of SMA values
+ self.stack.push(Value(sma_result.len() as u64))?;
+
+ // Push each SMA value and timestamp in reverse order
+ for (timestamp, value) in sma_result.iter().rev() {
+ self.stack.push(Value(*value))?;
+ self.stack.push(Value(*timestamp))?;
+ }
+ } else {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+ },
+ // Hypergraph operations
+ OpCode::HyperAddNode => {
+ let value = self.stack.pop()?.0;
+ let node_id = self.stack.pop()?.0;
+ let id = self.stack.pop()?.0 as usize;
+
+ if id >= self.data_structures.hypergraphs.len() {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+
+ if let Some(hypergraph) = &mut self.data_structures.hypergraphs[id] {
+ hypergraph.add_node(node_id, value)?;
+ } else {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+ },
+ OpCode::HyperAddEdge => {
+ let weight = self.stack.pop()?.0;
+ let edge_id = self.stack.pop()?.0;
+ let id = self.stack.pop()?.0 as usize;
+
+ if id >= self.data_structures.hypergraphs.len() {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+
+ if let Some(hypergraph) = &mut self.data_structures.hypergraphs[id] {
+ hypergraph.create_hyperedge(edge_id, weight)?;
+ } else {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+ },
+ OpCode::HyperAddNodeToEdge => {
+ let node_id = self.stack.pop()?.0;
+ let edge_id = self.stack.pop()?.0;
+ let id = self.stack.pop()?.0 as usize;
+
+ if id >= self.data_structures.hypergraphs.len() {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+
+ if let Some(hypergraph) = &mut self.data_structures.hypergraphs[id] {
+ hypergraph.add_node_to_edge(edge_id, node_id)?;
+ } else {
+ return Err(VMError::InvalidDataStructureOperation.into());
+ }
+ },
_ => return Err(VMError::InvalidInstruction.into()),
}
}
diff --git a/lessvm-solana/src/vm/data_structures.rs b/lessvm-solana/src/vm/data_structures.rs
index 1a8f46b..6885a0c 100644
--- a/lessvm-solana/src/vm/data_structures.rs
+++ b/lessvm-solana/src/vm/data_structures.rs
@@ -14,6 +14,7 @@ use solana_program::msg;
// Constants for data structure IDs
pub const MAX_DATA_STRUCTURES: usize = 32;
+pub const MAX_GRAPH_NODES: usize = 1024; // Maximum number of nodes in a graph
/// The type of data structure
#[derive(Debug, Clone, Copy, PartialEq)]
diff --git a/lessvm-solana/src/vm/memory.rs b/lessvm-solana/src/vm/memory.rs
index 69c9653..6fa23da 100644
--- a/lessvm-solana/src/vm/memory.rs
+++ b/lessvm-solana/src/vm/memory.rs
@@ -26,8 +26,8 @@ impl Memory {
#[inline(always)]
pub fn ensure_capacity(&mut self, required_size: usize) {
if required_size > self.data.len() {
- // Double the capacity or use required size, whichever is larger
- let new_capacity = self.data.len().max(required_size).max(1024) * 2;
+ // Use a more efficient growth strategy: grow by 50% or to required size, whichever is larger
+ let new_capacity = (self.data.len() * 3 / 2).max(required_size);
self.data.resize(new_capacity, 0);
}
}
From b6b1b5e77619bb68f953ae1f3a8f06e2ccc9faa8 Mon Sep 17 00:00:00 2001
From: "agentfarmx[bot]" <198411105+agentfarmx[bot]@users.noreply.github.com>
Date: Wed, 12 Mar 2025 16:20:02 +0000
Subject: [PATCH 2/2] docs: add implementation details documentation and
comprehensive tests
The commit adds detailed documentation about LessVM's internals and
comprehensive test coverage for new features including SIMD vector
operations, data structure management, and optimized memory handling.
---
docs/implementation_details.md | 186 +++++++++
.../vm/tests/data_structure_store_tests.rs | 137 +++++++
lessvm-solana/src/vm/tests/memory_tests.rs | 135 +++++++
lessvm-solana/src/vm/tests/mod.rs | 14 +-
lessvm-solana/src/vm/tests/opcode_tests.rs | 379 ++++++++++++++++++
.../src/vm/tests/vector_add_tests.rs | 107 +++++
6 files changed, 957 insertions(+), 1 deletion(-)
create mode 100644 docs/implementation_details.md
create mode 100644 lessvm-solana/src/vm/tests/data_structure_store_tests.rs
create mode 100644 lessvm-solana/src/vm/tests/memory_tests.rs
create mode 100644 lessvm-solana/src/vm/tests/opcode_tests.rs
create mode 100644 lessvm-solana/src/vm/tests/vector_add_tests.rs
diff --git a/docs/implementation_details.md b/docs/implementation_details.md
new file mode 100644
index 0000000..a15545b
--- /dev/null
+++ b/docs/implementation_details.md
@@ -0,0 +1,186 @@
+# LessVM Implementation Details
+
+This document provides details about the implementation of LessVM, focusing on recent changes and optimizations.
+
+## Table of Contents
+
+- [SIMD Vector Addition](#simd-vector-addition)
+- [Data Structure Store](#data-structure-store)
+- [Memory Management](#memory-management)
+- [Opcode Implementations](#opcode-implementations)
+ - [Solana Account Operations](#solana-account-operations)
+ - [Graph Operations](#graph-operations)
+ - [OHLCV Operations](#ohlcv-operations)
+ - [Hypergraph Operations](#hypergraph-operations)
+
+## SIMD Vector Addition
+
+The `vector_add` function uses SIMD (Single Instruction, Multiple Data) instructions to efficiently add multiple values at once. The implementation loads two vectors from the stack and adds them together.
+
+```mermaid
+sequenceDiagram
+ participant VM
+ VM->>VM: _mm256_loadu_si256(stack[top] to values2)
+ VM->>VM: stack.pop() x 4
+ VM->>VM: _mm256_loadu_si256(stack[top] to values1)
+ VM->>VM: result = _mm256_add_epi64(values1, values2)
+ VM->>VM: stack.push(result) x 4
+```
+
+The function works as follows:
+1. Load the second vector (values2) from the top of the stack
+2. Pop the first 4 values from the stack
+3. Load the first vector (values1) from the new top of the stack
+4. Add the two vectors together using SIMD instructions
+5. Push the result back onto the stack
+
+This approach ensures that we're adding two different vectors together, rather than adding a vector to itself.
+
+## Data Structure Store
+
+The `DataStructureStore` manages various data structures used by the VM, including BTreeMaps, Tries, Graphs, OHLCV, and Hypergraphs.
+
+```mermaid
+classDiagram
+ class DataStructureStore {
+ btrees: Vec >
+ tries: Vec >
+ graphs: Vec >
+ ohlcvs: Vec >
+ hypergraphs: Vec >
+ +new() DataStructureStore
+ +ensure_capacity(ds_type: DataStructureType, id: usize) void
+ }
+ note for DataStructureStore "Stores different types of data structures."
+```
+
+The `ensure_capacity` method ensures that the vectors have enough capacity to store a data structure at a specific index. If the index is beyond the current length of the vector, the vector is resized to accommodate the new index.
+
+```mermaid
+sequenceDiagram
+ participant VM
+ participant DataStructureStore
+ VM->>DataStructureStore: ensure_capacity(DataStructureType::BTreeMap, 5)
+ alt Vector length < 6
+ DataStructureStore->>DataStructureStore: resize_with(6, || None)
+ else Vector length >= 6
+ DataStructureStore->>DataStructureStore: Do nothing
+ end
+```
+
+## Memory Management
+
+The memory management system has been optimized to use a more efficient growth strategy. Instead of doubling the capacity when more space is needed, the new implementation grows the memory by 50% or to the required size, whichever is larger.
+
+```mermaid
+sequenceDiagram
+ participant Memory
+ participant Vec
+ Memory->>Memory: ensure_capacity(required_size)
+ alt required_size > data.len()
+ Memory->>Memory: new_capacity = (data.len() * 3 / 2).max(required_size)
+ Memory->>Vec: resize(new_capacity, 0)
+ else required_size <= data.len()
+ Memory->>Memory: Do nothing
+ end
+```
+
+This approach reduces memory waste while still providing amortized constant-time operations.
+
+## Opcode Implementations
+
+### Solana Account Operations
+
+The following Solana account operations have been implemented:
+
+- `GetBalance`: Gets the balance (lamports) of an account
+- `GetOwner`: Gets the owner of an account
+- `IsWritable`: Checks if an account is writable
+- `IsSigner`: Checks if an account is a signer
+
+```mermaid
+sequenceDiagram
+ participant VM
+ participant Account
+ VM->>VM: Pop account_idx from stack
+ VM->>VM: Check if account_idx is valid
+ alt Valid account_idx
+ VM->>Account: Get account information
+ Account->>VM: Return account information
+ VM->>VM: Push result to stack
+ else Invalid account_idx
+ VM->>VM: Return InvalidAccount error
+ end
+```
+
+### Graph Operations
+
+The following graph operations have been implemented:
+
+- `GraphAddEdge`: Adds an edge to a graph
+- `GraphGetNode`: Gets the value of a node
+- `GraphSetNode`: Sets the value of a node
+- `GraphGetNeighbors`: Gets the neighbors of a node
+- `GraphBfs`: Performs a breadth-first search starting from a node
+- `GraphClear`: Clears a graph
+
+```mermaid
+sequenceDiagram
+ participant VM
+ participant DataStructureStore
+ participant GraphDS
+ VM->>VM: Pop weight, to, from, and id from stack
+ VM->>DataStructureStore: Access graph with id
+ alt Graph exists
+ DataStructureStore->>GraphDS: add_edge(from, to, weight)
+ GraphDS-->>DataStructureStore: Result
+ else Graph does not exist
+ DataStructureStore-->>VM: Error: InvalidDataStructureOperation
+ end
+```
+
+### OHLCV Operations
+
+The following OHLCV (Open-High-Low-Close-Volume) operations have been implemented:
+
+- `OhlcvAddBar`: Adds a bar to an OHLCV
+- `OhlcvGetBar`: Gets a bar from an OHLCV
+- `OhlcvSma`: Calculates the Simple Moving Average (SMA) of an OHLCV
+
+```mermaid
+sequenceDiagram
+ participant VM
+ participant DataStructureStore
+ participant OHLCVDS
+ VM->>VM: Pop timestamp, open, high, low, close, volume, and id from stack
+ VM->>DataStructureStore: Access ohlcv with id
+ alt OHLCV exists
+ DataStructureStore->>OHLCVDS: add_entry(timestamp, open, high, low, close, volume)
+ OHLCVDS-->>DataStructureStore: Result
+ else OHLCV does not exist
+ DataStructureStore-->>VM: Error: InvalidDataStructureOperation
+ end
+```
+
+### Hypergraph Operations
+
+The following hypergraph operations have been implemented:
+
+- `HyperAddNode`: Adds a node to a hypergraph
+- `HyperAddEdge`: Adds an edge to a hypergraph
+- `HyperAddNodeToEdge`: Adds a node to an edge in a hypergraph
+
+```mermaid
+sequenceDiagram
+ participant VM
+ participant DataStructureStore
+ participant HypergraphDS
+ VM->>VM: Pop node_id, edge_id, and id from stack
+ VM->>DataStructureStore: Access hypergraph with id
+ alt Hypergraph exists
+ DataStructureStore->>HypergraphDS: add_node_to_edge(edge_id, node_id)
+ HypergraphDS-->>DataStructureStore: Result
+ else Hypergraph does not exist
+ DataStructureStore-->>VM: Error: InvalidDataStructureOperation
+ end
+```
\ No newline at end of file
diff --git a/lessvm-solana/src/vm/tests/data_structure_store_tests.rs b/lessvm-solana/src/vm/tests/data_structure_store_tests.rs
new file mode 100644
index 0000000..fd5a024
--- /dev/null
+++ b/lessvm-solana/src/vm/tests/data_structure_store_tests.rs
@@ -0,0 +1,137 @@
+use super::super::*;
+use super::super::data_structures::*;
+use solana_program::clock::Epoch;
+use solana_program::pubkey::Pubkey;
+use solana_program::account_info::AccountInfo;
+
+fn create_test_account(lamports: u64) -> (Pubkey, Vec, AccountInfo<'static>) {
+ let key = Pubkey::new_unique();
+ let mut lamports = lamports;
+ let mut data = vec![0; 32];
+
+ AccountInfo::new(
+ &key,
+ true,
+ true,
+ &mut lamports,
+ &mut data,
+ &Pubkey::new_unique(),
+ false,
+ Epoch::default(),
+ )
+}
+
+#[test]
+fn test_data_structure_store_new() {
+ let program_id = Pubkey::new_unique();
+ let (_, _, account) = create_test_account(1000000);
+ let accounts = vec![account];
+
+ let vm = VM::new(&program_id, &accounts, &[]);
+
+ // Verify the data structures are initialized with the correct capacity
+ assert_eq!(vm.data_structures.btrees.capacity(), MAX_DATA_STRUCTURES);
+ assert_eq!(vm.data_structures.tries.capacity(), MAX_DATA_STRUCTURES);
+ assert_eq!(vm.data_structures.graphs.capacity(), MAX_DATA_STRUCTURES);
+ assert_eq!(vm.data_structures.ohlcvs.capacity(), MAX_DATA_STRUCTURES);
+ assert_eq!(vm.data_structures.hypergraphs.capacity(), MAX_DATA_STRUCTURES);
+
+ // Verify the data structures are initially empty
+ assert_eq!(vm.data_structures.btrees.len(), 0);
+ assert_eq!(vm.data_structures.tries.len(), 0);
+ assert_eq!(vm.data_structures.graphs.len(), 0);
+ assert_eq!(vm.data_structures.ohlcvs.len(), 0);
+ assert_eq!(vm.data_structures.hypergraphs.len(), 0);
+}
+
+#[test]
+fn test_ensure_capacity_btree() {
+ let program_id = Pubkey::new_unique();
+ let (_, _, account) = create_test_account(1000000);
+ let accounts = vec![account];
+
+ let mut vm = VM::new(&program_id, &accounts, &[]);
+
+ // Initially the btrees vector is empty
+ assert_eq!(vm.data_structures.btrees.len(), 0);
+
+ // Ensure capacity for index 5
+ vm.data_structures.ensure_capacity(DataStructureType::BTreeMap, 5);
+
+ // The vector should now have length 6 (indices 0-5)
+ assert_eq!(vm.data_structures.btrees.len(), 6);
+
+ // All elements should be None
+ for i in 0..6 {
+ assert!(vm.data_structures.btrees[i].is_none());
+ }
+
+ // Create a BTreeMap at index 3
+ vm.data_structures.btrees[3] = Some(BTreeMapDS::new());
+
+ // Ensure capacity for index 10
+ vm.data_structures.ensure_capacity(DataStructureType::BTreeMap, 10);
+
+ // The vector should now have length 11 (indices 0-10)
+ assert_eq!(vm.data_structures.btrees.len(), 11);
+
+ // The BTreeMap at index 3 should still exist
+ assert!(vm.data_structures.btrees[3].is_some());
+
+ // Other elements should be None
+ for i in 0..11 {
+ if i != 3 {
+ assert!(vm.data_structures.btrees[i].is_none());
+ }
+ }
+}
+
+#[test]
+fn test_ensure_capacity_all_types() {
+ let program_id = Pubkey::new_unique();
+ let (_, _, account) = create_test_account(1000000);
+ let accounts = vec![account];
+
+ let mut vm = VM::new(&program_id, &accounts, &[]);
+
+ // Test ensure_capacity for all data structure types
+ vm.data_structures.ensure_capacity(DataStructureType::BTreeMap, 3);
+ vm.data_structures.ensure_capacity(DataStructureType::Trie, 4);
+ vm.data_structures.ensure_capacity(DataStructureType::Graph, 5);
+ vm.data_structures.ensure_capacity(DataStructureType::OHLCV, 6);
+ vm.data_structures.ensure_capacity(DataStructureType::Hypergraph, 7);
+
+ // Verify the lengths
+ assert_eq!(vm.data_structures.btrees.len(), 4);
+ assert_eq!(vm.data_structures.tries.len(), 5);
+ assert_eq!(vm.data_structures.graphs.len(), 6);
+ assert_eq!(vm.data_structures.ohlcvs.len(), 7);
+ assert_eq!(vm.data_structures.hypergraphs.len(), 8);
+}
+
+#[test]
+fn test_data_structure_operations_with_ensure_capacity() {
+ let program_id = Pubkey::new_unique();
+ let (_, _, account) = create_test_account(1000000);
+ let accounts = vec![account];
+
+ let mut vm = VM::new(&program_id, &accounts, &[]);
+
+ // Test BTreeClear with ensure_capacity
+ vm.stack.push(Value(10)).unwrap(); // id = 10
+ let result = vm.execute(&[OpCode::BTreeClear as u8]);
+ assert!(result.is_ok());
+
+ // Verify that the BTreeMap was created at index 10
+ assert_eq!(vm.data_structures.btrees.len(), 11);
+ assert!(vm.data_structures.btrees[10].is_some());
+
+ // Test GraphCreate with ensure_capacity
+ vm.stack.push(Value(15)).unwrap(); // id = 15
+ let result = vm.execute(&[OpCode::GraphCreate as u8]);
+ assert!(result.is_ok());
+
+ // Verify that the Graph was created at index 15
+ assert_eq!(vm.data_structures.graphs.len(), 16);
+ assert!(vm.data_structures.graphs[15].is_some());
+}
\ No newline at end of file
diff --git a/lessvm-solana/src/vm/tests/memory_tests.rs b/lessvm-solana/src/vm/tests/memory_tests.rs
new file mode 100644
index 0000000..9e4f47a
--- /dev/null
+++ b/lessvm-solana/src/vm/tests/memory_tests.rs
@@ -0,0 +1,135 @@
+use super::super::*;
+use solana_program::clock::Epoch;
+use solana_program::pubkey::Pubkey;
+use solana_program::account_info::AccountInfo;
+
+fn create_test_account(lamports: u64) -> (Pubkey, Vec, AccountInfo<'static>) {
+ let key = Pubkey::new_unique();
+ let mut lamports = lamports;
+ let mut data = vec![0; 32];
+
+ AccountInfo::new(
+ &key,
+ true,
+ true,
+ &mut lamports,
+ &mut data,
+ &Pubkey::new_unique(),
+ false,
+ Epoch::default(),
+ )
+}
+
+#[test]
+fn test_memory_initial_capacity() {
+ // Test that memory is initialized with the correct capacity
+ let memory = Memory::new();
+ assert_eq!(memory.capacity(), 1024);
+ assert_eq!(memory.size(), 0);
+}
+
+#[test]
+fn test_memory_growth_strategy() {
+ // Test the memory growth strategy
+ let mut memory = Memory::new();
+
+ // Initial capacity is 1024 bytes
+ assert_eq!(memory.capacity(), 1024);
+
+ // Store data that fits within the initial capacity
+ let data = vec![1; 1000];
+ memory.store(0, &data).unwrap();
+ assert_eq!(memory.capacity(), 1024);
+
+ // Store data that exceeds the initial capacity
+ let data = vec![2; 1024];
+ memory.store(1000, &data).unwrap();
+
+ // Capacity should grow by 50% (1024 * 1.5 = 1536) or to the required size (1000 + 1024 = 2024),
+ // whichever is larger
+ assert_eq!(memory.capacity(), 2024);
+
+ // Store more data to trigger another growth
+ let data = vec![3; 1000];
+ memory.store(2024, &data).unwrap();
+
+ // New capacity should be 2024 * 1.5 = 3036
+ assert_eq!(memory.capacity(), 3036);
+}
+
+#[test]
+fn test_memory_operations_with_growth() {
+ let mut memory = Memory::new();
+
+ // Fill memory with a pattern
+ for i in 0..1024 {
+ memory.store8(i, (i % 256) as u8).unwrap();
+ }
+
+ // Verify the pattern
+ for i in 0..1024 {
+ assert_eq!(memory.load8(i).unwrap(), (i % 256) as u8);
+ }
+
+ // Store beyond the initial capacity
+ let large_data = vec![0xAA; 2000];
+ memory.store(1024, &large_data).unwrap();
+
+ // Verify the original data is intact
+ for i in 0..1024 {
+ assert_eq!(memory.load8(i).unwrap(), (i % 256) as u8);
+ }
+
+ // Verify the new data
+ for i in 0..2000 {
+ assert_eq!(memory.load8(1024 + i).unwrap(), 0xAA);
+ }
+
+ // Test copy operation across the growth boundary
+ memory.copy(3000, 500, 100).unwrap();
+
+ // Verify the copied data
+ for i in 0..100 {
+ assert_eq!(memory.load8(3000 + i).unwrap(), ((500 + i) % 256) as u8);
+ }
+}
+
+#[test]
+fn test_memory_with_vm_operations() {
+ let program_id = Pubkey::new_unique();
+ let (_, _, account) = create_test_account(1000000);
+ let accounts = vec![account];
+
+ let mut vm = VM::new(&program_id, &accounts, &[]);
+
+ // Create a program that stores data beyond the initial memory capacity
+ // push8 , push8 , store, halt
+ let mut bytecode = vec![];
+
+ // Store a value at offset 2000 (beyond initial capacity)
+ bytecode.extend_from_slice(&[
+ OpCode::Push8 as u8,
+ ]);
+ bytecode.extend_from_slice(&0x1234567890ABCDEFu64.to_le_bytes());
+ bytecode.extend_from_slice(&[
+ OpCode::Push8 as u8,
+ ]);
+ bytecode.extend_from_slice(&2000u64.to_le_bytes());
+ bytecode.extend_from_slice(&[
+ OpCode::Store as u8,
+ OpCode::Halt as u8,
+ ]);
+
+ // Execute the program
+ let result = vm.execute(&bytecode);
+ assert!(result.is_ok());
+
+ // Verify the memory capacity has grown
+ assert!(vm.memory.capacity() > 1024);
+
+ // Verify the stored value
+ vm.stack.push(Value(2000)).unwrap();
+ let result = vm.execute(&[OpCode::Load as u8]);
+ assert!(result.is_ok());
+ assert_eq!(vm.stack.pop().unwrap().0, 0x1234567890ABCDEF);
+}
\ No newline at end of file
diff --git a/lessvm-solana/src/vm/tests/mod.rs b/lessvm-solana/src/vm/tests/mod.rs
index 51c9b1f..bc2ab29 100644
--- a/lessvm-solana/src/vm/tests/mod.rs
+++ b/lessvm-solana/src/vm/tests/mod.rs
@@ -1,2 +1,14 @@
#[cfg(test)]
-pub mod data_structures_tests;
\ No newline at end of file
+pub mod data_structures_tests;
+
+#[cfg(test)]
+pub mod vector_add_tests;
+
+#[cfg(test)]
+pub mod data_structure_store_tests;
+
+#[cfg(test)]
+pub mod opcode_tests;
+
+#[cfg(test)]
+pub mod memory_tests;
\ No newline at end of file
diff --git a/lessvm-solana/src/vm/tests/opcode_tests.rs b/lessvm-solana/src/vm/tests/opcode_tests.rs
new file mode 100644
index 0000000..e23bbd5
--- /dev/null
+++ b/lessvm-solana/src/vm/tests/opcode_tests.rs
@@ -0,0 +1,379 @@
+use super::super::*;
+use super::super::data_structures::*;
+use solana_program::clock::Epoch;
+use solana_program::pubkey::Pubkey;
+use solana_program::account_info::AccountInfo;
+
+fn create_test_account(lamports: u64) -> (Pubkey, Vec, AccountInfo<'static>) {
+ let key = Pubkey::new_unique();
+ let mut lamports = lamports;
+ let mut data = vec![0; 32];
+
+ AccountInfo::new(
+ &key,
+ true,
+ true,
+ &mut lamports,
+ &mut data,
+ &Pubkey::new_unique(),
+ false,
+ Epoch::default(),
+ )
+}
+
+// Test for Solana account operations
+#[test]
+fn test_solana_account_operations() {
+ let program_id = Pubkey::new_unique();
+ let (_, _, account1) = create_test_account(1000000);
+ let (_, _, account2) = create_test_account(500000);
+ let accounts = vec![account1, account2];
+
+ let mut vm = VM::new(&program_id, &accounts, &[]);
+
+ // Test GetBalance
+ vm.stack.push(Value(0)).unwrap(); // account index 0
+ let result = vm.execute(&[OpCode::GetBalance as u8]);
+ assert!(result.is_ok());
+ assert_eq!(vm.stack.pop().unwrap().0, 1000000);
+
+ // Test IsWritable
+ vm.stack.push(Value(1)).unwrap(); // account index 1
+ let result = vm.execute(&[OpCode::IsWritable as u8]);
+ assert!(result.is_ok());
+ assert_eq!(vm.stack.pop().unwrap().0, 1); // account is writable
+
+ // Test IsSigner
+ vm.stack.push(Value(0)).unwrap(); // account index 0
+ let result = vm.execute(&[OpCode::IsSigner as u8]);
+ assert!(result.is_ok());
+ assert_eq!(vm.stack.pop().unwrap().0, 1); // account is signer
+}
+
+// Test for Graph operations
+#[test]
+fn test_graph_operations() {
+ let program_id = Pubkey::new_unique();
+ let (_, _, account) = create_test_account(1000000);
+ let accounts = vec![account];
+
+ let mut vm = VM::new(&program_id, &accounts, &[]);
+
+ // Create a graph
+ vm.stack.push(Value(0)).unwrap(); // graph id 0
+ let result = vm.execute(&[OpCode::GraphCreate as u8]);
+ assert!(result.is_ok());
+
+ // Add nodes
+ vm.stack.push(Value(0)).unwrap(); // graph id
+ vm.stack.push(Value(1)).unwrap(); // node id
+ vm.stack.push(Value(100)).unwrap(); // node value
+ let result = vm.execute(&[OpCode::GraphAddNode as u8]);
+ assert!(result.is_ok());
+
+ vm.stack.push(Value(0)).unwrap(); // graph id
+ vm.stack.push(Value(2)).unwrap(); // node id
+ vm.stack.push(Value(200)).unwrap(); // node value
+ let result = vm.execute(&[OpCode::GraphAddNode as u8]);
+ assert!(result.is_ok());
+
+ vm.stack.push(Value(0)).unwrap(); // graph id
+ vm.stack.push(Value(3)).unwrap(); // node id
+ vm.stack.push(Value(300)).unwrap(); // node value
+ let result = vm.execute(&[OpCode::GraphAddNode as u8]);
+ assert!(result.is_ok());
+
+ // Add edges
+ vm.stack.push(Value(0)).unwrap(); // graph id
+ vm.stack.push(Value(1)).unwrap(); // from node
+ vm.stack.push(Value(2)).unwrap(); // to node
+ vm.stack.push(Value(10)).unwrap(); // weight
+ let result = vm.execute(&[OpCode::GraphAddEdge as u8]);
+ assert!(result.is_ok());
+
+ vm.stack.push(Value(0)).unwrap(); // graph id
+ vm.stack.push(Value(1)).unwrap(); // from node
+ vm.stack.push(Value(3)).unwrap(); // to node
+ vm.stack.push(Value(20)).unwrap(); // weight
+ let result = vm.execute(&[OpCode::GraphAddEdge as u8]);
+ assert!(result.is_ok());
+
+ // Get node value
+ vm.stack.push(Value(0)).unwrap(); // graph id
+ vm.stack.push(Value(2)).unwrap(); // node id
+ let result = vm.execute(&[OpCode::GraphGetNode as u8]);
+ assert!(result.is_ok());
+ assert_eq!(vm.stack.pop().unwrap().0, 200);
+
+ // Set node value
+ vm.stack.push(Value(0)).unwrap(); // graph id
+ vm.stack.push(Value(2)).unwrap(); // node id
+ vm.stack.push(Value(250)).unwrap(); // new value
+ let result = vm.execute(&[OpCode::GraphSetNode as u8]);
+ assert!(result.is_ok());
+
+ // Verify the new value
+ vm.stack.push(Value(0)).unwrap(); // graph id
+ vm.stack.push(Value(2)).unwrap(); // node id
+ let result = vm.execute(&[OpCode::GraphGetNode as u8]);
+ assert!(result.is_ok());
+ assert_eq!(vm.stack.pop().unwrap().0, 250);
+
+ // Get neighbors
+ vm.stack.push(Value(0)).unwrap(); // graph id
+ vm.stack.push(Value(1)).unwrap(); // node id
+ let result = vm.execute(&[OpCode::GraphGetNeighbors as u8]);
+ assert!(result.is_ok());
+
+ // Stack should have: [count, neighbor1, weight1, neighbor2, weight2]
+ let count = vm.stack.pop().unwrap().0;
+ assert_eq!(count, 2);
+
+ // Pop in reverse order of how they were pushed
+ let neighbor2 = vm.stack.pop().unwrap().0;
+ let weight2 = vm.stack.pop().unwrap().0;
+ let neighbor1 = vm.stack.pop().unwrap().0;
+ let weight1 = vm.stack.pop().unwrap().0;
+
+ // Check the neighbors (order may vary)
+ if neighbor1 == 2 {
+ assert_eq!(weight1, 10);
+ assert_eq!(neighbor2, 3);
+ assert_eq!(weight2, 20);
+ } else {
+ assert_eq!(neighbor1, 3);
+ assert_eq!(weight1, 20);
+ assert_eq!(neighbor2, 2);
+ assert_eq!(weight2, 10);
+ }
+
+ // Test BFS
+ vm.stack.push(Value(0)).unwrap(); // graph id
+ vm.stack.push(Value(1)).unwrap(); // start node
+ let result = vm.execute(&[OpCode::GraphBfs as u8]);
+ assert!(result.is_ok());
+
+ // Stack should have: [count, node1, node2, node3]
+ let count = vm.stack.pop().unwrap().0;
+ assert_eq!(count, 3);
+
+ // Pop nodes in BFS order
+ let node3 = vm.stack.pop().unwrap().0;
+ let node2 = vm.stack.pop().unwrap().0;
+ let node1 = vm.stack.pop().unwrap().0;
+
+ assert_eq!(node1, 1); // Start node
+ // node2 and node3 could be in either order depending on implementation
+ assert!(
+ (node2 == 2 && node3 == 3) ||
+ (node2 == 3 && node3 == 2)
+ );
+
+ // Test GraphClear
+ vm.stack.push(Value(0)).unwrap(); // graph id
+ let result = vm.execute(&[OpCode::GraphClear as u8]);
+ assert!(result.is_ok());
+
+ // Verify the graph is empty
+ vm.stack.push(Value(0)).unwrap(); // graph id
+ vm.stack.push(Value(1)).unwrap(); // node id
+ let result = vm.execute(&[OpCode::GraphGetNode as u8]);
+ assert!(result.is_ok());
+ assert_eq!(vm.stack.pop().unwrap().0, 0); // Should return 0 for non-existent node
+}
+
+// Test for OHLCV operations
+#[test]
+fn test_ohlcv_operations() {
+ let program_id = Pubkey::new_unique();
+ let (_, _, account) = create_test_account(1000000);
+ let accounts = vec![account];
+
+ let mut vm = VM::new(&program_id, &accounts, &[]);
+
+ // Create an OHLCV
+ vm.stack.push(Value(0)).unwrap(); // ohlcv id 0
+ let result = vm.execute(&[OpCode::OhlcvCreate as u8]);
+ assert!(result.is_ok());
+
+ // Add bars
+ // Bar 1: timestamp=1000, open=100, high=110, low=90, close=105, volume=1000
+ vm.stack.push(Value(0)).unwrap(); // ohlcv id
+ vm.stack.push(Value(1000)).unwrap(); // timestamp
+ vm.stack.push(Value(100)).unwrap(); // open
+ vm.stack.push(Value(110)).unwrap(); // high
+ vm.stack.push(Value(90)).unwrap(); // low
+ vm.stack.push(Value(105)).unwrap(); // close
+ vm.stack.push(Value(1000)).unwrap(); // volume
+ let result = vm.execute(&[OpCode::OhlcvAddBar as u8]);
+ assert!(result.is_ok());
+
+ // Bar 2: timestamp=2000, open=105, high=120, low=100, close=115, volume=1500
+ vm.stack.push(Value(0)).unwrap(); // ohlcv id
+ vm.stack.push(Value(2000)).unwrap(); // timestamp
+ vm.stack.push(Value(105)).unwrap(); // open
+ vm.stack.push(Value(120)).unwrap(); // high
+ vm.stack.push(Value(100)).unwrap(); // low
+ vm.stack.push(Value(115)).unwrap(); // close
+ vm.stack.push(Value(1500)).unwrap(); // volume
+ let result = vm.execute(&[OpCode::OhlcvAddBar as u8]);
+ assert!(result.is_ok());
+
+ // Bar 3: timestamp=3000, open=115, high=130, low=110, close=125, volume=2000
+ vm.stack.push(Value(0)).unwrap(); // ohlcv id
+ vm.stack.push(Value(3000)).unwrap(); // timestamp
+ vm.stack.push(Value(115)).unwrap(); // open
+ vm.stack.push(Value(130)).unwrap(); // high
+ vm.stack.push(Value(110)).unwrap(); // low
+ vm.stack.push(Value(125)).unwrap(); // close
+ vm.stack.push(Value(2000)).unwrap(); // volume
+ let result = vm.execute(&[OpCode::OhlcvAddBar as u8]);
+ assert!(result.is_ok());
+
+ // Get bar
+ vm.stack.push(Value(0)).unwrap(); // ohlcv id
+ vm.stack.push(Value(1)).unwrap(); // index
+ let result = vm.execute(&[OpCode::OhlcvGetBar as u8]);
+ assert!(result.is_ok());
+
+ // Stack should have: [timestamp, open, high, low, close, volume]
+ let volume = vm.stack.pop().unwrap().0;
+ let close = vm.stack.pop().unwrap().0;
+ let low = vm.stack.pop().unwrap().0;
+ let high = vm.stack.pop().unwrap().0;
+ let open = vm.stack.pop().unwrap().0;
+ let timestamp = vm.stack.pop().unwrap().0;
+
+ assert_eq!(timestamp, 2000);
+ assert_eq!(open, 105);
+ assert_eq!(high, 120);
+ assert_eq!(low, 100);
+ assert_eq!(close, 115);
+ assert_eq!(volume, 1500);
+
+ // Calculate SMA with period 2
+ vm.stack.push(Value(0)).unwrap(); // ohlcv id
+ vm.stack.push(Value(2)).unwrap(); // period
+ let result = vm.execute(&[OpCode::OhlcvSma as u8]);
+ assert!(result.is_ok());
+
+ // Stack should have: [count, timestamp1, value1, timestamp2, value2]
+ let count = vm.stack.pop().unwrap().0;
+ assert_eq!(count, 2);
+
+ // Pop SMA values
+ let timestamp2 = vm.stack.pop().unwrap().0;
+ let value2 = vm.stack.pop().unwrap().0;
+ let timestamp1 = vm.stack.pop().unwrap().0;
+ let value1 = vm.stack.pop().unwrap().0;
+
+ assert_eq!(timestamp1, 2000);
+ assert_eq!(value1, (105 + 115) / 2); // (bar1.close + bar2.close) / 2
+ assert_eq!(timestamp2, 3000);
+ assert_eq!(value2, (115 + 125) / 2); // (bar2.close + bar3.close) / 2
+}
+
+// Test for Hypergraph operations
+#[test]
+fn test_hypergraph_operations() {
+ let program_id = Pubkey::new_unique();
+ let (_, _, account) = create_test_account(1000000);
+ let accounts = vec![account];
+
+ let mut vm = VM::new(&program_id, &accounts, &[]);
+
+ // Create a hypergraph
+ vm.stack.push(Value(0)).unwrap(); // hypergraph id 0
+ let result = vm.execute(&[OpCode::HyperCreate as u8]);
+ assert!(result.is_ok());
+
+ // Add nodes
+ vm.stack.push(Value(0)).unwrap(); // hypergraph id
+ vm.stack.push(Value(1)).unwrap(); // node id
+ vm.stack.push(Value(100)).unwrap(); // node value
+ let result = vm.execute(&[OpCode::HyperAddNode as u8]);
+ assert!(result.is_ok());
+
+ vm.stack.push(Value(0)).unwrap(); // hypergraph id
+ vm.stack.push(Value(2)).unwrap(); // node id
+ vm.stack.push(Value(200)).unwrap(); // node value
+ let result = vm.execute(&[OpCode::HyperAddNode as u8]);
+ assert!(result.is_ok());
+
+ vm.stack.push(Value(0)).unwrap(); // hypergraph id
+ vm.stack.push(Value(3)).unwrap(); // node id
+ vm.stack.push(Value(300)).unwrap(); // node value
+ let result = vm.execute(&[OpCode::HyperAddNode as u8]);
+ assert!(result.is_ok());
+
+ // Create hyperedges
+ vm.stack.push(Value(0)).unwrap(); // hypergraph id
+ vm.stack.push(Value(101)).unwrap(); // edge id
+ vm.stack.push(Value(10)).unwrap(); // weight
+ let result = vm.execute(&[OpCode::HyperAddEdge as u8]);
+ assert!(result.is_ok());
+
+ vm.stack.push(Value(0)).unwrap(); // hypergraph id
+ vm.stack.push(Value(102)).unwrap(); // edge id
+ vm.stack.push(Value(20)).unwrap(); // weight
+ let result = vm.execute(&[OpCode::HyperAddEdge as u8]);
+ assert!(result.is_ok());
+
+ // Add nodes to edges
+ vm.stack.push(Value(0)).unwrap(); // hypergraph id
+ vm.stack.push(Value(101)).unwrap(); // edge id
+ vm.stack.push(Value(1)).unwrap(); // node id
+ let result = vm.execute(&[OpCode::HyperAddNodeToEdge as u8]);
+ assert!(result.is_ok());
+
+ vm.stack.push(Value(0)).unwrap(); // hypergraph id
+ vm.stack.push(Value(101)).unwrap(); // edge id
+ vm.stack.push(Value(2)).unwrap(); // node id
+ let result = vm.execute(&[OpCode::HyperAddNodeToEdge as u8]);
+ assert!(result.is_ok());
+
+ vm.stack.push(Value(0)).unwrap(); // hypergraph id
+ vm.stack.push(Value(102)).unwrap(); // edge id
+ vm.stack.push(Value(2)).unwrap(); // node id
+ let result = vm.execute(&[OpCode::HyperAddNodeToEdge as u8]);
+ assert!(result.is_ok());
+
+ vm.stack.push(Value(0)).unwrap(); // hypergraph id
+ vm.stack.push(Value(102)).unwrap(); // edge id
+ vm.stack.push(Value(3)).unwrap(); // node id
+ let result = vm.execute(&[OpCode::HyperAddNodeToEdge as u8]);
+ assert!(result.is_ok());
+
+ // Test auto-creation of nodes and edges
+ vm.stack.push(Value(0)).unwrap(); // hypergraph id
+ vm.stack.push(Value(103)).unwrap(); // new edge id
+ vm.stack.push(Value(4)).unwrap(); // new node id
+ let result = vm.execute(&[OpCode::HyperAddNodeToEdge as u8]);
+ assert!(result.is_ok());
+}
+
+// Test for Revert opcode
+#[test]
+fn test_revert_opcode() {
+ let program_id = Pubkey::new_unique();
+ let (_, _, account) = create_test_account(1000000);
+ let accounts = vec![account];
+
+ let mut vm = VM::new(&program_id, &accounts, &[]);
+
+ // Push error code 42
+ vm.stack.push(Value(42)).unwrap();
+
+ // Execute Revert
+ let result = vm.execute(&[OpCode::Revert as u8]);
+
+ // Should return a ProgramError::Custom with code 42
+ assert!(result.is_err());
+ if let Err(err) = result {
+ if let solana_program::program_error::ProgramError::Custom(code) = err {
+ assert_eq!(code, 42);
+ } else {
+ panic!("Expected ProgramError::Custom, got {:?}", err);
+ }
+ }
+}
\ No newline at end of file
diff --git a/lessvm-solana/src/vm/tests/vector_add_tests.rs b/lessvm-solana/src/vm/tests/vector_add_tests.rs
new file mode 100644
index 0000000..0e58608
--- /dev/null
+++ b/lessvm-solana/src/vm/tests/vector_add_tests.rs
@@ -0,0 +1,107 @@
+use super::super::*;
+use solana_program::clock::Epoch;
+use solana_program::pubkey::Pubkey;
+use solana_program::account_info::AccountInfo;
+
+#[cfg(target_arch = "x86_64")]
+use std::arch::x86_64::*;
+
+fn create_test_account(lamports: u64) -> (Pubkey, Vec, AccountInfo<'static>) {
+ let key = Pubkey::new_unique();
+ let mut lamports = lamports;
+ let mut data = vec![0; 32];
+
+ AccountInfo::new(
+ &key,
+ true,
+ true,
+ &mut lamports,
+ &mut data,
+ &Pubkey::new_unique(),
+ false,
+ Epoch::default(),
+ )
+}
+
+#[test]
+#[cfg(target_arch = "x86_64")]
+fn test_vector_add() {
+ let program_id = Pubkey::new_unique();
+ let (_, _, account) = create_test_account(1000000);
+ let accounts = vec![account];
+
+ let mut vm = VM::new(&program_id, &accounts, &[]);
+
+ // Push 8 values to the stack: [1, 2, 3, 4, 5, 6, 7, 8]
+ for i in 1..=8 {
+ vm.stack.push(Value(i)).unwrap();
+ }
+
+ // Execute vector_add operation
+ unsafe {
+ vm.vector_add().unwrap();
+ }
+
+ // The stack should now have 4 values: [6, 8, 10, 12]
+ // These are the sums of [1+5, 2+6, 3+7, 4+8]
+ assert_eq!(vm.stack.depth(), 4);
+ assert_eq!(vm.stack.pop().unwrap().0, 12);
+ assert_eq!(vm.stack.pop().unwrap().0, 10);
+ assert_eq!(vm.stack.pop().unwrap().0, 8);
+ assert_eq!(vm.stack.pop().unwrap().0, 6);
+}
+
+#[test]
+#[cfg(target_arch = "x86_64")]
+fn test_vector_add_with_different_values() {
+ let program_id = Pubkey::new_unique();
+ let (_, _, account) = create_test_account(1000000);
+ let accounts = vec![account];
+
+ let mut vm = VM::new(&program_id, &accounts, &[]);
+
+ // Push 8 values to the stack with different patterns
+ vm.stack.push(Value(10)).unwrap();
+ vm.stack.push(Value(20)).unwrap();
+ vm.stack.push(Value(30)).unwrap();
+ vm.stack.push(Value(40)).unwrap();
+ vm.stack.push(Value(5)).unwrap();
+ vm.stack.push(Value(15)).unwrap();
+ vm.stack.push(Value(25)).unwrap();
+ vm.stack.push(Value(35)).unwrap();
+
+ // Execute vector_add operation
+ unsafe {
+ vm.vector_add().unwrap();
+ }
+
+ // The stack should now have 4 values: [15, 35, 55, 75]
+ // These are the sums of [10+5, 20+15, 30+25, 40+35]
+ assert_eq!(vm.stack.depth(), 4);
+ assert_eq!(vm.stack.pop().unwrap().0, 75);
+ assert_eq!(vm.stack.pop().unwrap().0, 55);
+ assert_eq!(vm.stack.pop().unwrap().0, 35);
+ assert_eq!(vm.stack.pop().unwrap().0, 15);
+}
+
+#[test]
+#[cfg(target_arch = "x86_64")]
+fn test_vector_add_stack_underflow() {
+ let program_id = Pubkey::new_unique();
+ let (_, _, account) = create_test_account(1000000);
+ let accounts = vec![account];
+
+ let mut vm = VM::new(&program_id, &accounts, &[]);
+
+ // Push only 7 values to the stack (not enough for vector_add)
+ for i in 1..=7 {
+ vm.stack.push(Value(i)).unwrap();
+ }
+
+ // Execute vector_add operation - should fail with StackUnderflow
+ unsafe {
+ let result = vm.vector_add();
+ assert!(result.is_err());
+ assert_eq!(result.unwrap_err(), VMError::StackUnderflow);
+ }
+}
\ No newline at end of file