Skip to content
Snippets Groups Projects
Select Git revision
  • master
  • 1.6.2 protected
  • 1.6.1 protected
  • 1.6.0 protected
  • 1.5.0 protected
  • 1.4.1 protected
  • 1.4.0 protected
  • 1.3.0 protected
  • 1.2.0 protected
  • 1.1.0 protected
  • 1.0.9 protected
  • 1.0.8 protected
  • 1.0.7 protected
  • 1.0.6 protected
  • 1.0.4 protected
  • 1.0.5 protected
  • 1.0.3 protected
  • 1.0.2 protected
  • 1.0.1 protected
  • 1.0.0 protected
20 results

README.md

Blame
  • current_frame.rs 12.58 KiB
    //  Copyright (C) 2017-2019  The AXIOM TEAM Association.
    //
    // 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/>.
    
    //! Current frame : Interval of blocks taken for the calculation of the personalized difficulty.
    
    //use crate::constants::*;
    use crate::*;
    
    extern crate num;
    
    use crate::current_meta_datas::get_current_blockstamp_;
    use current_meta_datas::CurrentMetaDataKey;
    use dubp_common_doc::BlockNumber;
    use durs_dbs_tools::DbError;
    use durs_wot::WotId;
    use num::Float;
    use rkv::store::integer::IntegerStore;
    use serde::de::DeserializeOwned;
    use serde::{Deserialize, Serialize};
    
    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    /// Describe a member in current frame
    pub struct MemberInCurrentFrame {
        /// Number of blocks forged by the member in the current frame.
        pub forged_blocks: usize,
        /// Personal difficulty of the member.
        pub difficulty: PersonalDifficulty,
    }
    
    #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
    /// Personal difficulty of a member.
    pub struct PersonalDifficulty {
        /// Exclusion factor
        pub exclusion_factor: usize,
        /// handicap
        pub handicap: usize,
    }
    
    impl Default for PersonalDifficulty {
        fn default() -> Self {
            PersonalDifficulty {
                exclusion_factor: 1,
                handicap: 0,
            }
        }
    }
    
    #[derive(Debug, Copy, Clone, Serialize, Deserialize)]
    /// in frame member info data store
    pub struct MemberFrameInfo {
        last_personal_block_number: BlockNumber,
        last_personal_members_in_frame: usize,
        nb_personal_blocks_in_frame: usize,
        personal_difficulty: PersonalDifficulty,
    }
    //impl Default for MemberFrameInfo {
    //    fn default() -> Self {
    //        MemberFrameInfo {
    //            last_personal_block_number: BlockNumber,
    //            last_personal_members_in_frame: usize,
    //            nb_personal_blocks_in_frame: usize,
    //            personal_difficulty: PersonalDifficulty,
    //        }
    //    }
    //}
    
    /// Get current frame datas
    pub fn get_current_frame<DB: DbReadable>(
        _db: &DB,
    ) -> Result<Vec<(WotId, MemberInCurrentFrame)>, DbError> {
        unimplemented!();
    }
    
    // TODO: tester ça
    pub fn get_required_u64_in_int_store<DB: DbReadable, R: DbReader>(
        db: &DB,
        r: &R,
        store_name: &str,
        key: u32,
    ) -> Result<u64, DbError> {
        if let Some(value) = db.get_int_store(store_name).get(r, key)? {
            if let DbValue::U64(value_u64) = value {
                Ok(value_u64)
            } else {
                Err(DbError::DBCorrupted)
            }
        } else {
            Err(DbError::DBCorrupted)
        }
    }
    
    // TODO: tester ça
    //pub fn get_required_bin_in_int_store<DB: DbReadable, R: DbReader, V: DeserializeOwned>(
    //    db: &DB,
    //    r: &R,
    //    store_name: &str,
    //    key: u32,
    //) -> Result<V, DbError> {
    //    if let Some(value) = db.get_int_store(store_name).get(r, key)? {
    //        Ok(BcDbRo::from_db_value(value)?)
    //    } else {
    //        Err(DbError::DBCorrupted)
    //    }
    //}
    // TODO: tester ça
    pub fn get_bin_in_int_store<DB: DbReadable, R: DbReader, V: DeserializeOwned>(
        db: &DB,
        r: &R,
        store_name: &str,
        key: u32,
    ) -> Result<Option<V>, DbError> {
        if let Some(value) = db.get_int_store(store_name).get(r, key)? {
            Ok(Some(BcDbRo::from_db_value(value)?))
        } else {
            Ok(None)
        }
    }
    
    /// Get the personal difficulty of a member.
    /// If the member is not in the current window, returns `pow_min`.
    pub fn get_member_diffi<DB: DbReadable, R: DbReader>(
        db: &DB,
        r: &R,
        wot_id: WotId,
    ) -> Result<PersonalDifficulty, DbError> {
            // si membre absent du store return personnal;
            // difficulty par défaut
            // sinon récupérer le résultat et continuer
            let optional_member_info: Option<MemberFrameInfo> =
                get_bin_in_int_store(db, r, CURRENT_FRAME_MEMBERS, wot_id.0 as u32)?;
            if optional_member_info.is_none() {
                Ok(PersonalDifficulty {
                    exclusion_factor: 1,
                    handicap: 0,
                })
            } else {
                let current_blockstamp =
                    get_current_blockstamp_(db, r)?.ok_or(DbError::DBCorrupted)?;
                let median_frame_member = get_required_u64_in_int_store(
                    db,
                    r,
                    CURRENT_METAS_DATAS,
                    CurrentMetaDataKey::MedianFrameMember.to_u32(),
                )?;
                Ok(PersonalDifficulty {
                    exclusion_factor: 2,
                    handicap: 13,
                })
            }
    }
    
    /// calcule la difficulté personnalisée à partir des données prêtes à l'emploi
    /// reference dans le protocol : https://github.com/duniter/duniter/blob/master/doc/Protocol.md#br_g18---headpowzeros-and-headpowremainder
    pub fn compute_personal_difficulty(
        nb_member_in_frame: usize,
        last_personal_block_number: BlockNumber,
        current_block_number: BlockNumber,
        nb_personal_blocks_in_frame: usize,
        median_of_blocks_in_frame: usize,
    ) -> PersonalDifficulty {
        PersonalDifficulty {
            exclusion_factor: exclusion_factor(
                nb_member_in_frame,
                current_block_number.0 - last_personal_block_number.0,
            ),
            handicap: handicap(nb_personal_blocks_in_frame, median_of_blocks_in_frame),
        }
    }
    
    /// calcule le facteur d'exclusion à partir des membres de la fenêtres courante et du dernier block du membre concerné
    /// ne gère pas le cas où le membre n'est pas dans la fenêtre courante
    ///
    /// nb_member_in_frame : la valeur du champ issuersCount du dernier bloc trouvé par le membre
    /// nb_blocks_since : le nombre de blocs trouvés par le reste du réseau depuis que le membre considéré a trouvé son dernier bloc
    /// exclusion_factor = MAX [ 1 ; FLOOR (0.67 * nb_member_in_frame / (1 + nb_blocks_since)) ]
    ///
    /// reference dans le protocol : https://github.com/duniter/duniter/blob/master/doc/Protocol.md#br_g18---headpowzeros-and-headpowremainder
    pub fn exclusion_factor(nb_member_in_frame: usize, nb_blocks_since: u32) -> usize {
        std::cmp::max(
            1,
            (0.67 * (nb_member_in_frame as f64 / (1 + nb_blocks_since) as f64)).floor() as usize,
        )
    }
    
    /// handicap calcule le handicap d'un membre à partir du nombre de blocs qu'il a caclulé dans la fenêtre courante et du
    /// nombre median de blocs écrits par les membres dans la fenêtre courante.
    /// ne gère pas le cas où le membre n'est pas dans la fenêtre courante
    ///
    /// nb_personal_blocks_in_frame : le nombre de blocs écrits par le membre considéré dans la fenêtre courante
    /// median_of_blocks_in_frame : le nombre médian de blocs écrits par les membres au sein de la fenêtre courante.
    /// handicap = FLOOR(LN(MAX(1;(nb_personal_blocks_in_frame + 1) / median_of_blocks_in_frame)) / LN(1.189))
    ///
    /// reference dans le protocol : https://github.com/duniter/duniter/blob/master/doc/Protocol.md#br_g18---headpowzeros-and-headpowremainder
    pub fn handicap(nb_personal_blocks_in_frame: usize, median_of_blocks_in_frame: usize) -> usize {
        (((std::cmp::max(
            1,
            (nb_personal_blocks_in_frame + 1) / median_of_blocks_in_frame,
        )) as f64)
            .ln()
            / 1.189.ln())
        .floor() as usize
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        use crate::tests::*;
        use durs_dbs_tools::kv_db::KvFileDbHandler;
        use dubp_common_doc::Blockstamp;
    
        //TODO: déplacer la fonction métier dans blockchain ou bc-db-writer
    
        #[test]
        fn test_exclusion_factor() {
            assert_eq!(1, exclusion_factor(1, 0));
            assert_eq!(2, exclusion_factor(15, 4));
        }
    
        #[test]
        fn test_handicap() {
            assert_eq!(13, handicap(500, 50));
            assert_eq!(0, handicap(1, 50));
        }
    
        #[test]
        fn test_default_personal_difficulty() {
            assert_eq!(
                PersonalDifficulty {
                    exclusion_factor: 1,
                    handicap: 0,
                },
                PersonalDifficulty::default()
            );
        }
    
        #[test]
        fn test_compute_personal_difficulty_no_penality() {
            assert_eq!(
                PersonalDifficulty {
                    exclusion_factor: 1,
                    handicap: 0,
                },
                compute_personal_difficulty(100, BlockNumber(1000), BlockNumber(1100), 2, 5)
            );
        }
    
        #[test]
        fn test_compute_personal_difficulty_double_penality() {
            assert_eq!(
                PersonalDifficulty {
                    exclusion_factor: 3,
                    handicap: 10,
                },
                compute_personal_difficulty(50, BlockNumber(1000), BlockNumber(1010), 5, 1)
            );
            assert_eq!(
                PersonalDifficulty {
                    exclusion_factor: 2,
                    handicap: 13,
                },
                compute_personal_difficulty(6, BlockNumber(99), BlockNumber(100), 19, 2)
            );
        }
    
        // test avec db mockée
        fn factory_member_frame_info(
            last_bn: u32,
            last_mf: usize,
            nbr_pbf: usize,
        ) -> Result<Vec<u8>, DbError> {
            durs_dbs_tools::to_bytes(&MemberFrameInfo {
                last_personal_block_number: BlockNumber(last_bn),
                last_personal_members_in_frame: last_mf,
                nb_personal_blocks_in_frame: nbr_pbf,
                personal_difficulty: PersonalDifficulty::default(),
            })
        }
    
        fn put_member_frame_info_in_db(
            db: &KvFileDbHandler,
            w: &mut DbWriter,
            wot_id: u32,
            last_bn: u32,
            last_mf: usize,
            nbr_pbf: usize,
        ) -> Result<(), DbError> {
            db.get_int_store(CURRENT_FRAME_MEMBERS).put(
                w.as_mut(),
                wot_id,
                &DbValue::Blob(&factory_member_frame_info(last_bn, last_mf, nbr_pbf)?),
            )?;
            Ok(())
        }
    
        /// cré un store CURRENT_FRAME_MEMBERS avec comme
        ///  |- clef les wot_id des membres de la fenêtre courante
        ///  |- valeurs :
        ///     |- le block_number du dernier block de ce membre
        ///     |- le nombre de membre dans la fenetre courante au dernier block de ce membre
        ///     |- le nombre de block de ce membre dans la fenêtre courante
        fn init_mocked_db() -> Result<KvFileDbHandler, DbError> {
            let db = open_tmp_db()?;
            db.write(|mut w| {
                put_member_frame_info_in_db(&db, &mut w, 1, 99, 6, 15)?;
                put_member_frame_info_in_db(&db, &mut w, 3, 80, 6, 5)?;
                put_member_frame_info_in_db(&db, &mut w, 4, 97, 6, 3)?;
                put_member_frame_info_in_db(&db, &mut w, 5, 98, 6, 1)?;
                put_member_frame_info_in_db(&db, &mut w, 6, 90, 6, 1)?;
                put_member_frame_info_in_db(&db, &mut w, 8, 71, 6, 1)?;
    
                let blockstamp_bytes: Vec<u8> = Blockstamp{
                    id:BlockNumber(100),
                    ..Default::default()
                }.into();
                db.get_int_store(CURRENT_METAS_DATAS).put(
                    w.as_mut(),
                    CurrentMetaDataKey::CurrentBlockstamp.to_u32(),
                    &DbValue::Blob(&blockstamp_bytes),
                )?;
                //            db.get_int_store(CURRENT_METAS_DATAS).put(
                //                w.as_mut(),
                //                CurrentMetaDataKey::CurrentFrameMembersSize.to_u32(),
                //                &DbValue::U64(6),
                //            )?;
                db.get_int_store(CURRENT_METAS_DATAS).put(
                    w.as_mut(),
                    CurrentMetaDataKey::MedianFrameMember.to_u32(),
                    &DbValue::U64(2),
                )?;
    
                //CurrentBlockNumber : BlockNumber
                //CurrentFrameMembersSize : u32
                //MedianFrameMember : bloc_count
    
                Ok(w)
            })?;
            Ok(db)
        }
    
        #[test]
        fn test_personal_difficulty_member_not_in_frame() -> Result<(), DbError> {
            let db = init_mocked_db()?;
            assert_eq!(
                PersonalDifficulty {
                    exclusion_factor: 1,
                    handicap: 0,
                },
                db.read(|r| get_member_diffi(&db, r, WotId(0)))?
            );
            Ok(())
        }
    
        #[test]
        fn test_personal_difficulty_member_supercalculator() -> Result<(), DbError> {
            let db = init_mocked_db()?;
            assert_eq!(
                PersonalDifficulty {
                    exclusion_factor: 2,
                    handicap: 13,
                },
                db.read(|r| get_member_diffi(&db, r, WotId(1)))?
            );
            Ok(())
        }
    }