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

wip dal

parent dd3b2f96
Branches oxyde-dal
No related tags found
No related merge requests found
Showing
with 1399 additions and 0 deletions
......@@ -730,6 +730,27 @@ dependencies = [
"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]]
name = "duniter-dbex"
version = "0.1.0"
......@@ -855,6 +876,18 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "fastrand"
version = "1.4.0"
......@@ -1185,6 +1218,22 @@ version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "lock_api"
version = "0.3.4"
......@@ -1218,6 +1267,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "maplit"
version = "1.0.2"
......@@ -1624,6 +1682,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33"
[[package]]
name = "plotters"
version = "0.2.15"
......@@ -1850,6 +1914,22 @@ dependencies = [
"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]]
name = "rust-argon2"
version = "0.8.2"
......@@ -2266,6 +2346,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e33648dd74328e622c7be51f3b40a303c63f93e6fa5f08778b6203a4c25c20f"
[[package]]
name = "vcpkg"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
[[package]]
name = "vec-arena"
version = "1.0.0"
......
......@@ -32,6 +32,7 @@ members = [
"rust-bins/duniter-dbex",
"rust-bins/xtask",
"rust-libs/dubp-wot",
"rust-libs/duniter-dal",
"rust-libs/duniter-dbs",
"rust-libs/tools/kv_typed",
"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 to comment