-
Notifications
You must be signed in to change notification settings - Fork 1
feat: use storage abstraction #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| //! In-memory implementation of the Storage trait | ||
|
|
||
| use std::{ | ||
| collections::HashMap, | ||
| sync::{Arc, Mutex}, | ||
| }; | ||
|
|
||
| use bitcoin::ScriptBuf; | ||
|
|
||
| use crate::{ | ||
| error::Error, | ||
| storage::{Storage, model::MonitoredDeposit}, | ||
| }; | ||
|
|
||
| /// A store wrapped in an Arc<Mutex<...>> for interior mutability | ||
| pub type SharedStore = Arc<Mutex<Store>>; | ||
|
|
||
| /// In-memory store | ||
| #[derive(Debug, Clone, Default)] | ||
| pub struct Store { | ||
| last_next_address_id: u64, | ||
| monitored: HashMap<ScriptBuf, MonitoredDeposit>, | ||
| } | ||
|
Comment on lines
+19
to
+23
|
||
|
|
||
| impl Store { | ||
| /// Create an empty store wrapped in an Arc<Mutex<...>> | ||
| pub fn new_shared() -> SharedStore { | ||
| Arc::new(Mutex::new(Self::default())) | ||
| } | ||
| } | ||
|
|
||
| /// Storage trait implementation for the in-memory store | ||
| impl Storage for SharedStore { | ||
| /// Add a monitored deposit. If the script pubkey already exists it's a nop. | ||
| fn add(&self, monitored_deposit: MonitoredDeposit) -> Result<(), Error> { | ||
| let mut store = self.lock().map_err(|_| Error::PoisonedMutex)?; | ||
|
|
||
| let key = monitored_deposit.to_script_pubkey(); | ||
| store.monitored.entry(key).or_insert(monitored_deposit); | ||
| Ok(()) | ||
|
Comment on lines
+33
to
+40
|
||
| } | ||
|
|
||
| /// Get a monitored deposit by script pubkey. | ||
| fn get_by_script(&self, script: &ScriptBuf) -> Result<Option<MonitoredDeposit>, Error> { | ||
| let store = self.lock().map_err(|_| Error::PoisonedMutex)?; | ||
|
|
||
| Ok(store.monitored.get(script).cloned()) | ||
| } | ||
|
|
||
| /// Get the script pubkeys for monitored deposits | ||
| fn get_scripts(&self) -> Result<Vec<ScriptBuf>, Error> { | ||
| let store = self.lock().map_err(|_| Error::PoisonedMutex)?; | ||
|
|
||
| Ok(store.monitored.keys().cloned().collect()) | ||
| } | ||
|
|
||
| /// Get the last next-address-id from the registry | ||
| fn get_last_next_address_id(&self) -> Result<u64, Error> { | ||
| let store = self.lock().map_err(|_| Error::PoisonedMutex)?; | ||
|
|
||
| Ok(store.last_next_address_id) | ||
| } | ||
|
|
||
| /// Set the last next-address-id from the registry | ||
| fn set_last_next_address_id(&self, next_address_id: u64) -> Result<(), Error> { | ||
| let mut store = self.lock().map_err(|_| Error::PoisonedMutex)?; | ||
|
|
||
| store.last_next_address_id = next_address_id; | ||
| Ok(()) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| //! Contains functionality for interacting with the storage abstraction | ||
|
|
||
| use bitcoin::ScriptBuf; | ||
|
|
||
| use crate::error::Error; | ||
|
|
||
| pub mod memory; | ||
| pub mod model; | ||
|
|
||
| /// Interface for spox storage. | ||
| pub trait Storage { | ||
| /// Add a monitored deposit. If the script pubkey already exists it's a nop. | ||
| fn add(&self, monitored_deposit: model::MonitoredDeposit) -> Result<(), Error>; | ||
|
|
||
| /// Get a monitored deposit by script pubkey. | ||
| fn get_by_script(&self, script: &ScriptBuf) -> Result<Option<model::MonitoredDeposit>, Error>; | ||
|
|
||
| /// Get the script pubkeys for monitored deposits | ||
| fn get_scripts(&self) -> Result<Vec<ScriptBuf>, Error>; | ||
|
|
||
| /// Get the last next-address-id from the registry | ||
| fn get_last_next_address_id(&self) -> Result<u64, Error>; | ||
|
|
||
| /// Set the last next-address-id from the registry | ||
| fn set_last_next_address_id(&self, next_address_id: u64) -> Result<(), Error>; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,56 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //! Data models used in spox. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use bitcoin::ScriptBuf; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use sbtc::deposits::{DepositScriptInputs, ReclaimScriptInputs}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::{config::MonitoredDepositConfig, error::Error}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// The source for a deposit | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[derive(Debug, Clone)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub enum MonitoredDepositSource { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Address is configured in the config file (with this alias) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Config(String), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Address is registered on the smart contract registry (with this ID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Registry(u64), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// A deposit address to monitor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[derive(Debug, Clone)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub struct MonitoredDeposit { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Monitored deposit source | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub source: MonitoredDepositSource, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Deposit script inputs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub deposit_script_inputs: DepositScriptInputs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Reclaim script inputs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub reclaim_script_inputs: ReclaimScriptInputs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl MonitoredDeposit { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Get the scriptPubKey for this deposit address | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn to_script_pubkey(&self) -> ScriptBuf { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sbtc::deposits::to_script_pubkey( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.deposit_script_inputs.deposit_script(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.reclaim_script_inputs.reclaim_script(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl TryFrom<(&String, &MonitoredDepositConfig)> for MonitoredDeposit { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type Error = Error; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn try_from((alias, deposit): (&String, &MonitoredDepositConfig)) -> Result<Self, Self::Error> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let deposit = deposit.clone(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(MonitoredDeposit { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source: MonitoredDepositSource::Config(alias.clone()), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| deposit_script_inputs: DepositScriptInputs { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| signers_public_key: deposit.signers_xonly, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| recipient: deposit.recipient, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| max_fee: deposit.max_fee, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| reclaim_script_inputs: ReclaimScriptInputs::try_new( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| deposit.lock_time, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| deposit.reclaim_script, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )?, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| use bitcoin::opcodes::all::OP_TRUE; | |
| use bitcoin::script::Builder; | |
| use bitcoin::XOnlyPublicKey; | |
| use clarity::vm::types::PrincipalData; | |
| fn monitored_deposit_config(lock_time: u32, reclaim_script: ScriptBuf) -> MonitoredDepositConfig { | |
| MonitoredDepositConfig { | |
| signers_xonly: XOnlyPublicKey::from_str( | |
| "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", | |
| ) | |
| .unwrap(), | |
| recipient: PrincipalData::parse_standard_principal("ST000000000000000000002AMW42H") | |
| .unwrap(), | |
| max_fee: 1_000, | |
| lock_time, | |
| reclaim_script, | |
| } | |
| } | |
| #[test] | |
| fn try_from_builds_monitored_deposit_for_valid_config() { | |
| let alias = "test-deposit".to_string(); | |
| let reclaim_script = Builder::new().push_opcode(OP_TRUE).into_script(); | |
| let config = monitored_deposit_config(500, reclaim_script.clone()); | |
| let monitored = MonitoredDeposit::try_from((&alias, &config)).unwrap(); | |
| match monitored.source { | |
| MonitoredDepositSource::Config(source_alias) => assert_eq!(source_alias, alias), | |
| MonitoredDepositSource::Registry(_) => panic!("expected config source"), | |
| } | |
| assert_eq!( | |
| monitored.deposit_script_inputs.signers_public_key, | |
| config.signers_xonly | |
| ); | |
| assert_eq!(monitored.deposit_script_inputs.recipient, config.recipient); | |
| assert_eq!(monitored.deposit_script_inputs.max_fee, config.max_fee); | |
| assert_eq!( | |
| monitored.reclaim_script_inputs.reclaim_script(), | |
| reclaim_script.as_script() | |
| ); | |
| } | |
| #[test] | |
| fn try_from_returns_error_for_invalid_reclaim_script() { | |
| let alias = "test-deposit".to_string(); | |
| let config = monitored_deposit_config(500, ScriptBuf::new()); | |
| let result = MonitoredDeposit::try_from((&alias, &config)); | |
| assert!(result.is_err()); | |
| } | |
| #[test] | |
| fn try_from_returns_error_for_invalid_lock_time() { | |
| let alias = "test-deposit".to_string(); | |
| let reclaim_script = Builder::new().push_opcode(OP_TRUE).into_script(); | |
| let config = monitored_deposit_config(0, reclaim_script); | |
| let result = MonitoredDeposit::try_from((&alias, &config)); | |
| assert!(result.is_err()); | |
| } | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring says this returns “a reference to the storage”, but the function returns a cloned
SharedStorehandle (Arc<Mutex<...>>). Consider rewording to avoid implying a borrowed reference (e.g., “Get a handle to the shared storage”).