diff --git a/Cargo.lock b/Cargo.lock index 335797456d9a61fea4f92e07c37f298968ed9c84..85d67faab71989609734bd2faffc0535f2e25801 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -730,6 +730,17 @@ dependencies = [ "serde", ] +[[package]] +name = "duniter-bc-db" +version = "0.1.0" +dependencies = [ + "dubp-common", + "duniter-dbs", + "log", + "resiter", + "unwrap", +] + [[package]] name = "duniter-dbex" version = "0.1.0" @@ -791,6 +802,8 @@ dependencies = [ "dubp-documents-parser", "dubp-wallet", "dubp-wot", + "duniter-bc-db", + "duniter-dbs", "flate2", "flexi_logger", "log", @@ -1835,6 +1848,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "resiter" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd69ab1e90258b7769f0b5c46bfd802b8206d0707ced4ca4b9d5681b744de1be" + [[package]] name = "ring" version = "0.16.15" diff --git a/Cargo.toml b/Cargo.toml index 90a051b9c4c06f1dd6f307707e18fe04c1974128..057d7afd9153bc7918f34b6ccc9b1fe0eb83a225 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ members = [ "rust-bins/duniter-dbex", "rust-bins/xtask", "rust-libs/dubp-wot", + "rust-libs/duniter-bc-db", "rust-libs/duniter-dbs", "rust-libs/tools/kv_typed", "rust-libs/tools/kv_typed_code_gen" diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts index 4207f81fc1748bc9f52d522409d503d036df2029..ba6c9ca3d0234b9d8c034509c51a2092db1f0755 100644 --- a/app/lib/dal/fileDAL.ts +++ b/app/lib/dal/fileDAL.ts @@ -233,12 +233,12 @@ export class FileDAL implements ServerDAO { return this.metaDAL.getVersion(); } - writeFileOfBlock(block: DBBlock) { - return this.blockDAL.saveBlock(block); + writeFileOfBlock(block: DBBlock): void { + this.blockDAL.saveBlock(block); } - writeSideFileOfBlock(block: DBBlock) { - return this.blockDAL.saveSideBlock(block); + writeSideFileOfBlock(block: DBBlock): void { + this.blockDAL.saveSideBlock(block); } listAllPeers() { @@ -1305,12 +1305,12 @@ export class FileDAL implements ServerDAO { return this.msDAL.savePendingMembership(ms); } - async saveBlockInFile(block: DBBlock) { + async saveBlockInFile(block: DBBlock): Promise<void> { await this.writeFileOfBlock(block); } - saveSideBlockInFile(block: DBBlock) { - return this.writeSideFileOfBlock(block); + saveSideBlockInFile(block: DBBlock): void { + this.writeSideFileOfBlock(block); } async saveTxsInFiles( diff --git a/app/lib/dal/indexDAL/abstract/software/ForksDAO.ts b/app/lib/dal/indexDAL/abstract/software/ForksDAO.ts index 27bb1ca2b3b4b29e874a622a2e7c01e2e1c9afed..70665f9e4372cc42a96d42d2388ba71cdde87bc3 100644 --- a/app/lib/dal/indexDAL/abstract/software/ForksDAO.ts +++ b/app/lib/dal/indexDAL/abstract/software/ForksDAO.ts @@ -14,7 +14,7 @@ import { DBBlock } from "../../../../db/DBBlock"; export interface ForksDAO { - saveSideBlock(block: DBBlock): Promise<DBBlock>; + saveSideBlock(block: DBBlock): Promise<void>; setSideBlock(number: number, previousBlock: DBBlock | null): Promise<void>; diff --git a/app/lib/dal/indexDAL/leveldb/LevelDBBlockchain.ts b/app/lib/dal/indexDAL/leveldb/LevelDBBlockchain.ts index 03a9dc5e6b4e53e20e4ebdb42326d32fccd0e7fa..5a52f331d0e0db5a4ffc13a5a9a8a9e6543f0510 100644 --- a/app/lib/dal/indexDAL/leveldb/LevelDBBlockchain.ts +++ b/app/lib/dal/indexDAL/leveldb/LevelDBBlockchain.ts @@ -275,11 +275,10 @@ export class LevelDBBlockchain extends LevelDBTable<DBBlock> return this.get(LevelDBBlockchain.trimKey(block.number)); } - async saveSideBlock(block: DBBlock): Promise<DBBlock> { + async saveSideBlock(block: DBBlock): Promise<void> { const k = LevelDBBlockchain.trimForkKey(block.number, block.hash); block.fork = true; await this.forks.put(k, block); - return this.forks.get(k); } async setSideBlock( diff --git a/neon/native/Cargo.toml b/neon/native/Cargo.toml index 3f9b5be73008eb2c63001132a7fb958d51996854..b27f78066a1d8e1358abfaf3b1474d5d8a72375c 100644 --- a/neon/native/Cargo.toml +++ b/neon/native/Cargo.toml @@ -22,6 +22,8 @@ dubp-documents = { version = "0.25.2" } dubp-documents-parser = { version = "0.25.2" } dubp-wallet = { version = "0.25.2" } dubp-wot = { path = "../../rust-libs/dubp-wot" } +duniter-bc-db = { path = "../../rust-libs/duniter-bc-db" } +duniter-dbs = { path = "../../rust-libs/duniter-dbs" } flate2 = "1.0.16" flexi_logger = { version = "=0.16.0", default-features = false, features = ["compress"] } log = "0.4.11" diff --git a/neon/native/src/dbs.rs b/neon/native/src/dbs.rs new file mode 100644 index 0000000000000000000000000000000000000000..f6097cf7456aa06c259e3f059f029e7ef5470dd5 --- /dev/null +++ b/neon/native/src/dbs.rs @@ -0,0 +1,160 @@ +// 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 crate::into_neon_res; +use dubp_common::crypto::hashs::Hash; +use dubp_common::prelude::*; +use duniter_bc_db::DuniterBcDb; +use duniter_dbs::BlockDbV1; +use neon::declare_types; +use neon::prelude::*; +use std::path::PathBuf; + +pub struct RustDbs { + bc_db: DuniterBcDb, +} + +declare_types! { + pub class JsDbs for RustDbs { + init(mut cx) { + if let Some(arg0) = cx.argument_opt(0) { + if arg0.is_a::<JsString>() { + let home_path = arg0 + .downcast::<JsString>() + .or_throw(&mut cx)? + .value(); + let bc_db = into_neon_res(&mut cx, DuniterBcDb::open(Some(&PathBuf::from(home_path))))?; + Ok(RustDbs { bc_db }) + } else { + cx.throw_type_error("arg0 must be a string") + } + } else { + let bc_db = into_neon_res(&mut cx, DuniterBcDb::open(None))?; + Ok(RustDbs { bc_db }) + } + } + method saveBlock(mut cx) { + let block_js = cx.argument::<JsValue>(0)?; + let block: BlockDbV1 = neon_serde::from_value(&mut cx, block_js)?; + + let this = cx.this(); + let res = { + let guard = cx.lock(); + let rust_db = this.borrow(&guard); + rust_db.bc_db.save_block(block) + }; + match res { + Ok(()) => Ok(cx.undefined().upcast()), + Err(e) => cx.throw_error(format!("{}", e)), + } + } + method saveSideBlock(mut cx) { + let block_js = cx.argument::<JsValue>(0)?; + let block: BlockDbV1 = neon_serde::from_value(&mut cx, block_js)?; + + let this = cx.this(); + let res = { + let guard = cx.lock(); + let rust_db = this.borrow(&guard); + rust_db.bc_db.save_side_block(block) + }; + match res { + Ok(()) => Ok(cx.undefined().upcast()), + Err(e) => cx.throw_error(format!("{}", e)), + } + } + method dropOnForkBlocksAbove(mut cx) { + let block_number = BlockNumber(cx.argument::<JsNumber>(0)?.value() as u32); + + let this = cx.this(); + let res = { + let guard = cx.lock(); + let rust_db = this.borrow(&guard); + rust_db.bc_db.drop_on_fork_blocks_above(block_number) + }; + match res { + Ok(()) => Ok(cx.undefined().upcast()), + Err(e) => cx.throw_error(format!("{}", e)), + } + } + method getAbsoluteBlock(mut cx) { + let block_number = BlockNumber(cx.argument::<JsNumber>(0)?.value() as u32); + let block_hash = cx.argument::<JsString>(1)?.value(); + let block_hash = BlockHash(into_neon_res(&mut cx, Hash::from_hex(&block_hash))?); + + let this = cx.this(); + let res = { + let guard = cx.lock(); + let rust_db = this.borrow(&guard); + rust_db.bc_db.get_absolute_block(Blockstamp { number: block_number, hash: block_hash }) + }; + match res { + Ok(block_opt) => if let Some(block) = block_opt { + Ok(neon_serde::to_value(&mut cx, &block)?) + } else { + Ok(cx.null().upcast()) + }, + Err(e) => cx.throw_error(format!("{}", e)), + } + } + method getBlock(mut cx) { + let block_number = BlockNumber(cx.argument::<JsNumber>(0)?.value() as u32); + + let this = cx.this(); + let res = { + let guard = cx.lock(); + let rust_db = this.borrow(&guard); + rust_db.bc_db.get_block(block_number) + }; + match res { + Ok(block_opt) => if let Some(block) = block_opt { + Ok(neon_serde::to_value(&mut cx, &block)?) + } else { + Ok(cx.null().upcast()) + }, + Err(e) => cx.throw_error(format!("{}", e)), + } + } + method getBlocks(mut cx) { + let start = BlockNumber(cx.argument::<JsNumber>(0)?.value() as u32); + let end = BlockNumber(cx.argument::<JsNumber>(1)?.value() as u32); + + let this = cx.this(); + let res = { + let guard = cx.lock(); + let rust_db = this.borrow(&guard); + rust_db.bc_db.get_blocks(start, end) + }; + match res { + Ok(blocks) => Ok(neon_serde::to_value(&mut cx, &blocks)?), + Err(e) => cx.throw_error(format!("{}", e)), + } + } + method getCountOfBlocksIssuedBy(mut cx) { + let issuer = cx.argument::<JsString>(0)?.value(); + + let this = cx.this(); + let res = { + let guard = cx.lock(); + let rust_db = this.borrow(&guard); + rust_db.bc_db.get_count_of_blocks_issued_by(issuer) + }; + match res { + Ok(count) => Ok(cx.number(count as f64).upcast()), + Err(e) => cx.throw_error(format!("{}", e)), + } + } + } +} diff --git a/neon/native/src/lib.rs b/neon/native/src/lib.rs index 0fde2b572783bb433627466d337bf2dc1e4828a2..e867f58b1a1c6d751d3b70072ec160cc32c827c2 100644 --- a/neon/native/src/lib.rs +++ b/neon/native/src/lib.rs @@ -26,19 +26,21 @@ )] mod crypto; +mod dbs; mod logger; mod transaction; mod wot; use neon::{prelude::*, register_module}; +use std::fmt::Display; -fn into_neon_res<'c, C: Context<'c>, T, S: AsRef<str>>( +fn into_neon_res<'c, C: Context<'c>, T, S: Display>( context: &mut C, rust_result: Result<T, S>, ) -> NeonResult<T> { match rust_result { Ok(value) => Ok(value), - Err(e) => context.throw_error(e), + Err(e) => context.throw_error(format!("{}", e)), } } @@ -51,6 +53,7 @@ register_module!(mut cx, { cx.export_function("sha256", crate::crypto::sha256)?; cx.export_function("verify", crate::crypto::verify)?; cx.export_class::<crate::crypto::JsKeyPair>("Ed25519Signator")?; + cx.export_class::<crate::dbs::JsDbs>("RustDbs")?; cx.export_class::<crate::logger::JsLogger>("RustLogger")?; cx.export_function( "rawTxParseAndVerify", diff --git a/rust-libs/duniter-bc-db/Cargo.toml b/rust-libs/duniter-bc-db/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..b95ed2c92082383aa93eeb9dadddadeab71f582f --- /dev/null +++ b/rust-libs/duniter-bc-db/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "duniter-bc-db" +version = "0.1.0" +authors = ["librelois <elois@duniter.org>"] +license = "AGPL-3.0" +edition = "2018" + +[dependencies] +dubp-common = { version = "0.25.2", features = ["crypto_scrypt"] } +duniter-dbs = { path = "../../rust-libs/duniter-dbs" } +log = "0.4.11" +resiter = "0.4.0" + +[dev-dependencies] +unwrap = "1.2.1" diff --git a/rust-libs/duniter-bc-db/src/lib.rs b/rust-libs/duniter-bc-db/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..5f71f085bcbc14452beb97dc934b958d5325072f --- /dev/null +++ b/rust-libs/duniter-bc-db/src/lib.rs @@ -0,0 +1,286 @@ +// 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/>. + +mod write_block; + +use dubp_common::crypto::hashs::Hash; +use dubp_common::crypto::keys::{ + ed25519::{PublicKey, Signature}, + PublicKey as _, Signature as _, +}; +use dubp_common::prelude::*; +use duniter_dbs::{ + kv_typed::backend::leveldb::{LevelDb, LevelDbConf}, + kv_typed::backend::memory::{Mem, MemConf}, + kv_typed::prelude::*, + AllKeyV1, BcV1Db, BcV1DbReadable, BcV1DbWritable, BlockDbV1, BlockNumberKeyV1, BlockstampKeyV1, + PubKeyAndSigV1, PubKeyKeyV1, +}; +use resiter::filter::Filter; +use std::path::Path; + +pub enum DuniterBcDb { + File(BcV1Db<LevelDb>), + Mem(BcV1Db<Mem>), +} + +#[inline(always)] +fn e(msg: &'static str) -> KvError { + KvError::DeserError(msg.to_owned()) +} + +fn pk(p: &str) -> KvResult<PubKeyKeyV1> { + Ok(PubKeyKeyV1( + PublicKey::from_base58(p).map_err(|_| e("Wrong block"))?, + )) +} + +fn sig(p: &str) -> KvResult<Signature> { + Ok(Signature::from_base64(p).map_err(|_| e("Wrong block"))?) +} + +impl DuniterBcDb { + pub fn open(home_path_opt: Option<&Path>) -> KvResult<Self> { + if let Some(home_path) = home_path_opt { + let bc_db_path = home_path.join("data/leveldb"); + let bc_db: BcV1Db<LevelDb> = BcV1Db::open(LevelDbConf::path(bc_db_path))?; + Ok(DuniterBcDb::File(bc_db)) + } else { + Ok(DuniterBcDb::Mem(BcV1Db::open(MemConf::default())?)) + } + } + + /* + * WRITE BLOCKS + */ + + pub fn save_block(&self, block: BlockDbV1) -> KvResult<()> { + match self { + DuniterBcDb::File(db) => write_block::write_main_block(db, block), + DuniterBcDb::Mem(db) => write_block::write_main_block(db, block), + } + } + pub fn save_side_block(&self, block: BlockDbV1) -> KvResult<()> { + match self { + DuniterBcDb::File(db) => write_block::write_fork_block(db, block), + DuniterBcDb::Mem(db) => write_block::write_fork_block(db, block), + } + } + pub fn drop_on_fork_blocks_above(&self, number: BlockNumber) -> KvResult<()> { + let number = BlockNumberKeyV1(number); + let keys_res: KvResult<Vec<BlockNumberKeyV1>> = match self { + DuniterBcDb::File(db) => db.main_blocks().iter(number..).keys().collect(), + DuniterBcDb::Mem(db) => db.main_blocks().iter(number..).keys().collect(), + }; + for key in keys_res? { + match self { + DuniterBcDb::File(db) => db.main_blocks_write().remove(key)?, + DuniterBcDb::Mem(db) => db.main_blocks_write().remove(key)?, + } + } + Ok(()) + } + + /* + * READ BLOCKS + */ + + pub fn get_absolute_block(&self, blockstamp: Blockstamp) -> KvResult<Option<BlockDbV1>> { + if let Some(block) = self.get_block(blockstamp.number)? { + if block.hash == blockstamp.hash.0.to_hex() { + Ok(Some(block)) + } else { + self.get_fork_block(blockstamp) + } + } else { + self.get_fork_block(blockstamp) + } + } + pub fn get_block(&self, number: BlockNumber) -> KvResult<Option<BlockDbV1>> { + match self { + DuniterBcDb::File(db) => db.main_blocks().get(&BlockNumberKeyV1(number)), + DuniterBcDb::Mem(db) => db.main_blocks().get(&BlockNumberKeyV1(number)), + } + } + pub fn get_blocks(&self, start: BlockNumber, end: BlockNumber) -> KvResult<Vec<BlockDbV1>> { + let start = BlockNumberKeyV1(start); + let end = BlockNumberKeyV1(end); + match self { + DuniterBcDb::File(db) => db.main_blocks().iter(start..=end).values().collect(), + DuniterBcDb::Mem(db) => db.main_blocks().iter(start..=end).values().collect(), + } + } + pub fn get_count_of_blocks_issued_by(&self, issuer: String) -> KvResult<usize> { + Ok(match self { + DuniterBcDb::File(db) => db + .main_blocks() + .iter(..) + .values() + .filter_map(Result::ok) + .filter(|b| b.issuer == issuer) + .count(), + DuniterBcDb::Mem(db) => db + .main_blocks() + .iter(..) + .values() + .filter_map(Result::ok) + .filter(|b| b.issuer == issuer) + .count(), + }) + } + pub fn get_current_block(&self) -> KvResult<Option<BlockDbV1>> { + match self { + DuniterBcDb::File(db) => db.main_blocks().iter(..).values().reverse().next_res(), + DuniterBcDb::Mem(db) => db.main_blocks().iter(..).values().reverse().next_res(), + } + } + pub fn get_fork_block(&self, blockstamp: Blockstamp) -> KvResult<Option<BlockDbV1>> { + match self { + DuniterBcDb::File(db) => db.fork_blocks().get(&BlockstampKeyV1(blockstamp)), + DuniterBcDb::Mem(db) => db.fork_blocks().get(&BlockstampKeyV1(blockstamp)), + } + } + pub fn get_next_fork_blocks(&self, blockstamp: Blockstamp) -> KvResult<Vec<BlockDbV1>> { + let start = BlockstampKeyV1(Blockstamp { + number: BlockNumber(blockstamp.number.0 + 1), + hash: BlockHash(Hash::default()), + }); + let end = BlockstampKeyV1(Blockstamp { + number: BlockNumber(blockstamp.number.0 + 1), + hash: BlockHash(Hash([255u8; 32])), + }); + match self { + DuniterBcDb::File(db) => db + .fork_blocks() + .iter(start..=end) + .values() + .filter_ok(|b| b.previous_hash == Some(blockstamp.hash.0.to_hex())) + .collect(), + DuniterBcDb::Mem(db) => db + .fork_blocks() + .iter(start..=end) + .values() + .filter_ok(|b| b.previous_hash == Some(blockstamp.hash.0.to_hex())) + .collect(), + } + } + pub fn get_potential_fork_blocks( + &self, + number_start: BlockNumber, + median_time_start: u64, + max_number: BlockNumber, + ) -> KvResult<Vec<BlockDbV1>> { + let start = BlockstampKeyV1(Blockstamp { + number: BlockNumber(number_start.0 + 1), + hash: BlockHash(Hash::default()), + }); + let end = BlockstampKeyV1(Blockstamp { + number: BlockNumber(max_number.0 + 1), + hash: BlockHash(Hash([255u8; 32])), + }); + match self { + DuniterBcDb::File(db) => db + .fork_blocks() + .iter(start..=end) + .values() + .filter_ok(|b| b.median_time >= median_time_start) + .collect(), + DuniterBcDb::Mem(db) => db + .fork_blocks() + .iter(start..=end) + .values() + .filter_ok(|b| b.median_time >= median_time_start) + .collect(), + } + } + pub fn get_potential_roots(&self) -> KvResult<Vec<BlockDbV1>> { + let start = BlockstampKeyV1(Blockstamp { + number: BlockNumber(0), + hash: BlockHash(Hash::default()), + }); + let end = BlockstampKeyV1(Blockstamp { + number: BlockNumber(0), + hash: BlockHash(Hash([255u8; 32])), + }); + match self { + DuniterBcDb::File(db) => db.fork_blocks().iter(start..=end).values().collect(), + DuniterBcDb::Mem(db) => db.fork_blocks().iter(start..=end).values().collect(), + } + } + pub fn last_block_of_issuer(&self, issuer: String) -> KvResult<Option<BlockDbV1>> { + match self { + DuniterBcDb::File(db) => db + .main_blocks() + .iter(..) + .values() + .reverse() + .filter_ok(|b| b.issuer == issuer) + .next_res(), + DuniterBcDb::Mem(db) => db + .main_blocks() + .iter(..) + .values() + .reverse() + .filter_ok(|b| b.issuer == issuer) + .next_res(), + } + } +} + +#[cfg(test)] +impl DuniterBcDb { + fn mem(&self) -> &BcV1Db<Mem> { + if let DuniterBcDb::Mem(db) = self { + db + } else { + unreachable!() + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_write_block() -> KvResult<()> { + let db = DuniterBcDb::open(None)?; + + let block = BlockDbV1 { + certifications: vec![ + "J3hNuPirTEoxBQ7Z9gL5NCqQfb8vMGD9YRyhTJKoixAX:12JDJibbgZPRfD6Hoecg8rQRvCR5VYrMnqzfhQYmAr3k".to_owned(), + "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ".to_owned() + ], + hash: "55FBB918864D56922100211DAEF9CB71AC412BE10CC16B3A7AE56E10FB3A7569".to_owned(), + ..Default::default() + }; + assert_eq!(db.mem().fork_blocks().count()?, 0); + db.save_side_block(block.clone())?; + assert_eq!(db.mem().fork_blocks().count()?, 1); + db.save_block(block.clone())?; + assert_eq!(db.mem().fork_blocks().count()?, 0); + + if let DuniterBcDb::Mem(db) = db { + assert_eq!(db.mb_certs().count()?, 5); + assert_eq!( + db.main_blocks().get(&BlockNumberKeyV1(BlockNumber(0)))?, + Some(block) + ); + Ok(()) + } else { + unreachable!() + } + } +} diff --git a/rust-libs/duniter-bc-db/src/write_block.rs b/rust-libs/duniter-bc-db/src/write_block.rs new file mode 100644 index 0000000000000000000000000000000000000000..d60b3b57964d6fabf3ef10e669f61c7dd3d8104e --- /dev/null +++ b/rust-libs/duniter-bc-db/src/write_block.rs @@ -0,0 +1,144 @@ +// 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 crate::*; + +macro_rules! add_block_number { + ($block_key:ident, $db:ident, $K:ty, $k:ident, $col_ro:ident, $col_rw:ident) => { + let mut blocks_numbers = $db.$col_ro().get(&$k)?.unwrap_or_default(); + println!("blocks_numbers={:?} (k={:?})", blocks_numbers, $k); + blocks_numbers.0.push($block_key.0); + $db.$col_rw().upsert($k, blocks_numbers)?; + let key_all = <$K>::all(); + let mut all = $db.$col_ro().get(&key_all)?.unwrap_or_default(); + println!("all={:?}", all); + all.0.push($block_key.0); + $db.$col_rw().upsert(key_all, all)?; + }; +} + +pub(super) fn write_main_block<B: Backend>(db: &BcV1Db<B>, block: BlockDbV1) -> KvResult<()> { + let block_key = BlockNumberKeyV1(BlockNumber(block.number as u32)); + for idty in &block.identities { + let idty_issuer = pk(idty.split(':').next().ok_or_else(|| e("Wrong block"))?)?; + add_block_number!( + block_key, + db, + PubKeyKeyV1, + idty_issuer, + mb_idty, + mb_idty_write + ); + } + for cert in &block.certifications { + let mut cert = cert.split(':'); + let cert_issuer = pk(cert.next().ok_or_else(|| e("Wrong block"))?)?; + add_block_number!( + block_key, + db, + PubKeyKeyV1, + cert_issuer, + mb_certs, + mb_certs_write + ); + let cert_receiver = pk(cert.next().ok_or_else(|| e("Wrong block"))?)?; + let mut blocks_numbers = db.mb_certs().get(&cert_receiver)?.unwrap_or_default(); + blocks_numbers.0.push(block_key.0); + db.mb_certs_write().upsert(cert_receiver, blocks_numbers)?; + } + for joiner in &block.joiners { + let issuer = pk(joiner.split(':').next().ok_or_else(|| e("Wrong block"))?)?; + add_block_number!( + block_key, + db, + PubKeyKeyV1, + issuer, + mb_joiners, + mb_joiners_write + ); + } + for active in &block.actives { + let issuer = pk(active.split(':').next().ok_or_else(|| e("Wrong block"))?)?; + add_block_number!( + block_key, + db, + PubKeyKeyV1, + issuer, + mb_actives, + mb_actives_write + ); + } + for leaver in &block.leavers { + let issuer = pk(leaver.split(':').next().ok_or_else(|| e("Wrong block"))?)?; + add_block_number!( + block_key, + db, + PubKeyKeyV1, + issuer, + mb_leavers, + mb_leavers_write + ); + } + for excluded in &block.excluded { + let issuer = pk(excluded)?; + add_block_number!( + block_key, + db, + PubKeyKeyV1, + issuer, + mb_excluded, + mb_excluded_write + ); + } + for revoked in &block.revoked { + let mut revoked = revoked.split(':'); + let issuer = pk(revoked.next().ok_or_else(|| e("Wrong block"))?)?; + let sig = sig(revoked.next().ok_or_else(|| e("Wrong block"))?)?; + let k = PubKeyAndSigV1(issuer.0, sig); + add_block_number!( + block_key, + db, + PubKeyAndSigV1, + k, + mb_revoked, + mb_revoked_write + ); + } + if block.dividend.is_some() { + let mut all = db.mb_dividends().get(&AllKeyV1)?.unwrap_or_default(); + all.0.push(block_key.0); + db.mb_dividends_write().upsert(AllKeyV1, all)?; + } + if !block.transactions.is_empty() { + let mut all = db.mb_transactions().get(&AllKeyV1)?.unwrap_or_default(); + all.0.push(block_key.0); + db.mb_transactions_write().upsert(AllKeyV1, all)?; + } + let blockstamp = BlockstampKeyV1(Blockstamp { + number: block_key.0, + hash: BlockHash(Hash::from_hex(&block.hash).map_err(|_| e("Wrong block"))?), + }); + db.main_blocks_write().upsert(block_key, block)?; + db.fork_blocks_write().remove(blockstamp)?; + Ok(()) +} + +pub(super) fn write_fork_block<B: Backend>(db: &BcV1Db<B>, block: BlockDbV1) -> KvResult<()> { + let blockstamp = BlockstampKeyV1(Blockstamp { + number: BlockNumber(block.number as u32), + hash: BlockHash(Hash::from_hex(&block.hash).map_err(|_| e("Wrong block"))?), + }); + db.fork_blocks_write().upsert(blockstamp, block) +} diff --git a/rust-libs/duniter-dbs/src/keys/blockstamp.rs b/rust-libs/duniter-dbs/src/keys/blockstamp.rs index 36cf85b1ccb65c075489b82244b5ba498d45e646..0bccbbc49bf5586a20b249aa42bd7b1cb30a33b3 100644 --- a/rust-libs/duniter-dbs/src/keys/blockstamp.rs +++ b/rust-libs/duniter-dbs/src/keys/blockstamp.rs @@ -16,7 +16,7 @@ use crate::*; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)] -pub struct BlockstampKeyV1(Blockstamp); +pub struct BlockstampKeyV1(pub Blockstamp); impl KeyAsBytes for BlockstampKeyV1 { fn as_bytes<T, F: FnMut(&[u8]) -> T>(&self, mut f: F) -> T { diff --git a/rust-libs/duniter-dbs/src/keys/pubkey_and_sig.rs b/rust-libs/duniter-dbs/src/keys/pubkey_and_sig.rs index 65934750d8e4aee2e073d9cc2aa6a876788fd104..8613c359586ba083ad6c14d1cc496f109fb522fb 100644 --- a/rust-libs/duniter-dbs/src/keys/pubkey_and_sig.rs +++ b/rust-libs/duniter-dbs/src/keys/pubkey_and_sig.rs @@ -16,7 +16,7 @@ use crate::*; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct PubKeyAndSigV1(PublicKey, Signature); +pub struct PubKeyAndSigV1(pub PublicKey, pub Signature); impl PubKeyAndSigV1 { pub fn all() -> Self { diff --git a/rust-libs/duniter-dbs/src/values/block_number_array_db.rs b/rust-libs/duniter-dbs/src/values/block_number_array_db.rs index dd4a97937f92f055b886320079cf0ca16280506e..8c4739b7ce5cd4dd77b65ed94ee28dd5fe5b54d4 100644 --- a/rust-libs/duniter-dbs/src/values/block_number_array_db.rs +++ b/rust-libs/duniter-dbs/src/values/block_number_array_db.rs @@ -15,14 +15,14 @@ use crate::*; -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Default, Deserialize, PartialEq, Serialize)] pub struct BlockNumberArrayV1(pub SmallVec<[BlockNumber; 1]>); impl ValueAsBytes for BlockNumberArrayV1 { fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> { let json_string = serde_json::to_string(self).map_err(|e| KvError::DeserError(format!("{}", e)))?; - f(format!("[{}]", json_string).as_bytes()) + f(json_string.as_bytes()) } }