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/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);
}
}
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