Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 184-gva-rename-commontime-field-to-blockchaintime
  • Kiss/XXX-personal-difficulty
  • dev
  • dvermd/200-keypairs-dewif
  • elois/191-sled
  • elois/195
  • elois/195-bcdbwriteop
  • elois/deps-crypto
  • elois/exemple-gva-global-context
  • elois/gva-monetary-mass
  • elois/kv-parts-db
  • elois/use-rule-engine-example
  • elois/wot
  • elois/ws2pv2
  • hugo/151-ws2pv2-sync
  • jawaka/155-dbex-add-dump-fork-tree-command
  • ji_emme/181-gva-implement-identity-request
  • ji_emme/182-gva-implement-block-meta-data
  • ji_emme/89-implement-client-api-gva-graphql-verification-api
  • ji_emme/gva-humantimefield
  • ji_emme/rml14
  • jonas/141-durs-core-load-conf-from-environment-variables-as-a-priority
  • jonas/dev-gva
  • jonas/wip
  • logo
  • release/0.2
  • test-juniper-from-schema
  • tp/personal-diffi
  • tuxmain/84-add-block-checking-checking-all-rules-of-the-unit-protocol
  • vainamoinen197-transactiondocument-replace-vec-fields-by-smallvec-2
  • vindarel/feat/add-dbex-human-option
  • 0.1.0-a0.1
  • crypto/v0.1.0
  • crypto/v0.1.1
  • crypto/v0.1.2
  • crypto/v0.3.0-b1
  • crypto/v0.3.0-b2
  • crypto/v0.3.0-b3
  • crypto/v0.4.0-b1
  • documents/v0.1.1
  • documents/v0.10.0-b1
  • documents/v0.7.0
  • documents/v0.7.1
  • keys/v0.1.0
  • keys/v0.2.0
  • keys/v0.3.0
  • keys/v0.3.1-deprecated
  • protocol/v0.2.0
  • protocol/v0.3.0
  • protocol/v0.4.0
  • protocol/v0.4.1
  • v0.0.1
  • v0.0.1-a0.1
  • v0.0.1-a0.10
  • v0.0.1-a0.11
  • v0.0.1-a0.12
  • v0.0.1-a0.2
  • v0.0.1-a0.3
  • v0.0.1-a0.4
  • v0.0.1-a0.5
  • v0.0.1-a0.6
  • v0.0.1-a0.7
  • v0.0.1-a0.8
  • v0.0.1-a0.9
  • v0.1.1-a1
  • v0.2.0-a
  • v0.2.0-a2
  • v0.2.0-a4
  • wot/v0.8.0-a0.8
  • wot/v0.8.0-a0.9
  • wotb/0.7.0
  • wotb/0.7.1
  • wotb/v0.4.0
  • wotb/v0.4.1
  • wotb/v0.5.0
  • wotb/v0.6.0
  • wotb/v0.6.1
  • wotb/v0.8.0-a0.2
  • wotb/v0.8.0-a0.3
  • wotb/v0.8.0-a0.4
  • wotb/v0.8.0-a0.5
81 results

Target

Select target project
  • librelois/duniter-rs
  • ji_emme/duniter-rs
  • vindarel/duniter-rs
  • Hiroty1er/duniter-rs
  • dvermd/duniter-rs
  • 666titi999/duniter-rs
6 results
Select Git revision
  • 184-gva-rename-commontime-field-to-blockchaintime
  • Kiss/XXX-personal-difficulty
  • dev
  • dvermd/200-keypairs-dewif
  • elois/191-sled
  • elois/195
  • elois/195-bcdbwriteop
  • elois/deps-crypto
  • elois/exemple-gva-global-context
  • elois/gva-monetary-mass
  • elois/kv-parts-db
  • elois/use-rule-engine-example
  • elois/wot
  • elois/ws2pv2
  • hugo/151-ws2pv2-sync
  • jawaka/155-dbex-add-dump-fork-tree-command
  • ji_emme/181-gva-implement-identity-request
  • ji_emme/182-gva-implement-block-meta-data
  • ji_emme/89-implement-client-api-gva-graphql-verification-api
  • ji_emme/gva-humantimefield
  • ji_emme/rml14
  • jonas/141-durs-core-load-conf-from-environment-variables-as-a-priority
  • jonas/dev-gva
  • jonas/wip
  • logo
  • release/0.2
  • test-juniper-from-schema
  • tp/personal-diffi
  • tuxmain/84-add-block-checking-checking-all-rules-of-the-unit-protocol
  • vainamoinen197-transactiondocument-replace-vec-fields-by-smallvec-2
  • vindarel/feat/add-dbex-human-option
  • 0.1.0-a0.1
  • crypto/v0.1.0
  • crypto/v0.1.1
  • crypto/v0.1.2
  • crypto/v0.3.0-b1
  • crypto/v0.3.0-b2
  • crypto/v0.3.0-b3
  • crypto/v0.4.0-b1
  • documents/v0.1.1
  • documents/v0.10.0-b1
  • documents/v0.7.0
  • documents/v0.7.1
  • keys/v0.1.0
  • keys/v0.2.0
  • keys/v0.3.0
  • keys/v0.3.1-deprecated
  • protocol/v0.2.0
  • protocol/v0.3.0
  • protocol/v0.4.0
  • protocol/v0.4.1
  • v0.0.1
  • v0.0.1-a0.1
  • v0.0.1-a0.10
  • v0.0.1-a0.11
  • v0.0.1-a0.12
  • v0.0.1-a0.2
  • v0.0.1-a0.3
  • v0.0.1-a0.4
  • v0.0.1-a0.5
  • v0.0.1-a0.6
  • v0.0.1-a0.7
  • v0.0.1-a0.8
  • v0.0.1-a0.9
  • v0.1.1-a1
  • v0.2.0-a
  • v0.2.0-a2
  • v0.2.0-a4
  • wot/v0.8.0-a0.8
  • wot/v0.8.0-a0.9
  • wotb/0.7.0
  • wotb/0.7.1
  • wotb/v0.4.0
  • wotb/v0.4.1
  • wotb/v0.5.0
  • wotb/v0.6.0
  • wotb/v0.6.1
  • wotb/v0.8.0-a0.2
  • wotb/v0.8.0-a0.3
  • wotb/v0.8.0-a0.4
  • wotb/v0.8.0-a0.5
81 results
Show changes
Commits on Source (6)
Showing
with 1104 additions and 51 deletions
......@@ -393,12 +393,15 @@ dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"json-pest-parser 0.1.0",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pbr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rules-engine 0.1.0",
"serde 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
"threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unwrap 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
......@@ -1079,6 +1082,15 @@ name = "rprompt"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rules-engine"
version = "0.1.0"
dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rust-crypto-wasm"
version = "0.3.1"
......@@ -1368,6 +1380,11 @@ dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unwrap"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "url"
version = "1.7.2"
......@@ -1583,6 +1600,7 @@ dependencies = [
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum unwrap 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e33648dd74328e622c7be51f3b40a303c63f93e6fa5f08778b6203a4c25c20f"
"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
......
......@@ -18,6 +18,7 @@ members = [
"lib/tools/documents",
"lib/tools/json-pest-parser",
"lib/tools/network-documents",
"lib/tools/rules-engine",
"lib/tests-tools/crypto-tests-tools",
"lib/tests-tools/documents-tests-tools",
"lib/tests-tools/rust-tests-tools",
......
......@@ -23,13 +23,17 @@ duniter-network = { path = "../../../core/network" }
durs-wot = { path = "../../../tools/wot" }
failure = "0.1.5"
json-pest-parser = { path = "../../../tools/json-pest-parser" }
lazy_static = "1.3.0"
log = "0.4.*"
maplit = "1.0.1"
num_cpus = "1.8.*"
pbr = "1.0.*"
rayon = "1.0.3"
rules-engine = { path = "../../../tools/rules-engine" }
serde = "1.0.*"
serde_json = "1.0.*"
threadpool = "1.7.*"
unwrap = "1.2.1"
[dev-dependencies]
dup-crypto-tests-tools = { path = "../../../tests-tools/crypto-tests-tools" }
......
// Copyright (C) 2018 The Duniter Project Developers.
//
// 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/>.
//! Sub-module checking if a block complies with all the rules of the (DUBP DUniter Blockchain Protocol).
mod protocol_versions;
mod rules;
use super::InvalidBlockError;
use crate::dubp::BlockError;
use dubp_documents::documents::block::BlockDocument;
use dubp_documents::*;
use dup_crypto::keys::PubKey;
use durs_blockchain_dal::*;
use durs_wot::*;
pub use rules::InvalidRuleError;
use std::cmp::Ordering;
use std::collections::HashMap;
#[inline]
fn invalid_rule_error(error: InvalidRuleError) -> BlockError {
BlockError::InvalidBlock(InvalidBlockError::InvalidRule(error))
}
pub fn verify_genesis_block_validity(block: &BlockDocument) -> Result<(), BlockError> {
// BR_G03 - previous issuer
if block.previous_issuer != None {
return Err(invalid_rule_error(InvalidRuleError::WrongPreviousIssuer));
}
// BR_G04 - issuers count
if block.issuers_count != 0 {
return Err(invalid_rule_error(InvalidRuleError::WrongIssuersCount));
}
// BR_G05 - issuers count
if block.issuers_frame != 1 {
return Err(invalid_rule_error(InvalidRuleError::WrongIssuersFrame));
}
Ok(())
}
pub fn verify_block_validity<W: WebOfTrust>(
block: &BlockDocument,
blockchain_db: &BinDB<LocalBlockchainV10Datas>,
identities_db: &BinDB<IdentitiesV10Datas>,
_certs_db: &BinDB<CertsExpirV10Datas>,
_wot_index: &HashMap<PubKey, NodeId>,
_wot_db: &BinDB<W>,
) -> Result<(), BlockError> {
// Get previous block
let previous_block_opt = readers::block::get_block_in_local_blockchain(
blockchain_db,
BlockNumber(block.number.0 - 1),
)?;
// Previous block must exist
if previous_block_opt.is_none() {
return Err(BlockError::InvalidBlock(InvalidBlockError::NoPreviousBlock));
}
let previous_block = previous_block_opt.expect("safe unwrap");
// Block version must not decrease
if previous_block.version > block.version {
return Err(BlockError::InvalidBlock(InvalidBlockError::VersionDecrease));
}
// BR_G99 - same currency
if previous_block.currency != block.currency {
return Err(invalid_rule_error(InvalidRuleError::DifferentCurrency));
}
// BR_G03 - previous issuer
if Some(previous_block.issuers[0]) != block.previous_issuer {
return Err(invalid_rule_error(InvalidRuleError::WrongPreviousIssuer));
}
// BR_G100 - issuer is member
match readers::identity::get_identity(identities_db, &block.issuers[0])?
.expect("safe unwrap")
.state
{
entities::identity::DALIdentityState::Member(_) => {}
_ => return Err(invalid_rule_error(InvalidRuleError::NotMemberIssuer)),
}
// BR_G04 - issuers count
let issuers_frame = readers::block::get_current_frame(
&entities::block::DALBlock {
block: block.clone(),
expire_certs: None,
},
blockchain_db,
)?;
if issuers_frame.len() != block.issuers_count {
return Err(invalid_rule_error(InvalidRuleError::WrongIssuersCount));
}
//BR_G05 - issuers frame
if block.issuers_frame
!= previous_block.issuers_frame
+ match previous_block.issuers_frame_var.cmp(&0) {
Ordering::Less => -1,
Ordering::Greater => 1,
Ordering::Equal => 0,
}
{
return Err(invalid_rule_error(InvalidRuleError::WrongIssuersFrame));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use dubp_documents_tests_tools::mocks::*;
use dup_crypto_tests_tools::mocks::*;
use durs_blockchain_dal::entities::block::DALBlock;
use durs_blockchain_dal::entities::identity::*;
#[test]
fn test_verify_blocks() {
let blocks_dbs = BlocksV10DBs::open(None);
let wot_dbs = WotsV10DBs::open(None);
let wot_index: HashMap<PubKey, NodeId> = HashMap::new();
let idty_a = identity::gen_mock_idty(pubkey('A'), BlockNumber(0));
let pubkey_a = idty_a.issuers()[0];
let idty_b = identity::gen_mock_idty(pubkey('B'), BlockNumber(0));
let pubkey_b = idty_b.issuers()[0];
let mut blocks = gen_empty_timed_blocks(2, 300);
// Valid block 0
blocks[0].issuers_frame = 1;
blocks[0].issuers.push(pubkey_a);
blocks[0].identities.push(idty_a.clone());
blocks[0].identities.push(idty_b.clone());
verify_genesis_block_validity(&blocks[0]).expect("Fail to valid genesis block");
blocks_dbs
.blockchain_db
.write(|db| {
db.insert(
blocks[0].number,
DALBlock {
block: blocks[0].clone(),
expire_certs: None,
},
);
})
.expect("Fail write to blockchain db");
wot_dbs
.identities_db
.write(|db| {
db.insert(
pubkey_a,
DALIdentity {
hash: String::new(),
state: DALIdentityState::Member(vec![]),
joined_on: blocks[0].blockstamp(),
expired_on: None,
revoked_on: None,
idty_doc: idty_a,
wot_id: NodeId(0),
ms_created_block_id: blocks[0].number,
ms_chainable_on: vec![0],
cert_chainable_on: vec![0],
},
);
db.insert(
pubkey_b,
DALIdentity {
hash: String::new(),
state: DALIdentityState::Member(vec![]),
joined_on: blocks[0].blockstamp(),
expired_on: None,
revoked_on: None,
idty_doc: idty_b,
wot_id: NodeId(1),
ms_created_block_id: blocks[0].number,
ms_chainable_on: vec![0],
cert_chainable_on: vec![0],
},
);
})
.expect("Fail write to idty db");
// Valid block 1
blocks[1].issuers_count = 1;
blocks[1].issuers_frame = 1;
blocks[1].issuers_frame_var = 5;
blocks[1].issuers.push(pubkey_b);
blocks[1].previous_issuer = Some(pubkey_a);
verify_block_validity(
&blocks[1],
&blocks_dbs.blockchain_db,
&wot_dbs.identities_db,
&wot_dbs.certs_db,
&wot_index,
&wot_dbs.wot_db,
)
.expect("Fail to valid block");
}
}
// Copyright (C) 2018 The Duniter Project Developers.
//
// 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/>.
//! Sub-module manage blockchain protocol versions.
mod v11;
use rules_engine::{Protocol, ProtocolVersion};
#[inline]
pub fn get_blockchain_protocol() -> Protocol {
Protocol::new(btreemap![
ProtocolVersion(11) => v11::get_protocol_rules()
])
}
// Copyright (C) 2018 The Duniter Project Developers.
//
// 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/>.
//! List of rules applied for blockchain protocol V11.
use rules_engine::{ProtocolRules, RulesGroup};
#[inline]
pub fn get_protocol_rules() -> ProtocolRules {
vec![RulesGroup::pr(vec![99usize, 3, 100, 4, 5])].into()
}
// Copyright (C) 2018 The Duniter Project Developers.
//
// 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/>.
//! Sub-module define all rules of blockchain protocol.
use lazy_static::lazy_static;
use super::br_g03;
use super::RuleDatas;
use crate::dubp::check::global::rules::InvalidRuleError;
use rules_engine::rule::{Rule, RuleNumber};
use std::collections::BTreeMap;
lazy_static! {
pub static ref ALL_RULES: BTreeMap<RuleNumber, Rule<RuleDatas<'static>, InvalidRuleError>> =
get_all_rules();
}
#[inline]
pub fn get_all_rules<'a>() -> BTreeMap<RuleNumber, Rule<RuleDatas<'a>, InvalidRuleError>> {
btreemap![
RuleNumber(3) => br_g03::rule(),
]
}
// Copyright (C) 2018 The Duniter Project Developers.
//
// 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/>.
//! Rule BR_G03 of blockchain protocol
use super::{InvalidRuleError, RuleDatas};
use rules_engine::rule::{Rule, RuleFn, RuleFnRef, RuleNumber};
use rules_engine::ProtocolVersion;
use unwrap::unwrap;
#[inline]
pub fn rule<'a>() -> Rule<RuleDatas<'a>, InvalidRuleError> {
unwrap!(Rule::new(
RuleNumber(3),
btreemap![
ProtocolVersion(10) => RuleFn::Ref(v10 as RuleFnRef<RuleDatas, InvalidRuleError>),
]
))
}
fn v10(rule_datas: &RuleDatas) -> Result<(), InvalidRuleError> {
let RuleDatas {
ref block,
ref previous_block,
..
} = rule_datas;
if Some(previous_block.issuers[0]) == block.previous_issuer {
Ok(())
} else {
Err(InvalidRuleError::WrongPreviousIssuer)
}
}
// Copyright (C) 2018 The Duniter Project Developers.
//
// 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/>.
//! Sub-module define rules engine
mod all_rules;
mod br_g03;
use dubp_documents::documents::block::BlockDocument;
use dup_crypto::keys::PubKey;
use durs_blockchain_dal::*;
use durs_wot::*;
use std::collections::HashMap;
#[derive(Debug)]
pub struct RuleDatas<'a> {
block: &'a BlockDocument,
previous_block: &'a BlockDocument,
blockchain_db: BinDB<LocalBlockchainV10Datas>,
wot_dbs: WotsV10DBs,
wot_index: HashMap<PubKey, NodeId>,
current_frame: Option<HashMap<PubKey, usize>>,
}
#[derive(Copy, Clone, Debug, Eq, Fail, PartialEq)]
pub enum InvalidRuleError {
#[fail(display = "BR_G99: different currency")]
DifferentCurrency,
#[fail(display = "BR_G03: wrong previous issuer")]
WrongPreviousIssuer,
#[fail(display = "BR_G100: issuer is not a member")]
NotMemberIssuer,
#[fail(display = "BR_G04: wrong issuers count")]
WrongIssuersCount,
#[fail(display = "BR_G05: wrong issuers frame size")]
WrongIssuersFrame,
}
// Copyright (C) 2018 The Duniter Project Developers.
//
// 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/>.
//! Sub-module checking "local validation" of a block.
pub mod hashs;
......@@ -15,48 +15,17 @@
//! Sub-module checking if a block complies with all the rules of the (DUBP DUniter Blockchain Protocol).
pub mod hashs;
pub mod global;
pub mod local;
use crate::dubp::BlockError;
use dubp_documents::documents::block::BlockDocument;
use dubp_documents::*;
use dup_crypto::keys::PubKey;
use durs_blockchain_dal::*;
use durs_wot::*;
use std::collections::HashMap;
use global::InvalidRuleError;
#[derive(Debug, Copy, Clone)]
#[derive(Fail, Debug, Copy, Clone)]
pub enum InvalidBlockError {
#[fail(display = "Error: not found previous block")]
NoPreviousBlock,
#[fail(display = "Error: version decrease in new block")]
VersionDecrease,
}
pub fn verify_block_validity<W: WebOfTrust>(
block: &BlockDocument,
blockchain_db: &BinDB<LocalBlockchainV10Datas>,
_certs_db: &BinDB<CertsExpirV10Datas>,
_wot_index: &HashMap<PubKey, NodeId>,
_wot_db: &BinDB<W>,
) -> Result<(), BlockError> {
// Rules that do not concern genesis block
if block.number.0 > 0 {
// Get previous block
let previous_block_opt = readers::block::get_block_in_local_blockchain(
blockchain_db,
BlockNumber(block.number.0 - 1),
)?;
// Previous block must exist
if previous_block_opt.is_none() {
return Err(BlockError::InvalidBlock(InvalidBlockError::NoPreviousBlock));
}
let previous_block = previous_block_opt.expect("safe unwrap");
// Block version must not decrease
if previous_block.version > block.version {
return Err(BlockError::InvalidBlock(InvalidBlockError::VersionDecrease));
}
}
Ok(())
#[fail(display = "Error at {}", _0)]
InvalidRule(InvalidRuleError),
}
......@@ -73,7 +73,7 @@ pub fn check_and_apply_block(
)?;
// Verify block hashs
dubp::check::hashs::verify_block_hashs(&block_doc)?;
dubp::check::local::hashs::verify_block_hashs(&block_doc)?;
// Check block chainability
if (block_doc.number.0 == bc.current_blockstamp.id.0 + 1
......@@ -92,13 +92,18 @@ pub fn check_and_apply_block(
)?;
// Verify block validity (check all protocol rule, very long !)
verify_block_validity(
if block_doc.number.0 == 0 {
check::global::verify_genesis_block_validity(&block_doc)?;
} else {
check::global::verify_block_validity(
&block_doc,
&bc.blocks_databases.blockchain_db,
&bc.wot_databases.identities_db,
&bc.wot_databases.certs_db,
&bc.wot_index,
&bc.wot_databases.wot_db,
)?;
}
Ok(CheckAndApplyBlockReturn::ValidBlock(apply_valid_block(
block_doc,
......
......@@ -20,7 +20,6 @@
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
......@@ -28,10 +27,12 @@
unused_qualifications
)]
//#[macro_use]
//extern crate failure;
#[macro_use]
extern crate failure;
#[macro_use]
extern crate log;
#[macro_use]
extern crate maplit;
mod constants;
mod dbex;
......
......@@ -290,7 +290,7 @@ pub fn local_sync<DC: DuniterConf>(profile: &str, conf: &DC, sync_opts: SyncOpt)
// Verify block hashs
let verif_block_hashs_begin = SystemTime::now();
if verif_inner_hash {
dubp::check::hashs::verify_block_hashs(&block_doc)
dubp::check::local::hashs::verify_block_hashs(&block_doc)
.expect("Receive wrong block, please reset data and resync !");
}
all_verif_block_hashs_duration += SystemTime::now()
......
[package]
name = "rules-engine"
version = "0.1.0"
authors = ["librelois <elois@ifee.fr>"]
description = "Rules engine"
license = "AGPL-3.0"
edition = "2018"
[lib]
path = "src/lib.rs"
[dependencies]
failure = "0.1.5"
rayon = "1.0.3"
[dev-dependencies]
maplit = "1.0.1"
// Copyright (C) 2019 É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/>.
//! Rules engine
#![deny(
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces
)]
pub mod rule;
use failure::Fail;
use rayon::prelude::*;
use rule::{Rule, RuleError, RuleNumber};
use std::collections::BTreeMap;
use std::fmt::Debug;
#[derive(Copy, Clone, Debug, Ord, PartialEq, PartialOrd, Eq, Hash)]
pub struct ProtocolVersion(pub usize);
impl std::fmt::Display for ProtocolVersion {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ProtocolRules(pub Vec<RulesGroup>);
impl From<Vec<usize>> for ProtocolRules {
fn from(rules_numbers: Vec<usize>) -> Self {
ProtocolRules(vec![RulesGroup::Ser(
rules_numbers.into_iter().map(RuleNumber).collect(),
)])
}
}
impl From<Vec<RulesGroup>> for ProtocolRules {
fn from(rules_groups: Vec<RulesGroup>) -> Self {
ProtocolRules(rules_groups)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
/// Protocol
pub struct Protocol(BTreeMap<ProtocolVersion, ProtocolRules>);
impl Protocol {
/// Create new protocol
/// protocol_versions: Dictionary of rules to be applied for each version of the protocol (rules will be applied in the order provided)
pub fn new(protocol_versions: BTreeMap<ProtocolVersion, ProtocolRules>) -> Self {
Protocol(protocol_versions)
}
/// Get specific protocol version
pub fn get(&self, protocol_version: ProtocolVersion) -> Option<&ProtocolRules> {
self.0.get(&protocol_version)
}
}
/// Rules groups
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum RulesGroup {
/// In serial
Ser(Vec<RuleNumber>),
/// In parallel
Par(Vec<RulesGroup>),
}
impl RulesGroup {
#[inline]
/// Create singleton rules group
pub fn s1(rule_number: usize) -> Self {
RulesGroup::Ser(vec![RuleNumber(rule_number)])
}
#[inline]
/// Create parallel set of rules
pub fn pr(rules_numbers: Vec<usize>) -> Self {
RulesGroup::Par(rules_numbers.into_iter().map(RulesGroup::s1).collect())
}
}
/// Rules engine
pub struct RulesEngine<D: Debug + Sync, E: Eq + Fail + PartialEq> {
/// All rules
all_rules: BTreeMap<RuleNumber, Rule<D, E>>,
}
impl<D: Debug + Sync, E: Eq + Fail + PartialEq> RulesEngine<D, E> {
/// Create new rules engine
pub fn new(all_rules: BTreeMap<RuleNumber, Rule<D, E>>) -> Self {
RulesEngine { all_rules }
}
fn apply_rules_group_ref(
&self,
protocol_version: ProtocolVersion,
rules_group: RulesGroup,
rule_datas: &D,
) -> Result<(), EngineError<E>> {
match rules_group {
RulesGroup::Ser(rules_numbers) => rules_numbers
.into_iter()
.map(|rule_number| self.apply_rule_ref(protocol_version, rule_number, rule_datas))
.collect(),
RulesGroup::Par(rules_group) => rules_group
.into_par_iter()
.map(|rg| self.apply_rules_group_ref(protocol_version, rg, rule_datas))
.collect(),
}
}
fn apply_rule_ref(
&self,
protocol_version: ProtocolVersion,
rule_number: RuleNumber,
rule_datas: &D,
) -> Result<(), EngineError<E>> {
if let Some(rule) = self.all_rules.get(&rule_number) {
rule.execute(protocol_version, rule_number, rule_datas)
} else {
Err(EngineError::RuleNotExist {
rule_number,
protocol_version,
})
}
}
fn apply_rule_mut(
&self,
protocol_version: ProtocolVersion,
rule_number: RuleNumber,
rule_datas: &mut D,
) -> Result<(), EngineError<E>> {
if let Some(rule) = self.all_rules.get(&rule_number) {
rule.execute_mut(protocol_version, rule_number, rule_datas)
} else {
Err(EngineError::RuleNotExist {
rule_number,
protocol_version,
})
}
}
/// Apply a specific version of the protocol
pub fn apply_protocol(
&self,
protocol: Protocol,
protocol_version: ProtocolVersion,
rule_datas: &mut D,
) -> Result<(), EngineError<E>> {
if let Some(protocol_rules) = protocol.get(protocol_version) {
for rules_group in &protocol_rules.0 {
let result: Result<(), EngineError<E>> = match rules_group {
RulesGroup::Ser(rules_numbers) => rules_numbers
.iter()
.map(|rule_number| {
self.apply_rule_mut(protocol_version, *rule_number, rule_datas)
})
.collect(),
RulesGroup::Par(rules_group) => rules_group
.par_iter()
.map(|rg| {
self.apply_rules_group_ref(protocol_version, rg.clone(), rule_datas)
})
.collect(),
};
if let Err(err) = result {
return Err(err);
}
}
Ok(())
} else {
Err(EngineError::ProtocolVersionNotExist { protocol_version })
}
}
}
/// Protocol error
#[derive(Debug, Eq, Fail, PartialEq)]
pub enum EngineError<E: Eq + Fail + PartialEq> {
#[fail(display = "{}", _0)]
/// Rule Error
RuleError(RuleError<E>),
#[fail(display = "protocol V{} not exist", protocol_version)]
/// The protocol version does not exist
ProtocolVersionNotExist {
/// Protocole version
protocol_version: ProtocolVersion,
},
#[fail(
display = "Rule n°{} not exist (require by protocol V{})",
rule_number, protocol_version
)]
/// A rule required by the protocol version does not exist
RuleNotExist {
/// Rule number
rule_number: RuleNumber,
/// Protocole version
protocol_version: ProtocolVersion,
},
#[fail(
display = "Rule n°{} is mutable and called in parallel in the V{} protocol, this is prohibited.
A rule can be mutable or called in parallel but not both at the same time.",
rule_number, protocol_version
)]
/// Calling a mutable rule in a part executed in parallel
MutRuleInPar {
/// Rule number
rule_number: RuleNumber,
/// Protocole version
protocol_version: ProtocolVersion,
},
#[fail(
display = "Rule n°{} does not exist in a version less than or equal to the protocol version (V{})",
rule_number, protocol_version
)]
/// Calling a rule too recent
RuleTooRecent {
/// Rule number
rule_number: RuleNumber,
/// Protocole version
protocol_version: ProtocolVersion,
},
}
#[cfg(test)]
mod tests {
use super::rule::*;
use super::*;
use maplit::btreemap;
#[derive(Debug)]
struct Datas {
i: usize,
}
#[derive(Debug, Eq, Fail, PartialEq)]
#[fail(display = "")]
struct Error {}
fn r2_v1(datas: &mut Datas) -> Result<(), Error> {
if datas.i == 0 {
datas.i += 1;
Ok(())
} else {
Err(Error {})
}
}
fn r3_v2(datas: &Datas) -> Result<(), Error> {
if datas.i == 1 {
Ok(())
} else {
Err(Error {})
}
}
fn get_test_engine() -> RulesEngine<Datas, Error> {
let all_rules: BTreeMap<RuleNumber, Rule<Datas, Error>> = btreemap![
RuleNumber(2) => Rule::new(RuleNumber(2), btreemap![
ProtocolVersion(1) => RuleFn::RefMut(r2_v1),
]).expect("Fail to create rule n°2"),
RuleNumber(3) => Rule::new(RuleNumber(3), btreemap![
ProtocolVersion(2) => RuleFn::Ref(r3_v2),
]).expect("Fail to create rule n°2"),
];
RulesEngine::new(all_rules)
}
#[test]
fn rule_without_impl() {
if let Err(err) = Rule::<Datas, Error>::new(RuleNumber(1), btreemap![]) {
assert_eq!(
RuleWithoutImpl {
rule_number: RuleNumber(1),
},
err,
)
} else {
panic!("Rule creation must be fail")
}
println!("{}", ProtocolVersion(1));
println!("{}", RuleNumber(1));
}
#[test]
fn protocol_empty() -> Result<(), EngineError<Error>> {
let engine = get_test_engine();
let mut datas = Datas { i: 0 };
let protocol_empty: Protocol = Protocol::new(btreemap![
ProtocolVersion(1) => Vec::<usize>::with_capacity(0).into()
]);
engine.apply_protocol(protocol_empty, ProtocolVersion(1), &mut datas)
}
#[test]
fn protocol_version_not_exist() {
let engine = get_test_engine();
let mut datas = Datas { i: 0 };
let protocol_empty: Protocol = Protocol::new(btreemap![
ProtocolVersion(1) => Vec::<usize>::with_capacity(0).into()
]);
assert_eq!(
Err(EngineError::ProtocolVersionNotExist {
protocol_version: ProtocolVersion(2),
}),
engine.apply_protocol(protocol_empty, ProtocolVersion(2), &mut datas)
)
}
#[test]
fn rule_not_exist() {
let engine = get_test_engine();
let mut datas = Datas { i: 0 };
let protocol: Protocol = Protocol::new(btreemap![
ProtocolVersion(1) => vec![1usize].into()
]);
assert_eq!(
Err(EngineError::RuleNotExist {
rule_number: RuleNumber(1),
protocol_version: ProtocolVersion(1)
}),
engine.apply_protocol(protocol, ProtocolVersion(1), &mut datas)
);
let mut datas = Datas { i: 0 };
let protocol_par: Protocol = Protocol::new(btreemap![
ProtocolVersion(1) => vec![RulesGroup::pr(vec![1usize])].into()
]);
assert_eq!(
Err(EngineError::RuleNotExist {
rule_number: RuleNumber(1),
protocol_version: ProtocolVersion(1)
}),
engine.apply_protocol(protocol_par, ProtocolVersion(1), &mut datas)
);
}
#[test]
fn rule_fail() {
let engine = get_test_engine();
let mut datas = Datas { i: 1 };
let protocol: Protocol = Protocol::new(btreemap![
ProtocolVersion(1) => vec![2usize].into()
]);
assert_eq!(
Err(EngineError::RuleError(RuleError {
rule_number: RuleNumber(2),
cause: Error {},
})),
engine.apply_protocol(protocol, ProtocolVersion(1), &mut datas)
)
}
#[test]
fn par_rule_fail() {
let engine = get_test_engine();
let mut datas = Datas { i: 0 };
let protocol: Protocol = Protocol::new(btreemap![
ProtocolVersion(2) => vec![RulesGroup::pr(vec![3usize])].into()
]);
assert_eq!(
Err(EngineError::RuleError(RuleError {
rule_number: RuleNumber(3),
cause: Error {},
})),
engine.apply_protocol(protocol, ProtocolVersion(2), &mut datas)
)
}
#[test]
fn rule_too_recent() {
let engine = get_test_engine();
let mut datas = Datas { i: 0 };
let protocol: Protocol = Protocol::new(btreemap![
ProtocolVersion(1) => vec![2usize, 3].into()
]);
assert_eq!(
Err(EngineError::RuleTooRecent {
protocol_version: ProtocolVersion(1),
rule_number: RuleNumber(3),
}),
engine.apply_protocol(protocol, ProtocolVersion(1), &mut datas)
)
}
#[test]
fn par_rule_too_recent() {
let engine = get_test_engine();
let mut datas = Datas { i: 0 };
let protocol: Protocol = Protocol::new(btreemap![
ProtocolVersion(1) => vec![RulesGroup::pr(vec![3])].into()
]);
assert_eq!(
Err(EngineError::RuleTooRecent {
protocol_version: ProtocolVersion(1),
rule_number: RuleNumber(3),
}),
engine.apply_protocol(protocol, ProtocolVersion(1), &mut datas)
)
}
#[test]
fn mut_rule_in_par_protocol() {
let engine = get_test_engine();
let mut datas = Datas { i: 1 };
let protocol: Protocol = Protocol::new(btreemap![
ProtocolVersion(2) => vec![RulesGroup::pr(vec![2usize, 3])].into()
]);
assert_eq!(
Err(EngineError::MutRuleInPar {
protocol_version: ProtocolVersion(2),
rule_number: RuleNumber(2),
}),
engine.apply_protocol(protocol, ProtocolVersion(2), &mut datas)
)
}
#[test]
fn protocol_success() -> Result<(), EngineError<Error>> {
let engine = get_test_engine();
let mut datas = Datas { i: 0 };
let protocol: Protocol = Protocol::new(btreemap![
ProtocolVersion(2) => vec![2usize, 3].into()
]);
engine.apply_protocol(protocol, ProtocolVersion(2), &mut datas)
}
}
// Copyright (C) 2019 É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/>.
//! Rules engine : rules
use crate::{EngineError, ProtocolVersion};
use failure::Fail;
use std::collections::BTreeMap;
use std::fmt::Debug;
#[derive(Copy, Clone, Debug, Ord, PartialEq, PartialOrd, Eq, Hash)]
/// Rule number
pub struct RuleNumber(pub usize);
impl std::fmt::Display for RuleNumber {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
/// Rule error
#[derive(Debug, Eq, Fail, PartialEq)]
#[fail(display = "An error occurred with rule n°{} : {}", rule_number, cause)]
pub struct RuleError<E: Eq + Fail + PartialEq> {
/// Rule number
pub rule_number: RuleNumber,
/// Cause of the error
pub cause: E,
}
/// Rule immutable execution function
pub type RuleFnRef<D, E> = fn(&D) -> Result<(), E>;
/// Rule mutable execution function
pub type RuleFnRefMut<D, E> = fn(&mut D) -> Result<(), E>;
/// Rule execution function
pub enum RuleFn<D, E> {
Ref(RuleFnRef<D, E>),
RefMut(RuleFnRefMut<D, E>),
}
#[derive(Debug, Copy, Clone, Eq, Fail, PartialEq)]
#[fail(
display = "Fatal error: rules-engine: try to create rule n°{} without implementation !",
rule_number
)]
pub struct RuleWithoutImpl {
pub rule_number: RuleNumber,
}
/// Rule
pub struct Rule<D: Debug, E: Eq + Fail + PartialEq> {
/// Dictionary of the different versions of the rule execution function
rule_versions: BTreeMap<ProtocolVersion, RuleFn<D, E>>,
}
impl<D: Debug, E: Eq + Fail + PartialEq> Rule<D, E> {
/// Create new rule
pub fn new(
rule_number: RuleNumber,
rule_versions: BTreeMap<ProtocolVersion, RuleFn<D, E>>,
) -> Result<Self, RuleWithoutImpl> {
if rule_versions.is_empty() {
Err(RuleWithoutImpl { rule_number })
} else {
Ok(Rule { rule_versions })
}
}
/// Executes the correct version of the rule
pub fn execute(
&self,
protocol_version: ProtocolVersion,
rule_number: RuleNumber,
rule_datas: &D,
) -> Result<(), EngineError<E>> {
let rule_opt: Option<(&ProtocolVersion, &RuleFn<D, E>)> =
self.rule_versions.range(..=protocol_version).last();
if let Some((_, rule_fn)) = rule_opt {
match rule_fn {
RuleFn::Ref(rule_fn_ref) => rule_fn_ref(rule_datas).map_err(|err| {
EngineError::RuleError(RuleError {
rule_number,
cause: err,
})
}),
RuleFn::RefMut(_) => Err(EngineError::MutRuleInPar {
rule_number,
protocol_version,
}),
}
} else {
Err(EngineError::RuleTooRecent {
rule_number,
protocol_version,
})
}
}
/// Executes the correct version of the rule
pub fn execute_mut(
&self,
protocol_version: ProtocolVersion,
rule_number: RuleNumber,
rule_datas: &mut D,
) -> Result<(), EngineError<E>> {
let rule_opt: Option<(&ProtocolVersion, &RuleFn<D, E>)> =
self.rule_versions.range(..=protocol_version).last();
if let Some((_, rule_fn)) = rule_opt {
match rule_fn {
RuleFn::Ref(rule_fn_ref) => rule_fn_ref(rule_datas),
RuleFn::RefMut(rule_fn_ref_mut) => rule_fn_ref_mut(rule_datas),
}
.map_err(|err| {
EngineError::RuleError(RuleError {
rule_number,
cause: err,
})
})
} else {
Err(EngineError::RuleTooRecent {
rule_number,
protocol_version,
})
}
}
}