...
 
Commits (6)
......@@ -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(
&block_doc,
&bc.blocks_databases.blockchain_db,
&bc.wot_databases.certs_db,
&bc.wot_index,
&bc.wot_databases.wot_db,
)?;
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"
This diff is collapsed.
// 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,
})
}
}
}