sled_ledger.rs
1 use crate::balance::Balance; 2 use crate::error::TokenError; 3 use crate::ledger::{Address, Ledger}; 4 use sled::{Db, Transactional}; 5 6 /// Persistent ledger implementation using Sled 7 pub struct SledLedger { 8 #[allow(dead_code)] 9 db: Db, 10 balances: sled::Tree, 11 locked: sled::Tree, 12 } 13 14 impl SledLedger { 15 /// Open or create a SledLedger at the specified path 16 pub fn open<P: AsRef<std::path::Path>>(path: P) -> Result<Self, TokenError> { 17 let db = sled::open(path).map_err(|e| TokenError::Storage(e.to_string()))?; 18 let balances = db.open_tree("balances").map_err(|e| TokenError::Storage(e.to_string()))?; 19 let locked = db.open_tree("locked").map_err(|e| TokenError::Storage(e.to_string()))?; 20 21 Ok(Self { 22 db, 23 balances, 24 locked, 25 }) 26 } 27 28 /// Helper to get balance from a tree 29 fn get_balance_from_tree(&self, tree: &sled::Tree, address: &Address) -> Result<Balance, TokenError> { 30 match tree.get(address.as_bytes()).map_err(|e| TokenError::Storage(e.to_string()))? { 31 Some(bytes) => serde_json::from_slice(&bytes) 32 .map_err(|e| TokenError::Storage(format!("Serialization error: {}", e))), 33 None => Ok(Balance::ZERO), 34 } 35 } 36 37 /// Helper to set balance in a tree 38 fn set_balance_in_tree(&self, tree: &sled::Tree, address: &Address, balance: Balance) -> Result<(), TokenError> { 39 if balance.is_zero() { 40 tree.remove(address.as_bytes()).map_err(|e| TokenError::Storage(e.to_string()))?; 41 } else { 42 let bytes = serde_json::to_vec(&balance) 43 .map_err(|e| TokenError::Storage(format!("Serialization error: {}", e)))?; 44 tree.insert(address.as_bytes(), bytes).map_err(|e| TokenError::Storage(e.to_string()))?; 45 } 46 Ok(()) 47 } 48 } 49 50 impl Ledger for SledLedger { 51 fn balance(&self, address: &Address) -> Balance { 52 self.get_balance_from_tree(&self.balances, address).unwrap_or(Balance::ZERO) 53 } 54 55 fn transfer( 56 &self, 57 from: &Address, 58 to: &Address, 59 amount: Balance, 60 ) -> Result<(), TokenError> { 61 if amount.is_zero() { 62 return Ok(()); 63 } 64 65 let from_bytes = from.as_bytes(); 66 let to_bytes = to.as_bytes(); 67 68 // Transactional update to ensure atomicity 69 let tx_result = (&self.balances, &self.locked).transaction(|(tx_balances, tx_locked)| { 70 // Read From Balance 71 let from_bal_bytes = tx_balances.get(from_bytes)?.unwrap_or_default(); 72 let from_balance: Balance = if from_bal_bytes.is_empty() { 73 Balance::ZERO 74 } else { 75 serde_json::from_slice(&from_bal_bytes).map_err(|_| sled::transaction::ConflictableTransactionError::Abort(TokenError::Storage("Serde error".into())))? 76 }; 77 78 // Read Locked Balance 79 let locked_val_bytes = tx_locked.get(from_bytes)?.unwrap_or_default(); 80 let locked_val: Balance = if locked_val_bytes.is_empty() { 81 Balance::ZERO 82 } else { 83 serde_json::from_slice(&locked_val_bytes).map_err(|_| sled::transaction::ConflictableTransactionError::Abort(TokenError::Storage("Serde error".into())))? 84 }; 85 86 // Check available 87 let available = from_balance.saturating_sub(locked_val); 88 if available.drops() < amount.drops() { 89 return Err(sled::transaction::ConflictableTransactionError::Abort(TokenError::InsufficientBalance { 90 have: available.drops(), 91 need: amount.drops(), 92 })); 93 } 94 95 // Debit From 96 let new_from = from_balance.checked_sub(amount).map_err(|e| sled::transaction::ConflictableTransactionError::Abort(e))?; 97 if new_from.is_zero() { 98 tx_balances.remove(from_bytes)?; 99 } else { 100 let bytes = serde_json::to_vec(&new_from).unwrap(); 101 tx_balances.insert(from_bytes, bytes)?; 102 } 103 104 // Credit To 105 let to_bal_bytes = tx_balances.get(to_bytes)?.unwrap_or_default(); 106 let to_balance: Balance = if to_bal_bytes.is_empty() { 107 Balance::ZERO 108 } else { 109 serde_json::from_slice(&to_bal_bytes).map_err(|_| sled::transaction::ConflictableTransactionError::Abort(TokenError::Storage("Serde error".into())))? 110 }; 111 112 let new_to = to_balance.checked_add(amount).map_err(|e| sled::transaction::ConflictableTransactionError::Abort(e))?; 113 let to_bytes_ser = serde_json::to_vec(&new_to).unwrap(); 114 tx_balances.insert(to_bytes, to_bytes_ser)?; 115 116 Ok(()) 117 }); 118 119 tx_result.map_err(|e| match e { 120 sled::transaction::TransactionError::Abort(err) => err, 121 sled::transaction::TransactionError::Storage(err) => TokenError::Storage(err.to_string()), 122 }) 123 } 124 125 fn credit(&self, address: &Address, amount: Balance) -> Result<(), TokenError> { 126 let current = self.get_balance_from_tree(&self.balances, address)?; 127 let new_balance = current.checked_add(amount)?; 128 self.set_balance_in_tree(&self.balances, address, new_balance)?; 129 Ok(()) 130 } 131 132 fn debit(&self, address: &Address, amount: Balance) -> Result<(), TokenError> { 133 let current = self.get_balance_from_tree(&self.balances, address)?; 134 let new_balance = current.checked_sub(amount)?; 135 self.set_balance_in_tree(&self.balances, address, new_balance)?; 136 Ok(()) 137 } 138 139 fn lock(&self, address: &Address, amount: Balance) -> Result<(), TokenError> { 140 let available = self.available_balance(address); 141 if available.drops() < amount.drops() { 142 return Err(TokenError::InsufficientBalance { 143 have: available.drops(), 144 need: amount.drops(), 145 }); 146 } 147 148 let current_locked = self.get_balance_from_tree(&self.locked, address)?; 149 let new_locked = current_locked.checked_add(amount)?; 150 self.set_balance_in_tree(&self.locked, address, new_locked)?; 151 Ok(()) 152 } 153 154 fn unlock(&self, address: &Address, amount: Balance) -> Result<(), TokenError> { 155 let current_locked = self.get_balance_from_tree(&self.locked, address)?; 156 let new_locked = current_locked.checked_sub(amount)?; 157 self.set_balance_in_tree(&self.locked, address, new_locked)?; 158 Ok(()) 159 } 160 161 fn locked_balance(&self, address: &Address) -> Balance { 162 self.get_balance_from_tree(&self.locked, address).unwrap_or(Balance::ZERO) 163 } 164 }