truthlinked_sdk/backend.rs
1//! Storage backend abstraction for contract state persistence.
2//!
3//! This module provides a trait-based abstraction over storage operations,
4//! enabling both production (on-chain) and testing (in-memory) backends.
5//!
6//! # Storage Backends
7//!
8//! - **`HostStorage`**: Production backend that delegates to WASM host imports.
9//! Used when contracts run on-chain.
10//! - **`MemoryStorage`**: In-memory backend using `BTreeMap` for testing.
11//! Enables fast, isolated unit tests without WASM runtime.
12//!
13//! # Example
14//!
15//! ```ignore
16//! use truthlinked_sdk::backend::{StorageBackend, MemoryStorage};
17//!
18//! // Testing with in-memory storage
19//! let mut storage = MemoryStorage::new();
20//! let slot = [1u8; 32];
21//! storage.write_32(slot, [42u8; 32])?;
22//! let value = storage.read_32(&slot)?;
23//! assert_eq!(value[0], 42);
24//! ```
25
26extern crate alloc;
27
28use alloc::collections::BTreeMap;
29
30use crate::env;
31use crate::error::Result;
32
33/// Trait for storage backends that support 32-byte slot read/write operations.
34///
35/// All storage in TruthLinked contracts operates on fixed 32-byte slots.
36/// This trait abstracts the underlying storage mechanism, allowing contracts
37/// to work with both on-chain storage (via WASM host) and in-memory storage (for testing).
38pub trait StorageBackend {
39 /// Reads a 32-byte value from the specified storage slot.
40 ///
41 /// # Arguments
42 ///
43 /// * `key` - The 32-byte storage slot address
44 ///
45 /// # Returns
46 ///
47 /// The 32-byte value stored at the slot, or `[0u8; 32]` if uninitialized.
48 fn read_32(&self, key: &[u8; 32]) -> Result<[u8; 32]>;
49
50 /// Writes a 32-byte value to the specified storage slot.
51 ///
52 /// # Arguments
53 ///
54 /// * `key` - The 32-byte storage slot address
55 /// * `value` - The 32-byte value to store
56 fn write_32(&mut self, key: [u8; 32], value: [u8; 32]) -> Result<()>;
57}
58
59/// Storage backend that delegates to WASM host imports.
60///
61/// This is a zero-sized type that forwards all storage operations to the
62/// TruthLinked runtime via host function calls. Used when contracts execute on-chain.
63///
64/// # Example
65///
66/// ```ignore
67/// use truthlinked_sdk::backend::{HostStorage, StorageBackend};
68///
69/// let storage = HostStorage;
70/// let value = storage.read_32(&slot)?;
71/// ```
72#[derive(Clone, Copy, Debug, Default)]
73pub struct HostStorage;
74
75impl StorageBackend for HostStorage {
76 fn read_32(&self, key: &[u8; 32]) -> Result<[u8; 32]> {
77 env::storage_read_32(key)
78 }
79
80 fn write_32(&mut self, key: [u8; 32], value: [u8; 32]) -> Result<()> {
81 env::storage_write_32(&key, &value)
82 }
83}
84
85/// In-memory storage backend for testing and development.
86///
87/// Uses a `BTreeMap` to store slot values in memory. This enables fast,
88/// isolated unit tests without requiring a WASM runtime or blockchain.
89///
90/// # Example
91///
92/// ```ignore
93/// use truthlinked_sdk::backend::MemoryStorage;
94///
95/// let mut storage = MemoryStorage::new()
96/// .with_slot([1u8; 32], [42u8; 32])
97/// .with_slot([2u8; 32], [99u8; 32]);
98///
99/// let snapshot = storage.snapshot(); // Clone current state
100/// ```
101#[derive(Clone, Debug, Default)]
102pub struct MemoryStorage {
103 slots: BTreeMap<[u8; 32], [u8; 32]>,
104}
105
106impl MemoryStorage {
107 /// Creates a new empty in-memory storage backend.
108 pub fn new() -> Self {
109 Self {
110 slots: BTreeMap::new(),
111 }
112 }
113
114 /// Builder method to pre-populate a storage slot.
115 ///
116 /// Useful for setting up test fixtures with initial state.
117 ///
118 /// # Arguments
119 ///
120 /// * `key` - Storage slot address
121 /// * `value` - Initial value for the slot
122 ///
123 /// # Example
124 ///
125 /// ```ignore
126 /// let storage = MemoryStorage::new()
127 /// .with_slot([1u8; 32], [42u8; 32]);
128 /// ```
129 pub fn with_slot(mut self, key: [u8; 32], value: [u8; 32]) -> Self {
130 self.slots.insert(key, value);
131 self
132 }
133
134 /// Creates a snapshot of the current storage state.
135 ///
136 /// Returns a clone of the internal slot map. Useful for:
137 /// - Saving state before mutations
138 /// - Comparing state changes in tests
139 /// - Debugging storage operations
140 ///
141 /// # Returns
142 ///
143 /// A `BTreeMap` containing all current slot values.
144 pub fn snapshot(&self) -> BTreeMap<[u8; 32], [u8; 32]> {
145 self.slots.clone()
146 }
147}
148
149impl StorageBackend for MemoryStorage {
150 fn read_32(&self, key: &[u8; 32]) -> Result<[u8; 32]> {
151 Ok(self.slots.get(key).copied().unwrap_or([0u8; 32]))
152 }
153
154 fn write_32(&mut self, key: [u8; 32], value: [u8; 32]) -> Result<()> {
155 self.slots.insert(key, value);
156 Ok(())
157 }
158}