Commit b20ee880 authored by Éloïs's avatar Éloïs
Browse files

wip

parent 5e21113b
......@@ -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"
......
......@@ -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"
......
......@@ -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(
......
......@@ -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>;
......
......@@ -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(
......
......@@ -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"
......
// 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)),
}
}
}
}
......@@ -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",
......
[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"
// 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);