Skip to content
Snippets Groups Projects
Select Git revision
  • 85ef562c49e2f5a2a5d19098282ce375f4d649f6
  • dev default protected
  • vainamoinen197-transactiondocument-replace-vec-fields-by-smallvec-2
  • dvermd/200-keypairs-dewif
  • elois/wot
  • jawaka/155-dbex-add-dump-fork-tree-command
  • elois/195-bcdbwriteop
  • elois/deps-crypto
  • elois/gva-monetary-mass
  • elois/191-sled
  • elois/195
  • ji_emme/gva-humantimefield
  • 184-gva-rename-commontime-field-to-blockchaintime
  • ji_emme/182-gva-implement-block-meta-data
  • ji_emme/rml14
  • hugo/151-ws2pv2-sync
  • ji_emme/181-gva-implement-identity-request
  • ji_emme/89-implement-client-api-gva-graphql-verification-api
  • logo
  • test-juniper-from-schema
  • elois/exemple-gva-global-context
  • v0.2.0-a4 protected
  • v0.2.0-a2 protected
  • v0.2.0-a protected
  • v0.1.1-a1 protected
  • documents/v0.10.0-b1 protected
  • crypto/v0.4.0-b1 protected
  • crypto/v0.3.0-b3 protected
  • crypto/v0.3.0-b2 protected
  • crypto/v0.3.0-b1 protected
  • wot/v0.8.0-a0.9 protected
  • wot/v0.8.0-a0.8 protected
  • 0.1.0-a0.1 protected
  • v0.0.1-a0.12 protected
  • v0.0.1-a0.11 protected
  • v0.0.1-a0.10 protected
  • v0.0.1-a0.9 protected
  • v0.0.1-a0.8 protected
  • v0.0.1-a0.7 protected
  • v0.0.1-a0.6 protected
  • v0.0.1-a0.5 protected
41 results

lib.rs

Blame
  • lib.rs 13.84 KiB
    //  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)
        }
    }