From 8b4b5397cac66387990b5d54373f25428138e7c7 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 2 Aug 2024 16:19:29 +0200 Subject: [PATCH 1/9] persistence: add autosave feature --- src/persistence/fs.rs | 5 ++-- src/persistence/memory.rs | 54 ++++++++++++++++++++++++++++++++------- src/persistence/stock.rs | 25 ++++++++++++------ 3 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/persistence/fs.rs b/src/persistence/fs.rs index e952f58e..ceb35863 100644 --- a/src/persistence/fs.rs +++ b/src/persistence/fs.rs @@ -24,11 +24,12 @@ use std::path::{Path, PathBuf}; use strict_encoding::{DeserializeError, SerializeError}; pub trait FsStored: Sized { - fn new(path: impl ToOwned) -> Self; - fn load(path: impl ToOwned) -> Result; + fn new(path: impl ToOwned, autosave: bool) -> Self; + fn load(path: impl ToOwned, autosave: bool) -> Result; fn is_dirty(&self) -> bool; fn filename(&self) -> Option<&Path>; + fn autosave(&mut self); fn set_filename(&mut self, filename: impl ToOwned) -> Option; fn store(&self) -> Result<(), SerializeError>; diff --git a/src/persistence/memory.rs b/src/persistence/memory.rs index b017263a..0ed16299 100644 --- a/src/persistence/memory.rs +++ b/src/persistence/memory.rs @@ -82,6 +82,9 @@ pub struct MemStash { dirty: bool, #[cfg(feature = "fs")] #[strict_type(skip)] + autosave: bool, + #[cfg(feature = "fs")] + #[strict_type(skip)] filename: Option, schemata: TinyOrdMap, @@ -107,6 +110,8 @@ impl MemStash { Self { dirty: false, #[cfg(feature = "fs")] + autosave: false, + #[cfg(feature = "fs")] filename: None, schemata: empty!(), ifaces: empty!(), @@ -135,7 +140,7 @@ impl StoreTransaction for MemStash { fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> { #[cfg(feature = "fs")] - if self.dirty { + if self.dirty && self.autosave { self.store()?; } Ok(()) @@ -466,6 +471,9 @@ pub struct MemState { dirty: bool, #[cfg(feature = "fs")] #[strict_type(skip)] + autosave: bool, + #[cfg(feature = "fs")] + #[strict_type(skip)] filename: Option, witnesses: LargeOrdMap, @@ -480,6 +488,8 @@ impl MemState { Self { dirty: false, #[cfg(feature = "fs")] + autosave: false, + #[cfg(feature = "fs")] filename: None, witnesses: empty!(), contracts: empty!(), @@ -497,7 +507,7 @@ impl StoreTransaction for MemState { fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> { #[cfg(feature = "fs")] - if self.dirty { + if self.dirty && self.autosave { self.store()?; } Ok(()) @@ -1166,6 +1176,9 @@ pub struct MemIndex { dirty: bool, #[cfg(feature = "fs")] #[strict_type(skip)] + autosave: bool, + #[cfg(feature = "fs")] + #[strict_type(skip)] filename: Option, op_bundle_index: MediumOrdMap, @@ -1183,6 +1196,8 @@ impl MemIndex { Self { dirty: false, #[cfg(feature = "fs")] + autosave: false, + #[cfg(feature = "fs")] filename: None, op_bundle_index: empty!(), bundle_contract_index: empty!(), @@ -1203,7 +1218,7 @@ impl StoreTransaction for MemIndex { fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> { #[cfg(feature = "fs")] - if self.dirty { + if self.dirty && self.autosave { self.store()?; } Ok(()) @@ -1453,17 +1468,22 @@ mod fs { use crate::persistence::{MemIndex, MemStash, MemState}; impl FsStored for MemStash { - fn new(filename: impl ToOwned) -> Self { + fn new(filename: impl ToOwned, autosave: bool) -> Self { Self { dirty: true, + autosave, filename: Some(filename.to_owned()), ..Self::in_memory() } } - fn load(path: impl ToOwned) -> Result { + fn load( + path: impl ToOwned, + autosave: bool, + ) -> Result { let path = path.to_owned(); let mut me = Self::strict_deserialize_from_file::(&path)?; + me.autosave = autosave; me.set_filename(path); Ok(me) } @@ -1472,6 +1492,8 @@ mod fs { fn filename(&self) -> Option<&Path> { self.filename.as_deref() } + fn autosave(&mut self) { self.autosave = true; } + fn set_filename(&mut self, filename: impl ToOwned) -> Option { let prev = self.filename.to_owned(); self.filename = Some(filename.to_owned()); @@ -1490,18 +1512,23 @@ mod fs { } impl FsStored for MemState { - fn new(filename: impl ToOwned) -> Self { + fn new(filename: impl ToOwned, autosave: bool) -> Self { Self { dirty: true, + autosave, filename: Some(filename.to_owned()), ..Self::in_memory() } } - fn load(path: impl ToOwned) -> Result { + fn load( + path: impl ToOwned, + autosave: bool, + ) -> Result { let path = path.to_owned(); let mut me = Self::strict_deserialize_from_file::(&path)?; me.set_filename(path); + me.autosave = autosave; Ok(me) } @@ -1509,6 +1536,8 @@ mod fs { fn filename(&self) -> Option<&Path> { self.filename.as_deref() } + fn autosave(&mut self) { self.autosave = true } + fn set_filename(&mut self, filename: impl ToOwned) -> Option { let prev = self.filename.to_owned(); self.filename = Some(filename.to_owned()); @@ -1527,18 +1556,23 @@ mod fs { } impl FsStored for MemIndex { - fn new(filename: impl ToOwned) -> Self { + fn new(filename: impl ToOwned, autosave: bool) -> Self { Self { dirty: true, + autosave, filename: Some(filename.to_owned()), ..Self::in_memory() } } - fn load(path: impl ToOwned) -> Result { + fn load( + path: impl ToOwned, + autosave: bool, + ) -> Result { let path = path.to_owned(); let mut me = Self::strict_deserialize_from_file::(&path)?; me.set_filename(path); + me.autosave = autosave; Ok(me) } @@ -1546,6 +1580,8 @@ mod fs { fn filename(&self) -> Option<&Path> { self.filename.as_deref() } + fn autosave(&mut self) { self.autosave = true } + fn set_filename(&mut self, filename: impl ToOwned) -> Option { let prev = self.filename.to_owned(); self.filename = Some(filename.to_owned()); diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index bc594d4b..7f1e60f1 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -390,38 +390,47 @@ mod fs { H: FsStored, I: FsStored, { - pub fn new(path: impl ToOwned) -> Self { + pub fn new(path: impl ToOwned, autosave: bool) -> Self { let mut filename = path.to_owned(); filename.push("stash.dat"); - let stash = S::new(filename); + let stash = S::new(filename, autosave); let mut filename = path.to_owned(); filename.push("state.dat"); - let state = H::new(filename); + let state = H::new(filename, autosave); let mut filename = path.to_owned(); filename.push("index.dat"); - let index = I::new(filename); + let index = I::new(filename, autosave); Stock::with(stash, state, index) } - pub fn load(path: impl ToOwned) -> Result { + pub fn load( + path: impl ToOwned, + autosave: bool, + ) -> Result { let mut filename = path.to_owned(); filename.push("stash.dat"); - let stash = S::load(filename)?; + let stash = S::load(filename, autosave)?; let mut filename = path.to_owned(); filename.push("state.dat"); - let state = H::load(filename)?; + let state = H::load(filename, autosave)?; let mut filename = path.to_owned(); filename.push("index.dat"); - let index = I::load(filename)?; + let index = I::load(filename, autosave)?; Ok(Stock::with(stash, state, index)) } + pub fn autosave(&mut self) { + self.stash.as_provider_mut().autosave(); + self.state.as_provider_mut().autosave(); + self.index.as_provider_mut().autosave(); + } + pub fn is_dirty(&self) -> bool { self.as_stash_provider().is_dirty() || self.as_state_provider().is_dirty() From 3403b2b38db2d1fe1682c496b739ac225d55c7ad Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 9 Aug 2024 12:10:27 +0200 Subject: [PATCH 2/9] persistence: implement store providers --- src/persistence/index.rs | 9 +- src/persistence/memory.rs | 209 ++++++++++++++-------------- src/persistence/mod.rs | 4 +- src/persistence/stash.rs | 4 +- src/persistence/state.rs | 6 +- src/persistence/stock.rs | 139 ++++++++---------- src/persistence/{fs.rs => store.rs} | 34 +++-- 7 files changed, 198 insertions(+), 207 deletions(-) rename src/persistence/{fs.rs => store.rs} (50%) diff --git a/src/persistence/index.rs b/src/persistence/index.rs index 85c5c715..3d117ee0 100644 --- a/src/persistence/index.rs +++ b/src/persistence/index.rs @@ -29,13 +29,12 @@ use rgb::{ GraphSeal, OpId, Operation, Opout, TransitionBundle, TypedAssigns, XChain, XOutputSeal, XWitnessId, }; -use strict_encoding::SerializeError; use crate::containers::{BundledWitness, ConsignmentExt, ToWitnessId}; -use crate::persistence::StoreTransaction; +use crate::persistence::{StoreError, StoreTransaction}; use crate::SecretSeal; -#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] +#[derive(Debug, Display, Error, From)] #[display(inner)] pub enum IndexError { /// Connectivity errors which may be recoverable and temporary. @@ -85,7 +84,7 @@ impl From::Error>> f } } -impl From for IndexWriteError { +impl From for IndexWriteError { fn from(err: confinement::Error) -> Self { IndexWriteError::Connectivity(err.into()) } } @@ -386,7 +385,7 @@ pub trait IndexReadProvider { } pub trait IndexWriteProvider: StoreTransaction { - type Error: Clone + Eq + Error; + type Error: Error; fn register_contract(&mut self, contract_id: ContractId) -> Result; diff --git a/src/persistence/memory.rs b/src/persistence/memory.rs index 0ed16299..a3629ed0 100644 --- a/src/persistence/memory.rs +++ b/src/persistence/memory.rs @@ -25,8 +25,6 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::convert::Infallible; use std::fmt::{Debug, Formatter}; use std::num::NonZeroU32; -#[cfg(feature = "fs")] -use std::path::PathBuf; use std::{iter, mem}; use aluvm::library::{Lib, LibId}; @@ -49,23 +47,21 @@ use rgb::{ RevealedData, RevealedValue, Schema, SchemaId, SecretSeal, Transition, TransitionBundle, TypedAssigns, VoidState, XChain, XOutpoint, XOutputSeal, XWitnessId, }; -use strict_encoding::{SerializeError, StrictDeserialize, StrictSerialize}; +use strict_encoding::{StrictDeserialize, StrictSerialize}; use strict_types::TypeSystem; use super::{ ContractIfaceError, ContractStateRead, ContractStateWrite, IndexInconsistency, IndexProvider, IndexReadError, IndexReadProvider, IndexWriteError, IndexWriteProvider, SchemaIfaces, StashInconsistency, StashProvider, StashProviderError, StashReadProvider, StashWriteProvider, - StateInconsistency, StateProvider, StateReadProvider, StateWriteProvider, StoreTransaction, - UpdateRes, + StateInconsistency, StateProvider, StateReadProvider, StateWriteProvider, StoreError, + StoreProvider, StoreTransaction, Stored, UpdateRes, }; use crate::containers::{ AnchorSet, ContentId, ContentRef, ContentSigs, SealWitness, SigBlob, Supplement, TrustLevel, }; use crate::contract::{GlobalOut, KnownState, OpWitness, OutputAssignment}; use crate::interface::{Iface, IfaceClass, IfaceId, IfaceImpl, IfaceRef}; -#[cfg(feature = "fs")] -use crate::persistence::fs::FsStored; use crate::LIB_NAME_RGB_STORAGE; ////////// @@ -73,19 +69,22 @@ use crate::LIB_NAME_RGB_STORAGE; ////////// /// Hoard is an in-memory stash useful for WASM implementations. -#[derive(Getters, Clone, Debug)] +#[derive(Getters, Debug)] #[getter(prefix = "debug_")] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_STORAGE, dumb = Self::in_memory())] pub struct MemStash { + #[getter(prefix = "is_", as_copy)] #[strict_type(skip)] dirty: bool, - #[cfg(feature = "fs")] + + #[getter(prefix = "is_", as_copy)] #[strict_type(skip)] autosave: bool, - #[cfg(feature = "fs")] + + #[getter(skip)] #[strict_type(skip)] - filename: Option, + store_provider: Option>>, schemata: TinyOrdMap, ifaces: TinyOrdMap, @@ -109,10 +108,8 @@ impl MemStash { pub fn in_memory() -> Self { Self { dirty: false, - #[cfg(feature = "fs")] autosave: false, - #[cfg(feature = "fs")] - filename: None, + store_provider: None, schemata: empty!(), ifaces: empty!(), geneses: empty!(), @@ -131,7 +128,7 @@ impl MemStash { } impl StoreTransaction for MemStash { - type TransactionErr = SerializeError; + type TransactionErr = StoreError; fn begin_transaction(&mut self) -> Result<(), Self::TransactionErr> { self.dirty = true; @@ -139,7 +136,6 @@ impl StoreTransaction for MemStash { } fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> { - #[cfg(feature = "fs")] if self.dirty && self.autosave { self.store()?; } @@ -335,7 +331,7 @@ impl StashReadProvider for MemStash { } impl StashWriteProvider for MemStash { - type Error = SerializeError; + type Error = StoreError; fn replace_schema(&mut self, schema: Schema) -> Result { let schema_id = schema.schema_id(); @@ -462,19 +458,22 @@ impl StashWriteProvider for MemStash { // STATE ////////// -#[derive(Getters, Clone, Debug)] +#[derive(Getters, Debug)] #[getter(prefix = "debug_")] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_STORAGE, dumb = Self::in_memory())] pub struct MemState { + #[getter(prefix = "is_", as_copy)] #[strict_type(skip)] dirty: bool, - #[cfg(feature = "fs")] + + #[getter(prefix = "is_", as_copy)] #[strict_type(skip)] autosave: bool, - #[cfg(feature = "fs")] + + #[getter(skip)] #[strict_type(skip)] - filename: Option, + store_provider: Option>>, witnesses: LargeOrdMap, contracts: TinyOrdMap, @@ -487,10 +486,8 @@ impl MemState { pub fn in_memory() -> Self { Self { dirty: false, - #[cfg(feature = "fs")] autosave: false, - #[cfg(feature = "fs")] - filename: None, + store_provider: None, witnesses: empty!(), contracts: empty!(), } @@ -498,7 +495,7 @@ impl MemState { } impl StoreTransaction for MemState { - type TransactionErr = SerializeError; + type TransactionErr = StoreError; fn begin_transaction(&mut self) -> Result<(), Self::TransactionErr> { self.dirty = true; @@ -561,7 +558,7 @@ impl StateReadProvider for MemState { impl StateWriteProvider for MemState { type ContractWrite<'a> = MemContractWriter<'a>; - type Error = SerializeError; + type Error = StoreError; fn register_contract( &mut self, @@ -584,7 +581,7 @@ impl StateWriteProvider for MemState { }; let mut writer = MemContractWriter { writer: Box::new( - |witness_id: XWitnessId, ord: WitnessOrd| -> Result<(), SerializeError> { + |witness_id: XWitnessId, ord: WitnessOrd| -> Result<(), confinement::Error> { // NB: We do not check the existence of the witness since we have a newer // version anyway and even if it is known we have to replace it self.witnesses.insert(witness_id, ord)?; @@ -609,7 +606,7 @@ impl StateWriteProvider for MemState { // We can't move this constructor to a dedicated method due to the rust borrower // checker writer: Box::new( - |witness_id: XWitnessId, ord: WitnessOrd| -> Result<(), SerializeError> { + |witness_id: XWitnessId, ord: WitnessOrd| -> Result<(), confinement::Error> { // NB: We do not check the existence of the witness since we have a newer // version anyway and even if it is known we have to replace // it @@ -1013,7 +1010,7 @@ impl ContractStateEvolve for MemContract { fn writer(me: &mut MemContract) -> MemContractWriter { MemContractWriter { writer: Box::new( - |witness_id: XWitnessId, ord: WitnessOrd| -> Result<(), SerializeError> { + |witness_id: XWitnessId, ord: WitnessOrd| -> Result<(), confinement::Error> { // NB: We do not check the existence of the witness since we have a // newer version anyway and even if it is // known we have to replace it @@ -1038,11 +1035,11 @@ impl ContractStateEvolve for MemContract { writer.add_extension(extension, witness_id, ord) } } - .map_err(|err| match err { - SerializeError::Io(_) => { - unreachable!("I/O errors are not possible for memory structures") - } - SerializeError::Confinement(e) => e, + .map_err(|err| { + // TODO: remove once evolve_state would accept arbitrary errors + *err.0 + .downcast::() + .expect("only confinement errors are possible") })?; Ok(()) } @@ -1093,12 +1090,12 @@ impl> ContractStateRead for MemContract { } pub struct MemContractWriter<'mem> { - writer: Box Result<(), SerializeError> + 'mem>, + writer: Box Result<(), confinement::Error> + 'mem>, contract: &'mem mut MemContractState, } impl<'mem> ContractStateWrite for MemContractWriter<'mem> { - type Error = SerializeError; + type Error = StoreError; /// # Panics /// @@ -1167,19 +1164,22 @@ pub struct ContractIndex { outpoint_opouts: MediumOrdMap>, } -#[derive(Getters, Clone, Debug)] +#[derive(Getters, Debug)] #[getter(prefix = "debug_")] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_STORAGE, dumb = Self::in_memory())] pub struct MemIndex { + #[getter(prefix = "is_", as_copy)] #[strict_type(skip)] dirty: bool, - #[cfg(feature = "fs")] + + #[getter(prefix = "is_", as_copy)] #[strict_type(skip)] autosave: bool, - #[cfg(feature = "fs")] + + #[getter(skip)] #[strict_type(skip)] - filename: Option, + store_provider: Option>>, op_bundle_index: MediumOrdMap, bundle_contract_index: MediumOrdMap, @@ -1195,10 +1195,8 @@ impl MemIndex { pub fn in_memory() -> Self { Self { dirty: false, - #[cfg(feature = "fs")] autosave: false, - #[cfg(feature = "fs")] - filename: None, + store_provider: None, op_bundle_index: empty!(), bundle_contract_index: empty!(), bundle_witness_index: empty!(), @@ -1209,7 +1207,7 @@ impl MemIndex { } impl StoreTransaction for MemIndex { - type TransactionErr = SerializeError; + type TransactionErr = StoreError; fn begin_transaction(&mut self) -> Result<(), Self::TransactionErr> { self.dirty = true; @@ -1318,7 +1316,7 @@ impl IndexReadProvider for MemIndex { } impl IndexWriteProvider for MemIndex { - type Error = SerializeError; + type Error = StoreError; fn register_contract(&mut self, contract_id: ContractId) -> Result { if !self.contract_index.contains_key(&contract_id) { @@ -1459,140 +1457,135 @@ impl IndexWriteProvider for MemIndex { #[cfg(feature = "fs")] mod fs { - use std::path::{Path, PathBuf}; - - use amplify::confinement::U32; - use strict_encoding::{DeserializeError, SerializeError, StrictDeserialize, StrictSerialize}; - - use crate::persistence::fs::FsStored; - use crate::persistence::{MemIndex, MemStash, MemState}; + use crate::persistence::store::Stored; + use crate::persistence::{MemIndex, MemStash, MemState, StoreError, StoreProvider}; - impl FsStored for MemStash { - fn new(filename: impl ToOwned, autosave: bool) -> Self { + impl Stored for MemStash { + fn new_stored( + provider: impl StoreProvider + 'static, + autosave: bool, + ) -> Self { Self { dirty: true, autosave, - filename: Some(filename.to_owned()), + store_provider: Some(Box::new(provider)), ..Self::in_memory() } } fn load( - path: impl ToOwned, + provider: impl StoreProvider + 'static, autosave: bool, - ) -> Result { - let path = path.to_owned(); - let mut me = Self::strict_deserialize_from_file::(&path)?; + ) -> Result { + let mut me = provider.load()?; me.autosave = autosave; - me.set_filename(path); + me.store_provider = Some(Box::new(provider)); Ok(me) } fn is_dirty(&self) -> bool { self.dirty } - fn filename(&self) -> Option<&Path> { self.filename.as_deref() } - fn autosave(&mut self) { self.autosave = true; } - fn set_filename(&mut self, filename: impl ToOwned) -> Option { - let prev = self.filename.to_owned(); - self.filename = Some(filename.to_owned()); - self.dirty = self.filename != prev; - prev + fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool { + let res = self.store_provider.is_some(); + self.store_provider = Some(Box::new(provider)); + self.dirty = true; + res } - fn store(&self) -> Result<(), SerializeError> { + fn store(&self) -> Result<(), StoreError> { if self.is_dirty() { - if let Some(filename) = self.filename() { - return self.strict_serialize_to_file::(filename); + if let Some(provider) = &self.store_provider { + provider.store(self)?; } } Ok(()) } } - impl FsStored for MemState { - fn new(filename: impl ToOwned, autosave: bool) -> Self { + impl Stored for MemState { + fn new_stored( + provider: impl StoreProvider + 'static, + autosave: bool, + ) -> Self { Self { dirty: true, autosave, - filename: Some(filename.to_owned()), + store_provider: Some(Box::new(provider)), ..Self::in_memory() } } fn load( - path: impl ToOwned, + provider: impl StoreProvider + 'static, autosave: bool, - ) -> Result { - let path = path.to_owned(); - let mut me = Self::strict_deserialize_from_file::(&path)?; - me.set_filename(path); + ) -> Result { + let mut me = provider.load()?; me.autosave = autosave; + me.store_provider = Some(Box::new(provider)); Ok(me) } fn is_dirty(&self) -> bool { self.dirty } - fn filename(&self) -> Option<&Path> { self.filename.as_deref() } - - fn autosave(&mut self) { self.autosave = true } + fn autosave(&mut self) { self.autosave = true; } - fn set_filename(&mut self, filename: impl ToOwned) -> Option { - let prev = self.filename.to_owned(); - self.filename = Some(filename.to_owned()); - self.dirty = self.filename != prev; - prev + fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool { + let res = self.store_provider.is_some(); + self.store_provider = Some(Box::new(provider)); + self.dirty = true; + res } - fn store(&self) -> Result<(), SerializeError> { + fn store(&self) -> Result<(), StoreError> { if self.is_dirty() { - if let Some(filename) = self.filename() { - return self.strict_serialize_to_file::(filename); + if let Some(provider) = &self.store_provider { + provider.store(self)?; } } Ok(()) } } - impl FsStored for MemIndex { - fn new(filename: impl ToOwned, autosave: bool) -> Self { + impl Stored for MemIndex { + fn new_stored( + provider: impl StoreProvider + 'static, + autosave: bool, + ) -> Self { Self { dirty: true, autosave, - filename: Some(filename.to_owned()), + store_provider: Some(Box::new(provider)), ..Self::in_memory() } } fn load( - path: impl ToOwned, + provider: impl StoreProvider + 'static, autosave: bool, - ) -> Result { - let path = path.to_owned(); - let mut me = Self::strict_deserialize_from_file::(&path)?; - me.set_filename(path); + ) -> Result { + let mut me = provider.load()?; me.autosave = autosave; + me.store_provider = Some(Box::new(provider)); Ok(me) } fn is_dirty(&self) -> bool { self.dirty } - fn filename(&self) -> Option<&Path> { self.filename.as_deref() } - - fn autosave(&mut self) { self.autosave = true } + fn autosave(&mut self) { self.autosave = true; } - fn set_filename(&mut self, filename: impl ToOwned) -> Option { - let prev = self.filename.to_owned(); - self.filename = Some(filename.to_owned()); - self.dirty = self.filename != prev; - prev + fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool { + let res = self.store_provider.is_some(); + self.store_provider = Some(Box::new(provider)); + self.dirty = true; + res } - fn store(&self) -> Result<(), SerializeError> { + fn store(&self) -> Result<(), StoreError> { if self.is_dirty() { - if let Some(filename) = self.filename() { - return self.strict_serialize_to_file::(filename); + if let Some(provider) = &self.store_provider { + provider.store(self)?; } } Ok(()) diff --git a/src/persistence/mod.rs b/src/persistence/mod.rs index d013c842..8c004a4c 100644 --- a/src/persistence/mod.rs +++ b/src/persistence/mod.rs @@ -36,8 +36,7 @@ mod state; mod index; mod memory; -#[cfg(feature = "fs")] -pub mod fs; +mod store; pub use index::{ Index, IndexError, IndexInconsistency, IndexProvider, IndexReadError, IndexReadProvider, @@ -56,6 +55,7 @@ pub use stock::{ ComposeError, ConsignError, ContractIfaceError, FasciaError, InputError as StockInputError, Stock, StockError, StockErrorAll, StockErrorMem, UpdateRes, }; +pub use store::{StoreError, StoreProvider, Stored}; pub trait StoreTransaction { type TransactionErr: std::error::Error; diff --git a/src/persistence/stash.rs b/src/persistence/stash.rs index 937b71d0..f1cad291 100644 --- a/src/persistence/stash.rs +++ b/src/persistence/stash.rs @@ -48,7 +48,7 @@ use crate::interface::{ use crate::persistence::{ContractIfaceError, StoreTransaction}; use crate::{MergeReveal, MergeRevealError, SecretSeal, LIB_NAME_RGB_STD}; -#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] +#[derive(Debug, Display, Error, From)] #[display(inner)] pub enum StashError { /// Connectivity errors which may be recoverable and temporary. @@ -675,7 +675,7 @@ pub trait StashReadProvider { } pub trait StashWriteProvider: StoreTransaction { - type Error: Clone + Eq + Error; + type Error: Error; fn replace_schema(&mut self, schema: Schema) -> Result; fn replace_iface(&mut self, iface: Iface) -> Result; diff --git a/src/persistence/state.rs b/src/persistence/state.rs index 8f3d586a..2ddbab6b 100644 --- a/src/persistence/state.rs +++ b/src/persistence/state.rs @@ -38,7 +38,7 @@ use crate::containers::{ConsignmentExt, ToWitnessId}; use crate::contract::OutputAssignment; use crate::persistence::{StoreTransaction, UpdateRes}; -#[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)] +#[derive(Debug, Display, Error, From)] #[display(inner)] pub enum StateError { /// Connectivity errors which may be recoverable and temporary. @@ -272,7 +272,7 @@ pub trait StateReadProvider { pub trait StateWriteProvider: StoreTransaction { type ContractWrite<'a>: ContractStateWrite where Self: 'a; - type Error: Clone + Eq + Error; + type Error: Error; fn register_contract( &mut self, @@ -302,7 +302,7 @@ pub trait ContractStateRead: ContractStateAccess { } pub trait ContractStateWrite { - type Error: Clone + Eq + Error; + type Error: Error; fn add_genesis(&mut self, genesis: &Genesis) -> Result<(), Self::Error>; diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index 7f1e60f1..6f89dc95 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -45,7 +45,7 @@ use super::{ IndexWriteProvider, MemIndex, MemStash, MemState, PersistedState, SchemaIfaces, Stash, StashDataError, StashError, StashInconsistency, StashProvider, StashReadProvider, StashWriteProvider, State, StateError, StateInconsistency, StateProvider, StateReadProvider, - StateWriteProvider, StoreTransaction, + StateWriteProvider, StoreProvider, StoreTransaction, Stored, }; use crate::containers::{ AnchorSet, AnchoredBundles, Batch, BuilderSeal, BundledWitness, Consignment, ContainerVer, @@ -62,7 +62,7 @@ use crate::{MergeRevealError, RevealError}; pub type ContractAssignments = HashMap>; -#[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)] +#[derive(Debug, Display, Error, From)] #[display(inner)] pub enum StockError< S: StashProvider = MemStash, @@ -375,89 +375,70 @@ impl Stock { } } -#[cfg(feature = "fs")] -mod fs { - use std::path::PathBuf; - - use strict_encoding::{DeserializeError, SerializeError}; - - use super::*; - use crate::persistence::fs::FsStored; - - impl Stock - where - S: FsStored, - H: FsStored, - I: FsStored, - { - pub fn new(path: impl ToOwned, autosave: bool) -> Self { - let mut filename = path.to_owned(); - filename.push("stash.dat"); - let stash = S::new(filename, autosave); - - let mut filename = path.to_owned(); - filename.push("state.dat"); - let state = H::new(filename, autosave); - - let mut filename = path.to_owned(); - filename.push("index.dat"); - let index = I::new(filename, autosave); - - Stock::with(stash, state, index) - } - - pub fn load( - path: impl ToOwned, - autosave: bool, - ) -> Result { - let mut filename = path.to_owned(); - filename.push("stash.dat"); - let stash = S::load(filename, autosave)?; - - let mut filename = path.to_owned(); - filename.push("state.dat"); - let state = H::load(filename, autosave)?; - - let mut filename = path.to_owned(); - filename.push("index.dat"); - let index = I::load(filename, autosave)?; - - Ok(Stock::with(stash, state, index)) - } - - pub fn autosave(&mut self) { - self.stash.as_provider_mut().autosave(); - self.state.as_provider_mut().autosave(); - self.index.as_provider_mut().autosave(); - } +impl Stock +where + S: Stored, + H: Stored, + I: Stored, +{ + pub fn new_stored( + stash_provider: impl StoreProvider + 'static, + state_provider: impl StoreProvider + 'static, + index_provider: impl StoreProvider + 'static, + autosave: bool, + ) -> Self { + let stash = S::new_stored(stash_provider, autosave); + let state = H::new_stored(state_provider, autosave); + let index = I::new_stored(index_provider, autosave); + + Stock::with(stash, state, index) + } - pub fn is_dirty(&self) -> bool { - self.as_stash_provider().is_dirty() - || self.as_state_provider().is_dirty() - || self.as_index_provider().is_dirty() - } + pub fn load( + stash_provider: impl StoreProvider + 'static, + state_provider: impl StoreProvider + 'static, + index_provider: impl StoreProvider + 'static, + autosave: bool, + ) -> Result { + let stash = S::load(stash_provider, autosave)?; + let state = H::load(state_provider, autosave)?; + let index = I::load(index_provider, autosave)?; + + Ok(Stock::with(stash, state, index)) + } - pub fn set_path(&mut self, path: impl ToOwned) { - let mut filename = path.to_owned(); - filename.push("stash.dat"); - self.stash.as_provider_mut().set_filename(filename); + pub fn autosave(&mut self) { + self.stash.as_provider_mut().autosave(); + self.state.as_provider_mut().autosave(); + self.index.as_provider_mut().autosave(); + } - let mut filename = path.to_owned(); - filename.push("state.dat"); - self.state.as_provider_mut().set_filename(filename); + pub fn is_dirty(&self) -> bool { + self.as_stash_provider().is_dirty() || + self.as_state_provider().is_dirty() || + self.as_index_provider().is_dirty() + } - let mut filename = path.to_owned(); - filename.push("index.dat"); - self.index.as_provider_mut().set_filename(filename); - } + pub fn make_stored( + &mut self, + stash_provider: impl StoreProvider + 'static, + state_provider: impl StoreProvider + 'static, + index_provider: impl StoreProvider + 'static, + ) -> bool { + let _1 = self.stash.as_provider_mut().make_stored(stash_provider); + let _2 = self.state.as_provider_mut().make_stored(state_provider); + let _3 = self.index.as_provider_mut().make_stored(index_provider); + assert_eq!(_1, _2); + assert_eq!(_2, _3); + _1 + } - pub fn store(&self) -> Result<(), SerializeError> { - self.as_stash_provider().store()?; - self.as_state_provider().store()?; - self.as_index_provider().store()?; + pub fn store(&self) -> Result<(), String> { + self.as_stash_provider().store()?; + self.as_state_provider().store()?; + self.as_index_provider().store()?; - Ok(()) - } + Ok(()) } } diff --git a/src/persistence/fs.rs b/src/persistence/store.rs similarity index 50% rename from src/persistence/fs.rs rename to src/persistence/store.rs index ceb35863..002bc09e 100644 --- a/src/persistence/fs.rs +++ b/src/persistence/store.rs @@ -19,18 +19,36 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::path::{Path, PathBuf}; +use std::error::Error; +use std::fmt::Debug; -use strict_encoding::{DeserializeError, SerializeError}; +use amplify::confinement; -pub trait FsStored: Sized { - fn new(path: impl ToOwned, autosave: bool) -> Self; - fn load(path: impl ToOwned, autosave: bool) -> Result; +#[derive(Debug, Display, Error)] +#[display(inner)] +pub struct StoreError(pub Box); + +impl From for StoreError { + fn from(err: confinement::Error) -> Self { Self(Box::new(err)) } +} + +pub trait StoreProvider: Send + Debug { + type Object; + + fn load(&self) -> Result; + fn store(&self, object: &Self::Object) -> Result<(), StoreError>; +} + +pub trait Stored: Sized { + fn new_stored(provider: impl StoreProvider + 'static, autosave: bool) -> Self; + fn load( + provider: impl StoreProvider + 'static, + autosave: bool, + ) -> Result; fn is_dirty(&self) -> bool; - fn filename(&self) -> Option<&Path>; fn autosave(&mut self); - fn set_filename(&mut self, filename: impl ToOwned) -> Option; + fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool; - fn store(&self) -> Result<(), SerializeError>; + fn store(&self) -> Result<(), StoreError>; } From c0f6a6ce47b98284e64d558655a981b84de84926 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 9 Aug 2024 12:46:10 +0200 Subject: [PATCH 3/9] persistence: remove unneeded fs feature gates --- src/persistence/index.rs | 1 - src/persistence/memory.rs | 5 +---- src/persistence/stash.rs | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/persistence/index.rs b/src/persistence/index.rs index 3d117ee0..247e8cee 100644 --- a/src/persistence/index.rs +++ b/src/persistence/index.rs @@ -145,7 +145,6 @@ impl Index

{ pub fn as_provider(&self) -> &P { &self.provider } #[doc(hidden)] - #[cfg(feature = "fs")] pub(super) fn as_provider_mut(&mut self) -> &mut P { &mut self.provider } pub(super) fn index_consignment( diff --git a/src/persistence/memory.rs b/src/persistence/memory.rs index a3629ed0..12120360 100644 --- a/src/persistence/memory.rs +++ b/src/persistence/memory.rs @@ -503,7 +503,6 @@ impl StoreTransaction for MemState { } fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> { - #[cfg(feature = "fs")] if self.dirty && self.autosave { self.store()?; } @@ -1215,7 +1214,6 @@ impl StoreTransaction for MemIndex { } fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> { - #[cfg(feature = "fs")] if self.dirty && self.autosave { self.store()?; } @@ -1455,8 +1453,7 @@ impl IndexWriteProvider for MemIndex { } } -#[cfg(feature = "fs")] -mod fs { +mod store { use crate::persistence::store::Stored; use crate::persistence::{MemIndex, MemStash, MemState, StoreError, StoreProvider}; diff --git a/src/persistence/stash.rs b/src/persistence/stash.rs index f1cad291..d5f322b4 100644 --- a/src/persistence/stash.rs +++ b/src/persistence/stash.rs @@ -207,7 +207,6 @@ impl Stash

{ pub fn as_provider(&self) -> &P { &self.provider } #[doc(hidden)] - #[cfg(feature = "fs")] pub(super) fn as_provider_mut(&mut self) -> &mut P { &mut self.provider } pub(super) fn ifaces(&self) -> Result + '_, StashError

> { From 48594c258d8eb36863b5143ba73a10e8df60f7e1 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 9 Aug 2024 13:03:29 +0200 Subject: [PATCH 4/9] persistence: complete storage provider functionality --- src/persistence/memory.rs | 33 ++++------ src/persistence/mod.rs | 2 +- src/persistence/stock.rs | 59 +++++++---------- src/persistence/store.rs | 129 +++++++++++++++++++++++++++++++++++--- 4 files changed, 156 insertions(+), 67 deletions(-) diff --git a/src/persistence/memory.rs b/src/persistence/memory.rs index 12120360..78b6a7ce 100644 --- a/src/persistence/memory.rs +++ b/src/persistence/memory.rs @@ -84,7 +84,7 @@ pub struct MemStash { #[getter(skip)] #[strict_type(skip)] - store_provider: Option>>, + store_provider: Option>>, schemata: TinyOrdMap, ifaces: TinyOrdMap, @@ -473,7 +473,7 @@ pub struct MemState { #[getter(skip)] #[strict_type(skip)] - store_provider: Option>>, + store_provider: Option>>, witnesses: LargeOrdMap, contracts: TinyOrdMap, @@ -1178,7 +1178,7 @@ pub struct MemIndex { #[getter(skip)] #[strict_type(skip)] - store_provider: Option>>, + store_provider: Option>>, op_bundle_index: MediumOrdMap, bundle_contract_index: MediumOrdMap, @@ -1458,10 +1458,7 @@ mod store { use crate::persistence::{MemIndex, MemStash, MemState, StoreError, StoreProvider}; impl Stored for MemStash { - fn new_stored( - provider: impl StoreProvider + 'static, - autosave: bool, - ) -> Self { + fn new_stored(provider: impl StoreProvider + 'static, autosave: bool) -> Self { Self { dirty: true, autosave, @@ -1471,7 +1468,7 @@ mod store { } fn load( - provider: impl StoreProvider + 'static, + provider: impl StoreProvider + 'static, autosave: bool, ) -> Result { let mut me = provider.load()?; @@ -1484,7 +1481,7 @@ mod store { fn autosave(&mut self) { self.autosave = true; } - fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool { + fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool { let res = self.store_provider.is_some(); self.store_provider = Some(Box::new(provider)); self.dirty = true; @@ -1502,10 +1499,7 @@ mod store { } impl Stored for MemState { - fn new_stored( - provider: impl StoreProvider + 'static, - autosave: bool, - ) -> Self { + fn new_stored(provider: impl StoreProvider + 'static, autosave: bool) -> Self { Self { dirty: true, autosave, @@ -1515,7 +1509,7 @@ mod store { } fn load( - provider: impl StoreProvider + 'static, + provider: impl StoreProvider + 'static, autosave: bool, ) -> Result { let mut me = provider.load()?; @@ -1528,7 +1522,7 @@ mod store { fn autosave(&mut self) { self.autosave = true; } - fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool { + fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool { let res = self.store_provider.is_some(); self.store_provider = Some(Box::new(provider)); self.dirty = true; @@ -1546,10 +1540,7 @@ mod store { } impl Stored for MemIndex { - fn new_stored( - provider: impl StoreProvider + 'static, - autosave: bool, - ) -> Self { + fn new_stored(provider: impl StoreProvider + 'static, autosave: bool) -> Self { Self { dirty: true, autosave, @@ -1559,7 +1550,7 @@ mod store { } fn load( - provider: impl StoreProvider + 'static, + provider: impl StoreProvider + 'static, autosave: bool, ) -> Result { let mut me = provider.load()?; @@ -1572,7 +1563,7 @@ mod store { fn autosave(&mut self) { self.autosave = true; } - fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool { + fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool { let res = self.store_provider.is_some(); self.store_provider = Some(Box::new(provider)); self.dirty = true; diff --git a/src/persistence/mod.rs b/src/persistence/mod.rs index 8c004a4c..591ce935 100644 --- a/src/persistence/mod.rs +++ b/src/persistence/mod.rs @@ -55,7 +55,7 @@ pub use stock::{ ComposeError, ConsignError, ContractIfaceError, FasciaError, InputError as StockInputError, Stock, StockError, StockErrorAll, StockErrorMem, UpdateRes, }; -pub use store::{StoreError, StoreProvider, Stored}; +pub use store::{StockStoreProvider, StoreError, StoreProvider, Stored}; pub trait StoreTransaction { type TransactionErr: std::error::Error; diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index 6f89dc95..8356ab56 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -45,7 +45,7 @@ use super::{ IndexWriteProvider, MemIndex, MemStash, MemState, PersistedState, SchemaIfaces, Stash, StashDataError, StashError, StashInconsistency, StashProvider, StashReadProvider, StashWriteProvider, State, StateError, StateInconsistency, StateProvider, StateReadProvider, - StateWriteProvider, StoreProvider, StoreTransaction, Stored, + StateWriteProvider, StockStoreProvider, StoreError, StoreProvider, StoreTransaction, Stored, }; use crate::containers::{ AnchorSet, AnchoredBundles, Batch, BuilderSeal, BundledWitness, Consignment, ContainerVer, @@ -381,30 +381,15 @@ where H: Stored, I: Stored, { - pub fn new_stored( - stash_provider: impl StoreProvider + 'static, - state_provider: impl StoreProvider + 'static, - index_provider: impl StoreProvider + 'static, - autosave: bool, - ) -> Self { - let stash = S::new_stored(stash_provider, autosave); - let state = H::new_stored(state_provider, autosave); - let index = I::new_stored(index_provider, autosave); - - Stock::with(stash, state, index) - } - - pub fn load( - stash_provider: impl StoreProvider + 'static, - state_provider: impl StoreProvider + 'static, - index_provider: impl StoreProvider + 'static, - autosave: bool, - ) -> Result { - let stash = S::load(stash_provider, autosave)?; - let state = H::load(state_provider, autosave)?; - let index = I::load(index_provider, autosave)?; - - Ok(Stock::with(stash, state, index)) + pub fn load

(provider: P, autosave: bool) -> Result + where P: StoreProvider + 'static { + let mut stock = provider.load()?; + if autosave { + stock.stash.as_provider_mut().autosave(); + stock.state.as_provider_mut().autosave(); + stock.index.as_provider_mut().autosave(); + } + Ok(stock) } pub fn autosave(&mut self) { @@ -419,21 +404,12 @@ where self.as_index_provider().is_dirty() } - pub fn make_stored( - &mut self, - stash_provider: impl StoreProvider + 'static, - state_provider: impl StoreProvider + 'static, - index_provider: impl StoreProvider + 'static, - ) -> bool { - let _1 = self.stash.as_provider_mut().make_stored(stash_provider); - let _2 = self.state.as_provider_mut().make_stored(state_provider); - let _3 = self.index.as_provider_mut().make_stored(index_provider); - assert_eq!(_1, _2); - assert_eq!(_2, _3); - _1 + pub fn make_stored

(&mut self, provider: P) -> bool + where P: StockStoreProvider + 'static { + provider.make_stored(self) } - pub fn store(&self) -> Result<(), String> { + pub fn store(&self) -> Result<(), StoreError> { self.as_stash_provider().store()?; self.as_state_provider().store()?; self.as_index_provider().store()?; @@ -458,6 +434,13 @@ impl Stock { #[doc(hidden)] pub fn as_index_provider(&self) -> &P { self.index.as_provider() } + #[doc(hidden)] + pub fn as_stash_provider_mut(&mut self) -> &mut S { self.stash.as_provider_mut() } + #[doc(hidden)] + pub fn as_state_provider_mut(&mut self) -> &mut H { self.state.as_provider_mut() } + #[doc(hidden)] + pub fn as_index_provider_mut(&mut self) -> &mut P { self.index.as_provider_mut() } + pub fn ifaces(&self) -> Result + '_, StockError> { let names = self .stash diff --git a/src/persistence/store.rs b/src/persistence/store.rs index 002bc09e..ebe5e62c 100644 --- a/src/persistence/store.rs +++ b/src/persistence/store.rs @@ -23,6 +23,9 @@ use std::error::Error; use std::fmt::Debug; use amplify::confinement; +use strict_encoding::{DeserializeError, SerializeError}; + +use crate::persistence::{IndexProvider, StashProvider, StateProvider, Stock}; #[derive(Debug, Display, Error)] #[display(inner)] @@ -32,23 +35,135 @@ impl From for StoreError { fn from(err: confinement::Error) -> Self { Self(Box::new(err)) } } -pub trait StoreProvider: Send + Debug { - type Object; +impl From for StoreError { + fn from(err: SerializeError) -> Self { Self(Box::new(err)) } +} + +impl From for StoreError { + fn from(err: DeserializeError) -> Self { Self(Box::new(err)) } +} + +pub trait StockStoreProvider: + StoreProvider> +{ + fn make_stored(&self, stock: &mut Stock) -> bool; +} - fn load(&self) -> Result; - fn store(&self, object: &Self::Object) -> Result<(), StoreError>; +pub trait StoreProvider: Send + Debug { + fn load(&self) -> Result; + fn store(&self, object: &T) -> Result<(), StoreError>; } pub trait Stored: Sized { - fn new_stored(provider: impl StoreProvider + 'static, autosave: bool) -> Self; + fn new_stored(provider: impl StoreProvider + 'static, autosave: bool) -> Self; fn load( - provider: impl StoreProvider + 'static, + provider: impl StoreProvider + 'static, autosave: bool, ) -> Result; fn is_dirty(&self) -> bool; fn autosave(&mut self); - fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool; + fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool; fn store(&self) -> Result<(), StoreError>; } + +#[cfg(feature = "fs")] +mod fs { + use std::path::PathBuf; + + use amplify::confinement::U32 as U32MAX; + use strict_encoding::{StrictDeserialize, StrictSerialize}; + + use super::*; + use crate::persistence::{MemIndex, MemStash, MemState, Stock, Stored}; + + impl StoreProvider for PathBuf { + fn load(&self) -> Result { + Ok(MemStash::strict_deserialize_from_file::(&self)?) + } + + fn store(&self, object: &MemStash) -> Result<(), StoreError> { + object.strict_serialize_to_file::(&self)?; + Ok(()) + } + } + + impl StoreProvider for PathBuf { + fn load(&self) -> Result { + Ok(MemState::strict_deserialize_from_file::(&self)?) + } + + fn store(&self, object: &MemState) -> Result<(), StoreError> { + object.strict_serialize_to_file::(&self)?; + Ok(()) + } + } + + impl StoreProvider for PathBuf { + fn load(&self) -> Result { + Ok(MemIndex::strict_deserialize_from_file::(&self)?) + } + + fn store(&self, object: &MemIndex) -> Result<(), StoreError> { + object.strict_serialize_to_file::(&self)?; + Ok(()) + } + } + + impl StoreProvider for PathBuf { + fn load(&self) -> Result { + let mut filename = self.to_owned(); + filename.push("stash.dat"); + let stash: MemStash = filename.load()?; + + let mut filename = self.to_owned(); + filename.push("state.dat"); + let state: MemState = filename.load()?; + + let mut filename = self.to_owned(); + filename.push("index.dat"); + let index: MemIndex = filename.load()?; + + Ok(Stock::with(stash, state, index)) + } + + fn store(&self, stock: &Stock) -> Result<(), StoreError> { + // TODO: Revert files on failure + + let mut filename = self.to_owned(); + filename.push("stash.dat"); + filename.store(stock.as_stash_provider())?; + + let mut filename = self.to_owned(); + filename.push("state.dat"); + filename.store(stock.as_state_provider())?; + + let mut filename = self.to_owned(); + filename.push("index.dat"); + filename.store(stock.as_index_provider())?; + + Ok(()) + } + } + + impl StockStoreProvider for PathBuf { + fn make_stored(&self, stock: &mut Stock) -> bool { + let mut filename = self.to_owned(); + filename.push("stash.dat"); + let _1 = stock.as_stash_provider_mut().make_stored(filename); + + let mut filename = self.to_owned(); + filename.push("state.dat"); + let _2 = stock.as_state_provider_mut().make_stored(filename); + + let mut filename = self.to_owned(); + filename.push("index.dat"); + let _3 = stock.as_index_provider_mut().make_stored(filename); + + assert_eq!(_1, _2); + assert_eq!(_2, _3); + _1 + } + } +} From 4772823bfec095dbce2aeced4e92fa5f8d98ae48 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 30 Aug 2024 01:07:49 +0200 Subject: [PATCH 5/9] persistence: use new nonasync crate and its APIs --- Cargo.lock | 55 +++++--- Cargo.toml | 5 +- src/persistence/fs.rs | 90 +++++++++++++ src/persistence/index.rs | 7 +- src/persistence/memory.rs | 272 +++++++++----------------------------- src/persistence/mod.rs | 8 +- src/persistence/stash.rs | 3 +- src/persistence/state.rs | 3 +- src/persistence/stock.rs | 73 +++++----- src/persistence/store.rs | 169 ----------------------- 10 files changed, 250 insertions(+), 435 deletions(-) create mode 100644 src/persistence/fs.rs delete mode 100644 src/persistence/store.rs diff --git a/Cargo.lock b/Cargo.lock index 005c9f33..1b49b771 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.11.0-beta.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaa9bf6c54f2d232adc6a9e748f359f1b4570d0a498f92e7823321ae7c656b5" dependencies = [ - "amplify", + "amplify 4.7.0", "ascii-armor", "baid64", "blake3", @@ -40,6 +40,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "amplify" +version = "5.0.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41888768802fc62c27b46427127b119e8a16e1f1f59495aced93a340f55eb25" +dependencies = [ + "amplify_derive", + "amplify_num", + "ascii", + "wasm-bindgen", +] + [[package]] name = "amplify_apfloat" version = "0.3.1" @@ -126,7 +138,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4966ac403dc4a666d8131dfe4df684f45acc68d4c7e768db89c463aa5617910" dependencies = [ - "amplify", + "amplify 4.7.0", "baid64", "base85", "sha2", @@ -145,7 +157,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95dabc2759e01e2c382968639868a701f384a18890934f9e75d4feb4d6623794" dependencies = [ - "amplify", + "amplify 4.7.0", "base64", "mnemonic", "sha2", @@ -218,7 +230,7 @@ version = "0.11.0-beta.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d23ea438647522d1f1a8fc8fa1420cc56321433d5d5964636294991f18f9e0c9" dependencies = [ - "amplify", + "amplify 4.7.0", "chrono", "commit_verify", "secp256k1", @@ -233,7 +245,7 @@ version = "0.11.0-beta.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b43a6b5389fd99298fca47f4566dda754fa10bdb41200b5cdfc16147400f5951" dependencies = [ - "amplify", + "amplify 4.7.0", "bp-consensus", "bp-dbc", "bp-seals", @@ -252,7 +264,7 @@ version = "0.11.0-beta.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f70bd407dfcbeadfbc012959e04decbe63e7dc6f30a4f705b804b699b37a0a" dependencies = [ - "amplify", + "amplify 4.7.0", "base85", "bp-consensus", "commit_verify", @@ -267,7 +279,7 @@ version = "0.11.0-beta.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddcb55fe081418fc6e508370eaf8431001274727862e57e6672b6c892b8d3d66" dependencies = [ - "amplify", + "amplify 4.7.0", "bech32", "bp-consensus", "commit_verify", @@ -279,7 +291,7 @@ version = "0.11.0-beta.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497bb5989b9b549cd8e4ebfabc50a5d57cf318ab56affd3d4d7b7749fcd780da" dependencies = [ - "amplify", + "amplify 4.7.0", "baid64", "bp-consensus", "bp-dbc", @@ -338,7 +350,7 @@ version = "0.11.0-beta.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca41bd14a6c400486463a5b0e7e8916b1c7bad554a382f62c3b11bd58dea5934" dependencies = [ - "amplify", + "amplify 4.7.0", "amplify_syn", "proc-macro2", "quote", @@ -351,7 +363,7 @@ version = "0.11.0-beta.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "538b69bbb2f7259c1d07334fa8adae0006c8b559efbdb6daafacb6df249b897b" dependencies = [ - "amplify", + "amplify 4.7.0", "commit_encoding_derive", "rand", "ripemd", @@ -573,6 +585,14 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b8f3a258db515d5e91a904ce4ae3f73e091149b90cadbdb93d210bee07f63b" +[[package]] +name = "nonasync" +version = "0.1.0" +source = "git+https://github.com/rust-amplify/amplify-nonasync#9c6ab8f0e19d80cc787633bad328e7817c256de4" +dependencies = [ + "amplify 5.0.0-beta.1", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -663,7 +683,7 @@ version = "0.11.0-beta.7" source = "git+https://github.com/RGB-WG/rgb-core?branch=nonce#d465448967220b5b5575557fab850a899ae104a4" dependencies = [ "aluvm", - "amplify", + "amplify 4.7.0", "baid64", "bp-core", "chrono", @@ -682,7 +702,7 @@ dependencies = [ name = "rgb-invoice" version = "0.11.0-beta.7" dependencies = [ - "amplify", + "amplify 4.7.0", "baid64", "bp-core", "bp-invoice", @@ -702,7 +722,7 @@ name = "rgb-std" version = "0.11.0-beta.7" dependencies = [ "aluvm", - "amplify", + "amplify 4.7.0", "ascii-armor", "baid64", "base85", @@ -711,6 +731,7 @@ dependencies = [ "commit_verify", "getrandom", "indexmap", + "nonasync", "rand", "rgb-core", "rgb-invoice", @@ -725,7 +746,7 @@ dependencies = [ name = "rgb-stl" version = "0.11.0-beta.7" dependencies = [ - "amplify", + "amplify 4.7.0", "commit_verify", "rgb-std", "strict_types", @@ -900,7 +921,7 @@ version = "2.7.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "662b8c3ff360ff33370e6875dd5bdcbf3cecc992241f30e5f7071227ef693451" dependencies = [ - "amplify", + "amplify 4.7.0", "half", "serde", "strict_encoding_derive", @@ -926,7 +947,7 @@ version = "2.7.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e182f593e9c4f02ccfcea7929bef866cff12a3f8e213338ce48a706bb263c1" dependencies = [ - "amplify", + "amplify 4.7.0", "ascii-armor", "baid64", "half", @@ -1057,7 +1078,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f72ebd3b32f16ee8ace2bd3058c2bfa0f4820992bd4ea86e73ba228bb13dd2b0" dependencies = [ - "amplify", + "amplify 4.7.0", "strict_encoding", ] diff --git a/Cargo.toml b/Cargo.toml index a7c0065c..0e688fc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ license = "Apache-2.0" [workspace.dependencies] amplify = "4.7.0" +nonasync = "0.1.0" ascii-armor = "0.7.1" baid64 = "0.2.2" strict_encoding = "2.7.0-rc.1" @@ -54,6 +55,7 @@ crate-type = ["cdylib", "rlib"] # We need this for WASM [dependencies] amplify = { workspace = true } +nonasync = { workspace = true } ascii-armor = { workspace = true } baid64 = { workspace = true } strict_encoding = { workspace = true } @@ -97,4 +99,5 @@ wasm-bindgen-test = "0.3" features = ["all"] [patch.crates-io] -rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "nonce" } +nonasync = { git = "https://github.com/rust-amplify/amplify-nonasync" } +rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "nonce" } \ No newline at end of file diff --git a/src/persistence/fs.rs b/src/persistence/fs.rs new file mode 100644 index 00000000..f7c26316 --- /dev/null +++ b/src/persistence/fs.rs @@ -0,0 +1,90 @@ +// RGB standard library for working with smart contracts on Bitcoin & Lightning +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2024 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::path::PathBuf; + +use amplify::confinement::U32 as U32MAX; +use nonasync::persistence::{PersistenceError, PersistenceProvider}; +use strict_encoding::{StrictDeserialize, StrictSerialize}; + +use crate::persistence::{MemIndex, MemStash, MemState}; + +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct FsBinStore { + pub stash: PathBuf, + pub state: PathBuf, + pub index: PathBuf, +} + +impl FsBinStore { + pub fn new(path: PathBuf) -> Self { + let mut stash = path.clone(); + stash.push("stash.dat"); + let mut state = path.clone(); + state.push("state.dat"); + let mut index = path.clone(); + index.push("index.dat"); + + Self { + stash, + state, + index, + } + } +} +impl PersistenceProvider for FsBinStore { + fn load(&self) -> Result { + MemStash::strict_deserialize_from_file::(&self.stash) + .map_err(PersistenceError::with) + } + + fn store(&self, object: &MemStash) -> Result<(), PersistenceError> { + object + .strict_serialize_to_file::(&self.stash) + .map_err(PersistenceError::with) + } +} + +impl PersistenceProvider for FsBinStore { + fn load(&self) -> Result { + MemState::strict_deserialize_from_file::(&self.state) + .map_err(PersistenceError::with) + } + + fn store(&self, object: &MemState) -> Result<(), PersistenceError> { + object + .strict_serialize_to_file::(&self.state) + .map_err(PersistenceError::with) + } +} + +impl PersistenceProvider for FsBinStore { + fn load(&self) -> Result { + MemIndex::strict_deserialize_from_file::(&self.index) + .map_err(PersistenceError::with) + } + + fn store(&self, object: &MemIndex) -> Result<(), PersistenceError> { + object + .strict_serialize_to_file::(&self.index) + .map_err(PersistenceError::with) + } +} diff --git a/src/persistence/index.rs b/src/persistence/index.rs index 247e8cee..bc17a46d 100644 --- a/src/persistence/index.rs +++ b/src/persistence/index.rs @@ -24,6 +24,7 @@ use std::error::Error; use std::fmt::Debug; use amplify::confinement; +use nonasync::persistence::Persisting; use rgb::{ Assign, AssignmentType, BundleId, ContractId, ExposedState, Extension, Genesis, GenesisSeal, GraphSeal, OpId, Operation, Opout, TransitionBundle, TypedAssigns, XChain, XOutputSeal, @@ -31,7 +32,7 @@ use rgb::{ }; use crate::containers::{BundledWitness, ConsignmentExt, ToWitnessId}; -use crate::persistence::{StoreError, StoreTransaction}; +use crate::persistence::{MemError, StoreTransaction}; use crate::SecretSeal; #[derive(Debug, Display, Error, From)] @@ -84,7 +85,7 @@ impl From::Error>> f } } -impl From for IndexWriteError { +impl From for IndexWriteError { fn from(err: confinement::Error) -> Self { IndexWriteError::Connectivity(err.into()) } } @@ -349,7 +350,7 @@ impl StoreTransaction for Index

{ fn rollback_transaction(&mut self) { self.provider.rollback_transaction() } } -pub trait IndexProvider: Debug + IndexReadProvider + IndexWriteProvider {} +pub trait IndexProvider: Debug + Persisting + IndexReadProvider + IndexWriteProvider {} pub trait IndexReadProvider { type Error: Clone + Eq + Error; diff --git a/src/persistence/memory.rs b/src/persistence/memory.rs index 78b6a7ce..96eb56c6 100644 --- a/src/persistence/memory.rs +++ b/src/persistence/memory.rs @@ -35,6 +35,7 @@ use amplify::confinement::{ use amplify::num::u24; use bp::dbc::tapret::TapretCommitment; use commit_verify::{CommitId, Conceal}; +use nonasync::persistence::{Persistence, PersistenceError, Persisting}; use rgb::validation::ResolveWitness; use rgb::vm::{ ContractStateAccess, ContractStateEvolve, GlobalContractState, GlobalOrd, GlobalStateIter, @@ -54,8 +55,8 @@ use super::{ ContractIfaceError, ContractStateRead, ContractStateWrite, IndexInconsistency, IndexProvider, IndexReadError, IndexReadProvider, IndexWriteError, IndexWriteProvider, SchemaIfaces, StashInconsistency, StashProvider, StashProviderError, StashReadProvider, StashWriteProvider, - StateInconsistency, StateProvider, StateReadProvider, StateWriteProvider, StoreError, - StoreProvider, StoreTransaction, Stored, UpdateRes, + StateInconsistency, StateProvider, StateReadProvider, StateWriteProvider, StoreTransaction, + UpdateRes, }; use crate::containers::{ AnchorSet, ContentId, ContentRef, ContentSigs, SealWitness, SigBlob, Supplement, TrustLevel, @@ -64,6 +65,16 @@ use crate::contract::{GlobalOut, KnownState, OpWitness, OutputAssignment}; use crate::interface::{Iface, IfaceClass, IfaceId, IfaceImpl, IfaceRef}; use crate::LIB_NAME_RGB_STORAGE; +#[derive(Debug, Display, Error, From)] +#[display(inner)] +pub enum MemError { + #[from] + Persistence(PersistenceError), + + #[from] + Confinement(confinement::Error), +} + ////////// // STASH ////////// @@ -74,17 +85,9 @@ use crate::LIB_NAME_RGB_STORAGE; #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_STORAGE, dumb = Self::in_memory())] pub struct MemStash { - #[getter(prefix = "is_", as_copy)] - #[strict_type(skip)] - dirty: bool, - - #[getter(prefix = "is_", as_copy)] - #[strict_type(skip)] - autosave: bool, - #[getter(skip)] #[strict_type(skip)] - store_provider: Option>>, + persistence: Option>, schemata: TinyOrdMap, ifaces: TinyOrdMap, @@ -107,9 +110,7 @@ impl StrictDeserialize for MemStash {} impl MemStash { pub fn in_memory() -> Self { Self { - dirty: false, - autosave: false, - store_provider: None, + persistence: none!(), schemata: empty!(), ifaces: empty!(), geneses: empty!(), @@ -127,21 +128,23 @@ impl MemStash { } } -impl StoreTransaction for MemStash { - type TransactionErr = StoreError; +impl Persisting for MemStash { + #[inline] + fn persistence(&self) -> Option<&Persistence> { self.persistence.as_ref() } + #[inline] + fn persistence_mut(&mut self) -> Option<&mut Persistence> { self.persistence.as_mut() } +} +impl StoreTransaction for MemStash { + type TransactionErr = MemError; + #[inline] fn begin_transaction(&mut self) -> Result<(), Self::TransactionErr> { - self.dirty = true; + self.mark_dirty(); Ok(()) } - - fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> { - if self.dirty && self.autosave { - self.store()?; - } - Ok(()) - } - + #[inline] + fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> { Ok(self.store()?) } + #[inline] fn rollback_transaction(&mut self) { unreachable!() } } @@ -331,7 +334,7 @@ impl StashReadProvider for MemStash { } impl StashWriteProvider for MemStash { - type Error = StoreError; + type Error = MemError; fn replace_schema(&mut self, schema: Schema) -> Result { let schema_id = schema.schema_id(); @@ -463,17 +466,9 @@ impl StashWriteProvider for MemStash { #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_STORAGE, dumb = Self::in_memory())] pub struct MemState { - #[getter(prefix = "is_", as_copy)] - #[strict_type(skip)] - dirty: bool, - - #[getter(prefix = "is_", as_copy)] - #[strict_type(skip)] - autosave: bool, - #[getter(skip)] #[strict_type(skip)] - store_provider: Option>>, + persistence: Option>, witnesses: LargeOrdMap, contracts: TinyOrdMap, @@ -485,30 +480,30 @@ impl StrictDeserialize for MemState {} impl MemState { pub fn in_memory() -> Self { Self { - dirty: false, - autosave: false, - store_provider: None, + persistence: none!(), witnesses: empty!(), contracts: empty!(), } } } -impl StoreTransaction for MemState { - type TransactionErr = StoreError; +impl Persisting for MemState { + #[inline] + fn persistence(&self) -> Option<&Persistence> { self.persistence.as_ref() } + #[inline] + fn persistence_mut(&mut self) -> Option<&mut Persistence> { self.persistence.as_mut() } +} +impl StoreTransaction for MemState { + type TransactionErr = MemError; + #[inline] fn begin_transaction(&mut self) -> Result<(), Self::TransactionErr> { - self.dirty = true; - Ok(()) - } - - fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> { - if self.dirty && self.autosave { - self.store()?; - } + self.mark_dirty(); Ok(()) } - + #[inline] + fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> { Ok(self.store()?) } + #[inline] fn rollback_transaction(&mut self) { unreachable!() } } @@ -557,7 +552,7 @@ impl StateReadProvider for MemState { impl StateWriteProvider for MemState { type ContractWrite<'a> = MemContractWriter<'a>; - type Error = StoreError; + type Error = MemError; fn register_contract( &mut self, @@ -1036,9 +1031,10 @@ impl ContractStateEvolve for MemContract { } .map_err(|err| { // TODO: remove once evolve_state would accept arbitrary errors - *err.0 - .downcast::() - .expect("only confinement errors are possible") + match err { + MemError::Persistence(_) => unreachable!("only confinement errors are possible"), + MemError::Confinement(e) => e, + } })?; Ok(()) } @@ -1094,7 +1090,7 @@ pub struct MemContractWriter<'mem> { } impl<'mem> ContractStateWrite for MemContractWriter<'mem> { - type Error = StoreError; + type Error = MemError; /// # Panics /// @@ -1168,17 +1164,9 @@ pub struct ContractIndex { #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_STORAGE, dumb = Self::in_memory())] pub struct MemIndex { - #[getter(prefix = "is_", as_copy)] - #[strict_type(skip)] - dirty: bool, - - #[getter(prefix = "is_", as_copy)] - #[strict_type(skip)] - autosave: bool, - #[getter(skip)] #[strict_type(skip)] - store_provider: Option>>, + persistence: Option>, op_bundle_index: MediumOrdMap, bundle_contract_index: MediumOrdMap, @@ -1193,9 +1181,7 @@ impl StrictDeserialize for MemIndex {} impl MemIndex { pub fn in_memory() -> Self { Self { - dirty: false, - autosave: false, - store_provider: None, + persistence: None, op_bundle_index: empty!(), bundle_contract_index: empty!(), bundle_witness_index: empty!(), @@ -1205,21 +1191,23 @@ impl MemIndex { } } -impl StoreTransaction for MemIndex { - type TransactionErr = StoreError; +impl Persisting for MemIndex { + #[inline] + fn persistence(&self) -> Option<&Persistence> { self.persistence.as_ref() } + #[inline] + fn persistence_mut(&mut self) -> Option<&mut Persistence> { self.persistence.as_mut() } +} +impl StoreTransaction for MemIndex { + type TransactionErr = MemError; + #[inline] fn begin_transaction(&mut self) -> Result<(), Self::TransactionErr> { - self.dirty = true; - Ok(()) - } - - fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> { - if self.dirty && self.autosave { - self.store()?; - } + self.mark_dirty(); Ok(()) } - + #[inline] + fn commit_transaction(&mut self) -> Result<(), Self::TransactionErr> { Ok(self.store()?) } + #[inline] fn rollback_transaction(&mut self) { unreachable!() } } @@ -1314,7 +1302,7 @@ impl IndexReadProvider for MemIndex { } impl IndexWriteProvider for MemIndex { - type Error = StoreError; + type Error = MemError; fn register_contract(&mut self, contract_id: ContractId) -> Result { if !self.contract_index.contains_key(&contract_id) { @@ -1452,131 +1440,3 @@ impl IndexWriteProvider for MemIndex { Ok(()) } } - -mod store { - use crate::persistence::store::Stored; - use crate::persistence::{MemIndex, MemStash, MemState, StoreError, StoreProvider}; - - impl Stored for MemStash { - fn new_stored(provider: impl StoreProvider + 'static, autosave: bool) -> Self { - Self { - dirty: true, - autosave, - store_provider: Some(Box::new(provider)), - ..Self::in_memory() - } - } - - fn load( - provider: impl StoreProvider + 'static, - autosave: bool, - ) -> Result { - let mut me = provider.load()?; - me.autosave = autosave; - me.store_provider = Some(Box::new(provider)); - Ok(me) - } - - fn is_dirty(&self) -> bool { self.dirty } - - fn autosave(&mut self) { self.autosave = true; } - - fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool { - let res = self.store_provider.is_some(); - self.store_provider = Some(Box::new(provider)); - self.dirty = true; - res - } - - fn store(&self) -> Result<(), StoreError> { - if self.is_dirty() { - if let Some(provider) = &self.store_provider { - provider.store(self)?; - } - } - Ok(()) - } - } - - impl Stored for MemState { - fn new_stored(provider: impl StoreProvider + 'static, autosave: bool) -> Self { - Self { - dirty: true, - autosave, - store_provider: Some(Box::new(provider)), - ..Self::in_memory() - } - } - - fn load( - provider: impl StoreProvider + 'static, - autosave: bool, - ) -> Result { - let mut me = provider.load()?; - me.autosave = autosave; - me.store_provider = Some(Box::new(provider)); - Ok(me) - } - - fn is_dirty(&self) -> bool { self.dirty } - - fn autosave(&mut self) { self.autosave = true; } - - fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool { - let res = self.store_provider.is_some(); - self.store_provider = Some(Box::new(provider)); - self.dirty = true; - res - } - - fn store(&self) -> Result<(), StoreError> { - if self.is_dirty() { - if let Some(provider) = &self.store_provider { - provider.store(self)?; - } - } - Ok(()) - } - } - - impl Stored for MemIndex { - fn new_stored(provider: impl StoreProvider + 'static, autosave: bool) -> Self { - Self { - dirty: true, - autosave, - store_provider: Some(Box::new(provider)), - ..Self::in_memory() - } - } - - fn load( - provider: impl StoreProvider + 'static, - autosave: bool, - ) -> Result { - let mut me = provider.load()?; - me.autosave = autosave; - me.store_provider = Some(Box::new(provider)); - Ok(me) - } - - fn is_dirty(&self) -> bool { self.dirty } - - fn autosave(&mut self) { self.autosave = true; } - - fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool { - let res = self.store_provider.is_some(); - self.store_provider = Some(Box::new(provider)); - self.dirty = true; - res - } - - fn store(&self) -> Result<(), StoreError> { - if self.is_dirty() { - if let Some(provider) = &self.store_provider { - provider.store(self)?; - } - } - Ok(()) - } - } -} diff --git a/src/persistence/mod.rs b/src/persistence/mod.rs index 591ce935..321f7b14 100644 --- a/src/persistence/mod.rs +++ b/src/persistence/mod.rs @@ -36,13 +36,16 @@ mod state; mod index; mod memory; -mod store; +#[cfg(feature = "fs")] +pub mod fs; pub use index::{ Index, IndexError, IndexInconsistency, IndexProvider, IndexReadError, IndexReadProvider, IndexWriteError, IndexWriteProvider, }; -pub use memory::{MemContract, MemContractState, MemGlobalState, MemIndex, MemStash, MemState}; +pub use memory::{ + MemContract, MemContractState, MemError, MemGlobalState, MemIndex, MemStash, MemState, +}; pub use stash::{ ProviderError as StashProviderError, SchemaIfaces, Stash, StashDataError, StashError, StashInconsistency, StashProvider, StashReadProvider, StashWriteProvider, @@ -55,7 +58,6 @@ pub use stock::{ ComposeError, ConsignError, ContractIfaceError, FasciaError, InputError as StockInputError, Stock, StockError, StockErrorAll, StockErrorMem, UpdateRes, }; -pub use store::{StockStoreProvider, StoreError, StoreProvider, Stored}; pub trait StoreTransaction { type TransactionErr: std::error::Error; diff --git a/src/persistence/stash.rs b/src/persistence/stash.rs index d5f322b4..535594d0 100644 --- a/src/persistence/stash.rs +++ b/src/persistence/stash.rs @@ -29,6 +29,7 @@ use amplify::confinement::{Confined, MediumBlob, TinyOrdMap}; use bp::dbc::anchor::MergeError; use bp::dbc::tapret::TapretCommitment; use commit_verify::mpc; +use nonasync::persistence::Persisting; use rgb::validation::Scripts; use rgb::{ AttachId, BundleId, ContractId, Extension, Genesis, GraphSeal, Identity, OpId, Operation, @@ -618,7 +619,7 @@ impl StoreTransaction for Stash

{ fn rollback_transaction(&mut self) { self.provider.rollback_transaction() } } -pub trait StashProvider: Debug + StashReadProvider + StashWriteProvider {} +pub trait StashProvider: Debug + Persisting + StashReadProvider + StashWriteProvider {} pub trait StashReadProvider { /// Error type which must indicate problems on data retrieval. diff --git a/src/persistence/state.rs b/src/persistence/state.rs index 2ddbab6b..94a93fd2 100644 --- a/src/persistence/state.rs +++ b/src/persistence/state.rs @@ -26,6 +26,7 @@ use std::fmt::Debug; use std::iter; use invoice::Amount; +use nonasync::persistence::Persisting; use rgb::validation::{ResolveWitness, WitnessResolverError}; use rgb::vm::{ContractStateAccess, WitnessOrd}; use rgb::{ @@ -254,7 +255,7 @@ impl StoreTransaction for State

{ fn rollback_transaction(&mut self) { self.provider.rollback_transaction() } } -pub trait StateProvider: Debug + StateReadProvider + StateWriteProvider {} +pub trait StateProvider: Debug + Persisting + StateReadProvider + StateWriteProvider {} pub trait StateReadProvider { type ContractRead<'a>: ContractStateRead diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index 8356ab56..0951994b 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -32,6 +32,7 @@ use bp::seals::txout::CloseMethod; use bp::Vout; use chrono::Utc; use invoice::{Amount, Beneficiary, InvoiceState, NonFungible, RgbInvoice}; +use nonasync::persistence::{PersistenceError, PersistenceProvider}; use rgb::validation::{DbcProof, EAnchor, ResolveWitness, WitnessResolverError}; use rgb::{ validation, AssignmentType, BlindingFactor, BundleId, ContractId, DataState, GraphSeal, @@ -45,7 +46,7 @@ use super::{ IndexWriteProvider, MemIndex, MemStash, MemState, PersistedState, SchemaIfaces, Stash, StashDataError, StashError, StashInconsistency, StashProvider, StashReadProvider, StashWriteProvider, State, StateError, StateInconsistency, StateProvider, StateReadProvider, - StateWriteProvider, StockStoreProvider, StoreError, StoreProvider, StoreTransaction, Stored, + StateWriteProvider, StoreTransaction, }; use crate::containers::{ AnchorSet, AnchoredBundles, Batch, BuilderSeal, BundledWitness, Consignment, ContainerVer, @@ -375,44 +376,48 @@ impl Stock { } } -impl Stock -where - S: Stored, - H: Stored, - I: Stored, -{ - pub fn load

(provider: P, autosave: bool) -> Result - where P: StoreProvider + 'static { - let mut stock = provider.load()?; - if autosave { - stock.stash.as_provider_mut().autosave(); - stock.state.as_provider_mut().autosave(); - stock.index.as_provider_mut().autosave(); - } - Ok(stock) - } - - pub fn autosave(&mut self) { - self.stash.as_provider_mut().autosave(); - self.state.as_provider_mut().autosave(); - self.index.as_provider_mut().autosave(); +impl Stock { + pub fn load

(provider: P, autosave: bool) -> Result + where P: Clone + + PersistenceProvider + + PersistenceProvider + + PersistenceProvider + + 'static { + let stash = S::load(provider.clone(), autosave)?; + let state = H::load(provider.clone(), autosave)?; + let index = I::load(provider, autosave)?; + Ok(Self::with(stash, state, index)) } - pub fn is_dirty(&self) -> bool { - self.as_stash_provider().is_dirty() || - self.as_state_provider().is_dirty() || - self.as_index_provider().is_dirty() + pub fn make_persistent

( + &mut self, + provider: P, + autosave: bool, + ) -> Result + where + P: Clone + + PersistenceProvider + + PersistenceProvider + + PersistenceProvider + + 'static, + { + Ok(self + .as_stash_provider_mut() + .make_persistent(provider.clone(), autosave)? + && self + .as_state_provider_mut() + .make_persistent(provider.clone(), autosave)? + && self + .as_index_provider_mut() + .make_persistent(provider, autosave)?) } - pub fn make_stored

(&mut self, provider: P) -> bool - where P: StockStoreProvider + 'static { - provider.make_stored(self) - } + pub fn store(&mut self) -> Result<(), PersistenceError> { + // TODO: Revert on failure - pub fn store(&self) -> Result<(), StoreError> { - self.as_stash_provider().store()?; - self.as_state_provider().store()?; - self.as_index_provider().store()?; + self.as_stash_provider_mut().store()?; + self.as_state_provider_mut().store()?; + self.as_index_provider_mut().store()?; Ok(()) } diff --git a/src/persistence/store.rs b/src/persistence/store.rs deleted file mode 100644 index ebe5e62c..00000000 --- a/src/persistence/store.rs +++ /dev/null @@ -1,169 +0,0 @@ -// RGB standard library for working with smart contracts on Bitcoin & Lightning -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::error::Error; -use std::fmt::Debug; - -use amplify::confinement; -use strict_encoding::{DeserializeError, SerializeError}; - -use crate::persistence::{IndexProvider, StashProvider, StateProvider, Stock}; - -#[derive(Debug, Display, Error)] -#[display(inner)] -pub struct StoreError(pub Box); - -impl From for StoreError { - fn from(err: confinement::Error) -> Self { Self(Box::new(err)) } -} - -impl From for StoreError { - fn from(err: SerializeError) -> Self { Self(Box::new(err)) } -} - -impl From for StoreError { - fn from(err: DeserializeError) -> Self { Self(Box::new(err)) } -} - -pub trait StockStoreProvider: - StoreProvider> -{ - fn make_stored(&self, stock: &mut Stock) -> bool; -} - -pub trait StoreProvider: Send + Debug { - fn load(&self) -> Result; - fn store(&self, object: &T) -> Result<(), StoreError>; -} - -pub trait Stored: Sized { - fn new_stored(provider: impl StoreProvider + 'static, autosave: bool) -> Self; - fn load( - provider: impl StoreProvider + 'static, - autosave: bool, - ) -> Result; - - fn is_dirty(&self) -> bool; - fn autosave(&mut self); - fn make_stored(&mut self, provider: impl StoreProvider + 'static) -> bool; - - fn store(&self) -> Result<(), StoreError>; -} - -#[cfg(feature = "fs")] -mod fs { - use std::path::PathBuf; - - use amplify::confinement::U32 as U32MAX; - use strict_encoding::{StrictDeserialize, StrictSerialize}; - - use super::*; - use crate::persistence::{MemIndex, MemStash, MemState, Stock, Stored}; - - impl StoreProvider for PathBuf { - fn load(&self) -> Result { - Ok(MemStash::strict_deserialize_from_file::(&self)?) - } - - fn store(&self, object: &MemStash) -> Result<(), StoreError> { - object.strict_serialize_to_file::(&self)?; - Ok(()) - } - } - - impl StoreProvider for PathBuf { - fn load(&self) -> Result { - Ok(MemState::strict_deserialize_from_file::(&self)?) - } - - fn store(&self, object: &MemState) -> Result<(), StoreError> { - object.strict_serialize_to_file::(&self)?; - Ok(()) - } - } - - impl StoreProvider for PathBuf { - fn load(&self) -> Result { - Ok(MemIndex::strict_deserialize_from_file::(&self)?) - } - - fn store(&self, object: &MemIndex) -> Result<(), StoreError> { - object.strict_serialize_to_file::(&self)?; - Ok(()) - } - } - - impl StoreProvider for PathBuf { - fn load(&self) -> Result { - let mut filename = self.to_owned(); - filename.push("stash.dat"); - let stash: MemStash = filename.load()?; - - let mut filename = self.to_owned(); - filename.push("state.dat"); - let state: MemState = filename.load()?; - - let mut filename = self.to_owned(); - filename.push("index.dat"); - let index: MemIndex = filename.load()?; - - Ok(Stock::with(stash, state, index)) - } - - fn store(&self, stock: &Stock) -> Result<(), StoreError> { - // TODO: Revert files on failure - - let mut filename = self.to_owned(); - filename.push("stash.dat"); - filename.store(stock.as_stash_provider())?; - - let mut filename = self.to_owned(); - filename.push("state.dat"); - filename.store(stock.as_state_provider())?; - - let mut filename = self.to_owned(); - filename.push("index.dat"); - filename.store(stock.as_index_provider())?; - - Ok(()) - } - } - - impl StockStoreProvider for PathBuf { - fn make_stored(&self, stock: &mut Stock) -> bool { - let mut filename = self.to_owned(); - filename.push("stash.dat"); - let _1 = stock.as_stash_provider_mut().make_stored(filename); - - let mut filename = self.to_owned(); - filename.push("state.dat"); - let _2 = stock.as_state_provider_mut().make_stored(filename); - - let mut filename = self.to_owned(); - filename.push("index.dat"); - let _3 = stock.as_index_provider_mut().make_stored(filename); - - assert_eq!(_1, _2); - assert_eq!(_2, _3); - _1 - } - } -} From db393426b0b2ec94106e5881dde5b002df593896 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 30 Aug 2024 11:37:51 +0200 Subject: [PATCH 6/9] persistence: impl CloneNoPersistence for stock objects --- Cargo.lock | 2 +- src/persistence/index.rs | 17 ++++++++++++--- src/persistence/memory.rs | 46 ++++++++++++++++++++++++++++++++++++++- src/persistence/stash.rs | 17 ++++++++++++--- src/persistence/state.rs | 17 ++++++++++++--- src/persistence/stock.rs | 14 ++++++++++-- 6 files changed, 100 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b49b771..629ff1d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -588,7 +588,7 @@ checksum = "f2b8f3a258db515d5e91a904ce4ae3f73e091149b90cadbdb93d210bee07f63b" [[package]] name = "nonasync" version = "0.1.0" -source = "git+https://github.com/rust-amplify/amplify-nonasync#9c6ab8f0e19d80cc787633bad328e7817c256de4" +source = "git+https://github.com/rust-amplify/amplify-nonasync#d52db387df2282a73984d2d5ef238135d5267930" dependencies = [ "amplify 5.0.0-beta.1", ] diff --git a/src/persistence/index.rs b/src/persistence/index.rs index bc17a46d..5d0dfe38 100644 --- a/src/persistence/index.rs +++ b/src/persistence/index.rs @@ -24,7 +24,7 @@ use std::error::Error; use std::fmt::Debug; use amplify::confinement; -use nonasync::persistence::Persisting; +use nonasync::persistence::{CloneNoPersistence, Persisting}; use rgb::{ Assign, AssignmentType, BundleId, ContractId, ExposedState, Extension, Genesis, GenesisSeal, GraphSeal, OpId, Operation, Opout, TransitionBundle, TypedAssigns, XChain, XOutputSeal, @@ -124,11 +124,19 @@ pub enum IndexInconsistency { BundleWitnessUnknown(BundleId), } -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct Index { provider: P, } +impl CloneNoPersistence for Index

{ + fn clone_no_persistence(&self) -> Self { + Self { + provider: self.provider.clone_no_persistence(), + } + } +} + impl Default for Index

where P: Default { @@ -350,7 +358,10 @@ impl StoreTransaction for Index

{ fn rollback_transaction(&mut self) { self.provider.rollback_transaction() } } -pub trait IndexProvider: Debug + Persisting + IndexReadProvider + IndexWriteProvider {} +pub trait IndexProvider: + Debug + CloneNoPersistence + Persisting + IndexReadProvider + IndexWriteProvider +{ +} pub trait IndexReadProvider { type Error: Clone + Eq + Error; diff --git a/src/persistence/memory.rs b/src/persistence/memory.rs index 96eb56c6..6630bb26 100644 --- a/src/persistence/memory.rs +++ b/src/persistence/memory.rs @@ -35,7 +35,7 @@ use amplify::confinement::{ use amplify::num::u24; use bp::dbc::tapret::TapretCommitment; use commit_verify::{CommitId, Conceal}; -use nonasync::persistence::{Persistence, PersistenceError, Persisting}; +use nonasync::persistence::{CloneNoPersistence, Persistence, PersistenceError, Persisting}; use rgb::validation::ResolveWitness; use rgb::vm::{ ContractStateAccess, ContractStateEvolve, GlobalContractState, GlobalOrd, GlobalStateIter, @@ -128,6 +128,27 @@ impl MemStash { } } +impl CloneNoPersistence for MemStash { + fn clone_no_persistence(&self) -> Self { + Self { + persistence: None, + schemata: self.schemata.clone(), + ifaces: self.ifaces.clone(), + geneses: self.geneses.clone(), + suppl: self.suppl.clone(), + bundles: self.bundles.clone(), + extensions: self.extensions.clone(), + witnesses: self.witnesses.clone(), + attachments: self.attachments.clone(), + secret_seals: self.secret_seals.clone(), + type_system: self.type_system.clone(), + identities: self.identities.clone(), + libs: self.libs.clone(), + sigs: self.sigs.clone(), + } + } +} + impl Persisting for MemStash { #[inline] fn persistence(&self) -> Option<&Persistence> { self.persistence.as_ref() } @@ -487,6 +508,16 @@ impl MemState { } } +impl CloneNoPersistence for MemState { + fn clone_no_persistence(&self) -> Self { + Self { + persistence: None, + witnesses: self.witnesses.clone(), + contracts: self.contracts.clone(), + } + } +} + impl Persisting for MemState { #[inline] fn persistence(&self) -> Option<&Persistence> { self.persistence.as_ref() } @@ -1191,6 +1222,19 @@ impl MemIndex { } } +impl CloneNoPersistence for MemIndex { + fn clone_no_persistence(&self) -> Self { + Self { + persistence: None, + op_bundle_index: self.op_bundle_index.clone(), + bundle_contract_index: self.bundle_contract_index.clone(), + bundle_witness_index: self.bundle_witness_index.clone(), + contract_index: self.contract_index.clone(), + terminal_index: self.terminal_index.clone(), + } + } +} + impl Persisting for MemIndex { #[inline] fn persistence(&self) -> Option<&Persistence> { self.persistence.as_ref() } diff --git a/src/persistence/stash.rs b/src/persistence/stash.rs index 535594d0..deda9605 100644 --- a/src/persistence/stash.rs +++ b/src/persistence/stash.rs @@ -29,7 +29,7 @@ use amplify::confinement::{Confined, MediumBlob, TinyOrdMap}; use bp::dbc::anchor::MergeError; use bp::dbc::tapret::TapretCommitment; use commit_verify::mpc; -use nonasync::persistence::Persisting; +use nonasync::persistence::{CloneNoPersistence, Persisting}; use rgb::validation::Scripts; use rgb::{ AttachId, BundleId, ContractId, Extension, Genesis, GraphSeal, Identity, OpId, Operation, @@ -186,11 +186,19 @@ impl SchemaIfaces { } } -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct Stash { provider: P, } +impl CloneNoPersistence for Stash

{ + fn clone_no_persistence(&self) -> Self { + Self { + provider: self.provider.clone_no_persistence(), + } + } +} + impl Default for Stash

where P: Default { @@ -619,7 +627,10 @@ impl StoreTransaction for Stash

{ fn rollback_transaction(&mut self) { self.provider.rollback_transaction() } } -pub trait StashProvider: Debug + Persisting + StashReadProvider + StashWriteProvider {} +pub trait StashProvider: + Debug + CloneNoPersistence + Persisting + StashReadProvider + StashWriteProvider +{ +} pub trait StashReadProvider { /// Error type which must indicate problems on data retrieval. diff --git a/src/persistence/state.rs b/src/persistence/state.rs index 94a93fd2..866b5fea 100644 --- a/src/persistence/state.rs +++ b/src/persistence/state.rs @@ -26,7 +26,7 @@ use std::fmt::Debug; use std::iter; use invoice::Amount; -use nonasync::persistence::Persisting; +use nonasync::persistence::{CloneNoPersistence, Persisting}; use rgb::validation::{ResolveWitness, WitnessResolverError}; use rgb::vm::{ContractStateAccess, WitnessOrd}; use rgb::{ @@ -92,11 +92,19 @@ impl PersistedState { } } -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct State { provider: P, } +impl CloneNoPersistence for State

{ + fn clone_no_persistence(&self) -> Self { + Self { + provider: self.provider.clone_no_persistence(), + } + } +} + impl Default for State

where P: Default { @@ -255,7 +263,10 @@ impl StoreTransaction for State

{ fn rollback_transaction(&mut self) { self.provider.rollback_transaction() } } -pub trait StateProvider: Debug + Persisting + StateReadProvider + StateWriteProvider {} +pub trait StateProvider: + Debug + CloneNoPersistence + Persisting + StateReadProvider + StateWriteProvider +{ +} pub trait StateReadProvider { type ContractRead<'a>: ContractStateRead diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index 0951994b..10d1cbd1 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -32,7 +32,7 @@ use bp::seals::txout::CloseMethod; use bp::Vout; use chrono::Utc; use invoice::{Amount, Beneficiary, InvoiceState, NonFungible, RgbInvoice}; -use nonasync::persistence::{PersistenceError, PersistenceProvider}; +use nonasync::persistence::{CloneNoPersistence, PersistenceError, PersistenceProvider}; use rgb::validation::{DbcProof, EAnchor, ResolveWitness, WitnessResolverError}; use rgb::{ validation, AssignmentType, BlindingFactor, BundleId, ContractId, DataState, GraphSeal, @@ -343,7 +343,7 @@ stock_err_conv!(ContractIfaceError, InputError); pub type StockErrorMem = StockError; pub type StockErrorAll = StockError; -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct Stock< S: StashProvider = MemStash, H: StateProvider = MemState, @@ -354,6 +354,16 @@ pub struct Stock< index: Index

, } +impl CloneNoPersistence for Stock { + fn clone_no_persistence(&self) -> Self { + Self { + stash: self.stash.clone_no_persistence(), + state: self.state.clone_no_persistence(), + index: self.index.clone_no_persistence(), + } + } +} + impl Default for Stock where S: Default, From a3dde4c2631ba86408db941651a0263fa5dfcb2d Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 31 Aug 2024 17:51:14 +0200 Subject: [PATCH 7/9] persistence: fix invalid persistence replacement --- Cargo.lock | 2 +- src/persistence/memory.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 629ff1d6..5f0115c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -588,7 +588,7 @@ checksum = "f2b8f3a258db515d5e91a904ce4ae3f73e091149b90cadbdb93d210bee07f63b" [[package]] name = "nonasync" version = "0.1.0" -source = "git+https://github.com/rust-amplify/amplify-nonasync#d52db387df2282a73984d2d5ef238135d5267930" +source = "git+https://github.com/rust-amplify/amplify-nonasync#b1bf3542060beabe3dcdf66517be6ee3eb2ac302" dependencies = [ "amplify 5.0.0-beta.1", ] diff --git a/src/persistence/memory.rs b/src/persistence/memory.rs index 6630bb26..110c3f4d 100644 --- a/src/persistence/memory.rs +++ b/src/persistence/memory.rs @@ -154,6 +154,8 @@ impl Persisting for MemStash { fn persistence(&self) -> Option<&Persistence> { self.persistence.as_ref() } #[inline] fn persistence_mut(&mut self) -> Option<&mut Persistence> { self.persistence.as_mut() } + #[inline] + fn as_mut_persistence(&mut self) -> &mut Option> { &mut self.persistence } } impl StoreTransaction for MemStash { @@ -523,6 +525,8 @@ impl Persisting for MemState { fn persistence(&self) -> Option<&Persistence> { self.persistence.as_ref() } #[inline] fn persistence_mut(&mut self) -> Option<&mut Persistence> { self.persistence.as_mut() } + #[inline] + fn as_mut_persistence(&mut self) -> &mut Option> { &mut self.persistence } } impl StoreTransaction for MemState { @@ -1240,6 +1244,8 @@ impl Persisting for MemIndex { fn persistence(&self) -> Option<&Persistence> { self.persistence.as_ref() } #[inline] fn persistence_mut(&mut self) -> Option<&mut Persistence> { self.persistence.as_mut() } + #[inline] + fn as_mut_persistence(&mut self) -> &mut Option> { &mut self.persistence } } impl StoreTransaction for MemIndex { From 10690ef63fe09e413eb1068fcc7b67c4dcdb607d Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 31 Aug 2024 18:05:37 +0200 Subject: [PATCH 8/9] persistence: fix invalid make_persistence implementation --- src/persistence/stock.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index 10d1cbd1..8bdf1890 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -411,15 +411,16 @@ impl Stock { + PersistenceProvider + 'static, { - Ok(self + let a = self .as_stash_provider_mut() - .make_persistent(provider.clone(), autosave)? - && self - .as_state_provider_mut() - .make_persistent(provider.clone(), autosave)? - && self - .as_index_provider_mut() - .make_persistent(provider, autosave)?) + .make_persistent(provider.clone(), autosave)?; + let b = self + .as_state_provider_mut() + .make_persistent(provider.clone(), autosave)?; + let c = self + .as_index_provider_mut() + .make_persistent(provider, autosave)?; + Ok(a && b && c) } pub fn store(&mut self) -> Result<(), PersistenceError> { From 47e81b939e66ed03856ba3b970c44e4070ac8a25 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 2 Sep 2024 21:35:58 +0200 Subject: [PATCH 9/9] persistence: create directories on FsBinStore construction --- src/persistence/fs.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/persistence/fs.rs b/src/persistence/fs.rs index f7c26316..600cb217 100644 --- a/src/persistence/fs.rs +++ b/src/persistence/fs.rs @@ -20,6 +20,7 @@ // limitations under the License. use std::path::PathBuf; +use std::{fs, io}; use amplify::confinement::U32 as U32MAX; use nonasync::persistence::{PersistenceError, PersistenceProvider}; @@ -35,7 +36,9 @@ pub struct FsBinStore { } impl FsBinStore { - pub fn new(path: PathBuf) -> Self { + pub fn new(path: PathBuf) -> io::Result { + fs::create_dir_all(&path)?; + let mut stash = path.clone(); stash.push("stash.dat"); let mut state = path.clone(); @@ -43,11 +46,11 @@ impl FsBinStore { let mut index = path.clone(); index.push("index.dat"); - Self { + Ok(Self { stash, state, index, - } + }) } } impl PersistenceProvider for FsBinStore {