From cca4eb30184646af1b178bcaba2723ca00c185a3 Mon Sep 17 00:00:00 2001 From: Hugo Trentesaux <hugo.trentesaux@lilo.org> Date: Sun, 26 Nov 2023 14:36:52 +0100 Subject: [PATCH] Fix identities live tests (nodes/rust/duniter-v2s!146) * update live tests * wip add position of duplicate * wip add counter for owner key * wip add coherence test * wip --- Cargo.lock | 61 +++++++++++++-------- live-tests/Cargo.toml | 3 +- live-tests/README.md | 2 +- live-tests/tests/sanity_gdev.rs | 96 ++++++++++++++++++++++++++++----- 4 files changed, 127 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c1030846..3d78c74f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,7 +210,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" dependencies = [ - "num-traits", + "num-traits 0.2.15", ] [[package]] @@ -259,7 +259,7 @@ dependencies = [ "asn1-rs-impl", "displaydoc", "nom", - "num-traits", + "num-traits 0.2.15", "rusticata-macros", "thiserror", "time 0.3.23", @@ -275,7 +275,7 @@ dependencies = [ "asn1-rs-impl", "displaydoc", "nom", - "num-traits", + "num-traits 0.2.15", "rusticata-macros", "thiserror", "time 0.3.23", @@ -826,7 +826,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-integer", - "num-traits", + "num-traits 0.2.15", "time 0.1.45", "wasm-bindgen", "winapi 0.3.9", @@ -1145,6 +1145,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "countmap" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ef2a403c4af585607826502480ab6e453f320c230ef67255eee21f0cc72c0a6" +dependencies = [ + "num-traits 0.1.43", +] + [[package]] name = "cpp_demangle" version = "0.3.5" @@ -1688,7 +1697,7 @@ dependencies = [ "displaydoc", "nom", "num-bigint", - "num-traits", + "num-traits 0.2.15", "rusticata-macros", ] @@ -1702,7 +1711,7 @@ dependencies = [ "displaydoc", "nom", "num-bigint", - "num-traits", + "num-traits 0.2.15", "rusticata-macros", ] @@ -1868,7 +1877,7 @@ dependencies = [ "flate2", "fnv", "log", - "num-traits", + "num-traits 0.2.15", "parity-scale-codec", "rayon", "simple_logger", @@ -1914,7 +1923,7 @@ dependencies = [ [[package]] name = "duniter" -version = "0.3.0" +version = "0.7.0" dependencies = [ "async-io", "bs58 0.5.0", @@ -2023,6 +2032,7 @@ name = "duniter-live-tests" version = "3.0.0" dependencies = [ "anyhow", + "countmap", "hex-literal", "parity-scale-codec", "sp-core", @@ -2423,7 +2433,7 @@ dependencies = [ "futures 0.3.29", "futures-timer", "log", - "num-traits", + "num-traits 0.2.15", "parity-scale-codec", "parking_lot 0.12.1", "scale-info", @@ -2464,7 +2474,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" dependencies = [ - "num-traits", + "num-traits 0.2.15", ] [[package]] @@ -3889,7 +3899,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" dependencies = [ - "num-traits", + "num-traits 0.2.15", ] [[package]] @@ -5194,7 +5204,7 @@ dependencies = [ "nalgebra-macros", "num-complex", "num-rational", - "num-traits", + "num-traits 0.2.15", "simba", "typenum", ] @@ -5403,7 +5413,7 @@ checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ "autocfg", "num-integer", - "num-traits", + "num-traits 0.2.15", ] [[package]] @@ -5412,7 +5422,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" dependencies = [ - "num-traits", + "num-traits 0.2.15", ] [[package]] @@ -5432,7 +5442,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", - "num-traits", + "num-traits 0.2.15", ] [[package]] @@ -5444,7 +5454,16 @@ dependencies = [ "autocfg", "num-bigint", "num-integer", - "num-traits", + "num-traits 0.2.15", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.15", ] [[package]] @@ -7724,7 +7743,7 @@ dependencies = [ "log", "num-bigint", "num-rational", - "num-traits", + "num-traits 0.2.15", "parity-scale-codec", "parking_lot 0.12.1", "sc-client-api", @@ -8494,7 +8513,7 @@ dependencies = [ "futures-timer", "linked-hash-map", "log", - "num-traits", + "num-traits 0.2.15", "parity-scale-codec", "parking_lot 0.12.1", "sc-client-api", @@ -9038,7 +9057,7 @@ checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" dependencies = [ "approx", "num-complex", - "num-traits", + "num-traits 0.2.15", "paste", "wide", ] @@ -9195,7 +9214,7 @@ version = "6.0.0" source = "git+https://github.com/duniter/substrate?branch=duniter-substrate-v0.9.42#38b19717f847d3eda654b6465802c244ea6372a6" dependencies = [ "integer-sqrt", - "num-traits", + "num-traits 0.2.15", "parity-scale-codec", "scale-info", "serde", @@ -11280,7 +11299,7 @@ dependencies = [ "libm", "memory_units", "num-rational", - "num-traits", + "num-traits 0.2.15", "region", ] diff --git a/live-tests/Cargo.toml b/live-tests/Cargo.toml index 9882e6e1a..d58d98fc2 100644 --- a/live-tests/Cargo.toml +++ b/live-tests/Cargo.toml @@ -15,4 +15,5 @@ parity-scale-codec = "3.4.0" sp-core = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.42', default-features = false } subxt = { git = 'https://github.com/duniter/subxt', branch = 'duniter-substrate-v0.9.42', default-features = false, features = ["jsonrpsee-ws"] } tokio = { version = "1.28", features = ["macros", "time", "rt-multi-thread"], default-features = false } -sp-runtime = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.42', default-features = false , features = ["std"] } # https://github.com/paritytech/subxt/issues/437 \ No newline at end of file +sp-runtime = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.42', default-features = false , features = ["std"] } # https://github.com/paritytech/subxt/issues/437 +countmap = "0.2.0" diff --git a/live-tests/README.md b/live-tests/README.md index 67dce1756..bf13b53ee 100644 --- a/live-tests/README.md +++ b/live-tests/README.md @@ -16,7 +16,7 @@ Test suite that verifies the consistency of the onchain storage. #### Custom RPC endpoint You can choose to use another RPC endpoint by setting the environment variable `WS_RPC_ENDPOINT`. -This is also the only way to test against a different network that the default one. +This is also the only way to test against a different network that the default one which is `ws://localhost:9944`. #### run against a specific block diff --git a/live-tests/tests/sanity_gdev.rs b/live-tests/tests/sanity_gdev.rs index 766db91bd..e16979c77 100644 --- a/live-tests/tests/sanity_gdev.rs +++ b/live-tests/tests/sanity_gdev.rs @@ -17,14 +17,16 @@ #[subxt::subxt(runtime_metadata_path = "../resources/metadata.scale")] pub mod gdev {} +use countmap::CountMap; use hex_literal::hex; use sp_core::crypto::AccountId32; use sp_core::{blake2_128, ByteArray, H256}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use subxt::config::SubstrateConfig as GdevConfig; -const DEFAULT_ENDPOINT: &str = "wss://gdev.librelois.fr:443/ws"; +const DEFAULT_ENDPOINT: &str = "ws://localhost:9944"; +const EXISTENTIAL_DEPOSIT: u64 = 100; const TREASURY_ACCOUNT_ID: [u8; 32] = hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000"); @@ -89,7 +91,7 @@ async fn sanity_tests_at(client: Client, _maybe_block_hash: Option<H256>) -> any account_id_bytes.copy_from_slice(&key.0[48..]); accounts.insert(AccountId32::new(account_id_bytes), account_info); } - println!("accounts: {}.", accounts.len()); + println!("accounts.len(): {}.", accounts.len()); // Collect identities let mut identities: HashMap<IdtyIndex, IdtyValue> = HashMap::new(); @@ -113,7 +115,7 @@ async fn sanity_tests_at(client: Client, _maybe_block_hash: Option<H256>) -> any }; identities.insert(IdtyIndex::from_le_bytes(idty_index_bytes), idty_val); } - println!("identities: {}.", identities.len()); + println!("identities.len(): {}.", identities.len()); // Collect identity_index_of let mut identity_index_of: HashMap<[u8; 16], IdtyIndex> = HashMap::new(); @@ -126,10 +128,10 @@ async fn sanity_tests_at(client: Client, _maybe_block_hash: Option<H256>) -> any .await?; while let Some((key, idty_index)) = idty_index_of_iter.next().await? { let mut blake2_128_bytes = [0u8; 16]; - blake2_128_bytes.copy_from_slice(&key.0[32..]); + blake2_128_bytes.copy_from_slice(&key.0[32..48]); identity_index_of.insert(blake2_128_bytes, idty_index); } - println!("identity_index_of: {}.", identities.len()); + println!("identity_index_of.len(): {}.", identity_index_of.len()); let storage = Storage { accounts, @@ -154,12 +156,17 @@ mod verifier { Self { errors: Vec::new() } } + // FIXME why async functions when called with await? + + /// method to run all storage tests pub(super) async fn verify_storage(&mut self, storage: &Storage) -> anyhow::Result<()> { self.verify_accounts(&storage.accounts).await; self.verify_identities(&storage.accounts, &storage.identities) .await; self.verify_identity_index_of(&storage.identities, &storage.identity_index_of) .await; + self.verify_identity_coherence(&storage.identities, &storage.identity_index_of) + .await; if self.errors.is_empty() { Ok(()) @@ -174,12 +181,19 @@ mod verifier { } } + /// assert method to collect errors fn assert(&mut self, assertion: bool, error: String) { if !assertion { self.errors.push(error); } } + /// like assert but just push error + fn error(&mut self, error: String) { + self.errors.push(error); + } + + /// check accounts sufficients and consumers (specific to duniter-account pallet) async fn verify_accounts(&mut self, accounts: &HashMap<AccountId32, AccountInfo>) { for (account_id, account_info) in accounts { if account_info.sufficients == 0 { @@ -190,7 +204,8 @@ mod verifier { ); // Rule 2: If the account is not sufficient, it should comply to the existential deposit self.assert( - (account_info.data.free + account_info.data.reserved) >= 200, + (account_info.data.free + account_info.data.reserved) + >= EXISTENTIAL_DEPOSIT, format!( "Account {} not respect existential deposit rule.", account_id @@ -198,9 +213,9 @@ mod verifier { ); } - // Rule 3: If the account have consumers, it shoul have at least one provider + // Rule 3: If the account have consumers, it should have at least one provider if account_info.consumers > 0 { - // Rule 1: If the account is not s + // Rule 1: If the account is not sufficient [...] self.assert( account_info.providers > 0, format!("Account {} has no providers nor sufficients.", account_id), @@ -218,12 +233,31 @@ mod verifier { } } + /// check list of identities (account existence, sufficient) async fn verify_identities( &mut self, accounts: &HashMap<AccountId32, AccountInfo>, identities: &HashMap<IdtyIndex, IdtyValue>, ) { + // counts occurence of owner key + let mut countmap = CountMap::<AccountId32, u8>::new(); + // list owner key with multiple occurences + let mut duplicates = HashSet::new(); + for (idty_index, idty_value) in identities { + countmap.insert_or_increment(idty_value.owner_key.clone()); + if let Some(count) = countmap.get_count(&idty_value.owner_key) { + if count > 1 { + self.error(format!( + "address {} is the owner_key of {count} identities", + idty_value.owner_key + )); + if count == 2 { + duplicates.insert(idty_value.owner_key.clone()); + } + } + } + // Rule 1: each identity should have an account let maybe_account = accounts.get(&idty_value.owner_key); self.assert( @@ -244,7 +278,7 @@ mod verifier { match idty_value.status { IdtyStatus::Validated => { - // Rule 3: If the identity is validated, removable_on shoud be zero + // Rule 3: If the identity is validated, removable_on should be zero self.assert( idty_value.removable_on == 0, format!( @@ -254,7 +288,7 @@ mod verifier { ); } _ => { - // Rule 4: If the identity is not validated, next_creatable_identity_on shoud be zero + // Rule 4: If the identity is not validated, next_creatable_identity_on should be zero self.assert( idty_value.next_creatable_identity_on == 0, format!("Identity {} is corrupted: next_creatable_identity_on > 0 on non-validated idty", @@ -263,8 +297,18 @@ mod verifier { } } } + + for (idty_index, idty_value) in identities { + if duplicates.contains(&idty_value.owner_key) { + self.error(format!( + "duplicate key {} at position {idty_index}", + idty_value.owner_key + )); + } + } } + /// check the identity hashmap (length, identity existence, hash matches owner key) async fn verify_identity_index_of( &mut self, identities: &HashMap<IdtyIndex, IdtyValue>, @@ -273,7 +317,11 @@ mod verifier { // Rule1: identity_index_of should have the same lenght as identities self.assert( identities.len() == identity_index_of.len(), - "identities.len() != identity_index_of.len().".to_owned(), + format!( + "identities.len({}) != identity_index_of.len({}).", + identities.len(), + identity_index_of.len() + ), ); for (blake2_128_owner_key, idty_index) in identity_index_of { @@ -301,5 +349,29 @@ mod verifier { } } } + + /// check coherence between identity list and identity index hashmap + async fn verify_identity_coherence( + &mut self, + identities: &HashMap<IdtyIndex, IdtyValue>, + identity_index_of: &HashMap<[u8; 16], IdtyIndex>, + ) { + // each identity should be correcly referenced in the hashmap + for (idty_index, idty_value) in identities { + // hash owner key to get key + let blake2_128_owner_key = &blake2_128(idty_value.owner_key.as_slice()); + + // get identity index from hashmap + if let Some(index_of) = identity_index_of.get(blake2_128_owner_key) { + self.assert(idty_index == index_of, + format!("identity number {idty_index} with owner key {0} is mapped to identity index {index_of}", idty_value.owner_key)); + } else { + self.error(format!( + "identity with owner key {} is not present in hashmap", + idty_value.owner_key + )); + } + } + } } } -- GitLab