From af601c21c0267aa9af68bcf3c41910c0b2bbb862 Mon Sep 17 00:00:00 2001
From: bgallois <benjamin@gallois.cc>
Date: Wed, 21 Feb 2024 15:28:46 +0100
Subject: [PATCH] add live tests for identity status coherence
---
live-tests/tests/sanity_gdev.rs | 108 ++++++++++++++++++++++++++++++--
1 file changed, 102 insertions(+), 6 deletions(-)
diff --git a/live-tests/tests/sanity_gdev.rs b/live-tests/tests/sanity_gdev.rs
index eb4c296f1..d94cc95be 100644
--- a/live-tests/tests/sanity_gdev.rs
+++ b/live-tests/tests/sanity_gdev.rs
@@ -22,7 +22,7 @@
pub mod gdev {}
use countmap::CountMap;
-use hex_literal::hex;
+//use hex_literal::hex;
use sp_core::crypto::AccountId32;
use sp_core::{blake2_128, ByteArray, H256};
use std::collections::{HashMap, HashSet};
@@ -37,8 +37,8 @@ use subxt::ext::sp_core;
const DEFAULT_ENDPOINT: &str = "ws://localhost:9944";
const EXISTENTIAL_DEPOSIT: u64 = 100;
-const TREASURY_ACCOUNT_ID: [u8; 32] =
- hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000");
+//const TREASURY_ACCOUNT_ID: [u8; 32] =
+// hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000");
type Client = subxt::OnlineClient<GdevConfig>;
@@ -57,12 +57,15 @@ type IdtyIndex = u32;
type IdtyValue =
gdev::runtime_types::pallet_identity::types::IdtyValue<BlockNumber, AccountId32, IdtyData>;
type MembershipData = gdev::runtime_types::sp_membership::MembershipData<BlockNumber>;
-// use gdev::runtime_types::pallet_identity::types::IdtyStatus;
+use gdev::runtime_types::pallet_identity::types::IdtyName;
+use gdev::runtime_types::pallet_identity::types::IdtyStatus;
struct Storage {
accounts: HashMap<AccountId32, AccountInfo>,
identities: HashMap<IdtyIndex, IdtyValue>,
identity_index_of: HashMap<[u8; 16], IdtyIndex>,
+ memberships: HashMap<IdtyIndex, MembershipData>,
+ identities_names: HashMap<IdtyIndex, IdtyName>,
}
#[tokio::test(flavor = "current_thread")]
@@ -147,6 +150,21 @@ async fn sanity_tests_at(client: Client, _maybe_block_hash: Option<H256>) -> any
}
println!("identity_index_of.len(): {}.", identity_index_of.len());
+ // Collect identity_names
+ let mut identities_names: HashMap<IdtyIndex, IdtyName> = HashMap::new();
+ let mut idty_name_iter = client
+ .storage()
+ .at_latest()
+ .await
+ .unwrap()
+ .iter(gdev::storage().identity().identities_names_iter())
+ .await?;
+ while let Some(Ok((key, idty_index))) = idty_name_iter.next().await {
+ let name = IdtyName(key);
+ identities_names.insert(idty_index, name);
+ }
+ println!("identities_names.len(): {}.", identities_names.len());
+
// Collect memberships
let mut memberships: HashMap<IdtyIndex, MembershipData> = HashMap::new();
let mut membership_iter = client
@@ -170,6 +188,8 @@ async fn sanity_tests_at(client: Client, _maybe_block_hash: Option<H256>) -> any
accounts,
identities,
identity_index_of,
+ memberships,
+ identities_names,
};
// ===== Verify storage ===== //
@@ -198,6 +218,12 @@ mod verifier {
.await;
self.verify_identity_coherence(&storage.identities, &storage.identity_index_of)
.await;
+ self.verify_status_coherence(
+ &storage.identities,
+ &storage.memberships,
+ &storage.identities_names,
+ )
+ .await;
if self.errors.is_empty() {
Ok(())
@@ -253,14 +279,14 @@ mod verifier {
);
}
- if account_id.as_slice() != TREASURY_ACCOUNT_ID {
+ /*if account_id.as_slice() != TREASURY_ACCOUNT_ID {
// Rule 4: If the account is not a "special account",
// it should have a consumer
self.assert(
account_info.consumers > 0,
format!("Account {} has no consumer.", account_id),
);
- }
+ }*/ //TODO check that is not true anymore
}
}
@@ -360,6 +386,76 @@ mod verifier {
}
}
+ /// check identities status and membership coherence
+ async fn verify_status_coherence(
+ &mut self,
+ identities: &HashMap<IdtyIndex, IdtyValue>,
+ memberships: &HashMap<IdtyIndex, MembershipData>,
+ names: &HashMap<IdtyIndex, IdtyName>,
+ ) {
+ for (idty_index, idty_value) in identities {
+ // Rule 1: each Status::Member should have a membership and a name.
+ if let IdtyStatus::Member = idty_value.status {
+ self.assert(
+ memberships.get(idty_index).is_some(),
+ format!("identity number {idty_index} should have a valid membership"),
+ );
+ self.assert(
+ names.get(idty_index).is_some(),
+ format!("identity number {idty_index} should have a name"),
+ );
+ }
+
+ // Rule 2: each Status::NotMember should have a name but no membership.
+ if let IdtyStatus::NotMember = idty_value.status {
+ self.assert(
+ memberships.get(idty_index).is_none(),
+ format!("identity number {idty_index} should not have a valid membership"),
+ );
+ self.assert(
+ names.get(idty_index).is_some(),
+ format!("identity number {idty_index} should have a name"),
+ );
+ }
+
+ // Rule 3: each Status::Revoke should should have a name but no membership.
+ if let IdtyStatus::Revoked = idty_value.status {
+ self.assert(
+ memberships.get(idty_index).is_none(),
+ format!("identity number {idty_index} should not have a valid membership"),
+ );
+ self.assert(
+ names.get(idty_index).is_some(),
+ format!("identity number {idty_index} should have a name"),
+ );
+ }
+
+ // Rule 4: each Status::Unvalidaded should have a name but no membership.
+ if let IdtyStatus::Unvalidated = idty_value.status {
+ self.assert(
+ memberships.get(idty_index).is_none(),
+ format!("identity number {idty_index} should not have a valid membership"),
+ );
+ self.assert(
+ names.get(idty_index).is_some(),
+ format!("identity number {idty_index} should have a name"),
+ );
+ }
+
+ // Rule 5: each Status::Unconfirmed should not have a name neither a membership.
+ if let IdtyStatus::Unconfirmed = idty_value.status {
+ self.assert(
+ memberships.get(idty_index).is_none(),
+ format!("identity number {idty_index} should not have a valid membership"),
+ );
+ self.assert(
+ names.get(idty_index).is_none(),
+ format!("identity number {idty_index} should not have a name"),
+ );
+ }
+ }
+ }
+
/// check coherence between identity list and identity index hashmap
async fn verify_identity_coherence(
&mut self,
--
GitLab