Skip to content
Snippets Groups Projects
Commit 19483044 authored by Éloïs's avatar Éloïs
Browse files

wip dal

parent dd3b2f96
No related branches found
No related tags found
No related merge requests found
Showing
with 1399 additions and 0 deletions
...@@ -730,6 +730,27 @@ dependencies = [ ...@@ -730,6 +730,27 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "duniter-dal"
version = "0.1.0"
dependencies = [
"bincode",
"crossbeam-utils",
"dubp-common",
"dubp-wot",
"duniter-dbs",
"flate2",
"log",
"mockall",
"once_cell",
"rusqlite",
"serde",
"serde_json",
"tempdir",
"thiserror",
"unwrap",
]
[[package]] [[package]]
name = "duniter-dbex" name = "duniter-dbex"
version = "0.1.0" version = "0.1.0"
...@@ -855,6 +876,18 @@ version = "0.1.2" ...@@ -855,6 +876,18 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "1.4.0" version = "1.4.0"
...@@ -1185,6 +1218,22 @@ version = "0.2.78" ...@@ -1185,6 +1218,22 @@ version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98" checksum = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98"
[[package]]
name = "libsqlite3-sys"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a245984b1b06c291f46e27ebda9f369a94a1ab8461d0e845e23f9ced01f5db"
dependencies = [
"pkg-config",
"vcpkg",
]
[[package]]
name = "linked-hash-map"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.3.4" version = "0.3.4"
...@@ -1218,6 +1267,15 @@ version = "0.1.1" ...@@ -1218,6 +1267,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d0925aed5b12ed59857f438d25a910cf051dbcd4107907be1e7abf6c44ec903" checksum = "3d0925aed5b12ed59857f438d25a910cf051dbcd4107907be1e7abf6c44ec903"
[[package]]
name = "lru-cache"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
dependencies = [
"linked-hash-map",
]
[[package]] [[package]]
name = "maplit" name = "maplit"
version = "1.0.2" version = "1.0.2"
...@@ -1624,6 +1682,12 @@ version = "0.1.0" ...@@ -1624,6 +1682,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33"
[[package]] [[package]]
name = "plotters" name = "plotters"
version = "0.2.15" version = "0.2.15"
...@@ -1850,6 +1914,22 @@ dependencies = [ ...@@ -1850,6 +1914,22 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "rusqlite"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c78c3275d9d6eb684d2db4b2388546b32fdae0586c20a82f3905d21ea78b9ef"
dependencies = [
"bitflags",
"fallible-iterator",
"fallible-streaming-iterator",
"libsqlite3-sys",
"lru-cache",
"memchr",
"serde_json",
"smallvec",
]
[[package]] [[package]]
name = "rust-argon2" name = "rust-argon2"
version = "0.8.2" version = "0.8.2"
...@@ -2266,6 +2346,12 @@ version = "1.2.1" ...@@ -2266,6 +2346,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e33648dd74328e622c7be51f3b40a303c63f93e6fa5f08778b6203a4c25c20f" checksum = "7e33648dd74328e622c7be51f3b40a303c63f93e6fa5f08778b6203a4c25c20f"
[[package]]
name = "vcpkg"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
[[package]] [[package]]
name = "vec-arena" name = "vec-arena"
version = "1.0.0" version = "1.0.0"
......
...@@ -32,6 +32,7 @@ members = [ ...@@ -32,6 +32,7 @@ members = [
"rust-bins/duniter-dbex", "rust-bins/duniter-dbex",
"rust-bins/xtask", "rust-bins/xtask",
"rust-libs/dubp-wot", "rust-libs/dubp-wot",
"rust-libs/duniter-dal",
"rust-libs/duniter-dbs", "rust-libs/duniter-dbs",
"rust-libs/tools/kv_typed", "rust-libs/tools/kv_typed",
"rust-libs/tools/kv_typed_code_gen" "rust-libs/tools/kv_typed_code_gen"
......
[package]
name = "duniter-dal"
version = "0.1.0"
authors = ["elois <elois@duniter.org>"]
description = "Duniter Data Access Layer"
repository = "https://git.duniter.org/nodes/typescript/duniter"
keywords = ["duniter"]
license = "AGPL-3.0"
edition = "2018"
[lib]
path = "src/lib.rs"
[dependencies]
bincode = "1.2.1"
crossbeam-utils = "0.7.2"
dubp-common = { version = "0.25.2", features = ["crypto_scrypt"] }
dubp-wot = { path = "../dubp-wot" }
duniter-dbs = { path = "../duniter-dbs" }
flate2 = "1.0.16"
log = "0.4.8"
mockall = { version = "0.8.0", optional = true }
rusqlite = { version = "0.25.2", features = ["serde_json"] }
serde = { version = "1.0.105", features = ["derive"] }
serde_json = "1.0.53"
thiserror = "1.0.20"
[dev-dependencies]
once_cell = "1.4.0"
tempdir = "0.3.7"
unwrap = "1.2.1"
[features]
default = ["mock"]
#default = ["test_real"]
mock = ["duniter-dbs/mock", "mockall"]
test_real = []
use crate::*;
use duniter_dbs::kv_typed::backend::sled;
pub trait GenBackendConf<B: Backend> {
fn gen_backend_conf(db_name: &'static str, data_path_opt: Option<&Path>) -> B::Conf;
}
impl GenBackendConf<Mem> for Mem {
#[inline(always)]
fn gen_backend_conf(_db_name: &'static str, _data_path_opt: Option<&Path>) -> MemConf {
MemConf::default()
}
}
impl GenBackendConf<Sled> for Sled {
#[inline(always)]
fn gen_backend_conf(db_name: &'static str, data_path_opt: Option<&Path>) -> sled::Config {
if let Some(data_path) = data_path_opt {
sled::Config::default().path(data_path.join(format!("{}_sled", db_name)))
} else {
sled::Config::default().temporary(true)
}
}
}
impl GenBackendConf<LevelDb> for LevelDb {
#[inline(always)]
fn gen_backend_conf(_db_name: &'static str, data_path_opt: Option<&Path>) -> LevelDbConf {
if let Some(data_path) = data_path_opt {
LevelDbConf {
db_path: data_path.join("leveldb"),
..Default::default()
}
} else {
panic!("LevelDb backend not supported memory mode")
}
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Duniter databases
use crate::*;
/// Blockchain database
pub type BcDb<B> = BcV1Db<B>;
/// Blockchain database on read-only mode
pub type BcDbRo<B> = BcV1DbRo<B>;
#[derive(Clone)]
pub struct DatabasesRo<B: Backend> {
pub(crate) bc_db: BcDbRo<B>,
pub(crate) peers_db: Arc<rusqlite::Connection>,
pub(crate) txs_mp_db: Arc<rusqlite::Connection>,
pub(crate) wot_mp_db: Arc<rusqlite::Connection>,
}
impl<B: Backend> Debug for DatabasesRo<B> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "DatabasesRo {{ ... }}")
}
}
#[derive(Clone)]
pub struct Databases<B: Backend> {
pub(crate) bc_db: BcDb<B>,
pub(crate) peers_db: Arc<rusqlite::Connection>,
pub(crate) txs_mp_db: Arc<rusqlite::Connection>,
pub(crate) wot_mp_db: Arc<rusqlite::Connection>,
ro: DatabasesRo<B>,
}
impl<B: Backend> Debug for Databases<B> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Databases {{ ... }}")
}
}
impl<B: Backend> Databases<B> {
pub(crate) fn to_ro(&self) -> &DatabasesRo<B> {
&self.ro
}
}
impl<B: Backend + GenBackendConf<B>> Databases<B> {
pub(crate) fn open(data_path_opt: Option<&Path>) -> DalResult<Databases<B>> {
let bc_db_conf = B::gen_backend_conf(BC_DB_NAME, data_path_opt);
let bc_db = BcV1Db::<B>::open(bc_db_conf)?;
let ro = DatabasesRo {
bc_db: bc_db.get_ro_handler(),
peers_db: Arc::new(open_sqlite_db(data_path_opt, PEERS_DB)?),
txs_mp_db: Arc::new(open_sqlite_db(data_path_opt, TX_MP_DB)?),
wot_mp_db: Arc::new(open_sqlite_db(data_path_opt, WOT_MP_SQLITE_DB)?),
};
Ok(Databases {
bc_db,
peers_db: ro.peers_db.clone(),
txs_mp_db: ro.txs_mp_db.clone(),
wot_mp_db: ro.wot_mp_db.clone(),
ro,
})
}
}
// TMP - Sqlite will be removed on the future !
#[inline(always)]
fn open_sqlite_db(
data_path_opt: Option<&Path>,
db_file_name: &str,
) -> DalResult<rusqlite::Connection> {
Ok(if let Some(data_path) = data_path_opt.as_deref() {
let mut sqlite_db_path = data_path.to_owned();
sqlite_db_path.pop();
sqlite_db_path.push(db_file_name);
rusqlite::Connection::open(sqlite_db_path)?
} else {
rusqlite::Connection::open(":memory:")?
})
}
/// Vector of string deserialized from JSON string
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct JsonVecStr(pub Vec<String>);
impl rusqlite::types::FromSql for JsonVecStr {
fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
let json_value = serde_json::Value::column_result(value)?;
if let serde_json::Value::Array(array) = json_value {
let mut vec_string = Vec::with_capacity(array.len());
for json_value in array {
if let serde_json::Value::String(string) = json_value {
vec_string.push(string);
} else {
return Err(rusqlite::types::FromSqlError::InvalidType);
}
}
Ok(JsonVecStr(vec_string))
} else {
Err(rusqlite::types::FromSqlError::InvalidType)
}
}
}
impl rusqlite::types::ToSql for JsonVecStr {
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
let json_string = serde_json::to_string(&self.0)
.map_err(|e| rusqlite::Error::ToSqlConversionFailure(Box::new(e)))?;
Ok(rusqlite::types::ToSqlOutput::Owned(
rusqlite::types::Value::Text(json_string),
))
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Duniter DAL entities
pub mod peer;
use crate::*;
/// DUBP Block in database
pub type BlockDb = BlockDbV1;
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Define peer entity
use crate::*;
/// DUNP Peer card in database
#[allow(missing_docs)]
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct PeerCardDb {
pub version: i64,
pub currency: String,
pub status: String,
pub status_ts: i64,
pub hash: String,
pub first_down: Option<i64>,
pub last_try: Option<i64>,
pub last_contact: i64,
pub pubkey: String,
pub block: String,
pub signature: String,
pub endpoints: JsonVecStr,
pub raw: String,
pub non_wot: bool,
}
impl<'stmt> TryFrom<&rusqlite::Row<'stmt>> for PeerCardDb {
type Error = rusqlite::Error;
fn try_from(row: &rusqlite::Row<'stmt>) -> Result<Self, Self::Error> {
Ok(PeerCardDb {
version: row.get(0)?,
currency: row.get(1)?,
status: row.get(2)?,
status_ts: row.get(3)?,
hash: row.get(4)?,
first_down: row.get(5)?,
last_try: row.get(6)?,
last_contact: row.get(7)?,
pubkey: row.get(8)?,
block: row.get(9)?,
signature: row.get(10)?,
endpoints: row.get(11)?,
raw: row.get(12)?,
non_wot: row.get(13)?,
})
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Define DAL errors
use crate::*;
/// Duniter DAL Result
pub type DalResult<T> = Result<T, DalError>;
#[derive(Debug, Error)]
/// Dal error
pub enum DalError {
/// Database error
#[error("{0}")]
DbError(ErrorDb),
/// Fail to create folder
#[error("Fail to create folder: {0}")]
FailToCreateFolder(std::io::Error),
/// Fail to open wot file
#[error("Fail to open wot file: {0}")]
FailToOpenWotFile(bincode::Error),
/// Fail to save wot file
#[error("Fail to save wot file: {0}")]
FailToSaveWotFile(bincode::Error),
/// Fail to get currency parameters from genesis block
#[error("Fail to get genesis parameters: {0}")]
GenesisParamsError(String),
/// No blockchain
#[error("No blockchain")]
NoBlockchain,
/// Sqlite error
#[error("Sqlite error: {0}")]
SqLiteError(rusqlite::Error),
/// Wrong backend
#[error("Database already exist with {exist_backend} bakend, please use them.")]
WrongBackend {
/// Existing bakend
exist_backend: &'static str,
},
}
impl From<KvError> for DalError {
fn from(e: KvError) -> Self {
Self::DbError(ErrorDb::DbError(format!("{}", e)))
}
}
impl From<ErrorDb> for DalError {
fn from(e: ErrorDb) -> Self {
Self::DbError(e)
}
}
impl From<rusqlite::Error> for DalError {
fn from(e: rusqlite::Error) -> Self {
Self::SqLiteError(e)
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Duniter file data access layer
use crate::*;
#[derive(Clone, Debug)]
/// Duniter Data Access Layer on read-only mode that read data from filesystem
pub struct FileDalRo<B: Backend> {
pub(crate) databases: DatabasesRo<B>,
pub(crate) currency_params: CurrencyParameters,
pub(crate) wot: Wot,
}
#[derive(Clone, Debug)]
/// Duniter Data Access Layer that store data on filesystem
pub struct FileDal<B: Backend> {
pub(crate) databases: Databases<B>,
pub(crate) currency_params: CurrencyParameters,
pub(crate) wot: Wot,
}
impl<B: Backend + GenBackendConf<B>> FileDal<B> {
#[inline(always)]
/// Initialise file DAL
pub fn init<P: AsRef<Path>>(profile_path: P) -> DalResult<Self> {
let data_path = profile_path.as_ref().join(DATA_DIR);
verify_data_path(&data_path, B::NAME)?;
let databases = Databases::open(Some(data_path.as_path()))?;
let currency_params = get_currency_params(&databases.bc_db, data_path.as_path())?;
let wot = Wot::open(Some(data_path.as_path()), currency_params.sig_stock)
.map_err(DalError::FailToOpenWotFile)?;
Ok(FileDal {
currency_params,
databases,
wot,
})
}
}
impl<B: Backend> ToDalRo for FileDal<B> {
type DalRo = FileDalRo<B>;
fn to_dal_ro(&self) -> Self::DalRo {
FileDalRo {
databases: self.databases.to_ro().clone(),
currency_params: self.currency_params,
wot: self.wot.clone(),
}
}
}
fn get_currency_params<B: Backend>(
bc_db: &BcDb<B>,
data_path: &Path,
) -> DalResult<CurrencyParameters> {
Ok(if let Some(currency_params) = get_genesis_params(&bc_db)? {
currency_params
} else {
let mut file = fs::File::open(data_path.join(GENESIS_PARAMS_FILE))
.map_err(|e| DalError::GenesisParamsError(format!("{}", e)))?;
let mut file_contents = String::new();
file.read_to_string(&mut file_contents)
.map_err(|e| DalError::GenesisParamsError(format!("{}", e)))?;
serde_json::from_str::<CurrencyNameAndGenesisBlockParams>(&file_contents)
.map_err(|e| DalError::GenesisParamsError(format!("{}", e)))?
.into()
})
}
fn get_genesis_params<B: Backend>(bc_db: &BcDb<B>) -> DalResult<Option<CurrencyParameters>> {
let genesis_opt = bc_db
.main_blocks()
.get(&duniter_dbs::BlockNumberKeyV1(BlockNumber(0)))?;
Ok(if let Some(genesis) = genesis_opt {
Some(CurrencyParameters::from((
&CurrencyName(genesis.currency),
dubp_common::currency_params::BlockV10Parameters::from_str(&genesis.parameters)
.map_err(|e| DalError::GenesisParamsError(format!("{}", e)))?,
)))
} else {
None
})
}
fn verify_data_path(data_path: &PathBuf, backend_name: &'static str) -> DalResult<()> {
if !data_path.exists() {
fs::create_dir_all(data_path.as_path()).map_err(DalError::FailToCreateFolder)?;
} else {
match backend_name {
"leveldb" => {
if data_path.as_path().join("bc_v1_sled").exists() {
return Err(DalError::WrongBackend {
exist_backend: "sled",
});
}
}
"sled" => {
if data_path.as_path().join("leveldb").exists() {
return Err(DalError::WrongBackend {
exist_backend: "leveldb",
});
}
}
_ => unreachable!(),
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use dubp_common::currency_params::{BlockV10Parameters, GenesisBlockParams};
use tempdir::TempDir;
use unwrap::unwrap;
#[test]
fn test_file_dal() -> DalResult<()> {
let tmp_dir = unwrap!(TempDir::new("duniter_test_file_dal"));
let tmp_dir_path = tmp_dir.path();
let data_path = tmp_dir_path.join(DATA_DIR);
unwrap!(fs::create_dir(data_path));
/*if let Err(DalError::GenesisParamsError(_)) = FileDal::init(None, tmp_dir_path.to_owned()) {
} else {
panic!("FileDal::init must be fail with error GenesisParamsError !");
}*/
// Create currency params file
let params = CurrencyNameAndGenesisBlockParams {
genesis_block_params: GenesisBlockParams::V10(BlockV10Parameters {
sig_stock: 40,
..Default::default()
}),
..Default::default()
};
let mut file = unwrap!(fs::File::create(
tmp_dir_path.join(DATA_DIR).join(GENESIS_PARAMS_FILE)
));
unwrap!(file.write_all(unwrap!(serde_json::to_string(&params)).as_bytes()));
let dal = FileDal::<Sled>::init(tmp_dir_path)?;
assert_eq!(40, dal.wot().get_max_link());
unwrap!(tmp_dir.close().map_err(DalError::FailToCreateFolder));
Ok(())
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Duniter DAL inner
use crate::*;
pub trait InnerDalRo: Clone {
type Backend: Backend;
fn currency_params(&self) -> CurrencyParameters;
fn databases(&self) -> &DatabasesRo<Self::Backend>;
fn wot(&self) -> &Wot;
}
impl InnerDalRo for MemDalRo {
type Backend = Mem;
#[inline(always)]
fn currency_params(&self) -> CurrencyParameters {
self.currency_params
}
#[inline(always)]
fn databases(&self) -> &DatabasesRo<Mem> {
&self.databases
}
#[inline(always)]
fn wot(&self) -> &Wot {
&self.wot
}
}
impl InnerDalRo for MemDal {
type Backend = Mem;
#[inline(always)]
fn currency_params(&self) -> CurrencyParameters {
self.currency_params
}
#[inline(always)]
fn databases(&self) -> &DatabasesRo<Self::Backend> {
self.databases.to_ro()
}
#[inline(always)]
fn wot(&self) -> &Wot {
&self.wot
}
}
impl<B: Backend> InnerDalRo for FileDalRo<B> {
type Backend = B;
#[inline(always)]
fn currency_params(&self) -> CurrencyParameters {
self.currency_params
}
#[inline(always)]
fn databases(&self) -> &DatabasesRo<Self::Backend> {
&self.databases
}
#[inline(always)]
fn wot(&self) -> &Wot {
&self.wot
}
}
impl<B: Backend> InnerDalRo for FileDal<B> {
type Backend = B;
#[inline(always)]
fn currency_params(&self) -> CurrencyParameters {
self.currency_params
}
#[inline(always)]
fn databases(&self) -> &DatabasesRo<Self::Backend> {
self.databases.to_ro()
}
#[inline(always)]
fn wot(&self) -> &Wot {
&self.wot
}
}
pub trait InnerDalRw: InnerDalRo + ToDalRo {
fn databases_rw(&self) -> &Databases<<Self as InnerDalRo>::Backend>;
}
impl<B: Backend> InnerDalRw for FileDal<B> {
#[inline(always)]
fn databases_rw(&self) -> &Databases<B> {
&self.databases
}
}
impl InnerDalRw for MemDal {
#[inline(always)]
fn databases_rw(&self) -> &Databases<Mem> {
&self.databases
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Duniter Data Access Layer
#![deny(
clippy::expect_used,
clippy::unwrap_used,
missing_docs,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unstable_features,
unused_import_braces,
unused_qualifications
)]
mod conf_dbs;
mod databases;
mod entities;
mod errors;
mod file_dal;
mod inner;
mod mem_dal;
#[cfg(feature = "mock")]
mod mock_dal;
mod read;
mod wot;
mod write;
/// Prelude
pub mod prelude {
pub use crate::databases::JsonVecStr;
pub use crate::databases::{BcDb, BcDbRo};
pub use crate::entities::peer::PeerCardDb;
pub use crate::entities::BlockDb;
pub use crate::errors::{DalError, DalResult};
pub use crate::file_dal::FileDal;
pub use crate::mem_dal::MemDal;
#[cfg(feature = "mock")]
pub use crate::mock_dal::{MockDal, MockDalRo};
pub use crate::read::bc_db::DalReadBc;
pub use crate::read::peers::DalReadPeers;
pub use crate::read::DalReadable;
pub use crate::write::peers::DalWritePeers;
pub use crate::write::{DalWritable, ToDalRo};
pub use crate::Dal;
}
// Technical types
pub use duniter_dbs::kv_typed::backend::leveldb::LevelDb;
#[cfg(feature = "mock")]
pub use duniter_dbs::kv_typed::backend::mock::MockBackend;
pub use duniter_dbs::kv_typed::backend::sled::Sled;
// Crate imports
pub(crate) use crate::conf_dbs::GenBackendConf;
pub(crate) use crate::databases::{Databases, DatabasesRo};
pub(crate) use crate::errors::{DalError, DalResult};
pub(crate) use crate::file_dal::FileDalRo;
pub(crate) use crate::inner::{InnerDalRo, InnerDalRw};
pub(crate) use crate::mem_dal::MemDalRo;
pub(crate) use crate::prelude::*;
pub(crate) use crate::wot::Wot;
pub(crate) use dubp_common::currency_params::{
CurrencyNameAndGenesisBlockParams, CurrencyParameters,
};
pub(crate) use dubp_common::prelude::*;
pub(crate) use duniter_dbs::kv_typed::prelude::*;
pub(crate) use duniter_dbs::prelude::*;
pub(crate) use duniter_dbs::{
BcV1Db, BcV1DbReadable as BcDbReadable, BcV1DbRo, BcV1DbWritable as BcDbWritable, BlockDbV1,
};
pub(crate) use serde::{Deserialize, Serialize};
pub(crate) use std::{
convert::TryFrom,
fmt::Debug,
fs::{self, File},
io::prelude::*,
path::{Path, PathBuf},
str::FromStr,
sync::Arc,
};
pub(crate) use thiserror::Error;
// Crate internal constants
const BC_DB_NAME: &str = "bc_v1";
const DATA_DIR: &str = "data";
const GENESIS_PARAMS_FILE: &str = "genesis_parameters.json";
const WOT_FILE: &str = "wotb.bin.gz";
const PEERS_DB: &str = "peers.db";
const TX_MP_DB: &str = "txs.db";
const WOT_MP_SQLITE_DB: &str = "duniter.db";
/// Duniter Data Access Layer
pub trait Dal<B: Backend>: DalWritable {
/// Get read only handler to blockchain database
fn get_bc_db_ro(&self) -> BcDbRo<B>;
/// Get read only DAL
fn get_dal_ro(&self) -> <Self as ToDalRo>::DalRo;
}
impl<B: Backend, T> Dal<B> for T
where
T: InnerDalRo<Backend = B> + InnerDalRw + DalWritable + NotMock,
{
fn get_bc_db_ro(&self) -> BcDbRo<B> {
self.databases_rw().bc_db.get_ro_handler()
}
fn get_dal_ro(&self) -> Self::DalRo {
self.to_dal_ro()
}
}
#[doc(hidden)]
pub trait NotMock {}
impl<B: Backend> NotMock for FileDal<B> {}
impl<B: Backend> NotMock for FileDalRo<B> {}
impl NotMock for MemDal {}
impl NotMock for MemDalRo {}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Duniter memory data access layer
use crate::*;
#[derive(Clone, Debug)]
/// Duniter Data Access Layer on read-only mode that read data from memory
pub struct MemDalRo {
pub(crate) databases: DatabasesRo<Mem>,
pub(crate) currency_params: CurrencyParameters,
pub(crate) wot: Wot,
}
#[derive(Clone, Debug)]
/// Duniter Data Access Layer that store data on memory
pub struct MemDal {
pub(crate) databases: Databases<Mem>,
pub(crate) currency_params: CurrencyParameters,
pub(crate) wot: Wot,
}
impl MemDal {
#[inline(always)]
/// Initialise memory DAL
pub fn init(currency_params_opt: Option<CurrencyParameters>) -> DalResult<Self> {
let currency_params = currency_params_opt.unwrap_or_default();
// Memory mode, force sled backend
let databases = Databases::open(None)?;
let wot =
Wot::open(None, currency_params.sig_stock).map_err(DalError::FailToOpenWotFile)?;
Ok(MemDal {
currency_params,
databases,
wot,
})
}
}
impl ToDalRo for MemDal {
type DalRo = MemDalRo;
fn to_dal_ro(&self) -> Self::DalRo {
MemDalRo {
databases: self.databases.to_ro().clone(),
currency_params: self.currency_params,
wot: self.wot.clone(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mem_dal() -> DalResult<()> {
let currency_params = CurrencyParameters {
sig_stock: 42,
..Default::default()
};
let dal = MemDal::init(Some(currency_params))?;
assert_eq!(currency_params, dal.currency_params());
assert_eq!(42, dal.wot().get_max_link());
Ok(())
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Duniter mocked data access layer
#![allow(clippy::ptr_arg)]
use crate::*;
mockall::mock! {
pub DalRo<B: Backend> {}
trait DalReadBc {
fn get_block(&self, number: BlockNumber) -> DalResult<Option<BlockDb>>;
fn get_current_block(&self) -> DalResult<Option<BlockDb>>;
}
trait DalReadPeers {
fn count_mirror_peers(&self) -> DalResult<u32>;
fn get_peer_by_pubkey(&self, pubkey: &str) -> DalResult<Option<PeerCardDb>>;
fn get_peers_with_endpoints_like(&self, pattern: &str) -> DalResult<Vec<PeerCardDb>>;
fn get_all_peers(&self) -> DalResult<Vec<PeerCardDb>>;
fn get_up_peers(&self) -> DalResult<Vec<PeerCardDb>>;
}
trait DalReadable {
fn get_currency_params(&self) -> CurrencyParameters;
}
}
mockall::mock! {
pub Dal<B: Backend> {}
trait DalReadBc {
fn get_block(&self, number: BlockNumber) -> DalResult<Option<BlockDb>>;
fn get_current_block(&self) -> DalResult<Option<BlockDb>>;
}
trait DalReadPeers {
fn count_mirror_peers(&self) -> DalResult<u32>;
fn get_peer_by_pubkey(&self, pubkey: &str) -> DalResult<Option<PeerCardDb>>;
fn get_peers_with_endpoints_like(&self, pattern: &str) -> DalResult<Vec<PeerCardDb>>;
fn get_all_peers(&self) -> DalResult<Vec<PeerCardDb>>;
fn get_up_peers(&self) -> DalResult<Vec<PeerCardDb>>;
}
trait DalReadable {
fn get_currency_params(&self) -> CurrencyParameters;
}
trait ToDalRo {
type DalRo = MockDalRo<B>;
fn to_dal_ro(&self) -> MockDalRo<B>;
}
trait DalWritePeers {
fn delete_mirrror_peers_whose_last_contact_is_above(&self, threshold: i64) -> DalResult<()>;
fn remove_all(&self) -> DalResult<()>;
fn remove_peer_by_pubkey(&self, pubkey: String) -> DalResult<()>;
fn save_peer(&self, peer: PeerCardDb) -> DalResult<()>;
}
trait DalWritable {
fn save_wot(&self) -> DalResult<()>;
}
trait Dal<B>: DalReadable + DalWritable<B> {
fn get_bc_db_ro(&self) -> BcDbRo<B>;
fn get_dal_ro(&self) -> MockDalRo<B>;
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Duniter Data Access Layer read operations
pub mod bc_db;
pub mod peers;
use crate::*;
use bc_db::*;
/// DAL read operations
pub trait DalReadable: DalReadBc + DalReadPeers {
/// Get currency parameters
fn get_currency_params(&self) -> CurrencyParameters;
}
impl<T> DalReadable for T
where
T: DalReadBc + DalReadPeers + InnerDalRo + NotMock,
{
#[inline(always)]
fn get_currency_params(&self) -> CurrencyParameters {
self.currency_params()
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Define blockchain database read operations
use crate::*;
use duniter_dbs::BlockNumberKeyV1;
/// DAL read operations for blockcnain db
pub trait DalReadBc {
/// Get a block by number
fn get_block(&self, number: BlockNumber) -> DalResult<Option<BlockDb>>;
/// Get the current block (=the last block)
fn get_current_block(&self) -> DalResult<Option<BlockDb>>;
}
impl<T> DalReadBc for T
where
T: InnerDalRo + NotMock,
{
fn get_current_block(&self) -> DalResult<Option<BlockDb>> {
Ok(self
.databases()
.bc_db
.main_blocks()
.iter(..)
.values()
.reverse()
.next()
.transpose()?)
}
#[inline(always)]
fn get_block(&self, number: BlockNumber) -> DalResult<Option<BlockDb>> {
Ok(self
.databases()
.bc_db
.main_blocks()
.get(&BlockNumberKeyV1(number))?)
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Duniter Data Access Layer read operations for peers db
use crate::*;
/// DAL read operations for peers db
pub trait DalReadPeers {
/// Count mirror peers
fn count_mirror_peers(&self) -> DalResult<u32>;
/// Get one peer card by this pub-key
fn get_peer_by_pubkey(&self, pubkey: &str) -> DalResult<Option<PeerCardDb>>;
/// Get peers cards with endpoints like
fn get_peers_with_endpoints_like(&self, pattern: &str) -> DalResult<Vec<PeerCardDb>>;
/// Get all peer cards
fn get_all_peers(&self) -> DalResult<Vec<PeerCardDb>>;
/// Get UP peers
fn get_up_peers(&self) -> DalResult<Vec<PeerCardDb>>;
}
#[allow(clippy::redundant_closure)]
impl<T> DalReadPeers for T
where
T: InnerDalRo + NotMock,
{
fn count_mirror_peers(&self) -> DalResult<u32> {
Ok(self
.databases()
.peers_db
.prepare("SELECT COUNT(*) as _count FROM peers WHERE nonWoT")?
.query_map(rusqlite::params![], |row| row.get(0))?
.next()
.transpose()?
.unwrap_or_default())
}
fn get_peer_by_pubkey(&self, pubkey: &str) -> DalResult<Option<PeerCardDb>> {
Ok(self
.databases()
.peers_db
.prepare("SELECT * FROM peers WHERE pubkey = ?")?
.query_map(&[pubkey], |row| PeerCardDb::try_from(row))?
.next()
.transpose()?)
}
fn get_peers_with_endpoints_like(&self, pattern: &str) -> DalResult<Vec<PeerCardDb>> {
Ok(self
.databases()
.peers_db
.prepare("SELECT * FROM peers WHERE endpoints LIKE ?")?
.query_map(&[pattern], |row| PeerCardDb::try_from(row))?
.collect::<Result<Vec<PeerCardDb>, rusqlite::Error>>()?)
}
fn get_all_peers(&self) -> DalResult<Vec<PeerCardDb>> {
Ok(self
.databases()
.peers_db
.prepare("SELECT * FROM peers")?
.query_map(rusqlite::params![], |row| PeerCardDb::try_from(row))?
.collect::<Result<Vec<PeerCardDb>, rusqlite::Error>>()?)
}
fn get_up_peers(&self) -> DalResult<Vec<PeerCardDb>> {
Ok(self
.databases()
.peers_db
.prepare("SELECT * FROM peers WHERE status = ?")?
.query_map(&["UP"], |row| PeerCardDb::try_from(row))?
.collect::<Result<Vec<PeerCardDb>, rusqlite::Error>>()?)
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Duniter Data Access Layer: wot
mod read_from_file;
mod write_in_file;
use crate::*;
use crossbeam_utils::sync::ShardedLock;
use dubp_wot::{data::rusty::RustyWebOfTrust, data::NewLinkResult, WebOfTrust, WotId};
#[derive(Clone, Debug)]
pub struct Wot {
pub(crate) wot: Arc<ShardedLock<RustyWebOfTrust>>,
file_path_opt: Option<PathBuf>,
}
#[allow(dead_code)]
impl Wot {
pub(crate) fn open(
data_path_opt: Option<&Path>,
max_links: usize,
) -> Result<Self, bincode::Error> {
Ok(if let Some(data_path) = data_path_opt {
let wot_file_path = data_path.join(WOT_FILE);
Wot {
wot: Arc::new(ShardedLock::new(read_from_file::wot_from_file(
wot_file_path.as_path(),
max_links,
)?)),
file_path_opt: Some(wot_file_path),
}
} else {
Wot {
wot: Arc::new(ShardedLock::new(RustyWebOfTrust::new(max_links))),
file_path_opt: None,
}
})
}
#[allow(clippy::expect_used)]
#[inline(always)]
pub(crate) fn get_max_link(&self) -> usize {
let wot_reader = self.wot.read().expect("poisoned");
wot_reader.get_max_link()
}
#[allow(clippy::expect_used)]
pub(crate) fn add_links(&self, links: &[(WotId, WotId)]) -> Result<(), NewLinkResult> {
let mut wot_writer = self.wot.write().expect("poisoned");
for (source, target) in links {
match wot_writer.add_link(*source, *target) {
NewLinkResult::Ok(_) => continue,
r => return Err(r),
}
}
Ok(())
}
#[allow(clippy::expect_used)]
pub(crate) fn save(&self) -> Result<(), bincode::Error> {
if let Some(ref wot_file_path) = self.file_path_opt {
let wot_reader = self.wot.read().expect("poisoned");
write_in_file::wot_in_file(wot_file_path.as_path(), &wot_reader)
} else {
Ok(())
}
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use crate::*;
use flate2::read::ZlibDecoder;
pub(crate) fn wot_from_file(
file_path: &Path,
max_links: usize,
) -> Result<RustyWebOfTrust, bincode::Error> {
let bytes = read_and_decompress_bytes_from_file(file_path)?;
if bytes.is_empty() {
Ok(RustyWebOfTrust::new(max_links))
} else {
Ok(bincode::deserialize::<RustyWebOfTrust>(&bytes)?)
}
}
/// Read and decompress bytes from file
fn read_and_decompress_bytes_from_file(file_path: &Path) -> Result<Vec<u8>, std::io::Error> {
if !file_path.exists() {
if let Some(parent) = file_path.parent() {
std::fs::create_dir_all(parent)?
}
File::create(file_path)?;
}
if std::fs::metadata(file_path)?.len() > 0 {
let file = File::open(file_path)?;
let mut z = ZlibDecoder::new(file);
let mut decompressed_bytes = Vec::new();
z.read_to_end(&mut decompressed_bytes)?;
Ok(decompressed_bytes)
} else {
Ok(vec![])
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use crate::*;
use flate2::write::ZlibEncoder;
use flate2::Compression;
pub(crate) fn wot_in_file(file_path: &Path, wot: &RustyWebOfTrust) -> Result<(), bincode::Error> {
let bytes = bincode::serialize(wot)?;
write_and_compress_bytes_in_file(file_path, &bytes, flate2::Compression::default())?;
Ok(())
}
/// Write and compress bytes in file
fn write_and_compress_bytes_in_file(
file_path: &Path,
datas: &[u8],
compression: Compression,
) -> Result<(), std::io::Error> {
let file = File::create(file_path)?;
let mut e = ZlibEncoder::new(file, compression);
e.write_all(&datas[..])?;
e.finish()?;
Ok(())
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Duniter Data Access Layer write operations
pub mod peers;
use crate::*;
/// To read only DAL
pub trait ToDalRo {
/// Read-only DAL
type DalRo: DalReadable;
/// To read-only DAL
fn to_dal_ro(&self) -> Self::DalRo;
}
/// DAL write operations
pub trait DalWritable: DalReadable + DalWritePeers + ToDalRo {
/// save web of trust
fn save_wot(&self) -> DalResult<()>;
}
impl<T> DalWritable for T
where
T: DalWritePeers + InnerDalRw + NotMock,
{
#[inline(always)]
fn save_wot(&self) -> DalResult<()> {
self.wot().save().map_err(DalError::FailToSaveWotFile)?;
Ok(())
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment